Modeling an external ram VHDL design

E

Enes Erdin

Guest
Hi,

I am trying to simulate my design via ModelSim but my design will use
an external RAM in real environment and I could not manage to
instantiate a model for ram. The actual ram has 19 bits of address bus
and 36 bits of data bus. It is a ZBT ram.

What I tried was creating an signal array of 2^19x36 in the testbench
assuming it as an external ram and use it, however this time "out of
memory" error comes out and my simulation crashes.

Thank in advance for your suggestions.

Regards.

Enes
 
On Wed, 30 Jul 2008 02:18:46 -0700 (PDT), Enes Erdin
<eneserdin@gmail.com> wrote:

Hi,

I am trying to simulate my design via ModelSim but my design will use
an external RAM in real environment and I could not manage to
instantiate a model for ram. The actual ram has 19 bits of address bus
and 36 bits of data bus. It is a ZBT ram.
Some makers of external SRAM publish VHDL models which you can use in
simulation.

For example you could go to this page,
http://www.cypress.com/products/?gid=5
select your memory type, look for "models" under the "related materials"
tab, and pick the one appropriate to the memory you are using. Or search
for your memory's part number; the simulation models will be on the
search result page.

(The site heavily uses Javascript, so I can't just paste a better link)

- Brian
 
Enes Erdin a écrit :
[...]
What I tried was creating an signal array of 2^19x36 in the testbench
assuming it as an external ram and use it, however this time "out of
memory" error comes out and my simulation crashes.
Hi
A VHDL signal is an object that uses an enormous amount of simulator
memory because of all its attribute. Defining such a big signal is not a
very good idea.
If you want to roll your own simulation model instead of using existing
ones as Brian Drummond suggested, here is a first tip : use a variable
instead of a signal for your memory array.

Hope this helps
Nicolas
 
Here are some links:

http://www.opencores.com/projects.cgi/web/single_port/overview

http://www.freemodelfoundry.com/
 
For such a large memory model you might need to use a sparse array which
AFAIK is only supported in Verilog. If you have access to a dual language
license then it is very simple to add this.
you could do two things to reduce memory:
1. As suggested, use variables instead of signals as they use far less
memory.
2. The really elegant design is to use access types and dynamically
create memory as it is used. You could tie this in with an elegant
shared variable. Heres an example:

type mem_block;
type mem_block_ptr is access mem_block;

type mem_block is array(0 to 1023) of std_logic_vector(35 downto 0);
--this defines a small portion (10 bit addressable) of memory

type mem_storage_t is array(0 to 255) of mem_block_ptr;
--this is the entire memory array type

type memory_t is protected
impure function read( addr : std_logic_vector ) return
std_logic_vector;
procedure write(addr : std_logic_vector;
data : std_logic_vector);
end protected memory_t;
--this stores and gives access to an entire memory
--this memory is broken into rows of 1024 words.

type memory_t is protected body

variable mem_storage : mem_storage_t;

impure function read(addr : std_logic_vector ) return
std_logic_vector is
variable row : integer;
variable col : integer;

variable ret_slv : std_logic_vector(35 downto 0);
begin
row := to_integer(unsigned(addr(18 downto 10) ) );
col := to_integer(unsigned(addr(9 downto 0) ) );
--break down the address so you can index into the 2D array

if mem_storage(row) = null then
ret_slv := (others => 'X');
--no memory allocated here yet
else
ret_slv := mem_storage(row)(col);
--get the memory value
end if;

return ret_slv;
end function read;

procedure write(addr : std_logic_vector;
data : std_logic_vector ) is
variable row : integer;
variable col : integer;
begin
row := to_integer(unsigned(addr(18 downto 10) ) );
col := to_integer(unsigned(addr(9 downto 0) ) );

if mem_storage(row) = null then
mem_storage(row) := new mem_block;
--dynamically create some more ram

--initialise all of the memory that were just allocated
for i in 0 to 1023 loop
mem_storage(row)(i) := (others => 'X');
end loop;
end if;

mem_storage(row)(col) := data;
end procedure write;
end protected body memory_t;

--This is the varaible you actually use.
shared variable my_memory : memory_t;


With the above method, memory is not actually allocated until you
write to it, and should save you some if you only access a small part
of it.
 
Tricky wrote:

2. The really elegant design is to use access types and dynamically
create memory as it is used. You could tie this in with an elegant
shared variable. Heres an example:
....

--This is the varaible you actually use.
shared variable my_memory : memory_t;
Cool. Let's try it:
Here's a variable declaration and a test process:
_____________________________________
shared variable my_memory : memory_t;
begin -- architecture mem_demo
p : process is
subtype word_t is std_logic_vector(35 downto 0);
constant all_ones_c : word_t := x"fffffffff";
constant all_zero_c : word_t := x"000000000";
constant pattern1_c : word_t := x"aaaaaaaaa";
constant pattern2_c : word_t := x"555555555";
begin -- process p
my_memory.write(all_zero_c, pattern1_c);
assert my_memory.read( all_zero_c) = pattern1_c;
my_memory.write(all_ones_c, pattern2_c);
assert my_memory.read( all_ones_c) = pattern2_c;

report "Memory test complete. No assertions expected above.";
wait;
end process p;
end architecture mem_demo;
___________________________

Here goes:

vsim -c mem_demo
# Loading work.mem_pkg
# Loading work.mem_demo(mem_demo)
VSIM 1> run
# ** Fatal: (vsim-3421) Value 511 is out of range 0 to 255

Ok, lets try a bigger array:
-- type mem_storage_t is array(0 to 255) of mem_block_ptr;
type mem_storage_t is array(0 to 511) of mem_block_ptr;

Try again:

# vsim -c mem_demo
# Loading work.mem_pkg
# Loading work.mem_demo(mem_demo)
VSIM 1> run
# ** Note: Memory test complete. No assertions expected above.
# Time: 0 ns Iteration: 0 Instance: /mem_demo


Very cool example. Thanks for the posting.

-- Mike Treseler
 
On Thu, 31 Jul 2008 03:47:15 -0700 (PDT), Tricky <Trickyhead@gmail.com>
wrote:

For such a large memory model you might need to use a sparse array which
AFAIK is only supported in Verilog. If you have access to a dual language
license then it is very simple to add this.


you could do two things to reduce memory:
1. As suggested, use variables instead of signals as they use far less
memory.
2. The really elegant design is to use access types and dynamically
create memory as it is used. You could tie this in with an elegant
shared variable. Heres an example:
I believe the Micron models for SDRAM used this sort of technique.
Simulating 1GB would be difficult otherwise...

Thanks for distilling it into a simple example.

- Brian
 
On Thu, 31 Jul 2008 03:47:15 -0700 (PDT), Tricky wrote:

2. The really elegant design is to use access types and dynamically
create memory as it is used. You could tie this in with an elegant
shared variable. Heres an example:
Yes, as others have said: very nice. Thanks.

Note that there is a significant issue with modelling
sparse memory like this: You need to split the address
into "row" and "column" components. Each row points to
one block of memory which may or may not be allocated.
This is cool, but unfortunately it is easily broken by
certain pathological patterns of access to the memory.
To take a simple example, suppose you use it to model
a 1000x1000-pixel video RAM (I know, you wouldn't,
but bear with my example). If a test chooses to
write a vertical line on to that display, you'll
need to allocate one complete row for each written
pixel, even though that's the _only_ pixel in use
in that row. Consequently you end up allocating the
whole memory, merely in order to use only 1/1000th of it.

For more typical access patterns that exhibit some
locality, such as typical instruction fetch activity,
the row-by-row allocation scheme works brilliantly.

If you need truly random access, something based on
a hash table or a B-tree may be better. But the
Iliffe-vector scheme described by Tricky has the
best speed performance of any such scheme I know,
costing only two table lookups for each simulated
memory access. And it's surprisingly easy to code.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
Jonathan Bromley wrote:

If you need truly random access, something based on
a hash table or a B-tree may be better. But the
Iliffe-vector scheme described by Tricky has the
best speed performance of any such scheme I know,
costing only two table lookups for each simulated
memory access. And it's surprisingly easy to code.
And easier now, given a working example of protected types.
I won't miss seeing this one:
** Warning: some_package.vhd(99): (vcom-1236)
Shared variables must be of a protected type.

Thanks for the comments on access patterns.
A hash-table version, would be nice. Hmmmm.

-- Mike Treseler
 
On Fri, 01 Aug 2008 16:12:16 -0700, Mike Treseler wrote:

A hash-table version, would be nice. Hmmmm.
Is that a challenge? :)
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On Fri, 01 Aug 2008 16:12:16 -0700, Mike Treseler wrote:
A hash-table version, would be nice. Hmmmm.
Jonathan Bromley wrote:
Is that a challenge? :)
Just a push on my fun_to_do stack.
Probably too late in the summer to plant a seed ;)

-- Mike Treseler
 

Welcome to EDABoard.com

Sponsor

Back
Top