📖 Scenario: You are designing a simple memory module for a small digital system. This memory will store 8-bit data values and have 16 address locations. You will create a single-port RAM where you can read or write data at one address at a time.
🎯 Goal: Build a single-port RAM module in Verilog with 16 addresses and 8-bit data width. You will set up the memory, add control signals, implement the read/write logic, and finally display the stored data at a given address.
📋 What You'll Learn
Create a memory array with 16 locations, each 8 bits wide
Add input signals for address, data input, write enable, and clock
Implement logic to write data to memory on clock edge when write enable is high
Implement logic to read data from memory at the given address
Print the output data value after a write and read operation
💡 Why This Matters
🌍 Real World
Single-port RAM modules are used in small embedded systems and FPGA designs to store temporary data or configuration settings.
💼 Career
Understanding RAM design is important for hardware engineers working on digital circuit design, FPGA programming, and embedded system development.
Progress0 / 4 steps
1
Create the memory array
Create a reg array called mem with 16 locations, each 8 bits wide.
Verilog
Hint
Use reg [7:0] mem [0:15]; to declare the memory array.
Use input and output reg keywords to declare signals.
3
Implement write and read logic
Write an always @(posedge clk) block that writes data_in to mem[addr] when we is high, and always updates data_out with mem[addr].
Verilog
Hint
Use a clocked always block with an if statement for write enable.
4
Display the output data
Write a testbench code snippet that sets addr = 4, data_in = 8'hA5, we = 1, toggles clk, then sets we = 0 and toggles clk again. Finally, print data_out.
Verilog
Hint
Use $display to print the data_out value after clock edges.
Practice
(1/5)
1. What is the main characteristic of a single-port RAM in Verilog?
easy
A. It stores data without any clock or control signals.
B. It can read and write data simultaneously on separate ports.
C. It uses one address line for both reading and writing operations.
D. It requires two clocks for read and write operations.
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 C
Quick Check:
Single-port RAM = one address line for read/write [OK]
Hint: Single-port RAM uses one address for both read and write [OK]
Common Mistakes:
Thinking it can read and write at the same time
Assuming it needs two clocks
Believing it works without a clock
2. Which of the following is the correct Verilog syntax to declare a 256x8 single-port RAM memory array?
easy
A. wire [7:0] mem [255:0];
B. memory [7:0] mem [0:255];
C. reg mem [7:0] [0:255];
D. reg [7:0] mem [0:255];
Solution
Step 1: Understand Verilog memory declaration
Memory arrays are declared as reg [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 D
Quick Check:
RAM declaration = reg [7:0] mem [0:255] [OK]
Hint: Use reg with [width-1:0] and address range [OK]
Common Mistakes:
Using wire instead of reg for RAM
Swapping width and address range
Using invalid keywords like memory
3. Given the following Verilog code snippet for a single-port RAM, what is the value of 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
medium
A. 8'hAA
B. Previous value stored at mem[3]
C. 8'h00
D. Undefined (X)
Solution
Step 1: Analyze write and read order in always block
The write happens first: if we=1, mem[3] is updated with 8'hAA. Then data_out is assigned mem[3].
Step 2: Understand non-blocking assignment timing
Non-blocking assignments update after the always block finishes, so data_out reads the old value of mem[3] before write.
Final Answer:
Previous value stored at mem[3] -> Option B
Quick Check:
Non-blocking write then read = old value [OK]
Hint: Non-blocking writes update after reads in same clock [OK]
Common Mistakes:
Assuming data_out immediately shows new data_in
Confusing blocking and non-blocking assignments
Ignoring timing of memory update
4. Identify the error in this single-port RAM Verilog code snippet:
reg [7:0] mem [0:15];
always @(posedge clk) begin
if (we)
mem[addr] = data_in;
else
data_out = mem[addr];
end
medium
A. Using blocking assignments (=) instead of non-blocking (<=) inside always block.
B. Memory size declaration is incorrect.
C. Missing reset signal for memory initialization.
D. Address width is not declared.
Solution
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 A
Quick Check:
Use <= in clocked always blocks [OK]
Hint: Use <= for sequential logic inside always @(posedge clk) [OK]
Common Mistakes:
Using = instead of <= in clocked blocks
Assuming reset is mandatory
Ignoring address width declaration
5. You want to design a single-port RAM in Verilog that supports synchronous write and asynchronous read. Which code snippet correctly implements this behavior?
hard
A.
reg [7:0] mem [0:15];
always @(posedge clk) begin
if (we) mem[addr] <= data_in;
end
assign data_out = mem[addr];
B.
reg [7:0] mem [0:15];
always @(posedge clk) begin
if (we) mem[addr] <= data_in;
data_out <= mem[addr];
end
C.
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];
D.
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
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.