VerilogシングルポートRAM設計:同期読み書きの完全ガイド
シングルポート同期RAMの設計
同期RAMは、クロックエッジで読み書き両方を制御するメモリです。アドレス幅ADDR_WIDTH、データ幅DATA_WIDTH、深さDEPTHをパラメータ化し、汎用的に使えます。入出力は双方向バスdata(inout)で、チップセレクトcs、書き込みイネーブルwe、出力イネーブルoeで制御します。
module single_port_sync_ram #(
parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 32,
parameter DEPTH = 16
)(
input clk,
input [ADDR_WIDTH-1:0] addr,
inout [DATA_WIDTH-1:0] data,
input cs, // チップセレクト
input we, // 書き込みイネーブル(Highで書き込み)
input oe // 出力イネーブル(Highで出力)
);
reg [DATA_WIDTH-1:0] tmp_data; // 読み出しデータ保持用
reg [DATA_WIDTH-1:0] mem [DEPTH]; // メモリ本体
// 同期書き込み:クロック立ち上がりで cs & we なら書き込み
always @(posedge clk) begin
if (cs & we)
mem[addr] <= data;
end
// 同期読み出し:クロック立ち上がりで cs & !we なら読み出し
always @(posedge clk) begin
if (cs & !we)
tmp_data <= mem[addr];
end
// 双方向バス制御:cs & oe & !we なら tmp_data を駆動、 else Hi-Z
assign data = (cs & oe & !we) ? tmp_data : 'hz;
endmodule
重要な設計ポイント
- inoutポート:双方向バスは
inoutで宣言、内部でassignにより三態制御 - 同期読み出し:アドレス投入からデータ出力まで1クロック遅延(パイプライン向き)
- 書き込み優先:同一サイクルで読み書き競合時は書き込み優先(
we優先) - Hi-Z制御:
!cs || !oe || weのとき高インピーダンス
回路図
テストベンチ
全アドレス(0〜15)へ書き込み、次に全アドレスから読み出しを行います。
module tb;
parameter ADDR_WIDTH = 4;
parameter DATA_WIDTH = 16;
parameter DEPTH = 16;
reg clk;
reg cs, we, oe;
reg [ADDR_WIDTH-1:0] addr;
wire [DATA_WIDTH-1:0] data;
reg [DATA_WIDTH-1:0] tb_data;
single_port_sync_ram #(.DATA_WIDTH(DATA_WIDTH)) u0 (
.clk(clk), .addr(addr), .data(data),
.cs(cs), .we(we), .oe(oe)
);
always #10 clk = ~clk; // 20ns周期
// TB側からデータバス駆動:oe=0時にtb_dataを出力
assign data = !oe ? tb_data : 'hz;
initial begin
{clk, cs, we, addr, tb_data, oe} <= 0;
repeat (2) @(posedge clk);
// 書き込みフェーズ:全アドレスへランダムデータ書き込み
for (integer i = 0; i < 2**ADDR_WIDTH; i = i + 1) begin
repeat (1) @(posedge clk);
addr <= i;
we <= 1; cs <= 1; oe <= 0;
tb_data <= $random;
end
// 読み出しフェーズ:全アドレスから読み出し
for (integer i = 0; i < 2**ADDR_WIDTH; i = i + 1) begin
repeat (1) @(posedge clk);
addr <= i;
we <= 0; cs <= 1; oe <= 1;
end
#20 $finish;
end
endmodule
動作タイミング
クロック: ___/‾\___/‾\___/‾\___
書き込み: ▲ ▲
addr=0 addr=1 data駆動(tb_data)
読み出し: ▲ ▲
addr=0 addr=1 data出力(tmp_data) ← 1クロック遅延
実用上の考慮事項
| 項目 | 考慮点 |
|---|---|
| FPGAブロックRAM | 合成ツールが自動的にブロックRAM(BRAM)にマッピング。パラメータでサイズ指定 |
| 初期化 | $readmemh/$readmembでHEX/BINファイルから初期化可能 |
| 非同期読み出し | always @(*) tmp_data = mem[addr]で0遅延読み出し可能(タイミング注意) |
| バイトイネーブル | 部分書き込み対応ならinput [DATA_WIDTH/8-1:0] be追加 |
| 出力レジスタ | タイミング改善のため出力側にレジスタ段追加(reg [DATA_WIDTH-1:0] data_out)も常套 |