FPGAを使う理由として同期・非同期FIFOをたくさん用意したい、という点があると思います。
FPGAは内部がSRAMのお化けですし、タイミングが厳しいときはマイコンを使うわけにはいきません。
今回、MIGが生成したMCBラッパの32bit幅のデータをLPDDR SDRAM動作クロックより低速でデータをやり取りする必要がある上、その都度MCBラッパにリード・ライト命令、バースト長を指定しているとオーバラップが生ずるので、一旦、自前のFIFOにデータを貯めることにしました。
Verilogでデータ幅16bit、深さ128の2次元配列のRAMを作る場合を考えます。(VHDLのArray)
reg [15:0] mem_array [0:127]; // 16 bit, 127 word wire [16:0] d_in_wire; // in data wire [16:0] d_out_wire; // address wire [5:0] addr_in_wire; // an address wire write_ena_in_wire; // write enable(High-Active) assign d_out_wire = mem_array[addr_in_wire]; // read always @(posedge write_ena_in_wire) // write begin mem_array[addr_in_wire] = d_in_wire; end
これでISEがメモリとして推論してくれればいいのですが*1、不安だったので、CoreGenの力を借りました。
Project NavigatorのProject -> New Source...
New Source WizardでIP(Core Generator & Architecture Wizard)を選択し、File nameを「bram01」などとしておきます。(BRAMじゃネーヨという突込みが入りそう)
Next ->
Select IPで階層をたどり、Memories & Storage Elements-> RAMs & ROMs -> Distributed Memory Generatorを選択してNext ->
Distributed Memory Generatorの設定画面で、深さ128、データ幅32bitを選択します。Next->
Input Options、Output Optionsともに、Non Registeredになっていることを確認。Next->
次の画面は特になにも設定する項目はないのでそのままGenerate
生成したファイルをプロジェクトに取り込みます
Project -> Add Source でbram01.vを選択し、Hierarchyウィンドウに追加されていることを確認。
まずこのRAMをプロジェクトのトップファイルにインスタンシエートします。ちなみにMCBラッパとの連動を考えているのでuser_design/par/test.xiseの
MIGプロジェクトのトップファイルmymig.v上に形成しています。
reg[6:0] fifo_addr_in; reg[31:0] fifo_dat_in; wire[6:0] fifo_addr_wire_in; wire[31:0] fifo_dat_wire_in; wire[31:0] fifo_dat_wire_out; reg write_ena_in; assign fifo_addr_wire_in = fifo_addr_in; assign fifo_dat_wire_in = fifo_dat_in; // 分散RAMのインスタンシエート bram01 userfifo( .a (fifo_addr_wire_in), //address_in .d (fifo_dat_wire_in), //data_in .clk (c3_clk0), //clk_in .we (write_ena_in), //write_enable_in .spo (fifo_dat_wire_out) //data_out );
このRAMが動作しているかをMicroBoard上のLEDを使って確認します。
LPDDR SDARAMメモリクロックは高速でLEDチカチカでは確認しづらいので、レジスタの27bit目が1になるごとにLEDアサインを繰り上げています。
reg [31:0] count_led; wire [3:0] count_wire; //assign count_wire[3:0] = count_led[29:26]; always @(posedge c3_clk0 or posedge c3_sys_rst_i) begin if(c3_sys_rst_i == 1'b1) begin count_led <= 32'h0; write_ena_in <= 1'b0; fifo_addr_in <= 1'b0; fifo_dat_in <= 32'h0; end else if (count_led[26] == 1'b1) begin count_led <= 32'h0; fifo_dat_in <= fifo_dat_in - 1'b1; fifo_addr_in <= fifo_addr_in + 1'b1; write_ena_in <= 1; end else begin count_led <= count_led + 1'b1; write_ena_in <= 0; end end assign GPIO_LED = fifo_dat_wire_out[3:0];
なお、ucfファイルの制約には以下を追加します。
NET "GPIO_LED[*]" IOSTANDARD = LVCMOS18; NET "GPIO_LED[0]" LOC = P4; # "GPIO_LED1" NET "GPIO_LED[1]" LOC = L6; # "GPIO_LED2" NET "GPIO_LED[2]" LOC = F5; # "GPIO_LED3" NET "GPIO_LED[3]" LOC = C2; # "GPIO_LED4"
*1:クロックに同期させた場合、メモリとして推論してくれる