3. Verilog RAM - Dual Port Async Design

A Dual Port Asynchronous RAM provides two independent ports, each capable of read or write operations. This design supports different clock frequencies for read and write operations.

Features

  • Two independent ports: Port 0 and Port 1
  • Separate read/write clocks: wr_clk and rd_clk
  • Asynchronous reset: Clears all memory
  • Parameterized: Configurable DEPTH, WIDTH, ADDR

Supported Operations

  • Write to both Port 0 and Port 1 simultaneously
  • Read from both Port 0 and Port 1 simultaneously
  • Mixed: Read from one port, write to another

Note: To avoid memory contention, ensure different addresses when writing to both ports simultaneously. External arbitration logic should handle this.

Port Description

PortDirectionDescription
wr_clkinputWrite clock
rd_clkinputRead clock
resetinputAsynchronous reset
data_0/1inoutBidirectional data [WIDTH-1:0]
addr_0/1inputAddress [ADDR-1:0]
wr_en_0/1inputWrite enable per port
o_en_0/1inputOutput enable per port

RTL Implementation

// Module: dp_async_ram.v
// Dual Port Asynchronous RAM with Asynchronous Reset
// Read and Write Clock Frequencies are Different

module dp_async_ram #(
  parameter DEPTH = 16,
  parameter WIDTH = 8,
  parameter ADDR  = 4
)(
  input              wr_clk,
  input              rd_clk,
  input              reset,
  inout  [WIDTH-1:0] data_0, data_1,
  input  [ADDR-1:0]  addr_0, addr_1,
  input              wr_en_0, wr_en_1,
  input              o_en_0, o_en_1
);

  // Read data registers
  reg [WIDTH-1:0] data_0_reg, data_1_reg;
  integer i;

  // Memory array
  reg [WIDTH-1:0] mem [DEPTH-1:0];

  // Write Logic (synchronous to wr_clk, async reset)
  always @(posedge wr_clk or posedge reset) begin
    if (reset) begin
      for (i = 0; i < DEPTH; i = i + 1)
        mem[i] <= 0;
    end
    else begin
      if (wr_en_0 && !o_en_0)
        mem[addr_0] <= data_0;
      if (wr_en_1 && !o_en_1)
        mem[addr_1] <= data_1;
    end
  end

  // Read Logic - Tristate outputs
  assign data_0 = (o_en_0 && !wr_en_0) ? data_0_reg : {WIDTH{1'bz}};
  assign data_1 = (o_en_1 && !wr_en_1) ? data_1_reg : {WIDTH{1'bz}};

  // Read Logic (synchronous to rd_clk)
  always @(posedge rd_clk) begin
    if (o_en_0 && !wr_en_0)
      data_0_reg <= mem[addr_0];
    if (o_en_1 && !wr_en_1)
      data_1_reg <= mem[addr_1];
  end

endmodule

Architecture

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e0f2fe', 'primaryTextColor': '#0f172a', 'primaryBorderColor': '#0066cc', 'lineColor': '#475569', 'secondaryColor': '#f8fafc'}}}%%
flowchart LR
    subgraph Port0["Port 0"]
        D0[data_0]
        A0[addr_0]
        WE0[wr_en_0]
        OE0[o_en_0]
    end

    subgraph Port1["Port 1"]
        D1[data_1]
        A1[addr_1]
        WE1[wr_en_1]
        OE1[o_en_1]
    end

    MEM{{Memory Array}}

    D0 <--> MEM
    D1 <--> MEM
    A0 --> MEM
    A1 --> MEM

    style MEM fill:#d1fae5,stroke:#10b981,stroke-width:2px

This concludes the Verilog RAM series. You now have designs for both single-port and dual-port asynchronous memories!

Author
Mayur Kubavat
VLSI Design and Verification Engineer sharing knowledge about SystemVerilog, UVM, and hardware verification methodologies.

Comments (0)

Leave a Comment