Single-port RAM lets you store and read data using one address at a time. It helps save space and is simple to use in hardware designs.
Single-port RAM design in Verilog
module single_port_ram #( parameter DATA_WIDTH = 8, parameter ADDR_WIDTH = 4 ) ( input wire clk, input wire we, // write enable input wire [ADDR_WIDTH-1:0] addr, input wire [DATA_WIDTH-1:0] din, output reg [DATA_WIDTH-1:0] dout ); // Declare memory array reg [DATA_WIDTH-1:0] ram [0:(1<<ADDR_WIDTH)-1]; always @(posedge clk) begin if (we) begin ram[addr] <= din; // Write data end dout <= ram[addr]; // Read data end endmodule
The clk input controls when data is read or written.
The we signal decides if the memory writes new data or just reads.
module ram_example (
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) ram[addr] <= din;
dout <= ram[addr];
end
endmodulemodule ram_16x4 (
input wire clk,
input wire we,
input wire [3:0] addr,
input wire [3:0] din,
output reg [3:0] dout
);
reg [3:0] ram [0:15];
always @(posedge clk) begin
if (we) ram[addr] <= din;
dout <= ram[addr];
end
endmoduleThis testbench writes values to addresses 3 and 7, then reads them back and prints the results. It also reads an address that was never written to show default zero.
module testbench();
reg clk = 0;
reg we;
reg [3:0] addr;
reg [7:0] din;
wire [7:0] dout;
// Instantiate RAM
single_port_ram ram (
.clk(clk),
.we(we),
.addr(addr),
.din(din),
.dout(dout)
);
// Clock generation
always #5 clk = ~clk;
initial begin
// Write 42 at address 3
we = 1; addr = 4'd3; din = 8'd42;
#10;
// Write 100 at address 7
addr = 4'd7; din = 8'd100;
#10;
// Stop writing, read address 3
we = 0; addr = 4'd3;
#10;
$display("Read from addr 3: %d", dout);
// Read address 7
addr = 4'd7;
#10;
$display("Read from addr 7: %d", dout);
// Read address 0 (not written, default 0)
addr = 4'd0;
#10;
$display("Read from addr 0: %d", dout);
$finish;
end
endmodule
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];
always @(posedge clk) begin
if (we) ram[addr] <= din;
dout <= ram[addr];
end
endmoduleReading and writing happen on the clock's rising edge.
If you read an address that was never written, it usually returns zero or unknown depending on your simulator.
Single-port RAM can only read or write one address per clock cycle.
Single-port RAM stores data with one address line for both reading and writing.
Use a clock and write enable signal to control memory operations.
This design is simple and saves hardware but can only do one operation at a time.
Practice
single-port RAM in Verilog?Solution
Step 1: Understand single-port RAM basics
Single-port RAM has only one address line used for both reading and writing data.Step 2: Compare with other RAM types
Unlike dual-port RAM, it cannot perform read and write simultaneously on different ports.Final Answer:
It uses one address line for both reading and writing operations. -> Option CQuick Check:
Single-port RAM = one address line for read/write [OK]
- Thinking it can read and write at the same time
- Assuming it needs two clocks
- Believing it works without a clock
Solution
Step 1: Understand Verilog memory declaration
Memory arrays are declared asreg [width-1:0] name [address_range];for RAM.Step 2: Check each option
reg [7:0] mem [0:255]; correctly declares 256 locations (0 to 255) each 8 bits wide. wire [7:0] mem [255:0]; uses wire which is incorrect for RAM storage. reg mem [7:0] [0:255]; reverses dimensions. memory [7:0] mem [0:255]; uses invalid keyword.Final Answer:
reg [7:0] mem [0:255]; -> Option DQuick Check:
RAM declaration = reg [7:0] mem [0:255] [OK]
- Using wire instead of reg for RAM
- Swapping width and address range
- Using invalid keywords like memory
data_out after the clock edge if we = 1, addr = 3, and data_in = 8'hAA?
reg [7:0] mem [0:7]; always @(posedge clk) begin if (we) mem[addr] <= data_in; data_out <= mem[addr]; end
Solution
Step 1: Analyze write and read order in always block
The write happens first: ifwe=1,mem[3]is updated with8'hAA. Thendata_outis assignedmem[3].Step 2: Understand non-blocking assignment timing
Non-blocking assignments update after the always block finishes, sodata_outreads the old value ofmem[3]before write.Final Answer:
Previous value stored at mem[3] -> Option BQuick Check:
Non-blocking write then read = old value [OK]
- Assuming data_out immediately shows new data_in
- Confusing blocking and non-blocking assignments
- Ignoring timing of memory update
reg [7:0] mem [0:15];
always @(posedge clk) begin
if (we)
mem[addr] = data_in;
else
data_out = mem[addr];
endSolution
Step 1: Check assignment types in sequential always block
Inside clocked always blocks, non-blocking assignments (<=) should be used to avoid race conditions.Step 2: Verify other code parts
Memory size and address width are not shown but not necessarily errors here. Reset is optional.Final Answer:
Using blocking assignments (=) instead of non-blocking (<=) inside always block. -> Option AQuick Check:
Use <= in clocked always blocks [OK]
- Using = instead of <= in clocked blocks
- Assuming reset is mandatory
- Ignoring address width declaration
Solution
Step 1: Understand synchronous write and asynchronous read
Write happens on clock edge with write enable; read happens anytime from memory asynchronously.Step 2: Analyze each code snippet
reg [7:0] mem [0:15]; always @(posedge clk) begin if (we) mem[addr] <= data_in; end assign data_out = mem[addr];
uses clocked always block for write and continuous assignment for asynchronous read, which is correct.reg [7:0] mem [0:15]; always @(posedge clk) begin if (we) mem[addr] <= data_in; data_out <= mem[addr]; end
reads synchronously.reg [7:0] mem [0:15]; always @(addr or we or data_in) begin if (we) mem[addr] = data_in; end assign data_out = mem[addr];
uses combinational always block for write, which is incorrect.reg [7:0] mem [0:15]; always @(posedge clk) begin if (we) mem[addr] = data_in; end always @(addr) begin data_out = mem[addr]; end
uses blocking assignment in clocked block and asynchronous read in always block, which is not standard.Final Answer:
Option A code snippet -> Option AQuick Check:
Synchronous write + async read = clocked write + assign read [OK]
- Reading memory synchronously instead of asynchronously
- Using blocking assignments in clocked blocks
- Trying to write memory combinationally
Ready to prove it?
Pass the test and this pattern moves unknown
