EmbeddedRelated.com
Forums

Write to Block Memory

Started by wluiscam January 5, 2009
Hello everyone,
I'm currently having trouble writing to a dual port block memory generated
with the IP (Core Generator) from WebPack, I'm using the Spartan 3E
development board.  
I generated a dual port block memory intending to use one port for write
only (no read on write) and the other port for read only.  I'm able to read
data when loading an init file (I loaded this file just for testing
purposes) but I'm unsuccessful trying to write new data into the RAM.  The
instructions are really basic but I might be missing something trivial. 
I'd highly appreciate any samples or advise on this issue.
Thanks,

wluiscam wrote:

> I generated a dual port block memory intending to use one port for write > only (no read on write) and the other port for read only. I'm able to read > data when loading an init file (I loaded this file just for testing > purposes) but I'm unsuccessful trying to write new data into the RAM. The > instructions are really basic but I might be missing something trivial.
Did you set write enable to 1 when writing? comp.arch.fpga maybe a better place to ask FPGA questions, maybe with a small example which shows the error. -- Frank Buss, fb@frank-buss.de http://www.frank-buss.de, http://www.it4-systems.de
>wluiscam wrote: > >> I generated a dual port block memory intending to use one port for
write
>> only (no read on write) and the other port for read only. I'm able to
read
>> data when loading an init file (I loaded this file just for testing >> purposes) but I'm unsuccessful trying to write new data into the RAM.
The
>> instructions are really basic but I might be missing something trivial.
> >Did you set write enable to 1 when writing? comp.arch.fpga maybe a
better
>place to ask FPGA questions, maybe with a small example which shows the >error. > >-- >Frank Buss, fb@frank-buss.de >http://www.frank-buss.de, http://www.it4-systems.de >
Frank, Thanks for responding. Yes, I did set write enable to 1 and basically follow the timing diagram on page 11 of the block memory datasheet: http://www.xilinx.com/support/documentation/ip_documentation/dp_block_mem.pdf I extracted the portion of code that writes to memory (read is part of a UART and is working): Thanks, MYMEMORY_1 : mymemory_cis Port map (clka => CLK_A, addra => addressmema, dina => datamemin, wea => writemem, clkb => CLK_B, addrb => addressmemb, doutb => datamemout); ---------------this is a test that copies the value "11110000" ---------------into the first 16000 memory locations memo: process (CLOCK) begin if falling_edge (CLOCK) then --memory update if i > 16000 then writemem <= '0'; else writemem <= '1'; i <= i + 1; end if; if i mod 2 = 0 then if addressmema = 16000 then addressmema <= (others => '0'); else addressmema <= addressmema + 1; datamemin <= "11110000"; end if; end if; end if; end process; process (CLOCK) ------Clock Divider: begin if RISING_EDGE (CLOCK) then CLK_25 <= not (CLK_25); --from 50MHz to 25MHz end if; end process; CLK_A <= CLOCK_25; --memory clock(write)
On Jan 6, 12:55 pm, "wluiscam" <wluis...@gmail.com> wrote:
> > Frank, > Thanks for responding. Yes, I did set write enable to 1 and basically > follow the timing diagram on page 11 of the block memory datasheet:http://www.xilinx.com/support/documentation/ip_documentation/dp_block... > I extracted the portion of code that writes to memory (read is part of a > UART and is working): > Thanks,
Yes, when i < 16001 writemem is '1', but I don't see where i is ever initialized. Also, there are setup and hold time requirements on the signals into the ram. The way you are generating CLK_A you don't know which edge of CLK_A you are changing the data and address on. Am I reading the code correctly? Why don't you clock the test code from the same clock as the memory? Rick
> MYMEMORY_1 : mymemory_cis > Port map (clka => CLK_A, > addra => addressmema, > dina => datamemin, > wea => writemem, > clkb => CLK_B, > addrb => addressmemb, > doutb => datamemout); > > ---------------this is a test that copies the value "11110000" > ---------------into the first 16000 memory locations > memo: process (CLOCK) > begin > if falling_edge (CLOCK) then > --memory update > if i > 16000 then > writemem <= '0'; > else > writemem <= '1'; > i <= i + 1; > end if; > > if i mod 2 = 0 then > if addressmema = 16000 then > addressmema <= (others => '0'); > else > addressmema <= addressmema + 1; > datamemin <= "11110000"; > end if; > end if; > > end if; > > end process; > > process (CLOCK) ------Clock Divider: > begin > if RISING_EDGE (CLOCK) then > CLK_25 <= not (CLK_25); --from 50MHz to 25MHz > end if; > > end process; > > CLK_A <= CLOCK_25; --memory clock(write)
>On Jan 6, 12:55 pm, "wluiscam" <wluis...@gmail.com> wrote: >> >> Frank, >> Thanks for responding. Yes, I did set write enable to 1 and basically >> follow the timing diagram on page 11 of the block memory
datasheet:http://www.xilinx.com/support/documentation/ip_documentation/dp_block...
>> I extracted the portion of code that writes to memory (read is part of
a
>> UART and is working): >> Thanks, > >Yes, when i < 16001 writemem is '1', but I don't see where i is ever >initialized. Also, there are setup and hold time requirements on the >signals into the ram. The way you are generating CLK_A you don't know >which edge of CLK_A you are changing the data and address on. Am I >reading the code correctly? > >Why don't you clock the test code from the same clock as the memory? > >Rick > > >> MYMEMORY_1 : mymemory_cis >> Port map (clka => CLK_A, >> addra => addressmema, >> dina => datamemin, >> wea => writemem, >> clkb => CLK_B, >> addrb => addressmemb, >> doutb => datamemout); >> >> ---------------this is a test that copies the value "11110000" >> ---------------into the first 16000 memory locations >> memo: process (CLOCK) >> begin >> if falling_edge (CLOCK) then >> --memory update >> if i > 16000 then >> writemem <= '0'; >> else >> writemem <= '1'; >> i <= i + 1; >> end if; >> >> if i mod 2 = 0 then >> if addressmema = 16000 then >> addressmema <= (others => '0'); >> else >> addressmema <= addressmema + 1; >> datamemin <= "11110000"; >> end if; >> end if; >> >> end if; >> >> end process; >> >> process (CLOCK) ------Clock Divider: >> begin >> if RISING_EDGE (CLOCK) then >> CLK_25 <= not (CLK_25); --from 50MHz to
25MHz
>> end if; >> >> end process; >> >> CLK_A <= CLOCK_25; --memory clock(write) > >
Rick, Thanks for responding, First, "i" is initialized to zero at the beginning of the program. I run a test bench simulation of the signals and they looked accurate, I couldn't simulate the memory module so I assumed that if the control signals are good, the memory will work but it didn't. Secondly, I could use the same clock (see below) and change address and data during the falling edge so that when the rising edge of the clock comes in it writes the data to memory. I tried it and didn't work. I'm open and welcomed to any suggestions. Thanks, ---------------this is a test that copies the value "11110000" ---------------into the first 16000 memory locations memo: process (CLOCK) begin if falling_edge (CLOCK) then --memory update if i > 16000 then writemem <= '0'; else writemem <= '1'; i <= i + 1; end if; if addressmema = 16000 then addressmema <= (others => '0'); else addressmema <= addressmema + 1; datamemin <= "11110000"; end if; end if; end process; CLK_A <= CLOCK; --memory clock(write)
On Jan 6, 5:44=A0pm, "wluiscam" <wluis...@gmail.com> wrote:
> >On Jan 6, 12:55 pm, "wluiscam" <wluis...@gmail.com> wrote: > > >> Frank, > >> Thanks for responding. Yes, I did set write enable to 1 and basically > >> follow the timing diagram on page 11 of the block memory > > datasheet:http://www.xilinx.com/support/documentation/ip_documentation/dp=
_block... That link doesn't work for me. It just takes me to a page where I can do a search.
> >Why don't you clock the test code from the same clock as the memory? > > >Rick > > >> MYMEMORY_1 : mymemory_cis > >> =A0 =A0 =A0 =A0 =A0Port map (clka =A0 =A0=3D> =A0 =A0 =A0 =A0CLK_A, > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0addra =A0 =3D> =A0 =A0 =A0 =A0a=
ddressmema,
> >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dina =A0 =A0=3D> =A0 =A0 =A0 =
=A0datamemin,
> >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0wea =A0 =A0 =3D> =A0 =A0 =A0 =
=A0writemem,
> >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0clkb =A0 =A0=3D> =A0 =A0 =A0 =
=A0CLK_B,
> >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0addrb =A0 =3D> =A0 =A0 =A0 =A0a=
ddressmemb,
> >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0doutb =A0 =3D> =A0 =A0 =A0 =A0d=
atamemout);
> > >> ---------------this is a test that copies the value "11110000" > >> ---------------into the first 16000 memory locations > >> memo: =A0 process (CLOCK) > >> =A0 =A0 =A0 =A0 begin > >> =A0 =A0 =A0 =A0 if falling_edge (CLOCK) then > >> =A0 =A0 =A0 =A0 =A0 --memory update > >> =A0 =A0 =A0 =A0 =A0 if i > 16000 then > >> =A0 =A0 =A0 =A0 =A0 =A0 writemem <=3D '0'; > >> =A0 =A0 =A0 =A0 =A0 else > >> =A0 =A0 =A0 =A0 =A0 =A0 writemem <=3D '1'; > >> =A0 =A0 =A0 =A0 =A0 =A0 i <=3D i + 1; > >> =A0 =A0 =A0 =A0 =A0 end if; > > >> =A0 =A0 =A0 =A0 =A0 if i mod 2 =3D 0 then > >> =A0 =A0 =A0 =A0 =A0 =A0 if addressmema =3D 16000 then > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0addressmema <=3D (others =3D> '0'); > >> =A0 =A0 =A0 =A0 =A0 =A0 else > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0addressmema <=3D addressmema + 1; > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0datamemin <=3D "11110000"; > >> =A0 =A0 =A0 =A0 =A0 =A0 end if; > >> =A0 =A0 =A0 =A0 =A0 end if; > > >> =A0 =A0 =A0 =A0 end if; > > >> =A0 =A0 =A0 =A0 end process; > > >> process (CLOCK) =A0 ------Clock Divider: > >> begin > >> =A0 =A0 =A0 =A0 if RISING_EDGE (CLOCK) then > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 CLK_25 <=3D not (CLK_25); =A0 =A0 =A0 =
=A0 =A0 =A0 =A0--from 50MHz to
> 25MHz > >> =A0 =A0 =A0 =A0 end if; > > >> end process; > > >> CLK_A =A0 <=3D CLOCK_25; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 --memory clock(write)
> > Rick, > Thanks for responding, > First, "i" is initialized to zero at the beginning of the program. =A0I r=
un
> a test bench simulation of the signals and they looked accurate, I couldn=
't
> simulate the memory module so I assumed that if the control signals are > good, the memory will work but it didn't. > Secondly, I could use the same clock (see below) and change address and > data during the falling edge so that when the rising edge of the clock > comes in it writes the data to memory. =A0I tried it and didn't work. > I'm open and welcomed to any suggestions.
If you are initializing 'i' in the declaration, that is not considered to be universally synthesizable. It may work for XST or whatever you are using at the moment, but it will not work with all tools. Block RAM is synchronous, which will work with the input signals changing on the same edge of the clock as the block RAM. If your design did not work, it was not because of that. Look at the data sheet and you will find that the hold time is very small or even negative while output delays are much larger. This is by design to allow same edge clocking without clock delay problems. When you say "it didn't work", that is not much to go on. Was this in simulation or a real chip? What did it do exactly? What did you see that you didn't expect and what did you see that you did expect?
> ---------------this is a test that copies the value "11110000" > ---------------into the first 16000 memory locations > memo: =A0 process (CLOCK) > =A0 =A0 =A0 =A0 =A0begin > =A0 =A0 =A0 =A0 =A0if falling_edge (CLOCK) then > =A0 =A0 =A0 =A0 =A0 =A0--memory update > =A0 =A0 =A0 =A0 =A0 =A0if i > 16000 then > =A0 =A0 =A0 =A0 =A0 =A0 =A0writemem <=3D '0'; > =A0 =A0 =A0 =A0 =A0 =A0else > =A0 =A0 =A0 =A0 =A0 =A0 =A0writemem <=3D '1'; > =A0 =A0 =A0 =A0 =A0 =A0 =A0i <=3D i + 1; > =A0 =A0 =A0 =A0 =A0 =A0end if; > > =A0 =A0 =A0 =A0 =A0 =A0if addressmema =3D 16000 then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 addressmema <=3D (others =3D> '0'); > =A0 =A0 =A0 =A0 =A0 =A0else > =A0 =A0 =A0 =A0 =A0 =A0 =A0 addressmema <=3D addressmema + 1; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 datamemin <=3D "11110000"; > =A0 =A0 =A0 =A0 =A0 =A0end if; > > =A0 =A0 =A0 =A0 =A0end if; > > =A0 =A0 =A0 =A0 =A0end process; > > CLK_A =A0 <=3D CLOCK; =A0 =A0--memory clock(write)
This design does not give you any more or less setup and hold time that the other one using the half speed clock. Draw a timing diagram and you will see that. Don't make any assumptions about what phase the circuit starts in. Rick
wluiscam wrote:

> Thanks for responding. Yes, I did set write enable to 1 and basically > follow the timing diagram on page 11 of the block memory datasheet: > http://www.xilinx.com/support/documentation/ip_documentation/dp_block_mem.pdf > I extracted the portion of code that writes to memory (read is part of a > UART and is working):
As Rick wrote, maybe the problem is the initiaziation, because it is possible that Xilinx ISE doesn't initialize CLK_25, even if you write it like this: signal CLK_25 : std_logic := '0'; This means, CLK_25 could be X and it is possible, that it doesn't change. I had this problem with Quartus: I initialized a state machine to the second state, but the synthezizer simply ignored it and initialized it always to the first state. Even if it works, it is not a good idea to use the derived CLOCK_25 as a clock signal, because it is a gated clock and can cause problems in larger designs (maybe you can read some warnings about this in ISE while compiling). If you need a slower clock speed, which you don't need for this example, you can use a PLL. For Altera chips it is even possible to generate multiple synchronized clocks with one PLL. I think this is possible with ISE and Xilinx chips, too. So first initialize any signals in a reset section. Then you can implement an internal reset generator in VHDL: A counter (hopefully initialized to 0) counts to some low value and sets reset to 1 while counting. This works for my designs. Then simulate the design. Xilinx ISE has a fast simulator integrated, which compiles exe files for a simulation. The simplest form would be to create a waveform file, which simulates the external inputs and take a look at the outputs and internal registers. But it depends on the simulator, if this is possible, e.g. with Quartus you can't see any internal registers in the simulator, but with the free Modelsim for Quartus you can even set breakpoints in the VHDL code. Many simulators simulates BRAM, too and you can read and write it from a testbench or inspect it within the running simulator. For more complex designs it is a good idea to use a VHDL testbench, for better simulation of the input pins and automatic test of the output pins, e.g. see ds2432_testbench.vhd in http://www.frank-buss.de/vhdl/spartan3e.zip . Note: The integrated Altera Quartus simulator can't use testbench like this, because of the "wait for" statements, but it should work with Modelsim and with the integrated Xilinx ISE simulator. If the simulation works, but you still don't see the expected behaviour, try to route the CLK_25 signal to a pin and scope it. If it works, but the BRAM is still not written, maybe you've found a bug and you can try to submit a webcase to Xilinx. Both, Altera and Xilinx, have a good support, even for the free Web-editions (maybe this is one reason for high price of the non-free editions :-) -- Frank Buss, fb@frank-buss.de http://www.frank-buss.de, http://www.it4-systems.de