This website uses cookies. By using this site, you consent to the use of cookies. For more information, please take a look at our Privacy Policy.
Home > FPGA Technical Tutorials > Design Recipes for FPGAs Using Verilog and VHDL > Memory > Synchronous RAM

TABLE OF CONTENTS

Xilinx FPGA FPGA Forum

Synchronous RAM

FONT SIZE : AAA

In the preceding chapter, we observed how the memory is accessed asynchronously, whereas synchronous RAM (SRAM) requires a clock. In most practical designs, the RAM will be implemented off-chip as a separate memory device, but sometimes it is useful to define a small block of RAM on the FPGA for fast access or local storage close to the hardware device that requires frequent access to a relatively small memory block. 

The usual design constraints apply to memory, more so than some other possible functions, as the use of flip-flops to store data without using much of the other logic in a look-up table (LUT) is area intensive. The trade-off, as ever with FPGA design, is whether the potential for improved performance and speed using on-board RAM outweighs the increased area required as a result.

From the design perspective, the synchronous RAM model is very similar to the previously demonstrated basic asynchronous RAM model. The only difference is that, instead of the data being available immediately on the address being applied (or after some short delay), the data in a synchronous RAM is only accessed when the clock edge occurs (rising or falling edge depending on the design required). 

If we consider the VHDL for the SRAM, we can see that for a memory size of 2m and a data bus of 2n, the following model is required. The VHDL model has two parameters, m and n. In the default case, the value of m as 16 provides 64k address words and the number of bits (n) set to 16 gives a total of 1M bits in the RAM. Obviously this could be made any size, but this shows the type of calculation required to obtain the specified memory blocks.

1 library ieee;

2 use ieee.std_logic_1164.all; 3 use ieee.numeric_std.all; 45 entity sram is

6 generic ( 7 m : natural := 16;

8 n : natural := 16

9 );

10 port (

11 clk : in std_logic;

12 addr : in std_logic_vector (m−1 downto 0);

13 wr : in std_logic;

14 d : in std_logic_vector (n−1 downto 0);

15 q : out std_logic_vector (n−1 downto 0)

16 );

17 end entity sram;

18

19 architecture dualport of sram is

20 type sramdata is array (0 to 2∗∗m−1) of std_logic_vector (n−1 downto 0);

21 signal memory : sramdata;

22 begin

23 process (clk ) is

24 begin

25 if rising_edge(clk) then

26 if wr = ’0’ then

27 memory(to_integer(unsigned(addr))) <= d;

28 else

29 q <= memory(to_integer(unsigned(addr)));

30 end if;

31 end if;

32 end process;

33 end architecture dualport;

Notice that there are two control signals, the clock, clk, and the write enable, wr. We could make the memory synchronous write, synchronous read, or a more complex port structure, but in this case, we will show the operation as being synchronous read and write, on the rising edge of the clock. Also, the convention we will use is for the write enable state to be active when wr is low.

There are several interesting aspects to this model that are worth considering. The first is the access of the memory. If we define the address as a std_logic_vector type in VHDL, then we can’t simply use this value to access a specific element of an array. This requires an integer argument. We also cannot simply cast a std_logic_vector type directly to an integer. The first thing we must do is convert the std_logic_vector type to an unsigned number. This is a “halfway house” from std_logic_vector to integer, in that we can use the variable as a number, but it is limited to the same bit resolution as the original std_logic_vector. In this case, clearly this is not an issue as we do not want the address to be larger than the memory, otherwise errors will result. The final step is to convert the unsigned type to an integer. This is accomplished using the to_integer function and is the final step to convert the address into the integer form required to access the individual element of the array. As a consequence of using these numeric functions, we need to also include the IEEE standard numeric library in the header of the model as shown:

1 library ieee;

2 use ieee.std_logic_1164.all; 

3 use ieee.numeric_std.all;

It is also worth noting that the read and write functions are mutually exclusive, in that you cannot read from the memory and write to it at the same time. This ensures the integrity of the data. Also note that the read and write functions are both clocked and so the memory is both read and write synchronous.

We can test this model by using a test bench similar to that used for the previous RAM models as given in the following listing:

1 library ieee;

2 use ieee.std_logic_1164.all; 3 use ieee.numeric_std.all; 45 entity testram is

6 end entity testram;

78 architecture test of testram is

9 signal address : std_logic_vector ( 7 downto 0 );

10 signal clk : std_logic;

11 signal wr : std_logic;

12 signal d : std_logic_vector ( 15 downto 0 );

13 signal q : std_logic_vector ( 15 downto 0 );

14 constant period : time := 5 ns;

15 begin

16

17 sram: entity work.sram(dualport) generic map ( 8, 16) port map( clk, address,

wr, d, q ) ;

18

19 address <= ”00000001” after 0 ns, ”00000010” after 30 ns, ”00000001” after 90

ns;

20 wr <= ’0’ after 0 ns, ’1’ after 90 ns;

21 d <= X”1234” after 0 ns, X”5678” after 40 ns;

22

Figure 11.4 

Basic VHDL SRAM Simulation.

23

24 −− Clock process definitions( clock with 50% duty cycle is generated here.

25 clk_process :process

26 begin

27 clk <= ’0’;

28 wait for period/2;

29 clk <= ’1’;

30 wait for period/2;

31 end process;

32

33 end architecture test;

The results of testing this model can be seen in the waveform diagram in Figure 11.4, which shows the correct behavior of the address, data, and control lines. 

Implementing the SRAM in Verilog is similar to the dual port RAM model earlier in this chapter, with an address, data input, and output ports, but importantly this model is now synchronous and so the management of the data to and from the memory is controlled by the clock signal clk.

1 module sram_verilog ( address, clk, rw, d, q );

2 parameter m = 8; / Address Bus Width

3 parameter n = 16; / Data Bus Width

4 input [m−1:0] address; / Address

5 input clk; / Clock

6 input rw; / Read/Write

7 input [n−1:0] d ; / Data In

8 output [n−1:0] q ; / Data Out

9

10 wire [n−1:0] d;

11 reg [n−1:0] q ; / Data defined as a register

12

13

14 / define the memory array

15 reg [n−1:0] memory [0:2∗∗m−1]; / the memory array is n bits wide (data) and

2∗∗m −1 deep (address)

16

17 always @ (posedge clk)

18 begin

19 if(!rw) begin

20 memory[address] <= d;

21 end

22 else begin

23 q <= memory[address];

24 end

25 end

26 endmodule

The test bench of the model is almost identical to the previous memory test benches; however, the clock is defined in the test bench, and as a result the variables can be seen to be changing at a rate dependent on the clock (in this case the clock changes every 5 ‘ticks’ and the other signals are therefore defined to change every 10 ‘ticks’).

12 module sram_verilog_tb();

3 / declare the counter signals

4 parameter m = 8;

5 parameter n = 16;

6 reg rw;

7 reg clk;

8 reg [m−1:0] address;

9 wire [n−1:0] dataout;

10

11 reg [n−1:0] datain;

12

13 / Set up the initial variables and reset

14 initial begin

15 $display ("time\t rw address data data_set");

16 $monitor ("%g\t %b %d %d %d",

17 $time, rw, address, datain, dataout);

18 rw = 1; / set the rw to 1

19 clk = 0; / INit the clk to 0

20

21 #10 address = 0; / set the row to 1

22 #10 datain = 23;

23 #10 rw = 0; / set the rw to 0

24 #10 address = 1; / set the row to 1

25

26 #10 rw = 1; / set the rw to 1

27

28 / This should have written the data of 23 into location 0:1

29

30

31 #10 address = 0; / set the row to 1

32 #10 datain = 47; / set the data to 47

33 #10 address = 2; / set the row to 2

34 #10 rw = 0; / set the rw to 0

35 #10 rw = 1; / set the rw to 1

36

37 / This should have written the data of 47 into location 0:2

38

39 #10 address = 0; / set the row to 1

40 #10 address = 1; / set the row to 1

41

42

43 #100 $finish; / Finish the simulation

44 end

45

46 / Clock

47 always begin

48 #5 clk = ~clk; / Invert the clock every 5 time ticks

49 end

50

51 sram_verilog RAM ( address, clk, rw, datain, dataout);

52

53 endmodule

The results of testing this model can be seen in the waveform diagram in Figure 11.5, which shows the correct behavior of the address, data, and control lines.

  • XCS30XL-4VQ100C

    Manufacturer:Xilinx

  • FPGA
  • Product Categories: Disjoncteur

    Lifecycle:Obsolete -

    RoHS:

  • XC3090A-7PG175C

    Manufacturer:Xilinx

  • FPGA XC3000 Family 6K Gates 320 Cells 113MHz 5V 175-Pin CPGA
  • Product Categories:

    Lifecycle:Obsolete -

    RoHS: No RoHS

  • XC2V250-4FG256I

    Manufacturer:Xilinx

  • FPGA Virtex-II Family 250K Gates 3456 Cells 650MHz 0.15um Technology 1.5V 256-Pin FBGA
  • Product Categories: FPGAs (Field Programmable Gate Array)

    Lifecycle:Obsolete -

    RoHS:

  • XCV300-5PQG240I

    Manufacturer:Xilinx

  • FPGA Virtex Family 322.97K Gates 6912 Cells 294MHz 0.22um Technology 2.5V 240-Pin PQFP
  • Product Categories:

    Lifecycle:Obsolete -

    RoHS:

  • XC5VLX110T-2FFG1738C

    Manufacturer:Xilinx

  • FPGA Virtex-5 LXT Family 110592 Cells 65nm Technology 1V 1738-Pin FCBGA
  • Product Categories: FPGAs

    Lifecycle:Active Active

    RoHS:

Need Help?

Support

If you have any questions about the product and related issues, Please contact us.