Initializing memory from a testbench

I am testing a VHDL design using embedded CPU program memory which needs to be initialized. The data to be stored in the RAM comes from an external source and will be part of the configuration download in the final system. During simulation, I can't seem to figure out how to initialize it. I thought I might use the test bench to read the data from a file, but I can't figure out how to access the memory since it does not have an external interface. If I add logic to initialize the memory from the test bench, this will be unused in the real chip and so I will have a difference between my simulated chip and the real chip. I prefer not to do that. I have been using an initial value on the memory variable declaration, but the data changes as I work and this is very clumsy.

I also saw an example using a shared variable with one process being the normal memory model and the other being an init routine. But again, this will use code that is only part of the simulation and should not be there for the end device. I guess the code will not be synthesized, but since it has to be in the target source and not the test bench, it will either need to be removed or it will likely produce errors in synthesis.

Is there a way to directly access an internal signal or variable from a test bench? I seem to recall doing this before, but it was a long time ago and I may be getting a simulator command mixed up with VHDL.

--

Rick "rickman" Collins

rick.collins@XYarius.com
Ignore the reply address. To email me use the above address with the XY
removed.

Arius - A Signal Processing Solutions Company
Specializing in DSP and FPGA design      URL http://www.arius.com
4 King Ave                               301-682-7772 Voice
Frederick, MD 21701-3110                 301-682-7666 FAX
Reply to
rickman
Loading thread data ...

You might model the data block as a vhdl constant array of vectors.

-- Mike Treseler

Reply to
Mike Treseler

Hi,

Normally simulators provide TCL commands to do this, for instance MTI has:

mem load & mem save.

NCSIM has

memory -read memory -dump

I believe VCSMX also has similar command(s). Which simulator do you use?

Again it is simulator dependent, MTI has SignalSpy, NC - NCMirror etc. Some time ago I wrote a simple package to keep the TB code little independent of simulator by having "probe" commands and converting them to target simulators via a package, please see:

formatting link

HTH, Aji

formatting link

Reply to
Ajeetha Kumari

"rickman" escribió en el mensaje news: snipped-for-privacy@yahoo.com...

how about enclosing the initialisation part of the RAM (i guess you use a function that reads a file) between the pragmas "synopsys synthesis_off" and "synopsys synthesis_on"? (if you're initialising thru a function you should avoid synthetising it too in the same way)

and as for accessing an internal signal, i know that you can "up" hierarchy by specifiying it's full "pathname", but i dont know if it works "down" hierarchy, you can always try.

Reply to
roller

Another possible solution is to use perl or bash to convert the external hex file to a deferred constant package body something like:

constant MyRom : arrayT := (

-- begin data x"0000", x"0001", x"0002", x"0004", x"0008", x"0010", x"0020", x"0040"

-- ...

-- end data );

This would give you synthsizable code, but require running the script and a "vcom myPackageBody.vhd" for each iteration.

-- Mike Treseler

Reply to
Mike Treseler

I was trying to do that, but I can't figure out the format for specifying the heiarchy. I looked in the LRM and could not find any references to that. What is used as separators in a "path" name?

--

Rick "rickman" Collins

rick.collins@XYarius.com
Ignore the reply address. To email me use the above address with the XY
removed.

Arius - A Signal Processing Solutions Company
Specializing in DSP and FPGA design      URL http://www.arius.com
4 King Ave                               301-682-7772 Voice
Frederick, MD 21701-3110                 301-682-7666 FAX
Reply to
rickman

hierarchy

it's the dot like in Ada (and Java, or like "use work.package.all") though it's been a long time since i read about it (plus i havent ever used it) maybe you can check the visibility rules or tutorials explaining that, sorry i couldnt help you more

formatting link

Reply to
roller

Thanks for the info. Seems the way I am trying to access the shared variable is called a "selected name". But that will not work outside of the scope of the variable. So I am back to trying to find a way to make life easy in the simulation.

At this point, the external program to generate a deferred constant package seems like it has the best chance of working. It also may do the job for synthesis as well as simulation. But my program memory is not constant, it is read/write, so I may have to do an assignment between them and hope the synthesis tool can figure that out rather than to make two arrays and generate logic to copy them. :)

Thanks again. I will try to remember the 'dot' thing in the future.

--

Rick "rickman" Collins

rick.collins@XYarius.com
Ignore the reply address. To email me use the above address with the XY
removed.

Arius - A Signal Processing Solutions Company
Specializing in DSP and FPGA design      URL http://www.arius.com
4 King Ave                               301-682-7772 Voice
Frederick, MD 21701-3110                 301-682-7666 FAX
Reply to
rickman

This just might do the job. My only concern has to do with the fact that my program memory is actually read/write, so the ram will have to be initialized to this constant data. I don't know if the synthesis tool can understand that this does not require two memories. :)

--

Rick "rickman" Collins

rick.collins@XYarius.com
Ignore the reply address. To email me use the above address with the XY
removed.

Arius - A Signal Processing Solutions Company
Specializing in DSP and FPGA design      URL http://www.arius.com
4 King Ave                               301-682-7772 Voice
Frederick, MD 21701-3110                 301-682-7666 FAX
Reply to
rickman

I wrote one of those a few years back; I've attached the latest version below, along with some sample files:

input object file generated package with constant array generated package with INITs perl 'linker' that creates VHDL source files The perl script reads the assembler object file and coughs up two VHDL files, one a package with a constant array suitable for simulation or inferred RAM, the other a package with INITs and generics for instantiated BRAMs.

At the time I wrote this, XST didn't support indexing into a constant array from within a generate loop; I believe the current XST version would allow putting the INITs into an array, making it easier to support BRAM organizations other than the currently hardcoded 512x32 (four RAMB4_S8_S8's ).

Also, some old posts regarding how to initialize dual port variable width BRAMs (Harvard architecture, 16 bit instruction, 32 bit data) are at:

formatting link
formatting link

Other ROM generation programs:

If you download MikeJ's nifty Asteroids emulation, there's a C++ 8 bit ROM generation utility included .

formatting link

Mike Butts has a hex2init perl script (more elegant than mine) that generates a constraints file for 16 bit memories :

formatting link

Also, I believe the newer versions of Xilinx's DATA2MEM utility will cough up a file with INIT's for primitive simulation as well as update an existing bitstream with new memory data.

Brian

@0 opcode=0000011111110000 opcode=0000011111110000 opcode=0000010111110000 opcode=0000010111110000 opcode=0000010111110000 opcode=0000001111110000 opcode=0000001111110000 opcode=0000001111110000 @200 long=12345678 word=abcd byte=de byte=ad byte=be byte=ef

--

-- Auto-generated by ylink.pl

-- library ieee; use ieee.std_logic_1164.all;

package mem_dat_pkg is

constant MEM_SIZE : natural := 1024;

type mem_type is array (0 to MEM_SIZE-1) of std_logic_vector (15 downto 0);

constant mem_dat : mem_type := ( 0 => X"07F0", 1 => X"07F0", 2 => X"05F0", 3 => X"05F0", 4 => X"05F0", 5 => X"03F0", 6 => X"03F0", 7 => X"03F0", 256 => X"1234", 257 => X"5678", 258 => X"abcd", 259 => X"dead", 260 => X"beef",

others => ( others => '0') );

end mem_dat_pkg;

--

-- Auto-generated by ylink.pl

-- library ieee; use ieee.std_logic_1164.all;

package mem_init_pkg is

constant RAM3_INIT_00 : string := "0000000000000000000000000000000000000000000000000000000003050507"; constant RAM2_INIT_00 : string := "00000000000000000000000000000000000000000000000000000000F0F0F0F0"; constant RAM1_INIT_00 : string := "0000000000000000000000000000000000000000000000000000000003030507"; constant RAM0_INIT_00 : string := "00000000000000000000000000000000000000000000000000000000F0F0F0F0";

constant RAM3_BV_INIT_00 : bit_vector := X"0000000000000000000000000000000000000000000000000000000003050507"; constant RAM2_BV_INIT_00 : bit_vector := X"00000000000000000000000000000000000000000000000000000000F0F0F0F0"; constant RAM1_BV_INIT_00 : bit_vector := X"0000000000000000000000000000000000000000000000000000000003030507"; constant RAM0_BV_INIT_00 : bit_vector := X"00000000000000000000000000000000000000000000000000000000F0F0F0F0";

constant RAM3_INIT_04 : string := "0000000000000000000000000000000000000000000000000000000000beab12"; constant RAM2_INIT_04 : string := "0000000000000000000000000000000000000000000000000000000000efcd34"; constant RAM1_INIT_04 : string := "000000000000000000000000000000000000000000000000000000000000de56"; constant RAM0_INIT_04 : string := "000000000000000000000000000000000000000000000000000000000000ad78";

constant RAM3_BV_INIT_04 : bit_vector := X"0000000000000000000000000000000000000000000000000000000000beab12"; constant RAM2_BV_INIT_04 : bit_vector := X"0000000000000000000000000000000000000000000000000000000000efcd34"; constant RAM1_BV_INIT_04 : bit_vector := X"000000000000000000000000000000000000000000000000000000000000de56"; constant RAM0_BV_INIT_04 : bit_vector := X"000000000000000000000000000000000000000000000000000000000000ad78";

constant RAM3_INIT_0F : string := "0000000000000000000000000000000000000000000000000000000000000000"; constant RAM2_INIT_0F : string := "0000000000000000000000000000000000000000000000000000000000000000"; constant RAM1_INIT_0F : string := "0000000000000000000000000000000000000000000000000000000000000000"; constant RAM0_INIT_0F : string := "0000000000000000000000000000000000000000000000000000000000000000";

constant RAM3_BV_INIT_0F : bit_vector := X"0000000000000000000000000000000000000000000000000000000000000000"; constant RAM2_BV_INIT_0F : bit_vector := X"0000000000000000000000000000000000000000000000000000000000000000"; constant RAM1_BV_INIT_0F : bit_vector := X"0000000000000000000000000000000000000000000000000000000000000000"; constant RAM0_BV_INIT_0F : bit_vector := X"0000000000000000000000000000000000000000000000000000000000000000";

end mem_init_pkg;

#! /usr/local/bin/perl5

#------------------------------------------------------------------------------- # YARD-1 Linker v0.01 #-------------------------------------------------------------------------------

#------------------------------------------------------------------------------- # # ylink.pl v0.01 (C) COPYRIGHT 2001 B. Davis # # This program is free software; you may use, modify, and # redistribute it without restriction provided that: # 1) this header is preserved on all copies # 2) any modified versions are clearly identified as such # # Being free software, this program comes with neither support # nor warranty, including any implied warranty of merchantability # or fitness for a particular purpose. # #-------------------------------------------------------------------------------

#------------------------------------------------------------------------------- # # General Notes: # # - reads .obj file generated by yasm.pl # - writes VHDL file with INIT_XX constants # # currently, more of a memory image generator than a real linker # # - linker output files: # - .vh2 = VHDL file with INIT_XX constants # - .vh1 = VHDL file with constant array holding memory data # - .cmd = YARDBUG command file to load program # # #-------------------------------------------------------------------------------

#------------------------------------------------------------------------------- # # 'to do' list: # # # - add command line args for: # - memory image start/size # - block RAM organization ( generate different INIT_XX records as needed ) # - VHDL output file name and/or package name in VHDL file # # - add other output formats: # - S-record or similar hex format # - EPIC command script for editing block RAM # - .coe for COREGEN created memories # # #-------------------------------------------------------------------------------

#------------------------------------------------------------------------------- # # history: # # Nov. 2001 # - moved VHDL constant array file (.vh1) generation here from yasm.pl # # Jul. 2001 # - added bit vector INIT's for simulation generics ( instead of writing VHDL xlate function ) # # Mar. 2001 # - changed memory image and INIT code to use byte array instead of word array # # Feb. 2001 # - created # # # #-------------------------------------------------------------------------------

printf("\nYARD-1 Linker Version 0.01\n");

# should have one argument, the object file if ($#ARGV != 0) { die ("\nError: Expecting one (and only one) object file \n") };

$obj = $ARGV[0]; $obj =~ s/\.obj$//g;

$obj_file = $obj . '.obj'; $cmd_file = $obj . '.cmd'; $vho_file = $obj . '.vh2'; $vhc_file = $obj . '.vh1';

open (OBJ_F, "$obj_file") or die ("Can't open $obj_file: $!\n"); open (CMD_F, ">$cmd_file") or die ("Can't open $cmd_file: $!\n"); open (VHO_F, ">$vho_file") or die ("Can't open $vho_file: $!\n"); open (VHC_F, ">$vhc_file") or die ("Can't open $vhc_file: $!\n");

# # vhdl INIT output file header/trailer # $vho_file_header = "--

-- Auto-generated by ylink.pl

-- library ieee; use ieee.std_logic_1164.all;

package mem_init_pkg is

";

$vho_file_trailer = "

end mem_init_pkg; ";

# # vhdl constant array output file header/trailer # $vhc_file_header = "--

-- Auto-generated by ylink.pl

-- library ieee; use ieee.std_logic_1164.all;

package mem_dat_pkg is

constant MEM_SIZE : natural := 1024;

type mem_type is array (0 to MEM_SIZE-1) of std_logic_vector (15 downto 0);

constant mem_dat : mem_type := ( ";

$vhc_file_trailer = " others => ( others => '0') );

end mem_dat_pkg;

";

# $D1 turns debug prints on/off $D1 = 0;

# # memory array definitions ( for INIT_XX attribute calculation ) # currently hardcoded for a 2K byte RAM starting at zero # indexed by bytes # values stored as two digit hex string # $mem_init_value = "XX"; $mem_start = 0; $mem_size = 2048;

# # initialize memory image array # $mem_data[$mem_size-1] = $mem_init_value; for($i=0 ; $i < $mem_size; $i++ ) { $mem_data[$i] = $mem_init_value;}

# clear other flags $error = 0;

# # Read .obj file, build memory image # while ($line = ) { chop $line;

if ($line =~ /@(.+)/) { $address = oct ( "0x" . $1 ); }

elsif ($line =~ /opcode=(\d{16})$/) { $op16 = vec(pack("B16", $1),0,16); $dat_str = sprintf "%04X", $op16; $mem_data[ $address - $mem_start ] = substr($dat_str,0,2); $mem_data[ $address - $mem_start + 1 ] = substr($dat_str,2,2); $address = $address + 2; }

elsif ($line =~ /byte=(.{2})$/) { $mem_data[ $address - $mem_start ] = $1; $address = $address + 1; }

elsif($line =~ /word=(.{4})$/) { $mem_data[ $address - $mem_start ] = substr($1,0,2); $mem_data[ $address - $mem_start + 1 ] = substr($1,2,2); $address = $address + 2; }

elsif($line =~ /long=(.{8})$/) { $dat_str = substr($1,0,2); $mem_data[ $address - $mem_start ] = $dat_str;

$dat_str = substr($1,2,2); $mem_data[ $address - $mem_start + 1 ] = $dat_str;

$dat_str = substr($1,4,2); $mem_data[ $address - $mem_start + 2 ] = $dat_str;

$dat_str = substr($1,6,2); $mem_data[ $address - $mem_start + 3 ] = $dat_str;

$address = $address + 4; }

} # end while

# # Write YARDBUG command file # $c_start = -1;

for($i=0 ; $i < $mem_size; $i++ ) { if ( $c_start == -1) { if ( $mem_data[$i] ne "XX" ) { $c_start = $i; printf CMD_F ("M %08X %s ", $i,$mem_data[$i] ); } }

else { if ( $mem_data[$i] ne "XX" ) { printf CMD_F ("%s ", $mem_data[$i]); }

if ( ( $mem_data[$i] eq "XX" ) || ( $i >= ($c_start + 15) ) ) { $c_start = -1; printf CMD_F ("\n" ); } } }

printf CMD_F ("\n" );

# # Write VHDL constant array file #

# create output file boilerplate print VHC_F $vhc_file_header;

# dump memory values to VHDL word array ( dumps only word locations being used ) for($i=0 ; $i < $mem_size; $i+=2 ) { if ( ( $mem_data[$i] ne "XX" ) || ( $mem_data[$i+1] ne "XX" ) ) { printf VHC_F (" %12d => X\"%s%s\", \n", $i >> 1, $mem_data[$i], $mem_data[$i+1] ); } }

# VHDL boilerplate to end memory array print VHC_F $vhc_file_trailer;

# # fill unused locations with zeroes before calculating INIT strings # for($i=0 ; $i < $mem_size; $i++ ) { if ( $mem_data[$i] eq "XX" ) { $mem_data[$i] = "00"; } }

# # Write VHDL blockram init file #

# create output file boilerplate print VHO_F $vho_file_header;

# # spit out INIT_XX strings # - currently hardcoded to read a 2K x 8 memory image array # - writes INIT's for a 512 x 32 block RAM built from four RAMB4_S8_S8's # $init_blks = 16; $init_blk_size = 32; $init_str_size = 64;

for ( $i=0 ; $i < $init_blks; $i++ ) # INIT_00 through INIT_0F { for ( $j=0 ; $j < $init_blk_size; $j++ ) # 32 longs (32 bit) per INIT block { # two hex chars. per word for each x8 BRAM; LSB value = last char in string $str_index = $init_str_size - ( $j*2 ) - 1;

# byte address in memory array $baddr = ( $i * $init_blk_size * 4 ) + ( $j * 4 );

$s3[$str_index-1] = substr($mem_data[$baddr], 0, 1); $s3[$str_index] = substr($mem_data[$baddr], 1, 1);

$s2[$str_index-1] = substr($mem_data[$baddr+1], 0, 1); $s2[$str_index] = substr($mem_data[$baddr+1], 1, 1);

$s1[$str_index-1] = substr($mem_data[$baddr+2], 0, 1); $s1[$str_index] = substr($mem_data[$baddr+2], 1, 1);

$s0[$str_index-1] = substr($mem_data[$baddr+3], 0, 1); $s0[$str_index] = substr($mem_data[$baddr+3], 1, 1);

if ($D1) { printf ("INIT: %d %d %d %d %s %s\n", $i, $j, $str_index, $baddr, $mem_data[$baddr],$mem_data[$baddr+1] ); } if ($D1) { printf ("INIT: %d %d %d %d %s %s\n", $i, $j, $str_index-1, $baddr+1, $mem_data[$baddr+2], $mem_data[$baddr+3] ); } }

printf VHO_F ("constant RAM3_INIT_%02X : string := \"%s\";\n", $i, join('', @s3) ); printf VHO_F ("constant RAM2_INIT_%02X : string := \"%s\";\n", $i, join('', @s2) ); printf VHO_F ("constant RAM1_INIT_%02X : string := \"%s\";\n", $i, join('', @s1) ); printf VHO_F ("constant RAM0_INIT_%02X : string := \"%s\";\n", $i, join('', @s0) ); printf VHO_F ("\n"); printf VHO_F ("constant RAM3_BV_INIT_%02X : bit_vector := X\"%s\";\n", $i, join('', @s3) ); printf VHO_F ("constant RAM2_BV_INIT_%02X : bit_vector := X\"%s\";\n", $i, join('', @s2) ); printf VHO_F ("constant RAM1_BV_INIT_%02X : bit_vector := X\"%s\";\n", $i, join('', @s1) ); printf VHO_F ("constant RAM0_BV_INIT_%02X : bit_vector := X\"%s\";\n", $i, join('', @s0) ); printf VHO_F ("\n");

}

# finish up output file boilerplate print VHO_F $vho_file_trailer;

# tidy up close OBJ_F; close CMD_F; close VHO_F; close VHC_F;

printf("\nTotal Errors = %d\n", $error);

Reply to
Brian Davis

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.