Single-port RAM design in Verilog - Time & Space Complexity
When designing a single-port RAM in Verilog, it is important to understand how the time to access memory changes as the size of the memory grows.
We want to know how the number of operations changes when we increase the memory size.
Analyze the time complexity of the following single-port RAM code snippet.
module single_port_ram (
input wire clk,
input wire we,
input wire [7:0] addr,
input wire [7:0] data_in,
output reg [7:0] data_out
);
reg [7:0] ram [0:255];
always @(posedge clk) begin
if (we) ram[addr] <= data_in;
data_out <= ram[addr];
end
endmodule
This code models a memory with 256 locations, each 8 bits wide, accessed through a single port for reading or writing.
In this design, the main operation is accessing the memory array at a given address.
- Primary operation: Reading or writing one memory location per clock cycle.
- How many times: Once per clock cycle, regardless of memory size.
Accessing memory happens in a fixed time each cycle, no matter how big the memory is.
| Input Size (memory locations) | Approx. Operations per access |
|---|---|
| 10 | 1 |
| 100 | 1 |
| 1000 | 1 |
Pattern observation: The time to read or write one location stays the same even if the memory size grows.
Time Complexity: O(1)
This means each memory access takes a constant amount of time, no matter how large the memory is.
[X] Wrong: "Accessing larger memory takes more time because there are more locations to check."
[OK] Correct: Memory hardware uses direct addressing, so it jumps straight to the location without checking others, keeping access time constant.
Understanding that single-port RAM access time is constant helps you explain hardware efficiency clearly and confidently in interviews.
"What if we changed the design to a multi-port RAM allowing simultaneous accesses? How would the time complexity change?"
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
