Efinix Trion ビット幅の違うDual-Port Memoryの実装方法
作成者:mou-mou
DPRAMのVerilog-HDLでの記述については、Efinity® Synthesis User Guideの「RAM」チャプターに記載されています。しかし、ビット幅の違うDPRAMの例は記載がありません。そこで、Efinix社のホームページ内をさがしていると、VHDL版のソースコードがありました。
Support Center Home -> Knowledgebase -> Mixed Width RAM With Single Dual Port / Mixed Width RAM With True Dual Port
中を見てみると、VHDLで記載したコードとVerilog-HDLで記載されたwrapperが用意されていました。
とりあえず、プロジェクトを生成して合成してみました。使用されたリソースは以下の通りでした。
LE: LUTs/Adders | 2 |
LE: Registers | 0 |
Memory Blocks | 4 |
ソースコードをみてみると、そこそこのRTLが記述されていてなかなか大変だなと感じましたが、実際に合成されると、当然といえば当然なのですが、Memory Blockが置かれているだけです。
Parameterの初期値がPort A = 1024 x 8bit Port B 256 x 32bit となっていました。8Kbitなので5KのMemory Blockが2個で実現できるように感じますが、そこはEFX_DPRAM_5Kの制約によるところです。具体的には、Quantum Trion® Primitives User Guide の EFX_DPRAM_5Kの仕様をみると、WIDTHは、1,2,4,8,5,10bitからしか選択できません。32bit幅のメモリを構成するためには、必然的にMemory Blockが4個必要となります。mixed_width_ram.map.vをアドレスには先頭に1ビット’0’が付加されております。あと、不思議なことに、top_efx_mixed_width_ram_tdp.vのParameterは、READ_FIRSTになっているのに、合成してmixed_width_ram.map.vを確認するとWRITE_FIRSTとなっております。
これなら、複雑なRTLを書いてEfinityに解析させるまでもなく、EFX_DPRAM_5K primitiveを使って、都度実装したい仕様がParameterとして明示される形で記述したほうが保守性や可読性が高いと考えます。
以下に、サンプル例と同じPort A = 1024 x 8bit Port B 256 x 32bit の DPRAMを記述してみます。
module mixed_width_ram_user (
input clk,
input en,
input we_a,
input we_b,
input [9:0] addr_a,
input [7:0] addr_b,
input [7:0] data_in_a,
input [31:0] data_in_b,
output [7:0] data_out_a,
output [31:0] data_out_b
);
generate
genvar i;
for (i = 0; i < 4; i = i + 1) begin : gen_dpram
EFX_DPRAM_5K # (
.READ_WIDTH_A(2), // 2 2048x2
.WRITE_WIDTH_A(2), // 2 2048x2
.OUTPUT_REG_A(1'b0), // 1 add pipe-line read register
.CLKA_POLARITY(1'b1), // 0 falling edge, 1 rising edge
.WEA_POLARITY(1'b1), // 0 active low, 1 active high
.CLKEA_POLARITY(1'b1), // 0 falling edge, 1 rising edge
.WRITE_MODE_A("READ_FIRST"), // Output "new" data
.READ_WIDTH_B(8), // 4 512x4
.WRITE_WIDTH_B(8), // 4 512x4
.OUTPUT_REG_B(1'b0), // 1 add pipe-line read register
.CLKB_POLARITY(1'b1), // 0 falling edge, 1 rising edge
.WEB_POLARITY(1'b1), // 0 active low, 1 active high
.CLKEB_POLARITY(1'b1), // 0 falling edge, 1 rising edge
.WRITE_MODE_B("READ_FIRST"), // Output "old" data
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_10(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_11(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_12(256'h0000000000000000000000000000000000000000000000000000000000000000),
.INIT_13(256'h0000000000000000000000000000000000000000000000000000000000000000)
) EFX_DPRAM_5K_inst (
.RDATAA(data_out_a[i*2+1:i*2]), // Read data output A
.ADDRA({1'b0, addr_a}), // Address input A
.CLKA(clk), // Clock input A
.CLKEA(en), // Clock-enable input A
.WEA(we_a), // Write-enable input A
.WDATAA(data_in_a[i*2+1:i*2]), // Write data input A
.RDATAB({data_out_b[i*2+25:i*2+24], // Read data output B
data_out_b[i*2+17:i*2+16],
data_out_b[i*2+9:i*2+8],
data_out_b[i*2+1:i*2]}),
.ADDRB({1'b0, addr_b}), // Address input B
.CLKB(clk), // Clock input B
.CLKEB(en), // Clock-enable input B
.WEB(we_b), // Write-enable input B
.WDATAB({data_in_b[i*2+25:i*2+24], // Write data input B
data_in_b[i*2+17:i*2+16],
data_in_b[i*2+9:i*2+8],
data_in_b[i*2+1:i*2]})
);
end
endgenerate
endmodule