Verilog Code for Single Port RAM: Syntax and Example
A single port RAM in Verilog is a memory block with one address port used for both reading and writing. Use
always @(posedge clk) to control read/write operations with an enable signal and address input. The data output updates based on the address and write enable signals.Syntax
The basic syntax for a single port RAM includes inputs for clock (clk), write enable (we), address (addr), and data input (din). The output (dout) provides the data stored at the given address. The memory is declared as a reg array. The always @(posedge clk) block handles writing data when we is high and reading data otherwise.
- clk: Clock signal to synchronize operations.
- we: Write enable signal; when high, data is written.
- addr: Address input to select memory location.
- din: Data input to write into memory.
- dout: Data output from memory.
verilog
module single_port_ram (
input wire clk,
input wire we,
input wire [3:0] addr,
input wire [7:0] din,
output reg [7:0] dout
);
reg [7:0] ram [0:15]; // 16 locations of 8-bit memory
always @(posedge clk) begin
if (we) begin
ram[addr] <= din; // Write data
dout <= din; // Output new data immediately
end else begin
dout <= ram[addr]; // Read data
end
end
endmoduleExample
This example shows a single port RAM with 16 memory locations, each 8 bits wide. It writes data to the memory when we is high on the rising clock edge and reads data when we is low. The output updates immediately after writing or reading.
verilog
module testbench();
reg clk = 0;
reg we;
reg [3:0] addr;
reg [7:0] din;
wire [7:0] dout;
single_port_ram ram_inst(
.clk(clk),
.we(we),
.addr(addr),
.din(din),
.dout(dout)
);
always #5 clk = ~clk; // Clock toggles every 5 time units
initial begin
// Write 8'hAA to address 3
we = 1; addr = 4'd3; din = 8'hAA;
#10;
// Write 8'h55 to address 7
addr = 4'd7; din = 8'h55;
#10;
// Read from address 3
we = 0; addr = 4'd3;
#10;
$display("Read from addr 3: %h", dout);
// Read from address 7
addr = 4'd7;
#10;
$display("Read from addr 7: %h", dout);
$finish;
end
endmoduleOutput
Read from addr 3: aa
Read from addr 7: 55
Common Pitfalls
Common mistakes when coding single port RAM include:
- Not using
posedge clkfor synchronous memory operations, causing timing issues. - Forgetting to update the output
doutimmediately after writing, which can cause stale data on output. - Using blocking assignments (
=) inside thealways @(posedge clk)block instead of non-blocking (<=), which can cause simulation mismatches. - Not declaring the memory array size correctly, leading to out-of-bound errors.
verilog
/* Wrong way: Using blocking assignment and no output update on write */ module wrong_ram ( input wire clk, input wire we, input wire [3:0] addr, input wire [7:0] din, output reg [7:0] dout ); reg [7:0] ram [0:15]; always @(posedge clk) begin if (we) begin ram[addr] = din; // blocking assignment (wrong) // dout not updated here end else begin dout = ram[addr]; end end endmodule /* Correct way: Use non-blocking and update dout on write */ module correct_ram ( input wire clk, input wire we, input wire [3:0] addr, input wire [7:0] din, output reg [7:0] dout ); reg [7:0] ram [0:15]; always @(posedge clk) begin if (we) begin ram[addr] <= din; // non-blocking assignment dout <= din; // update output immediately end else begin dout <= ram[addr]; end end endmodule
Quick Reference
Key points for single port RAM in Verilog:
| Concept | Description |
|---|---|
| clk | Clock signal for synchronous operation |
| we | Write enable; high to write data |
| addr | Address input to select memory location |
| din | Data input for writing |
| dout | Data output for reading |
| ram array | Memory storage declared as reg array |
| always @(posedge clk) | Use for synchronous read/write |
| Non-blocking (<=) | Use for assignments inside clocked blocks |
Key Takeaways
Use a clocked always block with posedge clk for synchronous RAM operations.
Write enable controls whether data is written or read from the memory.
Use non-blocking assignments (<=) inside clocked always blocks for correct simulation.
Update the output immediately after writing to reflect new data.
Declare memory as a reg array sized by address width and data width.