Verilog Code for Dual Port RAM: Syntax and Example
dual port RAM in Verilog allows simultaneous read and write operations on two ports. It typically uses two separate address, data, and control signals for each port. You can implement it using always @(posedge clk) blocks with separate read and write logic for each port.Syntax
A dual port RAM module usually has two sets of ports: Port A and Port B. Each port has its own address, data input, data output, write enable, and clock signals. The RAM memory is declared as a reg array. The always @(posedge clk) blocks handle read and write operations for each port independently.
clk_a, clk_b: Clock signals for each port.we_a, we_b: Write enable signals for each port.addr_a, addr_b: Address inputs for each port.din_a, din_b: Data inputs for writing.dout_a, dout_b: Data outputs for reading.
module dual_port_ram (
input wire clk_a,
input wire we_a,
input wire [3:0] addr_a,
input wire [7:0] din_a,
output reg [7:0] dout_a,
input wire clk_b,
input wire we_b,
input wire [3:0] addr_b,
input wire [7:0] din_b,
output reg [7:0] dout_b
);
reg [7:0] ram [0:15]; // 16 locations of 8-bit memory
// Port A operations
always @(posedge clk_a) begin
if (we_a) begin
ram[addr_a] <= din_a; // Write data
dout_a <= din_a; // Output written data
end else begin
dout_a <= ram[addr_a]; // Read data
end
end
// Port B operations
always @(posedge clk_b) begin
if (we_b) begin
ram[addr_b] <= din_b; // Write data
dout_b <= din_b; // Output written data
end else begin
dout_b <= ram[addr_b]; // Read data
end
end
endmoduleExample
This example shows a simple dual port RAM with 16 memory locations of 8 bits each. Port A and Port B can read or write independently on their own clock edges. When we is high, data is written; otherwise, data is read from the given address.
module tb_dual_port_ram();
reg clk_a = 0, clk_b = 0;
reg we_a, we_b;
reg [3:0] addr_a, addr_b;
reg [7:0] din_a, din_b;
wire [7:0] dout_a, dout_b;
dual_port_ram ram_inst(
.clk_a(clk_a), .we_a(we_a), .addr_a(addr_a), .din_a(din_a), .dout_a(dout_a),
.clk_b(clk_b), .we_b(we_b), .addr_b(addr_b), .din_b(din_b), .dout_b(dout_b)
);
// Clock generation
always #5 clk_a = ~clk_a;
always #7 clk_b = ~clk_b;
initial begin
// Write 0xAA to address 3 on port A
we_a = 1; addr_a = 4'd3; din_a = 8'hAA;
// Write 0x55 to address 5 on port B
we_b = 1; addr_b = 4'd5; din_b = 8'h55;
#10;
// Disable write, read from address 3 and 5
we_a = 0; addr_a = 4'd3;
we_b = 0; addr_b = 4'd5;
#20;
$display("Port A read data: %h", dout_a); // Expect AA
$display("Port B read data: %h", dout_b); // Expect 55
$finish;
end
endmoduleCommon Pitfalls
Common mistakes when writing dual port RAM include:
- Using the same clock for both ports without proper synchronization can cause data corruption.
- Not handling simultaneous writes to the same address can lead to undefined behavior.
- Forgetting to register outputs can cause glitches.
- Mixing blocking and non-blocking assignments incorrectly in
alwaysblocks.
Always use non-blocking assignments (<=) inside clocked always blocks for proper simulation and synthesis.
/* Wrong: Blocking assignment in clocked block */ always @(posedge clk_a) begin if (we_a) begin ram[addr_a] = din_a; // Blocking assignment - not recommended dout_a = din_a; end else begin dout_a = ram[addr_a]; end end /* Right: Non-blocking assignment in clocked block */ always @(posedge clk_a) begin if (we_a) begin ram[addr_a] <= din_a; // Non-blocking assignment dout_a <= din_a; end else begin dout_a <= ram[addr_a]; end end
Quick Reference
Dual Port RAM Signals:
clk_a, clk_b: Separate clocks for each port.we_a, we_b: Write enable signals.addr_a, addr_b: Address inputs.din_a, din_b: Data inputs for writing.dout_a, dout_b: Data outputs for reading.
Best Practices: Use non-blocking assignments, avoid simultaneous writes to the same address, and register outputs.