Lynx-EyEDの電音鍵盤 新館

広帯域制御屋の駄文とか

分散RAMのインスタンシエートとMCBの連結(その1)

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じゃネーヨという突込みが入りそう)
f:id:Lynx-EyED:20111002195850j:image
Next ->

Select IPで階層をたどり、Memories & Storage Elements-> RAMs & ROMs -> Distributed Memory Generatorを選択してNext ->
f:id:Lynx-EyED:20111002200117j:image


Distributed Memory Generatorの設定画面で、深さ128、データ幅32bitを選択します。Next->
f:id:Lynx-EyED:20111002200441j:image

Input Options、Output Optionsともに、Non Registeredになっていることを確認。Next->
f:id:Lynx-EyED:20111002200725j:image

次の画面は特になにも設定する項目はないのでそのまま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&#39;h0;
		write_ena_in	<= 1&#39;b0;
		fifo_addr_in	<= 1&#39;b0;
		fifo_dat_in	<= 32&#39;h0;
	end else if (count_led[26] ==  1&#39;b1)
	begin
		count_led	<= 32&#39;h0;
		fifo_dat_in	<= fifo_dat_in - 1&#39;b1;
		fifo_addr_in	<= fifo_addr_in + 1&#39;b1;
		write_ena_in	<= 1;
	end else 
	begin
		count_led 	<= count_led + 1&#39;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:クロックに同期させた場合、メモリとして推論してくれる