FRAMにアクセス (FPGA Top作成編)
Platform DesignerとFPGA Topの作成になります。
FPGAのTOP構成
FPGAのTOP構成は以下となります。(Quartusを抜粋)
ソースコードは以下の通りです。
尚、FRAM I/FブロックのPRM_???は固定値にしています。本来はブートシーケンスが制御することを想定しているため、FPGAでは簡易的に外部からの指示に応じて、メモリ内に保存するような記述にしています。
module TOP( input wire RESET_N , // @@ リセット input wire CLOCK , // @@ クロック output wire FRAM_WP_N , // @@ FRAM ライトプロテクト[0:プロテクト] output wire FRAM_CS_N , // @@ FRAM SPI チップセレクト output wire FRAM_SCLK , // @@ FRAM SPI シリアルクロック output wire FRAM_MOSI , // @@ FRAM SPI シリアル出力 input wire FRAM_MISO , // @@ FRAM SPI シリアル入力 input wire SW_SLAVE , // @@ Master/Slave切替 [0:Master] input wire SW_GO , // @@ 処理イネーブル output wire LED_LOCK // @@ LED Lock ); // @@ // @@ クロック/リセット生成 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wire cpu_rst_n; wire cpu_clk ; wire sys_rst_n; wire sys_clk ; NML_CLKRST UnCLKRST( .PAD_RST_N (RESET_N ), // input wire PAD_RST_N , // @@ 入力リセット .PAD_CLK (CLOCK ), // input wire PAD_CLK , // @@ 入力クロック .CPU_RST_N (cpu_rst_n ), // output reg CPU_RST_N , // @@ CPU用 リセット .CPU_CLK (cpu_clk ), // output wire CPU_CLK , // @@ CPU用 クロック[50MHz] .SYS_RST_N (sys_rst_n ), // output reg SYS_RST_N , // @@ System用 リセット .SYS_CLK (sys_clk ), // output wire SYS_CLK , // @@ System用 クロック[200MHz] .PLL_LOCK (LED_LOCK ) // output wire PLL_LOCK // @@ PLL LOCK ); // @@ // @@ CPU // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wire fram_ami_wen; wire [31:0] fram_ami_add; wire [ 3:0] fram_ami_ben; wire [ 7:0] fram_ami_wdt; wire fram_asi_wen; wire fram_asi_ren; wire [ 5:0] fram_asi_add; wire [31:0] fram_asi_wdt; wire fram_asi_wit; wire fram_asi_rvl; wire [31:0] fram_asi_rdt; wire fram_cpu_irq; NML_CPU UnCPU( .clk_clk (cpu_clk ), // input wire clk_clk, // clk.clk .irq_irq (fram_cpu_irq ), // input wire [0:0] irq_irq, // irq.irq .fram_write (fram_asi_wen ), // output wire fram_write, // .write .fram_read (fram_asi_ren ), // output wire fram_read, // .read .fram_address (fram_asi_add ), // output wire [5:0] fram_address, // .address .fram_writedata (fram_asi_wdt ), // output wire [31:0] fram_writedata, // .writedata .fram_waitrequest (fram_asi_wit ), // input wire fram_waitrequest, // fram.waitrequest .fram_readdatavalid (fram_asi_rvl ), // input wire fram_readdatavalid, // .readdatavalid .fram_readdata (fram_asi_rdt ), // input wire [31:0] fram_readdata, // .readdata .fram_burstcount (/** OPEN **/ ), // output wire [0:0] fram_burstcount, // .burstcount .fram_byteenable (/** OPEN **/ ), // output wire [3:0] fram_byteenable, // .byteenable .fram_debugaccess (/** OPEN **/ ), // output wire fram_debugaccess, // .debugaccess .fram_wp_export (FRAM_WP_N ), // output wire fram_wp_export, // fram_wp.export .param_clken (1'b1 ), // input wire param_clken, // .clken .param_chipselect (1'b1 ), // input wire param_chipselect, // .chipselect .param_write ( fram_ami_wen ), // input wire param_write, // .write .param_address ( fram_ami_add[11: 2] ), // input wire [9:0] param_address, // param.address .param_byteenable ( fram_ami_ben ), // input wire [3:0] param_byteenable, // .byteenable .param_writedata ({4{fram_ami_wdt}} ), // input wire [31:0] param_writedata, // .writedata .param_readdata (/** OPEN **/ ), // output wire [31:0] param_readdata, // .readdata .port_a_export ( ), // output wire [7:0] port_a_export, // port_a.export .port_b_export ( ), // output wire [7:0] port_b_export, // port_b.export .reset_reset_n (cpu_rst_n ) // input wire reset_reset_n // reset.reset_n ); // @@ // @@ FRAM I/F // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NML_FRAMIF UnFRAMIF( .CPU_RST_N (cpu_rst_n ), // input wire CPU_RST_N , // @@ CPU I/F用 リセット .CPU_CLK (cpu_clk ), // input wire CPU_CLK , // @@ CPU I/F用 クロック .SYS_RST_N (sys_rst_n ), // input wire SYS_RST_N , // @@ システム動作用 リセット .SYS_CLK (sys_clk ), // input wire SYS_CLK , // @@ システム動作用 クロック .MST_SLV (SW_SLAVE ), // input wire MST_SLV , // @@ Avalon-MM Master/Slave切替 [0:Master] .PRM_ENB (SW_GO ), // input wire PRM_ENB , // @@ 処理イネーブル [1→0:OFF 0→1:ON] .PRM_DIV (4'd0 ), // input wire [ 3:0] PRM_DIV , // @@ シリアルクロック周波数設定 .PRM_POL (1'b0 ), // input wire PRM_POL , // @@ シリアルクロック極性設定 .PRM_TAK (2'd0 ), // input wire [ 1:0] PRM_TAK , // @@ シリアルデータ取り込みタイミング設定 .PRM_ACS (21'd8192 ), // input wire [20:0] PRM_ACS , // @@ アクセスバイト数 (最大値:1,048,579) .PRM_OTC (2'd3 ), // input wire [ 1:0] PRM_OTC , // @@ 出力バイト数 (最大値:3) .PRM_MSK (1'b0 ), // input wire PRM_MSK , // @@ 割り込みマスク .PRM_CLR (1'b0 ), // input wire PRM_CLR , // @@ 割り込みクリア .PRM_STA (32'h0000_0000 ), // input wire [31:0] PRM_STA , // @@ Avalon-MM(Master側) ライトベースアドレス .PRM_CMD (8'h03 ), // input wire [ 7:0] PRM_CMD , // @@ RAMコマンド .PRM_ADD (24'h00_0000 ), // input wire [23:0] PRM_ADD , // @@ FRAMアドレス .SAV_WEN (fram_asi_wen ), // input wire SAV_WEN , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ライトイネーブル .SAV_REN (fram_asi_ren ), // input wire SAV_REN , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードイネーブル .SAV_ADD (fram_asi_add ), // input wire [ 5:0] SAV_ADD , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リード/ライトイネーブル .SAV_WDT (fram_asi_wdt ), // input wire [31:0] SAV_WDT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ライトデータ .SAV_WIT (fram_asi_wit ), // output wire SAV_WIT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ウェイトリクエスト .SAV_RVL (fram_asi_rvl ), // output wire SAV_RVL , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードデータイネーブル .SAV_RDT (fram_asi_rdt ), // output wire [31:0] SAV_RDT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードデータ .MAV_WEN (fram_ami_wen ), // output wire MAV_WEN , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトイネーブル .MAV_ADD (fram_ami_add ), // output wire [31:0] MAV_ADD , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトアドレス .MAV_BEN (fram_ami_ben ), // output wire [ 3:0] MAV_BEN , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) バイトイネーブル .MAV_WDT (fram_ami_wdt ), // output wire [ 7:0] MAV_WDT , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトデータ .MAV_WIT (1'b0 ), // input wire MAV_WIT , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ウェイトリクエスト .CPU_IRQ (fram_cpu_irq ), // output wire CPU_IRQ , // @@ [CPU_CLK↑同期] 割り込み信号 .MON_ENB (/** OPEN **/ ), // output wire MON_ENB , // @@ [CPU_CLK↑同期] 動作モニタ [0:停止中 1:動作中] .FRAM_CS_N (FRAM_CS_N ), // output wire FRAM_CS_N , // @@ [SYS_CLK↑同期] FRAM SPI チップセレクト .FRAM_SCLK (FRAM_SCLK ), // output wire FRAM_SCLK , // @@ [SYS_CLK↑同期] FRAM SPI シリアルクロック .FRAM_MOSI (FRAM_MOSI ), // output wire FRAM_MOSI , // @@ [SYS_CLK↑同期] FRAM SPI シリアル出力 .FRAM_MISO (FRAM_MISO ), // input wire FRAM_MISO , // @@ [SYS_CLK↑同期] FRAM SPI シリアル入力 .FRAM_OUTE (/** OPEN **/ ) // output wire FRAM_OUTE // @@ [SYS_CLK↑同期] FRAM SPI 出力イネーブル[1:出力 0:Hi-Z] ); endmodule
Platform Designer
QuartusのPlatform DesignerでNiosIIやメモリなどを配置します。
ただし、PLLやユーザーブロック(FRAM I/Fブロック)は配置せず、FPGAトップに配置します。
これはFPGAトップからRTLシミュレーションする時に、Platform Designerで生成したRTLの配線を検索するのが大変なことと、バグがあった時に修正に手間がかかるためです。
また、PLLを外に置くことにより、クロックの追加やリセット回路の変更を容易にすることが目的です。
私個人の考えとして、Platform Designerで作成する所は、変更が少ない所を配置したいと考えています。
また、Qsysファイルにはタイムスタンプがあり、このブロックを変更すると、NiosのBSPを再実行する必要があるためです。
ブロック構成図
ブロック構成を以下に示します。
※Platform Designerで作成するブロックは、モジュール「NML_CPU」になります。
※モジュールNML_FRAMIFのverilogソースは、以下をご参照ください。
nao-milk.hatenablog.com
System Contents
アドレスマップ
CPUから見たアドレスマップを以下に示します。
Quartusに読み込ませるファイル
Platform Designerの[Generate HDL]でVerilogコードが生成されます。
その時、フォルダ「モジュール名\synthesis」に"モジュール名.qip"が出来上がります。
このファイルをQuartusに読み込ませます。
"モジュール名.qsys"を読み込ませると、合成時にGenerate HDLもするため、タイムスタンプが変わり、NiosのBSPにも影響があり面倒なので、私はqipを読み込ませています。
クロック/リセット生成
PLLを配置し、リファレンスクロック(25MHz)から50MHzと200MHzクロックを生成し、クロックドメインに対応したリセット信号を生成します。
ソースコードは以下の通りです。
// ** // ** クロック/リセット生成 // ** // ***************************************************************************** module NML_CLKRST( input wire PAD_RST_N , // @@ 入力リセット input wire PAD_CLK , // @@ 入力クロック output reg CPU_RST_N , // @@ CPU用 リセット output wire CPU_CLK , // @@ CPU用 クロック[50MHz] output reg SYS_RST_N , // @@ System用 リセット output wire SYS_CLK , // @@ System用 クロック[200MHz] output wire PLL_LOCK // @@ PLL LOCK ); // @@ // @@ PLL Macro // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NML_CLKRST_PLL UnPLL( .refclk ( PAD_CLK ), // input wire refclk, // refclk.clk .rst (~PAD_RST_N ), // input wire rst, // reset.reset .outclk_0 ( CPU_CLK ), // output wire outclk_0, // outclk0.clk .outclk_1 ( SYS_CLK ), // output wire outclk_1, // outclk1.clk .locked ( PLL_LOCK ) // output wire locked // locked.export ); // @@ // @@ リセット回路 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ reg [ 1:0] cpu_rst_sft; reg [ 1:0] sys_rst_sft; always @( posedge CPU_CLK or negedge PLL_LOCK ) begin if( ~PLL_LOCK ) {CPU_RST_N,cpu_rst_sft} <= {1'b0,2'b00}; else {CPU_RST_N,cpu_rst_sft} <= {cpu_rst_sft,1'b1}; end always @( posedge SYS_CLK or negedge PLL_LOCK ) begin if( ~PLL_LOCK ) {SYS_RST_N,sys_rst_sft} <= {1'b0,2'b00}; else {SYS_RST_N,sys_rst_sft} <= {sys_rst_sft,1'b1}; end endmodule
最後に
Platform DesignerはIPマクロを配線するのに便利です。
が、ユーザーロジックを含めて配線するとデバッグ時にユーザー回路のポートを修正すると手間が掛かります。