0
0
VerilogHow-ToBeginner · 3 min read

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

endmodule
💻

Example

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
endmodule
Output
Read from addr 3: aa Read from addr 7: 55
⚠️

Common Pitfalls

Common mistakes when coding single port RAM include:

  • Not using posedge clk for synchronous memory operations, causing timing issues.
  • Forgetting to update the output dout immediately after writing, which can cause stale data on output.
  • Using blocking assignments (=) inside the always @(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:

ConceptDescription
clkClock signal for synchronous operation
weWrite enable; high to write data
addrAddress input to select memory location
dinData input for writing
doutData output for reading
ram arrayMemory 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.