TABLE OF CONTENTS (HIDE)

Memory Modules Implemented with Verilog HDL

Memory Types

  • Asynchronous memory

    Use Look-Up Tables - We do not recommend to use it for the large memory implementation

  • Synchronous memory
    • Register input address
    • Register output data
    • Register both input address and output data

    Use Block Memory. Notes:

    • Registering either input or output results in a one-clock-cycle delay.
    • Registering both input and output results in a two-clock-cycle delay.
    • Tested in Altera Quartus II. Some codes below may use Look-Up Tables in Xilinx.
a-sync-mem.svg

Asynchronous Memory

1
2
3
4
5
6
7
8
9
10
11
12
13
module async_single_port_ram(
   input      [6:0] data,
   input     [12:0] addr,
   input            we, clk,
   output     [6:0] q);
   reg        [6:0] ram[0:4799];
   always @ (posedge clk)
     begin
        if (we)
           ram[addr] <= data;
     end
   assign q = ram[addr];         // read with input address directly
endmodule

Synchronous Memory - Register Input - Single port

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module  sync_single_port_ram_input_reg(
   input      [6:0] data,
   input     [12:0] addr,
   input            we, clk,
   output     [6:0] q);
   reg        [6:0] ram[0:4799];
   reg       [12:0] addr_reg;
   always @ (posedge clk)
     begin
        if (we)
           ram[addr] <= data;
        addr_reg <= addr;        // register address - input
     end
   assign q = ram[addr_reg];     // read with registered address
endmodule

Synchronous Memory - Register Output - Single port

1
2
3
4
5
6
7
8
9
10
11
12
13
module  sync_single_port_ram_output_reg(
   input      [6:0] data,
   input     [12:0] addr,
   input            we, clk,
   output reg [6:0] q);
   reg        [6:0] ram[0:4799];
   always @ (posedge clk)
     begin
        if (we)
           ram[addr] <= data;
        q <= ram[addr];          // register output data - output
     end
endmodule

Synchronous Memory - Register Input and Output - Single port, Single-Clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module  sync_single_port_ram_single_clock_input_output_reg(
   input      [6:0] data,
   input     [12:0] addr,
   input            we, clk,
   output reg [6:0] q);
   reg        [6:0] ram[0:4799];
   reg       [12:0] addr_reg;
   always @ (posedge clk)
     begin
        if (we)
           ram[addr] <= data;
        addr_reg <= addr;        // register address - input
        q <= ram[addr_reg];      // register output data - output
     end
endmodule

Synchronous Memory - Register Input and Output - Single port, Dual-Clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module  sync_single_port_ram_dual_clock_input_output_reg(
   input      [6:0] data,
   input     [12:0] addr,
   input            we, in_clk, out_clk,
   output reg [6:0] q);
   reg        [6:0] ram[0:4799];
   reg       [12:0] addr_reg;
   always @ (posedge in_clk)
     begin
        if (we)
          ram[addr] <= data;
        addr_reg <= addr;        // register address - input
     end
   always @ (posedge out_clk)
     q <= ram[addr_reg];         // register output data - output
endmodule

Synchronous Memory - Register Input - Simple Dual Port Memory - Single-Clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module sync_simple_dual_port_ram_single_clock_input_reg(
   input      [6:0] data,
   input     [12:0] read_addr, write_addr, // two addresses
   input            we, clk,
   output     [6:0] q);
   reg        [6:0] ram[0:4799];
   reg       [12:0] read_addr_reg;
   always @ (posedge clk)
     begin
        if (we)
           ram[write_addr] <= data;
        read_addr_reg <= read_addr;        // register input
     end
   assign q = ram[read_addr_reg];          // read with registered address
endmodule

Synchronous Memory - Register Output - Simple Dual Port Memory - Single-Clock

1
2
3
4
5
6
7
8
9
10
11
12
13
module sync_simple_dual_port_ram_single_clock_output_reg(
   input      [6:0] data,
   input     [12:0] read_addr, write_addr, // two addresses
   input            we, clk,
   output reg [6:0] q);
   reg        [6:0] ram[0:4799];
   always @ (posedge clk)
     begin
        if (we)
           ram[write_addr] <= data;
        q <= ram[read_addr];               // register output
     end
endmodule

Synchronous Memory - Register Input - Simple Dual Port Memory - Dual-Clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module sync_simple_dual_port_ram_dual_clock_input_reg(
   input      [6:0] data,
   input     [12:0] read_addr, write_addr,       // two addresses
   input            we, read_clock, write_clock, // two clocks
   output     [6:0] q);
   reg        [6:0] ram[0:4799];
   reg       [12:0] read_addr_reg;
   always @ (posedge write_clock)                // write clock
     begin
        if (we)
           ram[write_addr] <= data;
     end
   always @ (posedge read_clock)                 // read clock
     begin
        read_addr_reg <= read_addr;              // register input
     end
   assign q = ram[read_addr_reg];                // read with registered address
endmodule

Synchronous Memory - Register Output - Simple Dual Port Memory - Dual-Clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module sync_simple_dual_port_ram_dual_clock_output_reg(
   input      [6:0] data,
   input     [12:0] read_addr, write_addr,       // two addresses
   input            we, read_clock, write_clock, // two clocks
   output reg [6:0] q);
   reg        [6:0] ram[0:4799];
   always @ (posedge write_clock)                // write clock
     begin
        if (we)
           ram[write_addr] <= data;
     end
   always @ (posedge read_clock)                 // read clock
     begin
        q <= ram[read_addr];                     // register output
     end
endmodule

Synchronous Memory - Register Output - True Dual Port Memory - Single-Clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module sync_true_dual_port_ram_single_clock_output_reg(
   input      [6:0] data_a, data_b,
   input     [12:0] addr_a, addr_b,
   input            we_a, we_b, clk,
   output reg [6:0] q_a, q_b);
   reg        [6:0] ram[0:4799];
   always @ (posedge clk)                        // port a, single-clock
      if (we_a) begin
         ram[addr_a] <= data_a;
         q_a <= data_a;
      end else
         q_a <= ram[addr_a];
   always @ (posedge clk)                        // port b, single-clock
      if (we_b) begin
         ram[addr_b] <= data_b;
         q_b <= data_b;
      end else
         q_b <= ram[addr_b];
endmodule

Synchronous Memory - Register Output - True Dual Port Memory - Dual-Clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module sync_true_dual_port_ram_dual_clock_output_reg(
   input      [6:0] data_a, data_b,
   input     [12:0] addr_a, addr_b,
   input            we_a, we_b, clk_a, clk_b,
   output reg [6:0] q_a, q_b);
   reg        [6:0] ram[0:4799];
   always @ (posedge clk_a)                      // port a, dual-clock
      if (we_a) begin
         ram[addr_a] <= data_a;
         q_a <= data_a;
      end else
         q_a <= ram[addr_a];
   always @ (posedge clk_b)                      // port b, dual-clock
      if (we_b) begin
         ram[addr_b] <= data_b;
         q_b <= data_b;
      end else
         q_b <= ram[addr_b];
endmodule

Exercise

Try to design

  1. Synchronous Memory - Register Input - True Dual Port Memory - Single-Clock
  2. Synchronous Memory - Register Input - True Dual Port Memory - Dual-Clock
  3. Synchronous Memory - Register Input and Output for all the combinations of
    • simple dual port and true dual port
    • single-clock and dual-clock
  4. Prepare test bench for each implementation and simulate it with ModelSim