Verilog Single-Port RAM Design: Umfassender Leitfaden
Design
Ein synchrones Single-Port-RAM mit gemeinsamer Datenleitung (inout) für Lesen und Schreiben. Die Steuerung erfolgt über Chip-Select (cs), Write-Enable (we) und Output-Enable (oe). Schreib- und Lesezugriff sind beide taktflankensynchron.
module single_port_sync_ram #(
parameter int ADDR_WIDTH = 4,
parameter int DATA_WIDTH = 32,
parameter int DEPTH = 16
) (
input logic clk,
input logic [ADDR_WIDTH-1:0] addr,
inout tri [DATA_WIDTH-1:0] data,
input logic cs,
input logic we,
input logic oe
);
logic [DATA_WIDTH-1:0] tmp_data;
logic [DATA_WIDTH-1:0] mem [DEPTH];
// Schreiboperation
always_ff @(posedge clk) begin
if (cs && we) begin
mem[addr] <= data;
end
end
// Leseoperation (registered output)
always_ff @(posedge clk) begin
if (cs && !we) begin
tmp_data <= mem[addr];
end
end
// Tristate-Treiber für bidirektionale Datenleitung
assign data = (cs && oe && !we) ? tmp_data : 'z;
endmodule

Testbench
Die Testbench schreibt zunächst alle 16 Adressen mit Zufallsdaten und liest sie anschließend sequenziell zurück.
module tb;
parameter int ADDR_WIDTH = 4;
parameter int DATA_WIDTH = 16;
parameter int DEPTH = 16;
logic clk, cs, we, oe;
logic [ADDR_WIDTH-1:0] addr;
logic [DATA_WIDTH-1:0] data;
logic [DATA_WIDTH-1:0] tb_data;
single_port_sync_ram #(
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH),
.DEPTH(DEPTH)
) u0 (
.clk (clk),
.addr (addr),
.data (data),
.cs (cs),
.we (we),
.oe (oe)
);
always #10 clk = ~clk;
assign data = (!oe) ? tb_data : 'z;
initial begin
clk = 0; cs = 0; we = 0; oe = 0; addr = '0; tb_data = '0;
repeat (2) @(posedge clk);
// Schreibphase
for (int i = 0; i < 2**ADDR_WIDTH; i++) begin
@(posedge clk);
addr <= i;
we <= 1;
cs <= 1;
oe <= 0;
tb_data <= $urandom;
end
// Lesephase
for (int i = 0; i < 2**ADDR_WIDTH; i++) begin
@(posedge clk);
addr <= i;
we <= 0;
cs <= 1;
oe <= 1;
end
#20 $finish;
end
endmodule
Verilog
- Verilog meistern: Komplettes Tutorial für digitales Schaltungsdesign
- Verilog Concatenation meistern: Praktischer Leitfaden
- Verilog Assignments verstehen: Typen, Syntax und Best Practices
- Verilog Blocking vs. Non-Blocking: Wichtige Unterschiede erklärt
- Verilog Functions meistern für effizientes RTL-Design
- Verilog Tasks verstehen: Functions vs. Tasks, Syntax und Verwendung
- Verilog Clock Generator: Design & Implementierungsleitfaden
- Verilog Math Functions meistern für effizientes Hardware-Design
- Verilog $timeformat meistern: Präzision und Anzeigesteuerung
- Verilog Timescales an jedem Scope verstehen und ausgeben