ROM (Read-Only Memory) in Verilog - Time & Space Complexity
We want to understand how the time to read data from a ROM changes as the size of the memory grows.
How does the number of steps needed to get data change when the ROM size increases?
Analyze the time complexity of the following code snippet.
module rom(
input wire [3:0] addr,
output reg [7:0] data
);
always @(*) begin
case(addr)
4'd0: data = 8'hA1;
4'd1: data = 8'hB2;
4'd2: data = 8'hC3;
// ... more addresses ...
default: data = 8'h00;
endcase
end
endmodule
This code models a ROM that outputs a fixed 8-bit value based on a 4-bit address input.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The
casestatement checks the input address against stored values. - How many times: Each read checks the address once; no loops or repeated traversals happen during a single read.
Reading from ROM is like looking up a value in a fixed list by address.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 1 check per read |
| 100 | 1 check per read |
| 1000 | 1 check per read |
Pattern observation: The number of steps to read data stays the same no matter how big the ROM is.
Time Complexity: O(1)
This means reading data from ROM takes the same amount of time regardless of how many entries it has.
[X] Wrong: "Reading from ROM takes longer if the ROM has more data because it checks each entry one by one."
[OK] Correct: ROM uses the address directly to get data instantly, not by searching through all entries.
Knowing that ROM reads are constant time helps you understand hardware design and how memory access works efficiently.
"What if the ROM was implemented using a loop to search for the address instead of a direct case statement? How would the time complexity change?"
Practice
Solution
Step 1: Understand ROM functionality
ROM is designed to hold data that is fixed and cannot be modified during operation.Step 2: Compare with other memory types
Unlike RAM, ROM data is read-only and does not support writing during runtime.Final Answer:
To store fixed data that cannot be changed during simulation or operation -> Option AQuick Check:
ROM = fixed data storage [OK]
- Confusing ROM with RAM (read-write memory)
- Thinking ROM can perform operations
- Assuming ROM generates signals
Solution
Step 1: Identify correct ROM declaration
ROM is declared as a reg array with an initial block to assign fixed values.Step 2: Check each option
reg [7:0] rom_mem [0:3]; initial begin rom_mem[0] = 8'hAA; rom_mem[1] = 8'hBB; rom_mem[2] = 8'hCC; rom_mem[3] = 8'hDD; end uses reg array and initial block correctly. wire [7:0] rom_mem [0:3]; always @(posedge clk) rom_mem[0] <= 8'hAA; uses wire and always block incorrectly. Options C and D do not declare arrays properly.Final Answer:
reg [7:0] rom_mem [0:3]; initial begin rom_mem[0] = 8'hAA; rom_mem[1] = 8'hBB; rom_mem[2] = 8'hCC; rom_mem[3] = 8'hDD; end -> Option DQuick Check:
ROM array + initial block = reg [7:0] rom_mem [0:3]; initial begin rom_mem[0] = 8'hAA; rom_mem[1] = 8'hBB; rom_mem[2] = 8'hCC; rom_mem[3] = 8'hDD; end [OK]
- Using wire instead of reg for ROM array
- Assigning inside always block for ROM
- Not using an array for multiple ROM entries
data_out when addr = 2'b10?
module rom_example(input [1:0] addr, output reg [7:0] data_out);
always @(*) begin
case(addr)
2'b00: data_out = 8'h1A;
2'b01: data_out = 8'h2B;
2'b10: data_out = 8'h3C;
2'b11: data_out = 8'h4D;
default: data_out = 8'h00;
endcase
end
endmoduleSolution
Step 1: Locate the case for addr = 2'b10
The case statement assigns data_out = 8'h3C when addr is 2'b10.Step 2: Confirm output value
Since addr matches 2'b10, data_out will be 8'h3C.Final Answer:
8'h3C -> Option CQuick Check:
addr 2'b10 -> data_out 8'h3C [OK]
- Choosing wrong case label
- Confusing hex values
- Ignoring default case
module rom_bug(input [1:0] addr, output reg [7:0] data_out);
reg [7:0] rom_mem [0:3];
initial begin
rom_mem[0] = 8'hAA;
rom_mem[1] = 8'hBB;
rom_mem[2] = 8'hCC;
rom_mem[3] = 8'hDD;
end
always @(*) begin
data_out = rom_mem[addr];
end
endmoduleSolution
Step 1: Check ROM declaration and initialization
ROM is declared as reg array and initialized in initial block, which is correct for simulation.Step 2: Verify data output assignment
Using addr as index to read rom_mem is valid and data_out is assigned in combinational always block.Final Answer:
No error; code correctly implements ROM -> Option BQuick Check:
ROM reg array + initial + combinational read = correct [OK]
- Thinking initial block is illegal for ROM
- Confusing reg and wire for ROM arrays
- Assuming addr needs conversion
Solution
Step 1: Analyze each code snippet for ROM implementation
reg [7:0] rom [0:3]; initial begin rom[0]=8'd0; rom[1]=8'd1; rom[2]=8'd4; rom[3]=8'd9; end always @(*) data_out = rom[addr]; lacks module and always block structure; data_out assignment is incomplete. always @(addr) begin case(addr) 2'b00: data_out=8'd0; 2'b01: data_out=8'd1; 2'b10: data_out=8'd4; 2'b11: data_out=8'd9; default: data_out=8'd0; endcase end uses a case statement to output fixed squares based on addr, which is a valid ROM implementation. reg [7:0] rom [0:3]; assign rom[0]=0; assign rom[1]=1; assign rom[2]=4; assign rom[3]=9; assign data_out=rom[addr]; tries to assign values to wire array elements, which is illegal. reg [7:0] rom [0:3]; always @(posedge clk) begin rom[0]<=0; rom[1]<=1; rom[2]<=4; rom[3]<=9; end assign data_out=rom[addr]; tries to assign ROM values inside a clocked always block, which is not suitable for ROM initialization.Step 2: Confirm correct ROM behavior
always @(addr) begin case(addr) 2'b00: data_out=8'd0; 2'b01: data_out=8'd1; 2'b10: data_out=8'd4; 2'b11: data_out=8'd9; default: data_out=8'd0; endcase end correctly outputs squares for each address using a combinational case statement, matching ROM behavior.Final Answer:
always @(addr) begin case(addr) 2'b00: data_out=8'd0; 2'b01: data_out=8'd1; 2'b10: data_out=8'd4; 2'b11: data_out=8'd9; default: data_out=8'd0; endcase end -> Option AQuick Check:
ROM as combinational case statement = always @(addr) begin case(addr) 2'b00: data_out=8'd0; 2'b01: data_out=8'd1; 2'b10: data_out=8'd4; 2'b11: data_out=8'd9; default: data_out=8'd0; endcase end [OK]
- Assigning to wire arrays
- Initializing ROM inside clocked always block
- Not using combinational logic for ROM output
Ready to prove it?
Pass the test and this pattern moves unknown
