ZedBoard Linux (6) [FPGA]
FPGA側にDMA付けてみます。
まずは読むだけのメモリダンプ。
ZedBoardにはアナログRGBのD-SUBコネクタが付いてます。ここに指定したアドレスのデータをメモリダンプさせます。要はフレームバッファー。むか~しのマイコン時代から使われている、画像メモリでプログラムを動かしてデバッグしよう、方式です。
なんでHDMIがあるのにわざわざアナログRGBなんてレガシーな物を…って話ですが、レガシーだからこそ。レガシーなインターフェースって無手順で相手の都合を考えず一方的にデータを送りつけても良い、という使い方ができます。デバッグ当初のまだいろいろ立ち上がってない環境では楽ちんです。相変わらずUART(RS-232C)が使われ続ける理由と似てると思います。
12bitBGRなので、画素のサイズは16bit/pixelにします。4bitは無視。
そして書くだけのメモリフィラー。
こちらはボード上に簡単なデータ入力装置が無いので(マイク端子が楽ですが)指定したアドレスから指定したサイズのメモリにカウンターの値を無理矢理書き込むマスターにしてみます。もしLinuxの管理領域に書き込んだら即暴走。先に確保した128MB以降全部埋めてもLinuxが動き続けるのを確認します。本来はカメラのデータとか音声データを流し込みます。
あと、DMAでメモリから読み出したデータをアナログRGBのタイミングに載せるペリフェラルも用意します。これは昔からAvalon(AlteraのNios)で使ってる物をAPBに焼き直しました。タイミングのパラメータをX.Org(XFree86)のmodelineの値と同じ値が使えるようになってます。が、ちゃんと解像度可変にするにはピクセルクロックも可変にしないとならないので、結局固定です(笑)。まぁモニタの対応周波数内で動かせますがあまり意味ないです。周波数は100MHzにしてます。
とりあえずtopデザイン。tab=4そのままなので見にくいかも。
こう見ると長いですが、ほとんど同じ名前の信号をつなぐだけになってます。SystemVerilogのInterface記述やQsys、XPSといったツールが出てきたのもわかりますね。手で書いてたら無駄ですもん。コピペとマクロでやってても面倒くさい。(記述が無駄に冗長なのは半自動的だからです。)
DMA本体やVGAインターフェースもそのうち公開すると思いますが、複数のモジュールに別れてるので結構面倒くさい。どういう公開方法が楽かな?バージョン管理にgit使ってるのでそのままgithubとかに上げるのが良いのかな?ライセンスとかどうするのが良いんでしょ…オープンソースなハードウェアのライセンスってGPLとかCCとかで良いのかな?
モジュールに別れてますが、意外に小さいと思います。
VGA DMA:約270行
AXI Read シーケンサー:約240行
メモリフィル・DMA:約330行
AXI Write シーケンサー:約340行
VGAインターフェース:約280行
その他100~150行ぐらいの細かいファイルが5~6個
という構成。機能削ればこの程度です。
VGAのピン配置をZedBoardのマスターucfから持ってきて、コンパイルします。Sliceの使用率は6%程度。(そのうち約半分はXilinxのAPB関連のIP。しかもその中でまた新たにCritical Warning出てるしw。)
できあがったbitストリームを差し替えてBOOT.BINを作成。他のLinux関連のファイルはそのままです。立ち上げてもなにも変わりません。Linuxにはペリフェラル増えたとか通知してませんし。
PC側でSDKを立ち上げて、Linuxアプリを作成します。
レジスタ構成とかはまた今度として、こんな感じでプログラム作ってLinuxに転送し実行すると画面に画が出てきます。
Fill DMAがカウンター値を書き込んでるので、格子状のベタ画像になります。Linuxは動作し続けてるので、Fill DMAが書き込んだのはLinux管理外の領域となってるようです。Fill DMAの先頭アドレスを0x0000_0000とかにしてLinuxが管理してるはずのメモリ領域に書き込ませると、ちゃんと暴走します。VGA DMAの先頭アドレスをLinuxの領域にしてなにか操作すると画像が変わるのがわかります。
Linuxのどこかの管理領域。
Linuxでも絶対アドレスを指定したポインタによるアクセスって意外と簡単にできるんですね。知らんかったです。デバイスドライバとか書かなくて済むのでデバッグ時は楽です。デバイスドライバ書くとかpcore形式にしてXPSに登録するとか、これがうれしいのは繰り返し使うときで、一回しか使わないときは手間なんですよね。
これでまずはメモリを分けてLinuxの管理領域外のメモリを用意し、FPGAから直接操作する基本的な構成ができました。CPUのキャッシュとの関連とか帯域制御とかまだ色々ありそうですが、必要最低限は用意できたと思います。多分他のプラットフォーム(MBとかNiosIIとか)でも同じ方法で行けるはずです。
Linuxが使えるメモリが減ってしまい、自由度が下がるのが問題ですが、LinuxのページングモデルにFPGAロジックを摺り合わせるよりは楽だと思います。
まずは読むだけのメモリダンプ。
ZedBoardにはアナログRGBのD-SUBコネクタが付いてます。ここに指定したアドレスのデータをメモリダンプさせます。要はフレームバッファー。むか~しのマイコン時代から使われている、画像メモリでプログラムを動かしてデバッグしよう、方式です。
なんでHDMIがあるのにわざわざアナログRGBなんてレガシーな物を…って話ですが、レガシーだからこそ。レガシーなインターフェースって無手順で相手の都合を考えず一方的にデータを送りつけても良い、という使い方ができます。デバッグ当初のまだいろいろ立ち上がってない環境では楽ちんです。相変わらずUART(RS-232C)が使われ続ける理由と似てると思います。
12bitBGRなので、画素のサイズは16bit/pixelにします。4bitは無視。
そして書くだけのメモリフィラー。
こちらはボード上に簡単なデータ入力装置が無いので(マイク端子が楽ですが)指定したアドレスから指定したサイズのメモリにカウンターの値を無理矢理書き込むマスターにしてみます。もしLinuxの管理領域に書き込んだら即暴走。先に確保した128MB以降全部埋めてもLinuxが動き続けるのを確認します。本来はカメラのデータとか音声データを流し込みます。
あと、DMAでメモリから読み出したデータをアナログRGBのタイミングに載せるペリフェラルも用意します。これは昔からAvalon(AlteraのNios)で使ってる物をAPBに焼き直しました。タイミングのパラメータをX.Org(XFree86)のmodelineの値と同じ値が使えるようになってます。が、ちゃんと解像度可変にするにはピクセルクロックも可変にしないとならないので、結局固定です(笑)。まぁモニタの対応周波数内で動かせますがあまり意味ないです。周波数は100MHzにしてます。
とりあえずtopデザイン。tab=4そのままなので見にくいかも。
/************************ * top ************************/ `timescale 1ns / 1ps `default_nettype none //************************************************************************************************** // module top ( // ============================================== // Hard Wired inout wire [53:0] MIO , input wire PS_SRSTB , input wire PS_CLK , input wire PS_PORB , inout wire DDR_Clk , inout wire DDR_Clk_n , inout wire DDR_CKE , inout wire DDR_CS_n , inout wire DDR_RAS_n , inout wire DDR_CAS_n , output wire DDR_WEB , inout wire [ 2:0] DDR_BankAddr , inout wire [14:0] DDR_Addr , inout wire DDR_ODT , inout wire DDR_DRSTB , inout wire [31:0] DDR_DQ , inout wire [ 3:0] DDR_DM , inout wire [ 3:0] DDR_DQS , inout wire [ 3:0] DDR_DQS_n , inout wire DDR_VRN , inout wire DDR_VRP , // ============================================== // VGA output wire VGA_HS , output wire VGA_VS , output wire [ 4:1] VGA_B , output wire [ 4:1] VGA_G , output wire [ 4:1] VGA_R ); //////////////////////////// // // Property parameter blen = 16 ; // burst length parameter qdep = 8 ; // queue depth = 2**qdep parameter Xsymb = 1'bX ; // invalid Symbol // Video parameter vodw = 12 ; // VGA Out Data Width parameter vsdw = 16 ; // Video Stream Data Width parameter cntw = 11 ; // Counter Width // AXI parameter axiw = 6 ; // ID parameter axaw = 32 ; // Address parameter axdw = 64 ; // Data parameter axmw = axdw/8 ; // Mask parameter axrw = 2 ; // Response parameter axlw = 4 ; // Length parameter axsw = 3 ; // Size // AXI master (static) parameter axbw = 2 ; // Burst parameter axcw = 4 ; // Cache parameter axpw = 3 ; // Prot parameter axkw = 2 ; // Lock parameter axqw = 4 ; // QoS // APB Slave parameter apaw = 32 ; // Address parameter apdw = 32 ; // Data parameter apsw = 4 ; // Slaves //////////////////////////// // Clock wire FCLK_CLK0; // 66.6MHz wire FCLK_CLK1; // 100MHz wire FCLK_CLK2; // 166.6MHz wire FCLK_CLK3; // 200Mhz //////////////////////////// // EMIO GPIO wire [63:0] EMIO_GPIO_I; wire [63:0] EMIO_GPIO_O; wire [63:0] EMIO_GPIO_T; assign EMIO_GPIO_I = EMIO_GPIO_O ^ EMIO_GPIO_T; //////////////////////////// // APB wire apb_pclk ; // clock wire apb_presetn ; // reset_n wire [apaw-1:0] apb_paddr ; // address wire [apsw-1:0] apb_psel ; // select wire apb_penable ; // enalbe wire apb_pwrite ; // write wire [apdw-1:0] apb_pwdata ; // w data wire [apdw-1:0] apb_prdata [apsw-1:0] ; // r data wire [apsw-1:0] apb_pready ; // ready wire [apsw-1:0] apb_pslverr ; // error assign apb_pready = apb_psel; // no-wait assign apb_pslverr = {apsw{1'b0}}; // no-error assign apb_prdata[3] = apb_paddr; // unused slave //////////////////////////// // AXI HP wire axi_hp_aclk ; // // Address Write wire axi_hp_awready ; // wire axi_hp_awvalid ; // wire [axiw-1:0] axi_hp_awid ; // wire [axaw-1:0] axi_hp_awaddr ; // wire [axlw-1:0] axi_hp_awlen ; // wire [axsw-1:0] axi_hp_awsize ; // wire [axbw-1:0] axi_hp_awburst ; // static wire [axcw-1:0] axi_hp_awcache ; // static wire [axpw-1:0] axi_hp_awprot ; // static wire [axkw-1:0] axi_hp_awlock ; // static wire [axqw-1:0] axi_hp_awqos ; // static // Write Data wire axi_hp_wready ; // wire axi_hp_wvalid ; // wire [axiw-1:0] axi_hp_wid ; // wire axi_hp_wlast ; // wire [axdw-1:0] axi_hp_wdata ; // wire [axmw-1:0] axi_hp_wstrb ; // // Write Response wire axi_hp_bready ; // wire axi_hp_bvalid ; // wire [axiw-1:0] axi_hp_bid ; // wire [axrw-1:0] axi_hp_bresp ; // // Address Read wire axi_hp_arready ; // wire axi_hp_arvalid ; // wire [axiw-1:0] axi_hp_arid ; // wire [axaw-1:0] axi_hp_araddr ; // wire [axlw-1:0] axi_hp_arlen ; // wire [axsw-1:0] axi_hp_arsize ; // wire [axbw-1:0] axi_hp_arburst ; // static wire [axcw-1:0] axi_hp_arcache ; // static wire [axpw-1:0] axi_hp_arprot ; // static wire [axkw-1:0] axi_hp_arlock ; // static wire [axqw-1:0] axi_hp_arqos ; // static // Read Data wire axi_hp_rready ; // wire axi_hp_rvalid ; // wire [axiw-1:0] axi_hp_rid ; // wire axi_hp_rlast ; // wire [axdw-1:0] axi_hp_rdata ; // wire [axrw-1:0] axi_hp_rresp ; // //////////////////////////// // clock,reset wiring wire a_reset; wire c_clock; wire c_reset; assign a_reset = ~apb_presetn; // assign c_clock = FCLK_CLK1; // assign c_reset = 1'b0; // assign axi_hp_aclk = c_clock; // //////////////////////////// // Video wire v_hsync ; // H Sync wire v_vsync ; // V Sync wire [vodw-1:0] v_data ; // data wire tx_kick ; // kick wire tx_ready ; // ready wire tx_valid ; // valid wire [vsdw-1:0] tx_data ; // data assign VGA_HS = v_hsync; assign VGA_VS = v_vsync; assign {VGA_R,VGA_G,VGA_B} = v_data; //////////////////////////// // VGA I/F vga_if #( // Property .vodw (vodw ), // parameter vodw = 12 , // VGA Out Data Width .vsdw (vsdw ), // parameter vsdw = 16 , // Video Stream Data Width .cntw (cntw ), // parameter cntw = 11 , // Counter Width // APB Slave .apaw (apaw ), // parameter apaw = 32 , // Address .apdw (apdw ) // parameter apdw = 32 // Data ) voif ( // ============================================== // common .a_reset (a_reset ), // input wire a_reset , // reset .c_clock (c_clock ), // input wire c_clock , // clock .c_reset (c_reset ), // input wire c_reset , // reset // ============================================== // VGA .v_hsync (v_hsync ), // output wire v_hsync , // H Sync .v_vsync (v_vsync ), // output wire v_vsync , // V Sync .v_data (v_data ), // output wire [vodw-1:0] v_data , // data // ============================================== // Transmit Stream + kick .tx_kick (tx_kick ), // output wire tx_kick , // kick .tx_ready (tx_ready ), // output wire tx_ready , // ready .tx_valid (tx_valid ), // input wire tx_valid , // valid .tx_data (tx_data ), // input wire [vsdw-1:0] tx_data , // data // ============================================== // APB .apb_pclk (apb_pclk ), // input wire apb_pclk , // clock .apb_presetn (apb_presetn ), // input wire apb_presetn , // reset_n .apb_paddr (apb_paddr ), // input wire [apaw-1:0] apb_paddr , // address .apb_psel (apb_psel [0] ), // input wire apb_psel , // select .apb_penable (apb_penable ), // input wire apb_penable , // enalbe .apb_pwrite (apb_pwrite ), // input wire apb_pwrite , // write .apb_pwdata (apb_pwdata ), // input wire [apdw-1:0] apb_pwdata , // data .apb_prdata (apb_prdata [0] ) // output reg [apdw-1:0] apb_prdata , // data ); //////////////////////////// // VGA DMA vga_dma #( // Property .vsdw (vsdw ), // parameter vsdw = 16 , // Video Stream data width .blen (blen ), // parameter blen = 16 , // burst length .qdep (qdep ), // parameter qdep = 8 , // queue depth = 2**qdep // AXI master .axiw (axiw ), // parameter axiw = 6 , // ID .axaw (axaw ), // parameter axaw = 32 , // Address .axdw (axdw ), // parameter axdw = 64 , // Data .axmw (axmw ), // parameter axmw = axdw/8 , // Mask .axrw (axrw ), // parameter axrw = 2 , // Response .axlw (axlw ), // parameter axlw = 4 , // Length .axsw (axsw ), // parameter axsw = 3 , // Size // AXI master (static) .axbw (axbw ), // parameter axbw = 2 , // Burst .axcw (axcw ), // parameter axcw = 4 , // Cache .axpw (axpw ), // parameter axpw = 3 , // Prot .axkw (axkw ), // parameter axkw = 2 , // Lock .axqw (axqw ), // parameter axqw = 4 , // QoS // APB Slave .apaw (apaw ), // parameter apaw = 32 , // Address .apdw (apdw ), // parameter apdw = 32 , // Data // misc .Xsymb (Xsymb ) // parameter Xsymb = 1'bX // invalid Symbol ) vdma ( // ============================================== // common .a_reset (a_reset ), // input wire a_reset , // reset .c_clock (c_clock ), // input wire c_clock , // clock .c_reset (c_reset ), // input wire c_reset , // reset // ============================================== // Transmit Stream + kick .tx_kick (tx_kick ), // input wire tx_kick , // kick .tx_ready (tx_ready ), // output wire tx_ready , // ready .tx_valid (tx_valid ), // input wire tx_valid , // valid .tx_data (tx_data ), // input wire [vsdw-1:0] tx_data , // data // ============================================== // Address Read .axi_arready (axi_hp_arready ), // input wire axi_arready , // .axi_arvalid (axi_hp_arvalid ), // output wire axi_arvalid , // .axi_arid (axi_hp_arid ), // output wire [axiw-1:0] axi_arid , // .axi_araddr (axi_hp_araddr ), // output wire [axaw-1:0] axi_araddr , // .axi_arlen (axi_hp_arlen ), // output wire [axlw-1:0] axi_arlen , // .axi_arsize (axi_hp_arsize ), // output wire [axsw-1:0] axi_arsize , // .axi_arburst (axi_hp_arburst ), // output wire [axbw-1:0] axi_arburst , // static .axi_arcache (axi_hp_arcache ), // output wire [axcw-1:0] axi_arcache , // static .axi_arprot (axi_hp_arprot ), // output wire [axpw-1:0] axi_arprot , // static .axi_arlock (axi_hp_arlock ), // output wire [axkw-1:0] axi_arlock , // static .axi_arqos (axi_hp_arqos ), // output wire [axqw-1:0] axi_arqos , // static // Read Data .axi_rready (axi_hp_rready ), // output wire axi_rready , // .axi_rvalid (axi_hp_rvalid ), // input wire axi_rvalid , // .axi_rlast (axi_hp_rlast ), // input wire axi_rlast , // .axi_rid (axi_hp_rid ), // input wire [axiw-1:0] axi_rid , // .axi_rdata (axi_hp_rdata ), // input wire [axdw-1:0] axi_rdata , // .axi_rresp (axi_hp_rresp ), // input wire [axrw-1:0] axi_rresp , // // ============================================== // APB .apb_pclk (apb_pclk ), // input wire apb_pclk , // clock .apb_presetn (apb_presetn ), // input wire apb_presetn , // reset_n .apb_paddr (apb_paddr ), // input wire [apaw-1:0] apb_paddr , // address .apb_psel (apb_psel [1] ), // input wire apb_psel , // select .apb_penable (apb_penable ), // input wire apb_penable , // enalbe .apb_pwrite (apb_pwrite ), // input wire apb_pwrite , // write .apb_pwdata (apb_pwdata ), // input wire [apdw-1:0] apb_pwdata , // data .apb_prdata (apb_prdata [1] ) // output reg [apdw-1:0] apb_prdata , // data ); //////////////////////////// // Filler fill_dma #( // Property .blen (blen ), // parameter blen = 16 , // burst length .qdep (qdep ), // parameter qdep = 8 , // queue depth = 2**qdep // AXI master .axiw (axiw ), // parameter axiw = 6 , // ID .axaw (axaw ), // parameter axaw = 32 , // Address .axdw (axdw ), // parameter axdw = 64 , // Data .axmw (axmw ), // parameter axmw = axdw/8 , // Mask .axlw (axlw ), // parameter axlw = 4 , // Length .axsw (axsw ), // parameter axsw = 3 , // Size .axrw (axrw ), // parameter axrw = 2 , // Response // AXI master (static) .axbw (axbw ), // parameter axbw = 2 , // Burst .axcw (axcw ), // parameter axcw = 4 , // Cache .axpw (axpw ), // parameter axpw = 3 , // Prot .axkw (axkw ), // parameter axkw = 2 , // Lock .axqw (axqw ), // parameter axqw = 4 , // QoS // APB Slave .apaw (apaw ), // parameter apaw = 32 , // Address .apdw (apdw ), // parameter apdw = 32 , // Data // misc .Xsymb (Xsymb ) // parameter Xsymb = 1'bX // invalid Symbol ) filler ( // ============================================== // common .a_reset (a_reset ), // input wire a_reset , // reset .c_clock (c_clock ), // input wire c_clock , // clock .c_reset (c_reset ), // input wire c_reset , // reset // ============================================== // Address Write .axi_awready (axi_hp_awready ), // input wire axi_awready , // .axi_awvalid (axi_hp_awvalid ), // output wire axi_awvalid , // .axi_awid (axi_hp_awid ), // output wire [axiw-1:0] axi_awid , // .axi_awaddr (axi_hp_awaddr ), // output wire [axaw-1:0] axi_awaddr , // .axi_awlen (axi_hp_awlen ), // output wire [axlw-1:0] axi_awlen , // .axi_awsize (axi_hp_awsize ), // output wire [axsw-1:0] axi_awsize , // .axi_awburst (axi_hp_awburst ), // output wire [axbw-1:0] axi_awburst , // static .axi_awcache (axi_hp_awcache ), // output wire [axcw-1:0] axi_awcache , // static .axi_awprot (axi_hp_awprot ), // output wire [axpw-1:0] axi_awprot , // static .axi_awlock (axi_hp_awlock ), // output wire [axkw-1:0] axi_awlock , // static .axi_awqos (axi_hp_awqos ), // output wire [axqw-1:0] axi_awqos , // static // Write Data .axi_wready (axi_hp_wready ), // input wire axi_wready , // .axi_wvalid (axi_hp_wvalid ), // output wire axi_wvalid , // .axi_wlast (axi_hp_wlast ), // output wire axi_wlast , // .axi_wid (axi_hp_wid ), // output wire [axiw-1:0] axi_wid , // .axi_wdata (axi_hp_wdata ), // output wire [axdw-1:0] axi_wdata , // .axi_wstrb (axi_hp_wstrb ), // output wire [axmw-1:0] axi_wstrb , // // Write Response .axi_bready (axi_hp_bready ), // output wire axi_bready , // .axi_bvalid (axi_hp_bvalid ), // input wire axi_bvalid , // .axi_bid (axi_hp_bid ), // input wire [axiw-1:0] axi_bid , // .axi_bresp (axi_hp_bresp ), // input wire [axrw-1:0] axi_bresp , // // ============================================== // APB .apb_pclk (apb_pclk ), // input wire apb_pclk , // clock .apb_presetn (apb_presetn ), // input wire apb_presetn , // reset_n .apb_paddr (apb_paddr ), // input wire [apaw-1:0] apb_paddr , // address .apb_psel (apb_psel [2] ), // input wire apb_psel , // select .apb_penable (apb_penable ), // input wire apb_penable , // enalbe .apb_pwrite (apb_pwrite ), // input wire apb_pwrite , // write .apb_pwdata (apb_pwdata ), // input wire [apdw-1:0] apb_pwdata , // data .apb_prdata (apb_prdata [2] ) // output reg [apdw-1:0] apb_prdata , // data ); //////////////////////////// // PS (* BOX_TYPE = "user_black_box" *) ps_module ps ( // ============================================== // Hard Wired .processing_system7_0_MIO (MIO ), .processing_system7_0_PS_SRSTB_pin (PS_SRSTB ), .processing_system7_0_PS_CLK_pin (PS_CLK ), .processing_system7_0_PS_PORB_pin (PS_PORB ), .processing_system7_0_DDR_Clk (DDR_Clk ), .processing_system7_0_DDR_Clk_n (DDR_Clk_n ), .processing_system7_0_DDR_CKE (DDR_CKE ), .processing_system7_0_DDR_CS_n (DDR_CS_n ), .processing_system7_0_DDR_RAS_n (DDR_RAS_n ), .processing_system7_0_DDR_CAS_n (DDR_CAS_n ), .processing_system7_0_DDR_WEB_pin (DDR_WEB ), .processing_system7_0_DDR_BankAddr (DDR_BankAddr ), .processing_system7_0_DDR_Addr (DDR_Addr ), .processing_system7_0_DDR_ODT (DDR_ODT ), .processing_system7_0_DDR_DRSTB (DDR_DRSTB ), .processing_system7_0_DDR_DQ (DDR_DQ ), .processing_system7_0_DDR_DM (DDR_DM ), .processing_system7_0_DDR_DQS (DDR_DQS ), .processing_system7_0_DDR_DQS_n (DDR_DQS_n ), .processing_system7_0_DDR_VRN (DDR_VRN ), .processing_system7_0_DDR_VRP (DDR_VRP ), // ============================================== // EMIO .processing_system7_0_GPIO_I_pin (EMIO_GPIO_I ), .processing_system7_0_GPIO_O_pin (EMIO_GPIO_O ), .processing_system7_0_GPIO_T_pin (EMIO_GPIO_T ), // ============================================== // Clock .processing_system7_0_FCLK_CLK0_pin (FCLK_CLK0 ), .processing_system7_0_FCLK_CLK1_pin (FCLK_CLK1 ), .processing_system7_0_FCLK_CLK2_pin (FCLK_CLK2 ), .processing_system7_0_FCLK_CLK3_pin (FCLK_CLK3 ), // ============================================== // APB .axi_apb_bridge_0_M_APB_PCLK_pin (apb_pclk ), .axi_apb_bridge_0_M_APB_PRESETN_pin (apb_presetn ), .axi_apb_bridge_0_M_APB_PADDR_pin (apb_paddr ), .axi_apb_bridge_0_M_APB_PSEL_pin (apb_psel ), .axi_apb_bridge_0_M_APB_PENABLE_pin (apb_penable ), .axi_apb_bridge_0_M_APB_PWRITE_pin (apb_pwrite ), .axi_apb_bridge_0_M_APB_PWDATA_pin (apb_pwdata ), .axi_apb_bridge_0_M_APB_PREADY_pin (apb_pready ), .axi_apb_bridge_0_M_APB_PRDATA_pin (apb_prdata[0] ), .axi_apb_bridge_0_M_APB_PRDATA2_pin (apb_prdata[1] ), .axi_apb_bridge_0_M_APB_PRDATA3_pin (apb_prdata[2] ), .axi_apb_bridge_0_M_APB_PRDATA4_pin (apb_prdata[3] ), .axi_apb_bridge_0_M_APB_PSLVERR_pin (apb_pslverr ), // ============================================== // AXI HP0 .processing_system7_0_S_AXI_HP0_ARREADY_pin (axi_hp_arready ), .processing_system7_0_S_AXI_HP0_AWREADY_pin (axi_hp_awready ), .processing_system7_0_S_AXI_HP0_BVALID_pin (axi_hp_bvalid ), .processing_system7_0_S_AXI_HP0_RLAST_pin (axi_hp_rlast ), .processing_system7_0_S_AXI_HP0_RVALID_pin (axi_hp_rvalid ), .processing_system7_0_S_AXI_HP0_WREADY_pin (axi_hp_wready ), .processing_system7_0_S_AXI_HP0_BRESP_pin (axi_hp_bresp ), .processing_system7_0_S_AXI_HP0_RRESP_pin (axi_hp_rresp ), .processing_system7_0_S_AXI_HP0_BID_pin (axi_hp_bid ), .processing_system7_0_S_AXI_HP0_RID_pin (axi_hp_rid ), .processing_system7_0_S_AXI_HP0_RDATA_pin (axi_hp_rdata ), .processing_system7_0_S_AXI_HP0_ACLK_pin (axi_hp_aclk ), .processing_system7_0_S_AXI_HP0_ARVALID_pin (axi_hp_arvalid ), .processing_system7_0_S_AXI_HP0_AWVALID_pin (axi_hp_awvalid ), .processing_system7_0_S_AXI_HP0_BREADY_pin (axi_hp_bready ), .processing_system7_0_S_AXI_HP0_RREADY_pin (axi_hp_rready ), .processing_system7_0_S_AXI_HP0_WLAST_pin (axi_hp_wlast ), .processing_system7_0_S_AXI_HP0_WVALID_pin (axi_hp_wvalid ), .processing_system7_0_S_AXI_HP0_ARBURST_pin (axi_hp_arburst ), .processing_system7_0_S_AXI_HP0_ARLOCK_pin (axi_hp_arlock ), .processing_system7_0_S_AXI_HP0_ARSIZE_pin (axi_hp_arsize ), .processing_system7_0_S_AXI_HP0_AWBURST_pin (axi_hp_awburst ), .processing_system7_0_S_AXI_HP0_AWLOCK_pin (axi_hp_awlock ), .processing_system7_0_S_AXI_HP0_AWSIZE_pin (axi_hp_awsize ), .processing_system7_0_S_AXI_HP0_ARPROT_pin (axi_hp_arprot ), .processing_system7_0_S_AXI_HP0_AWPROT_pin (axi_hp_awprot ), .processing_system7_0_S_AXI_HP0_ARADDR_pin (axi_hp_araddr ), .processing_system7_0_S_AXI_HP0_AWADDR_pin (axi_hp_awaddr ), .processing_system7_0_S_AXI_HP0_ARCACHE_pin (axi_hp_arcache ), .processing_system7_0_S_AXI_HP0_ARLEN_pin (axi_hp_arlen ), .processing_system7_0_S_AXI_HP0_ARQOS_pin (axi_hp_arqos ), .processing_system7_0_S_AXI_HP0_AWCACHE_pin (axi_hp_awcache ), .processing_system7_0_S_AXI_HP0_AWLEN_pin (axi_hp_awlen ), .processing_system7_0_S_AXI_HP0_AWQOS_pin (axi_hp_awqos ), .processing_system7_0_S_AXI_HP0_ARID_pin (axi_hp_arid ), .processing_system7_0_S_AXI_HP0_AWID_pin (axi_hp_awid ), .processing_system7_0_S_AXI_HP0_WID_pin (axi_hp_wid ), .processing_system7_0_S_AXI_HP0_WDATA_pin (axi_hp_wdata ), .processing_system7_0_S_AXI_HP0_WSTRB_pin (axi_hp_wstrb ) ); endmodule // //************************************************************************************************** `default_nettype wire
こう見ると長いですが、ほとんど同じ名前の信号をつなぐだけになってます。SystemVerilogのInterface記述やQsys、XPSといったツールが出てきたのもわかりますね。手で書いてたら無駄ですもん。コピペとマクロでやってても面倒くさい。(記述が無駄に冗長なのは半自動的だからです。)
DMA本体やVGAインターフェースもそのうち公開すると思いますが、複数のモジュールに別れてるので結構面倒くさい。どういう公開方法が楽かな?バージョン管理にgit使ってるのでそのままgithubとかに上げるのが良いのかな?ライセンスとかどうするのが良いんでしょ…オープンソースなハードウェアのライセンスってGPLとかCCとかで良いのかな?
モジュールに別れてますが、意外に小さいと思います。
VGA DMA:約270行
AXI Read シーケンサー:約240行
メモリフィル・DMA:約330行
AXI Write シーケンサー:約340行
VGAインターフェース:約280行
その他100~150行ぐらいの細かいファイルが5~6個
という構成。機能削ればこの程度です。
VGAのピン配置をZedBoardのマスターucfから持ってきて、コンパイルします。Sliceの使用率は6%程度。(そのうち約半分はXilinxのAPB関連のIP。しかもその中でまた新たにCritical Warning出てるしw。)
できあがったbitストリームを差し替えてBOOT.BINを作成。他のLinux関連のファイルはそのままです。立ち上げてもなにも変わりません。Linuxにはペリフェラル増えたとか通知してませんし。
PC側でSDKを立ち上げて、Linuxアプリを作成します。
(相変わらずエラーチェック無しのいい加減プログラム…) #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> typedef unsigned int uint32; typedef unsigned short uint16; const uint32 param[] = {1280, 1360, 1496, 1712, 960, 961, 964, 994}; タイミング inline void putPixel(uint16 *fb, int x, int y, uint16 c) { fb[y*param[0]+x] = c; 直接書き込みとか } … #define MAP_SIZE 4096UL レジスタ空間のサイズ #define FB_SIZE 1024UL*1024UL*(512-128) Linuxが取った128MB以外全部 … main() /dev/mem開いて直接アドレスでマッピング fd = open("/dev/mem", O_RDWR | O_SYNC); uint32 *voif = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x72800000); VGA I/F uint32 *vdma = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x60A00000); VGA DMA uint32 *fill = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x60A20000); Filler uint16 *fb = mmap(0, FB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x08000000); フレームバッファー close(fd); vdma[4] = 0x08000000; ベースアドレス=128MB vdma[5] = width*height*2; 転送サイズ=画像サイズ voif[0x08] = param[0]; voif[0x09] = param[1]; voif[0x0a] = param[2]; voif[0x0b] = param[3]; voif[0x0c] = param[4]; voif[0x0d] = param[5]; voif[0x0e] = param[6]; voif[0x0f] = param[7]; パラメータ設定 vdma[1] = 0x00000001; DMAスタート voif[0] = 0x0000000b; VGAスタート fill[4] = 0x08000000; 128MB先頭から fill[5] = 384*1024*1024; 384MB一気書き込みとか fill[1] = 0x00000001; fill[1] = 0x00000000; 線引いたり円描いたり line(fb, width-1, 0, 0, height-1, 0xffff); circle(fb, width/2, height/2, 100, 0xffff); 最後に開放 munmap(voif, MAP_SIZE); munmap(vdma, MAP_SIZE); munmap(fill, MAP_SIZE); munmap(fb , FB_SIZE);
レジスタ構成とかはまた今度として、こんな感じでプログラム作ってLinuxに転送し実行すると画面に画が出てきます。
Fill DMAがカウンター値を書き込んでるので、格子状のベタ画像になります。Linuxは動作し続けてるので、Fill DMAが書き込んだのはLinux管理外の領域となってるようです。Fill DMAの先頭アドレスを0x0000_0000とかにしてLinuxが管理してるはずのメモリ領域に書き込ませると、ちゃんと暴走します。VGA DMAの先頭アドレスをLinuxの領域にしてなにか操作すると画像が変わるのがわかります。
Linuxのどこかの管理領域。
Linuxでも絶対アドレスを指定したポインタによるアクセスって意外と簡単にできるんですね。知らんかったです。デバイスドライバとか書かなくて済むのでデバッグ時は楽です。デバイスドライバ書くとかpcore形式にしてXPSに登録するとか、これがうれしいのは繰り返し使うときで、一回しか使わないときは手間なんですよね。
これでまずはメモリを分けてLinuxの管理領域外のメモリを用意し、FPGAから直接操作する基本的な構成ができました。CPUのキャッシュとの関連とか帯域制御とかまだ色々ありそうですが、必要最低限は用意できたと思います。多分他のプラットフォーム(MBとかNiosIIとか)でも同じ方法で行けるはずです。
Linuxが使えるメモリが減ってしまい、自由度が下がるのが問題ですが、LinuxのページングモデルにFPGAロジックを摺り合わせるよりは楽だと思います。
ZedBoard Linux (5) [FPGA]
Linux管理外のメモリ空間がホントに思ったとおりになってるのか、FPGAから読み書きしてみます。
接続方法ですが、諸事情(笑)によりAXI3。
…はじめの頃はAXI4ににしてpcore形式で登録したりもしてたのですが、最近はハードマクロのAXI-HP0~3を直接外に出してそこにつなぐパターンが多くなってます。作る時に毎回XPS立ち上げてうんたらかんたらやるのが面倒に感じてしまい、まずはAXIを外に出してXPSの作業はおしまい、後は外で作成、って感じです。この場合、ハードマクロのインターフェースは、AXI4ではなくAXI3+QoSです。pcore形式でつなぐ場合はAXI4~AXI3変換が自動的に追加されてます。
AlteraのSOPC Builder、Qsysでは面倒に感じず、そのまま作業することが多いんですよね。なにかセンスが違うんでしょう。ARMの付いたSoCシリーズは使ったこと無いのでわかりませんが…VivadoのIPインテグレーターがどうかもまだ試していません。
ということで、FPGA側のAXIマスターはAXI3になります。スレーブはレジスタの読み書きぐらいしかしないので、AXI~APBブリッジのIPを使ってしまいます。手抜き手抜き。
ちなみにAXIの3と4の違いは中の情報(パケット、プロトコル)の違いであって、タイミングは変わりません(依存関係に追加規定はあります)。簡単なAXIマスターモジュールで関係するのはバースト長(AXI3:4bit:最大16回、AXI4:8bit:最大256回)ぐらいじゃないですかね?
XPSの作業。まずはAXI-APBブリッジを追加します。
APBのスレーブ本数は適当に4本。
自動的にアドレスが割り当てられ、外部にポートが出ます。
PSセッティングの画面からHigh Performance AXIをクリックし、設定画面からS_AXI_HP0を有効に。
最初はどこにも接続されてないので、Portタブから「S_AXI_HP0」を「Make Ports External」にします。
「S_AXI_HP0」が直接外部ポートに出てきます。
※「S_AXI_HP0_ACLK」が入力な点に注意。AXI_HPはFIFOになっていて、クロックを自由に設定できます。他のAXIもクロックブリッジが付いてます。
こうしてできた_stub.vがこちら。
XPSの作業は終了。後は普通にロジック作るだけです。
接続方法ですが、諸事情(笑)によりAXI3。
…はじめの頃はAXI4ににしてpcore形式で登録したりもしてたのですが、最近はハードマクロのAXI-HP0~3を直接外に出してそこにつなぐパターンが多くなってます。作る時に毎回XPS立ち上げてうんたらかんたらやるのが面倒に感じてしまい、まずはAXIを外に出してXPSの作業はおしまい、後は外で作成、って感じです。この場合、ハードマクロのインターフェースは、AXI4ではなくAXI3+QoSです。pcore形式でつなぐ場合はAXI4~AXI3変換が自動的に追加されてます。
AlteraのSOPC Builder、Qsysでは面倒に感じず、そのまま作業することが多いんですよね。なにかセンスが違うんでしょう。ARMの付いたSoCシリーズは使ったこと無いのでわかりませんが…VivadoのIPインテグレーターがどうかもまだ試していません。
ということで、FPGA側のAXIマスターはAXI3になります。スレーブはレジスタの読み書きぐらいしかしないので、AXI~APBブリッジのIPを使ってしまいます。手抜き手抜き。
ちなみにAXIの3と4の違いは中の情報(パケット、プロトコル)の違いであって、タイミングは変わりません(依存関係に追加規定はあります)。簡単なAXIマスターモジュールで関係するのはバースト長(AXI3:4bit:最大16回、AXI4:8bit:最大256回)ぐらいじゃないですかね?
XPSの作業。まずはAXI-APBブリッジを追加します。
APBのスレーブ本数は適当に4本。
自動的にアドレスが割り当てられ、外部にポートが出ます。
PSセッティングの画面からHigh Performance AXIをクリックし、設定画面からS_AXI_HP0を有効に。
最初はどこにも接続されてないので、Portタブから「S_AXI_HP0」を「Make Ports External」にします。
「S_AXI_HP0」が直接外部ポートに出てきます。
※「S_AXI_HP0_ACLK」が入力な点に注意。AXI_HPはFIFOになっていて、クロックを自由に設定できます。他のAXIもクロックブリッジが付いてます。
こうしてできた_stub.vがこちら。
//----------------------------------------------------------------------------- // ps_module_stub.v //----------------------------------------------------------------------------- module ps_module_stub ( processing_system7_0_MIO, processing_system7_0_PS_SRSTB_pin, processing_system7_0_PS_CLK_pin, processing_system7_0_PS_PORB_pin, processing_system7_0_DDR_Clk, processing_system7_0_DDR_Clk_n, processing_system7_0_DDR_CKE, processing_system7_0_DDR_CS_n, processing_system7_0_DDR_RAS_n, processing_system7_0_DDR_CAS_n, processing_system7_0_DDR_WEB_pin, processing_system7_0_DDR_BankAddr, processing_system7_0_DDR_Addr, processing_system7_0_DDR_ODT, processing_system7_0_DDR_DRSTB, processing_system7_0_DDR_DQ, processing_system7_0_DDR_DM, processing_system7_0_DDR_DQS, processing_system7_0_DDR_DQS_n, processing_system7_0_DDR_VRN, processing_system7_0_DDR_VRP, processing_system7_0_GPIO_I_pin, processing_system7_0_GPIO_O_pin, processing_system7_0_GPIO_T_pin, processing_system7_0_FCLK_CLK0_pin, processing_system7_0_FCLK_CLK1_pin, processing_system7_0_FCLK_CLK2_pin, processing_system7_0_FCLK_CLK3_pin, axi_apb_bridge_0_M_APB_PCLK_pin, axi_apb_bridge_0_M_APB_PRESETN_pin, axi_apb_bridge_0_M_APB_PADDR_pin, axi_apb_bridge_0_M_APB_PSEL_pin, axi_apb_bridge_0_M_APB_PENABLE_pin, axi_apb_bridge_0_M_APB_PWRITE_pin, axi_apb_bridge_0_M_APB_PWDATA_pin, axi_apb_bridge_0_M_APB_PREADY_pin, axi_apb_bridge_0_M_APB_PRDATA_pin, axi_apb_bridge_0_M_APB_PRDATA2_pin, axi_apb_bridge_0_M_APB_PRDATA3_pin, axi_apb_bridge_0_M_APB_PRDATA4_pin, axi_apb_bridge_0_M_APB_PSLVERR_pin, processing_system7_0_S_AXI_HP0_ARREADY_pin, processing_system7_0_S_AXI_HP0_AWREADY_pin, processing_system7_0_S_AXI_HP0_BVALID_pin, processing_system7_0_S_AXI_HP0_RLAST_pin, processing_system7_0_S_AXI_HP0_RVALID_pin, processing_system7_0_S_AXI_HP0_WREADY_pin, processing_system7_0_S_AXI_HP0_BRESP_pin, processing_system7_0_S_AXI_HP0_RRESP_pin, processing_system7_0_S_AXI_HP0_BID_pin, processing_system7_0_S_AXI_HP0_RID_pin, processing_system7_0_S_AXI_HP0_RDATA_pin, processing_system7_0_S_AXI_HP0_ACLK_pin, processing_system7_0_S_AXI_HP0_ARVALID_pin, processing_system7_0_S_AXI_HP0_AWVALID_pin, processing_system7_0_S_AXI_HP0_BREADY_pin, processing_system7_0_S_AXI_HP0_RREADY_pin, processing_system7_0_S_AXI_HP0_WLAST_pin, processing_system7_0_S_AXI_HP0_WVALID_pin, processing_system7_0_S_AXI_HP0_ARBURST_pin, processing_system7_0_S_AXI_HP0_ARLOCK_pin, processing_system7_0_S_AXI_HP0_ARSIZE_pin, processing_system7_0_S_AXI_HP0_AWBURST_pin, processing_system7_0_S_AXI_HP0_AWLOCK_pin, processing_system7_0_S_AXI_HP0_AWSIZE_pin, processing_system7_0_S_AXI_HP0_ARPROT_pin, processing_system7_0_S_AXI_HP0_AWPROT_pin, processing_system7_0_S_AXI_HP0_ARADDR_pin, processing_system7_0_S_AXI_HP0_AWADDR_pin, processing_system7_0_S_AXI_HP0_ARCACHE_pin, processing_system7_0_S_AXI_HP0_ARLEN_pin, processing_system7_0_S_AXI_HP0_ARQOS_pin, processing_system7_0_S_AXI_HP0_AWCACHE_pin, processing_system7_0_S_AXI_HP0_AWLEN_pin, processing_system7_0_S_AXI_HP0_AWQOS_pin, processing_system7_0_S_AXI_HP0_ARID_pin, processing_system7_0_S_AXI_HP0_AWID_pin, processing_system7_0_S_AXI_HP0_WID_pin, processing_system7_0_S_AXI_HP0_WDATA_pin, processing_system7_0_S_AXI_HP0_WSTRB_pin ); inout [53:0] processing_system7_0_MIO; input processing_system7_0_PS_SRSTB_pin; input processing_system7_0_PS_CLK_pin; input processing_system7_0_PS_PORB_pin; inout processing_system7_0_DDR_Clk; inout processing_system7_0_DDR_Clk_n; inout processing_system7_0_DDR_CKE; inout processing_system7_0_DDR_CS_n; inout processing_system7_0_DDR_RAS_n; inout processing_system7_0_DDR_CAS_n; output processing_system7_0_DDR_WEB_pin; inout [2:0] processing_system7_0_DDR_BankAddr; inout [14:0] processing_system7_0_DDR_Addr; inout processing_system7_0_DDR_ODT; inout processing_system7_0_DDR_DRSTB; inout [31:0] processing_system7_0_DDR_DQ; inout [3:0] processing_system7_0_DDR_DM; inout [3:0] processing_system7_0_DDR_DQS; inout [3:0] processing_system7_0_DDR_DQS_n; inout processing_system7_0_DDR_VRN; inout processing_system7_0_DDR_VRP; input [63:0] processing_system7_0_GPIO_I_pin; output [63:0] processing_system7_0_GPIO_O_pin; output [63:0] processing_system7_0_GPIO_T_pin; output processing_system7_0_FCLK_CLK0_pin; output processing_system7_0_FCLK_CLK1_pin; output processing_system7_0_FCLK_CLK2_pin; output processing_system7_0_FCLK_CLK3_pin; output axi_apb_bridge_0_M_APB_PCLK_pin; output axi_apb_bridge_0_M_APB_PRESETN_pin; output [31:0] axi_apb_bridge_0_M_APB_PADDR_pin; output [3:0] axi_apb_bridge_0_M_APB_PSEL_pin; output axi_apb_bridge_0_M_APB_PENABLE_pin; output axi_apb_bridge_0_M_APB_PWRITE_pin; output [31:0] axi_apb_bridge_0_M_APB_PWDATA_pin; input [3:0] axi_apb_bridge_0_M_APB_PREADY_pin; input [31:0] axi_apb_bridge_0_M_APB_PRDATA_pin; input [31:0] axi_apb_bridge_0_M_APB_PRDATA2_pin; input [31:0] axi_apb_bridge_0_M_APB_PRDATA3_pin; input [31:0] axi_apb_bridge_0_M_APB_PRDATA4_pin; input [3:0] axi_apb_bridge_0_M_APB_PSLVERR_pin; output processing_system7_0_S_AXI_HP0_ARREADY_pin; output processing_system7_0_S_AXI_HP0_AWREADY_pin; output processing_system7_0_S_AXI_HP0_BVALID_pin; output processing_system7_0_S_AXI_HP0_RLAST_pin; output processing_system7_0_S_AXI_HP0_RVALID_pin; output processing_system7_0_S_AXI_HP0_WREADY_pin; output [1:0] processing_system7_0_S_AXI_HP0_BRESP_pin; output [1:0] processing_system7_0_S_AXI_HP0_RRESP_pin; output [5:0] processing_system7_0_S_AXI_HP0_BID_pin; output [5:0] processing_system7_0_S_AXI_HP0_RID_pin; output [63:0] processing_system7_0_S_AXI_HP0_RDATA_pin; input processing_system7_0_S_AXI_HP0_ACLK_pin; input processing_system7_0_S_AXI_HP0_ARVALID_pin; input processing_system7_0_S_AXI_HP0_AWVALID_pin; input processing_system7_0_S_AXI_HP0_BREADY_pin; input processing_system7_0_S_AXI_HP0_RREADY_pin; input processing_system7_0_S_AXI_HP0_WLAST_pin; input processing_system7_0_S_AXI_HP0_WVALID_pin; input [1:0] processing_system7_0_S_AXI_HP0_ARBURST_pin; input [1:0] processing_system7_0_S_AXI_HP0_ARLOCK_pin; input [2:0] processing_system7_0_S_AXI_HP0_ARSIZE_pin; input [1:0] processing_system7_0_S_AXI_HP0_AWBURST_pin; input [1:0] processing_system7_0_S_AXI_HP0_AWLOCK_pin; input [2:0] processing_system7_0_S_AXI_HP0_AWSIZE_pin; input [2:0] processing_system7_0_S_AXI_HP0_ARPROT_pin; input [2:0] processing_system7_0_S_AXI_HP0_AWPROT_pin; input [31:0] processing_system7_0_S_AXI_HP0_ARADDR_pin; input [31:0] processing_system7_0_S_AXI_HP0_AWADDR_pin; input [3:0] processing_system7_0_S_AXI_HP0_ARCACHE_pin; input [3:0] processing_system7_0_S_AXI_HP0_ARLEN_pin; input [3:0] processing_system7_0_S_AXI_HP0_ARQOS_pin; input [3:0] processing_system7_0_S_AXI_HP0_AWCACHE_pin; input [3:0] processing_system7_0_S_AXI_HP0_AWLEN_pin; input [3:0] processing_system7_0_S_AXI_HP0_AWQOS_pin; input [5:0] processing_system7_0_S_AXI_HP0_ARID_pin; input [5:0] processing_system7_0_S_AXI_HP0_AWID_pin; input [5:0] processing_system7_0_S_AXI_HP0_WID_pin; input [63:0] processing_system7_0_S_AXI_HP0_WDATA_pin; input [7:0] processing_system7_0_S_AXI_HP0_WSTRB_pin; (* BOX_TYPE = "user_black_box" *) ps_module ps_module_i ( .processing_system7_0_MIO ( processing_system7_0_MIO ), .processing_system7_0_PS_SRSTB_pin ( processing_system7_0_PS_SRSTB_pin ), .processing_system7_0_PS_CLK_pin ( processing_system7_0_PS_CLK_pin ), .processing_system7_0_PS_PORB_pin ( processing_system7_0_PS_PORB_pin ), .processing_system7_0_DDR_Clk ( processing_system7_0_DDR_Clk ), .processing_system7_0_DDR_Clk_n ( processing_system7_0_DDR_Clk_n ), .processing_system7_0_DDR_CKE ( processing_system7_0_DDR_CKE ), .processing_system7_0_DDR_CS_n ( processing_system7_0_DDR_CS_n ), .processing_system7_0_DDR_RAS_n ( processing_system7_0_DDR_RAS_n ), .processing_system7_0_DDR_CAS_n ( processing_system7_0_DDR_CAS_n ), .processing_system7_0_DDR_WEB_pin ( processing_system7_0_DDR_WEB_pin ), .processing_system7_0_DDR_BankAddr ( processing_system7_0_DDR_BankAddr ), .processing_system7_0_DDR_Addr ( processing_system7_0_DDR_Addr ), .processing_system7_0_DDR_ODT ( processing_system7_0_DDR_ODT ), .processing_system7_0_DDR_DRSTB ( processing_system7_0_DDR_DRSTB ), .processing_system7_0_DDR_DQ ( processing_system7_0_DDR_DQ ), .processing_system7_0_DDR_DM ( processing_system7_0_DDR_DM ), .processing_system7_0_DDR_DQS ( processing_system7_0_DDR_DQS ), .processing_system7_0_DDR_DQS_n ( processing_system7_0_DDR_DQS_n ), .processing_system7_0_DDR_VRN ( processing_system7_0_DDR_VRN ), .processing_system7_0_DDR_VRP ( processing_system7_0_DDR_VRP ), .processing_system7_0_GPIO_I_pin ( processing_system7_0_GPIO_I_pin ), .processing_system7_0_GPIO_O_pin ( processing_system7_0_GPIO_O_pin ), .processing_system7_0_GPIO_T_pin ( processing_system7_0_GPIO_T_pin ), .processing_system7_0_FCLK_CLK0_pin ( processing_system7_0_FCLK_CLK0_pin ), .processing_system7_0_FCLK_CLK1_pin ( processing_system7_0_FCLK_CLK1_pin ), .processing_system7_0_FCLK_CLK2_pin ( processing_system7_0_FCLK_CLK2_pin ), .processing_system7_0_FCLK_CLK3_pin ( processing_system7_0_FCLK_CLK3_pin ), .axi_apb_bridge_0_M_APB_PCLK_pin ( axi_apb_bridge_0_M_APB_PCLK_pin ), .axi_apb_bridge_0_M_APB_PRESETN_pin ( axi_apb_bridge_0_M_APB_PRESETN_pin ), .axi_apb_bridge_0_M_APB_PADDR_pin ( axi_apb_bridge_0_M_APB_PADDR_pin ), .axi_apb_bridge_0_M_APB_PSEL_pin ( axi_apb_bridge_0_M_APB_PSEL_pin ), .axi_apb_bridge_0_M_APB_PENABLE_pin ( axi_apb_bridge_0_M_APB_PENABLE_pin ), .axi_apb_bridge_0_M_APB_PWRITE_pin ( axi_apb_bridge_0_M_APB_PWRITE_pin ), .axi_apb_bridge_0_M_APB_PWDATA_pin ( axi_apb_bridge_0_M_APB_PWDATA_pin ), .axi_apb_bridge_0_M_APB_PREADY_pin ( axi_apb_bridge_0_M_APB_PREADY_pin ), .axi_apb_bridge_0_M_APB_PRDATA_pin ( axi_apb_bridge_0_M_APB_PRDATA_pin ), .axi_apb_bridge_0_M_APB_PRDATA2_pin ( axi_apb_bridge_0_M_APB_PRDATA2_pin ), .axi_apb_bridge_0_M_APB_PRDATA3_pin ( axi_apb_bridge_0_M_APB_PRDATA3_pin ), .axi_apb_bridge_0_M_APB_PRDATA4_pin ( axi_apb_bridge_0_M_APB_PRDATA4_pin ), .axi_apb_bridge_0_M_APB_PSLVERR_pin ( axi_apb_bridge_0_M_APB_PSLVERR_pin ), .processing_system7_0_S_AXI_HP0_ARREADY_pin ( processing_system7_0_S_AXI_HP0_ARREADY_pin ), .processing_system7_0_S_AXI_HP0_AWREADY_pin ( processing_system7_0_S_AXI_HP0_AWREADY_pin ), .processing_system7_0_S_AXI_HP0_BVALID_pin ( processing_system7_0_S_AXI_HP0_BVALID_pin ), .processing_system7_0_S_AXI_HP0_RLAST_pin ( processing_system7_0_S_AXI_HP0_RLAST_pin ), .processing_system7_0_S_AXI_HP0_RVALID_pin ( processing_system7_0_S_AXI_HP0_RVALID_pin ), .processing_system7_0_S_AXI_HP0_WREADY_pin ( processing_system7_0_S_AXI_HP0_WREADY_pin ), .processing_system7_0_S_AXI_HP0_BRESP_pin ( processing_system7_0_S_AXI_HP0_BRESP_pin ), .processing_system7_0_S_AXI_HP0_RRESP_pin ( processing_system7_0_S_AXI_HP0_RRESP_pin ), .processing_system7_0_S_AXI_HP0_BID_pin ( processing_system7_0_S_AXI_HP0_BID_pin ), .processing_system7_0_S_AXI_HP0_RID_pin ( processing_system7_0_S_AXI_HP0_RID_pin ), .processing_system7_0_S_AXI_HP0_RDATA_pin ( processing_system7_0_S_AXI_HP0_RDATA_pin ), .processing_system7_0_S_AXI_HP0_ACLK_pin ( processing_system7_0_S_AXI_HP0_ACLK_pin ), .processing_system7_0_S_AXI_HP0_ARVALID_pin ( processing_system7_0_S_AXI_HP0_ARVALID_pin ), .processing_system7_0_S_AXI_HP0_AWVALID_pin ( processing_system7_0_S_AXI_HP0_AWVALID_pin ), .processing_system7_0_S_AXI_HP0_BREADY_pin ( processing_system7_0_S_AXI_HP0_BREADY_pin ), .processing_system7_0_S_AXI_HP0_RREADY_pin ( processing_system7_0_S_AXI_HP0_RREADY_pin ), .processing_system7_0_S_AXI_HP0_WLAST_pin ( processing_system7_0_S_AXI_HP0_WLAST_pin ), .processing_system7_0_S_AXI_HP0_WVALID_pin ( processing_system7_0_S_AXI_HP0_WVALID_pin ), .processing_system7_0_S_AXI_HP0_ARBURST_pin ( processing_system7_0_S_AXI_HP0_ARBURST_pin ), .processing_system7_0_S_AXI_HP0_ARLOCK_pin ( processing_system7_0_S_AXI_HP0_ARLOCK_pin ), .processing_system7_0_S_AXI_HP0_ARSIZE_pin ( processing_system7_0_S_AXI_HP0_ARSIZE_pin ), .processing_system7_0_S_AXI_HP0_AWBURST_pin ( processing_system7_0_S_AXI_HP0_AWBURST_pin ), .processing_system7_0_S_AXI_HP0_AWLOCK_pin ( processing_system7_0_S_AXI_HP0_AWLOCK_pin ), .processing_system7_0_S_AXI_HP0_AWSIZE_pin ( processing_system7_0_S_AXI_HP0_AWSIZE_pin ), .processing_system7_0_S_AXI_HP0_ARPROT_pin ( processing_system7_0_S_AXI_HP0_ARPROT_pin ), .processing_system7_0_S_AXI_HP0_AWPROT_pin ( processing_system7_0_S_AXI_HP0_AWPROT_pin ), .processing_system7_0_S_AXI_HP0_ARADDR_pin ( processing_system7_0_S_AXI_HP0_ARADDR_pin ), .processing_system7_0_S_AXI_HP0_AWADDR_pin ( processing_system7_0_S_AXI_HP0_AWADDR_pin ), .processing_system7_0_S_AXI_HP0_ARCACHE_pin ( processing_system7_0_S_AXI_HP0_ARCACHE_pin ), .processing_system7_0_S_AXI_HP0_ARLEN_pin ( processing_system7_0_S_AXI_HP0_ARLEN_pin ), .processing_system7_0_S_AXI_HP0_ARQOS_pin ( processing_system7_0_S_AXI_HP0_ARQOS_pin ), .processing_system7_0_S_AXI_HP0_AWCACHE_pin ( processing_system7_0_S_AXI_HP0_AWCACHE_pin ), .processing_system7_0_S_AXI_HP0_AWLEN_pin ( processing_system7_0_S_AXI_HP0_AWLEN_pin ), .processing_system7_0_S_AXI_HP0_AWQOS_pin ( processing_system7_0_S_AXI_HP0_AWQOS_pin ), .processing_system7_0_S_AXI_HP0_ARID_pin ( processing_system7_0_S_AXI_HP0_ARID_pin ), .processing_system7_0_S_AXI_HP0_AWID_pin ( processing_system7_0_S_AXI_HP0_AWID_pin ), .processing_system7_0_S_AXI_HP0_WID_pin ( processing_system7_0_S_AXI_HP0_WID_pin ), .processing_system7_0_S_AXI_HP0_WDATA_pin ( processing_system7_0_S_AXI_HP0_WDATA_pin ), .processing_system7_0_S_AXI_HP0_WSTRB_pin ( processing_system7_0_S_AXI_HP0_WSTRB_pin ) ); endmodule一気に信号が増えましたが、「axi_apb_bridge_0_M_APB」がAPB関連、「processing_system7_0_S_AXI_HP0」がAXI_HP関連です。基本一対一でつなぐだけなので、私はエディタのコピペと置換とマクロで半自動的につないでます。ところで、自動的にできるんならツールに任せようよ、ってできたツールがSOPC Builder、Qsys、XPSなどです。
XPSの作業は終了。後は普通にロジック作るだけです。
ZedBoard Linux (4) [FPGA]
ちょっと空きましたが、続き。
ZynqのFPGA部分が空っぽになったところで、改めて使い方を考えてみます。
私がZynqを使うときは大体、
・DDRメモリに直接のデータを出し入れするペリフェラルを作る。画像とかパケットとか音声とか色々。
・CPUはその管理。ちょっとデータ処理も行う。
というパターンがほとんどです。
一方、LinuxなどOSを使いたい理由は
・ネットワークアプリが作りやすい
・リモート管理がやりたい
・ファイルシステムが便利
などなどで、実はデータ処理にはあまり関連しません。
FPGA側のメモリの使い方を見てると、いったん確保したら電源切るまで二度と開放しない事もあります。Linuxのダイナミックなメモリ管理の機能はFPGAのロジックには必要なかったりします。逆に複雑なメモリ管理機構は邪魔になりかねません。
※PCのGPGPUでも似た問題が起きますよね。こちらはがんばってGPUがCPUのメモリ空間に合わせようとしています。コヒーレンシーの採れた共通アドレス空間が使えるようなシステムに向かってる感じでしょうか。
そう考えると、DDRメモリはできるだけFPGA側に開放して、Linuxはできるだけ小さくまとまってくれてた方が便利です。物理的に別のメモリになってくれてるとより良いんですけどね。terasicのSoCKitはHPSとFPGA個別にDDRメモリが付いてて良さげです。
解放されたメモリ領域はLinuxの管理外領域として、単なるI/Oとして認識してくれればOKです。FPGAを使うときはLinuxは主役ではありません。※Linuxが主役なら多分FPGA内部のCPUは使いません。
例えば、ZedBoardのZynqが管理している512MBのメモリのうち、384MBを確保してDMAが使える連続空間にマップする、しかも電源切るまで確保しっぱなし、なんて使い方なら、最初からLinuxが使う空間を下位128MBに切っておいて、残りは管理外とした方がスマートだと思います。
それをやるオプションは…devicetreeにあるbootargsだけで良いのかな?
とりあえず「mem=128M」だけつけて、なんか騙されてるような気分のままブート。
…ホントか?これで残りの384MBは管理外のメモリとしてDMAとかで荒らしちゃっても良いのか?
なんかすごく不安なので、確認用のハードウェアをつないでみたいと思います。
ZynqのFPGA部分が空っぽになったところで、改めて使い方を考えてみます。
私がZynqを使うときは大体、
・DDRメモリに直接のデータを出し入れするペリフェラルを作る。画像とかパケットとか音声とか色々。
・CPUはその管理。ちょっとデータ処理も行う。
というパターンがほとんどです。
一方、LinuxなどOSを使いたい理由は
・ネットワークアプリが作りやすい
・リモート管理がやりたい
・ファイルシステムが便利
などなどで、実はデータ処理にはあまり関連しません。
FPGA側のメモリの使い方を見てると、いったん確保したら電源切るまで二度と開放しない事もあります。Linuxのダイナミックなメモリ管理の機能はFPGAのロジックには必要なかったりします。逆に複雑なメモリ管理機構は邪魔になりかねません。
※PCのGPGPUでも似た問題が起きますよね。こちらはがんばってGPUがCPUのメモリ空間に合わせようとしています。コヒーレンシーの採れた共通アドレス空間が使えるようなシステムに向かってる感じでしょうか。
そう考えると、DDRメモリはできるだけFPGA側に開放して、Linuxはできるだけ小さくまとまってくれてた方が便利です。物理的に別のメモリになってくれてるとより良いんですけどね。terasicのSoCKitはHPSとFPGA個別にDDRメモリが付いてて良さげです。
解放されたメモリ領域はLinuxの管理外領域として、単なるI/Oとして認識してくれればOKです。FPGAを使うときはLinuxは主役ではありません。※Linuxが主役なら多分FPGA内部のCPUは使いません。
例えば、ZedBoardのZynqが管理している512MBのメモリのうち、384MBを確保してDMAが使える連続空間にマップする、しかも電源切るまで確保しっぱなし、なんて使い方なら、最初からLinuxが使う空間を下位128MBに切っておいて、残りは管理外とした方がスマートだと思います。
それをやるオプションは…devicetreeにあるbootargsだけで良いのかな?
bootargs = "mem=128M console=ttyPS0,115200 root=/dev/ram rw initrd=0x800000,8M earlyprintk rootwait devtmpfs.mount=1";「mem=128M」が追加オプション。…え?ホント?これだけで良いの?
ps7_ddr_0: memory@0 { device_type = "memory"; reg = < 0x0 0x20000000 >; };それともこっち?
とりあえず「mem=128M」だけつけて、なんか騙されてるような気分のままブート。
[ 0.000000] Kernel command line: mem=128M console=ttyPS0,115200 root=/dev/ram rw initrd=0x800000,8M earlyprintk rootwait devtmpfs.mount=1 [ 0.000000] PID hash table entries: 512 (order: -1, 2048 bytes) [ 0.000000] Dentry cache hash table entries: 16384 (order: 4, 65536 bytes) [ 0.000000] Inode-cache hash table entries: 8192 (order: 3, 32768 bytes) [ 0.000000] Memory: 128MB = 128MB total [ 0.000000] Memory: 116588k/116588k available, 14484k reserved, 0K highmem … zynq> cat /proc/meminfo MemTotal: 124928 kB MemFree: 105980 kB Buffers: 284 kB Cached: 3496 kB SwapCached: 0 kB Active: 2720 kB Inactive: 2388 kB Active(anon): 1336 kB Inactive(anon): 56 kB Active(file): 1384 kB Inactive(file): 2332 kB Unevictable: 0 kB Mlocked: 0 kB HighTotal: 0 kB HighFree: 0 kB LowTotal: 124928 kB LowFree: 105980 kB SwapTotal: 0 kB SwapFree: 0 kB Dirty: 0 kB Writeback: 0 kB AnonPages: 1344 kB Mapped: 1344 kB Shmem: 64 kB Slab: 4236 kB SReclaimable: 1912 kB SUnreclaim: 2324 kB KernelStack: 336 kB PageTables: 204 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 62464 kB Committed_AS: 4812 kB VmallocTotal: 860160 kB VmallocUsed: 376 kB VmallocChunk: 777856 kBなったっぽい。
…ホントか?これで残りの384MBは管理外のメモリとしてDMAとかで荒らしちゃっても良いのか?
なんかすごく不安なので、確認用のハードウェアをつないでみたいと思います。
ZedBoard Linux (3) [FPGA]
FPGAをカスタマイズしてみます。
…じゃなくて、標準デザインで色々付いてるPLのハードウェアを全部無くして、PSブロックだけで動作させます。自分のロジックを足すときのベースになります。だいたいZynqのチュートリアルに出てる手順です。
標準デザインではProject NavigatorやPlanAheadを使わず、XPSだけでFPGAを作っています。あまり一般的では無いと思います。XPSで扱える形式(pcore)でしか作れず、階層化も困難です。マイコン形式のプロセッサを中心としたシステムがトップデザインになる場合は便利なのでしょう(Suzakuもこの形式だったと記憶してます)。自分としてはあまり便利と思わないのですが…
XPSもプロセッサをただの部品として扱い、接続することをメインに考えたツールになれば良いのになぁと思います。その点AlteraのQsys、SOPC Builderは好みです。
ということで、プロジェクトのトップはPlanAheadで作成します。
テンプレートで入ってるZedBoardをターゲットにして空のプロジェクトを作ります。Add Sources → Add or Create Embedded Sources → Create Sub-Design、「ps_module」とか名前付けてFinishするとXPSが立ち上がります。「PSを追加するか?」というダイアログが出てくるので、ここではYesです。
ボードやLinuxへのインパクトは少なくしたいので、PSの設定は標準のデザインと同じにしたいところです。標準の設定をImportするのが一番簡単ですかね。色々な設定を忘れずにすみます。
「Import」アイコンを押すとこんな画面が出てきます。
「User Template」の所に標準デザインのxmlを足します。これを選んでOKすると設定が反映されます。「System Template」の「ZedBoard Development Board Template」もだいたい同じ内容でした。
ちょっと変更します。EMIO GPIOをFPGA内部で使えるようにします。
EMIO GPIOを有効にして本数を指定します。
外部ポートに出します。このときI/Oを出した場合はFPGAのI/Oピンに接続することになります。内部で使う場合は_I、_O、_Tをそれぞれ出します。_Iは入力、_Oは出力、_Tも出力として使えます。
アドレスは標準のまま。
PLに渡すクロックは適当に設定します。外部の33.333MHzを基準にした周波数が作れます。
XPSを閉じるとPlanAheadに戻ります。
できた「Design Sources」の「ps_module」を右クリックして「Create Top HDL」を選びます。「ps_module_stub.v(vhd)」ができあがります。これをそのままトップとして使ってもかまいませんし、別途top.vを作ってもかまいません。_stubはPSを変更して「Create Top HDL」を行うたびに上書きされてしまうので、別途トップを作り、コピペすることにします。
このままインプリします。ハードマクロ側のピンや制約は自動生成されます。というかピンは固定なので動きませんが。
なんとまぁ、これだけでCritical Warningが6個も出ます。PORBとSRSTBとCLKにIBUFが入らないよ~というワーニング。ハードマクロ側のピンなので手が出ません。Xilinxのアンサーでは「無視してくれ」とのこと。昔から似たようなバグがあり続けて一向に直る気配が無いんですが…
こういう、誰もが必ず当たるところにバグがあるから「Xilinxってまともに検証してないでしょ?他の所もバグだらけなんじゃね?」って疑ってしまうんですよ~実際あちらこちらにバグあるし。
CriticalでもWarningなので、そのままインプリは進みます。PLが空なので、実はビットストリームいりません。Zynqはハードマクロだけで動けます。
できあがったら、File → Export → Export Hardware for SDKを選びます。デフォルトでは「プロジェクト名.sdk\SDK\SDK_Export」にSDKのワークスペースができます。後はこれを元に(1)と同じ手順でFSBL~BOOT.BINを作ります。
今度は試しにWindows上のSDKで作ってみます。u-boot.elfはLinuxで作ったのをコピーしておきます。
操作は全く同じ。「.\FSBL\bootimage\u-boot.bin」ができるのでこれをBOOT.BINにします。
DeviceTreeの編集。
やっぱりまだよくわからない点が多いので、標準の「devicetree.dts」を編集します。といってもハードマクロに載ってるペリフェラル以外を削除していくだけです。「ps7_ddr_0」や「ps7_axi_interconnect_0」など、「ps7_」で始まってるのはハードマクロのペリフェラルです。アドレスも決まってるので変更する必要ないです。それ以外のエントリー「axi_vdma_0」「axi_iic_hdmi」などを削除します。
これで必要なファイルがそろったので、SDカードにコピーします。zImageとramdiskは前のままです。
まぁ、色々謎のままですが、これでPLが空っぽのLinux環境ができたので、自分の回路を足していくことになります。
…じゃなくて、標準デザインで色々付いてるPLのハードウェアを全部無くして、PSブロックだけで動作させます。自分のロジックを足すときのベースになります。だいたいZynqのチュートリアルに出てる手順です。
標準デザインではProject NavigatorやPlanAheadを使わず、XPSだけでFPGAを作っています。あまり一般的では無いと思います。XPSで扱える形式(pcore)でしか作れず、階層化も困難です。マイコン形式のプロセッサを中心としたシステムがトップデザインになる場合は便利なのでしょう(Suzakuもこの形式だったと記憶してます)。自分としてはあまり便利と思わないのですが…
XPSもプロセッサをただの部品として扱い、接続することをメインに考えたツールになれば良いのになぁと思います。その点AlteraのQsys、SOPC Builderは好みです。
ということで、プロジェクトのトップはPlanAheadで作成します。
テンプレートで入ってるZedBoardをターゲットにして空のプロジェクトを作ります。Add Sources → Add or Create Embedded Sources → Create Sub-Design、「ps_module」とか名前付けてFinishするとXPSが立ち上がります。「PSを追加するか?」というダイアログが出てくるので、ここではYesです。
ボードやLinuxへのインパクトは少なくしたいので、PSの設定は標準のデザインと同じにしたいところです。標準の設定をImportするのが一番簡単ですかね。色々な設定を忘れずにすみます。
「Import」アイコンを押すとこんな画面が出てきます。
「User Template」の所に標準デザインのxmlを足します。これを選んでOKすると設定が反映されます。「System Template」の「ZedBoard Development Board Template」もだいたい同じ内容でした。
ちょっと変更します。EMIO GPIOをFPGA内部で使えるようにします。
EMIO GPIOを有効にして本数を指定します。
外部ポートに出します。このときI/Oを出した場合はFPGAのI/Oピンに接続することになります。内部で使う場合は_I、_O、_Tをそれぞれ出します。_Iは入力、_Oは出力、_Tも出力として使えます。
アドレスは標準のまま。
PLに渡すクロックは適当に設定します。外部の33.333MHzを基準にした周波数が作れます。
XPSを閉じるとPlanAheadに戻ります。
できた「Design Sources」の「ps_module」を右クリックして「Create Top HDL」を選びます。「ps_module_stub.v(vhd)」ができあがります。これをそのままトップとして使ってもかまいませんし、別途top.vを作ってもかまいません。_stubはPSを変更して「Create Top HDL」を行うたびに上書きされてしまうので、別途トップを作り、コピペすることにします。
module top ( inout wire [53:0] MIO, input wire PS_SRSTB, input wire PS_CLK, input wire PS_PORB, inout wire DDR_Clk, inout wire DDR_Clk_n, inout wire DDR_CKE, inout wire DDR_CS_n, inout wire DDR_RAS_n, inout wire DDR_CAS_n, output wire DDR_WEB, inout wire [2:0] DDR_BankAddr, inout wire [14:0] DDR_Addr, inout wire DDR_ODT, inout wire DDR_DRSTB, inout wire [31:0] DDR_DQ, inout wire [3:0] DDR_DM, inout wire [3:0] DDR_DQS, inout wire [3:0] DDR_DQS_n, inout wire DDR_VRN, inout wire DDR_VRP ); wire [63:0] EMIO_GPIO_I; wire [63:0] EMIO_GPIO_O; wire [63:0] EMIO_GPIO_T; wire FCLK_CLK0; wire FCLK_CLK1; wire FCLK_CLK2; wire FCLK_CLK3; (* BOX_TYPE = "user_black_box" *) ps_module ps_module_i ( .processing_system7_0_MIO (MIO ), .processing_system7_0_PS_SRSTB_pin (PS_SRSTB ), .processing_system7_0_PS_CLK_pin (PS_CLK ), .processing_system7_0_PS_PORB_pin (PS_PORB ), .processing_system7_0_DDR_Clk (DDR_Clk ), .processing_system7_0_DDR_Clk_n (DDR_Clk_n ), .processing_system7_0_DDR_CKE (DDR_CKE ), .processing_system7_0_DDR_CS_n (DDR_CS_n ), .processing_system7_0_DDR_RAS_n (DDR_RAS_n ), .processing_system7_0_DDR_CAS_n (DDR_CAS_n ), .processing_system7_0_DDR_WEB_pin (DDR_WEB ), .processing_system7_0_DDR_BankAddr (DDR_BankAddr ), .processing_system7_0_DDR_Addr (DDR_Addr ), .processing_system7_0_DDR_ODT (DDR_ODT ), .processing_system7_0_DDR_DRSTB (DDR_DRSTB ), .processing_system7_0_DDR_DQ (DDR_DQ ), .processing_system7_0_DDR_DM (DDR_DM ), .processing_system7_0_DDR_DQS (DDR_DQS ), .processing_system7_0_DDR_DQS_n (DDR_DQS_n ), .processing_system7_0_DDR_VRN (DDR_VRN ), .processing_system7_0_DDR_VRP (DDR_VRP ), .processing_system7_0_GPIO_I_pin (EMIO_GPIO_I ), .processing_system7_0_GPIO_O_pin (EMIO_GPIO_O ), .processing_system7_0_GPIO_T_pin (EMIO_GPIO_T ), .processing_system7_0_FCLK_CLK0_pin (FCLK_CLK0 ), .processing_system7_0_FCLK_CLK1_pin (FCLK_CLK1 ), .processing_system7_0_FCLK_CLK2_pin (FCLK_CLK2 ), .processing_system7_0_FCLK_CLK3_pin (FCLK_CLK3 ) ); endmodulePL内部ではなにもしていません。ハードマクロ側のピン名はどうでも良いみたいですw。
このままインプリします。ハードマクロ側のピンや制約は自動生成されます。というかピンは固定なので動きませんが。
なんとまぁ、これだけでCritical Warningが6個も出ます。PORBとSRSTBとCLKにIBUFが入らないよ~というワーニング。ハードマクロ側のピンなので手が出ません。Xilinxのアンサーでは「無視してくれ」とのこと。昔から似たようなバグがあり続けて一向に直る気配が無いんですが…
こういう、誰もが必ず当たるところにバグがあるから「Xilinxってまともに検証してないでしょ?他の所もバグだらけなんじゃね?」って疑ってしまうんですよ~実際あちらこちらにバグあるし。
CriticalでもWarningなので、そのままインプリは進みます。PLが空なので、実はビットストリームいりません。Zynqはハードマクロだけで動けます。
できあがったら、File → Export → Export Hardware for SDKを選びます。デフォルトでは「プロジェクト名.sdk\SDK\SDK_Export」にSDKのワークスペースができます。後はこれを元に(1)と同じ手順でFSBL~BOOT.BINを作ります。
今度は試しにWindows上のSDKで作ってみます。u-boot.elfはLinuxで作ったのをコピーしておきます。
操作は全く同じ。「.\FSBL\bootimage\u-boot.bin」ができるのでこれをBOOT.BINにします。
DeviceTreeの編集。
やっぱりまだよくわからない点が多いので、標準の「devicetree.dts」を編集します。といってもハードマクロに載ってるペリフェラル以外を削除していくだけです。「ps7_ddr_0」や「ps7_axi_interconnect_0」など、「ps7_」で始まってるのはハードマクロのペリフェラルです。アドレスも決まってるので変更する必要ないです。それ以外のエントリー「axi_vdma_0」「axi_iic_hdmi」などを削除します。
/* * digilent-zed.dts - Default Device Tree for ZedBoard * * (C) Copyright 2012 Digilent, Inc. * * Code based on Device Tree Generator version: 1.1 * * (C) Copyright 2007-2012 Xilinx, Inc. * (C) Copyright 2007-2012 Michal Simek * (C) Copyright 2007-2012 PetaLogix Qld Pty Ltd * * Michal SIMEK以前と同様に* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * CAUTION: This file is automatically generated by libgen. * Version: Xilinx EDK 14.4 EDK_P.49d * Today is: Wednesday, the 26 of December, 2012; 15:18:18 * * XPS project directory: device-tree_bsp_0 */ /dts-v1/; / { #address-cells = <1>; #size-cells = <1>; compatible = "xlnx,zynq-zed"; model = "Xilinx Zynq ZED"; aliases { ethernet0 = &ps7_ethernet_0; serial0 = &ps7_uart_1; }; chosen { /*bootargs = "console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=1";*/ bootargs = "console=ttyPS0,115200 root=/dev/ram rw initrd=0x800000,8M earlyprintk rootwait devtmpfs.mount=1"; linux,stdout-path = "/axi@0/serial@e0001000"; }; cpus { #address-cells = <1>; #cpus = <0x2>; #size-cells = <0>; ps7_cortexa9_0: cpu@0 { clock-frequency = <666666687>; compatible = "xlnx,ps7-cortexa9-1.00.a"; d-cache-line-size = <0x20>; d-cache-size = <0x8000>; device_type = "cpu"; i-cache-line-size = <0x20>; i-cache-size = <0x8000>; model = "ps7_cortexa9,1.00.a"; reg = <0>; timebase-frequency = <333333343>; xlnx,cpu-1x-clk-freq-hz = <0x69f6bcb>; xlnx,cpu-clk-freq-hz = <0x27bc86bf>; }; ps7_cortexa9_1: cpu@1 { clock-frequency = <666666687>; compatible = "xlnx,ps7-cortexa9-1.00.a"; d-cache-line-size = <0x20>; d-cache-size = <0x8000>; device_type = "cpu"; i-cache-line-size = <0x20>; i-cache-size = <0x8000>; model = "ps7_cortexa9,1.00.a"; reg = <1>; timebase-frequency = <333333343>; xlnx,cpu-1x-clk-freq-hz = <0x69f6bcb>; xlnx,cpu-clk-freq-hz = <0x27bc86bf>; }; }; ps7_ddr_0: memory@0 { device_type = "memory"; reg = < 0x0 0x20000000 >; }; ps7_axi_interconnect_0: axi@0 { #address-cells = <1>; #size-cells = <1>; compatible = "xlnx,ps7-axi-interconnect-1.00.a", "simple-bus"; ranges; /* PS system internal devices */ ps7_afi_0: ps7-afi@f8008000 { compatible = "xlnx,ps7-afi-1.00.a"; reg = < 0xf8008000 0x1000 >; }; ps7_afi_1: ps7-afi@f8009000 { compatible = "xlnx,ps7-afi-1.00.a"; reg = < 0xf8009000 0x1000 >; }; ps7_afi_2: ps7-afi@f800a000 { compatible = "xlnx,ps7-afi-1.00.a"; reg = < 0xf800a000 0x1000 >; }; ps7_afi_3: ps7-afi@f800b000 { compatible = "xlnx,ps7-afi-1.00.a"; reg = < 0xf800b000 0x1000 >; }; ps7_ddrc_0: ps7-ddrc@f8006000 { compatible = "xlnx,ps7-ddrc-1.00.a"; reg = < 0xf8006000 0x1000 >; xlnx,has-ecc = <0x0>; }; ps7_dev_cfg_0: ps7-dev-cfg@f8007000 { compatible = "xlnx,ps7-dev-cfg-1.00.a"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 8 4 >; reg = < 0xf8007000 0x1000 >; }; ps7_dma_ns: ps7-dma@f8004000 { compatible = "xlnx,ps7-dma-1.00.a", "arm,pl330"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 13 4 0 14 4 0 15 4 0 16 4 0 17 4 0 40 4 0 41 4 0 42 4 0 43 4 >; reg = < 0xf8004000 0x1000 >; }; ps7_dma_s: ps7-dma@f8003000 { compatible = "xlnx,ps7-dma-1.00.a", "arm,pl330"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 13 4 0 14 4 0 15 4 0 16 4 0 17 4 0 40 4 0 41 4 0 42 4 0 43 4 >; reg = < 0xf8003000 0x1000 >; }; ps7_ethernet_0: ps7-ethernet@e000b000 { #address-cells = <1>; #size-cells = <0>; compatible = "xlnx,ps7-ethernet-1.00.a"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 22 4 >; local-mac-address = [ 00 0a 35 00 00 00 ]; phy-handle = <&phy0>; reg = < 0xe000b000 0x1000 >; xlnx,enet-clk-freq-hz = <0x7735940>; xlnx,enet-reset = <0xffffffff>; xlnx,enet-slcr-1000mbps-div0 = <0x8>; xlnx,enet-slcr-1000mbps-div1 = <0x1>; xlnx,enet-slcr-100mbps-div0 = <0x8>; xlnx,enet-slcr-100mbps-div1 = <0x5>; xlnx,enet-slcr-10mbps-div0 = <0x8>; xlnx,enet-slcr-10mbps-div1 = <0x32>; xlnx,eth-mode = <0x1>; xlnx,has-mdio = <0x1>; xlnx,ptp-enet-clock = <111111115>; mdio { #address-cells = <1>; #size-cells = <0>; phy0: phy@0 { compatible = "marvell,88e1510"; device_type = "ethernet-phy"; reg = <0>; marvell,reg-init = <0x3 0x10 0xff00 0x1e 0x3 0x11 0xfff0 0xa>; }; }; }; ps7_gpio_0: ps7-gpio@e000a000 { #gpio-cells = <2>; compatible = "xlnx,ps7-gpio-1.00.a"; emio-gpio-width = <60>; gpio-controller; gpio-mask-high = <0xc0000>; gpio-mask-low = <0xfe81>; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 20 4 >; reg = < 0xe000a000 0x1000 >; }; ps7_iop_bus_config_0: ps7-iop-bus-config@e0200000 { compatible = "xlnx,ps7-iop-bus-config-1.00.a"; reg = < 0xe0200000 0x1000 >; }; ps7_pl310_0: ps7-pl310@f8f02000 { arm,data-latency = < 3 2 2 >; arm,tag-latency = < 2 2 2 >; cache-level = < 2 >; cache-unified; compatible = "xlnx,ps7-pl310-1.00.a", "arm,pl310-cache"; reg = < 0xf8f02000 0x1000 >; }; ps7_qspi_0: ps7-qspi@e000d000 { bus-num = <0>; compatible = "xlnx,ps7-qspi-1.00.a"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 19 4 >; is-dual = <0>; num-chip-select = <1>; reg = < 0xe000d000 0x1000 >; speed-hz = <200000000>; xlnx,fb-clk = <0x1>; xlnx,qspi-clk-freq-hz = <0xbebc200>; xlnx,qspi-mode = <0x0>; }; ps7_qspi_linear_0: ps7-qspi-linear@fc000000 { compatible = "xlnx,ps7-qspi-linear-1.00.a"; reg = < 0xfc000000 0x1000000 >; xlnx,qspi-clk-freq-hz = <0xe4e1c0>; }; ps7_scugic_0: ps7-scugic@f8f01000 { #address-cells = < 2 >; #interrupt-cells = < 3 >; #size-cells = < 1 >; compatible = "xlnx,ps7-scugic-1.00.a", "arm,cortex-a9-gic", "arm,gic"; interrupt-controller; linux,phandle = < 0x1 >; phandle = < 0x1 >; reg = < 0xf8f01000 0x1000 0xf8f00100 0x100 >; }; ps7_scutimer_0: ps7-scutimer@f8f00600 { clock-frequency = <333333343>; compatible = "xlnx,ps7-scutimer-1.00.a", "arm,cortex-a9-twd-timer"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 1 13 769 >; reg = < 0xf8f00600 0x20 >; }; ps7_scuwdt_0: ps7-scuwdt@f8f00620 { clock-frequency = <333333343>; /* clock-frequency = <111111111>; */ compatible = "xlnx,ps7-scuwdt-1.00.a"; device_type = "watchdog"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 1 14 769 >; reg = < 0xf8f00620 0xe0 >; }; ps7_sd_0: ps7-sdio@e0100000 { clock-frequency = <50000000>; compatible = "xlnx,ps7-sdio-1.00.a", "generic-sdhci"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 24 4 >; reg = < 0xe0100000 0x1000 >; xlnx,has-cd = <0x1>; xlnx,has-power = <0x0>; xlnx,has-wp = <0x1>; xlnx,sdio-clk-freq-hz = <0x2faf080>; /* clock-frequency = <0x1fc9f08>; */ }; ps7_slcr_0: ps7-slcr@f8000000 { compatible = "xlnx,ps7-slcr-1.00.a"; reg = < 0xf8000000 0x1000 >; }; ps7_ttc_0: ps7-ttc@f8001000 { clock-frequency = <111111115>; clock-frequency-timer0 = <111111115>; clock-frequency-timer1 = <111111115>; clock-frequency-timer2 = <111111115>; compatible = "xlnx,ps7-ttc-1.00.a"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 10 4 0 11 4 0 12 4 >; reg = < 0xf8001000 0x1000 >; xlnx,ttc-clk0-clksrc = <0x0>; xlnx,ttc-clk0-freq-hz = <0x69f6bcb>; xlnx,ttc-clk1-clksrc = <0x0>; xlnx,ttc-clk1-freq-hz = <0x69f6bcb>; xlnx,ttc-clk2-clksrc = <0x0>; xlnx,ttc-clk2-freq-hz = <0x69f6bcb>; }; ps7_uart_1: serial@e0001000 { clock = <50000000>; clock-frequency = <50000000>; compatible = "xlnx,ps7-uart-1.00.a", "xlnx,xuartps"; current-speed = <115200>; device_type = "serial"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 50 4 >; port-number = <0>; reg = < 0xe0001000 0x1000 >; xlnx,has-modem = <0x0>; xlnx,uart-clk-freq-hz = <0x2faf080>; }; ps7_usb_0: ps7-usb@e0002000 { compatible = "xlnx,ps7-usb-1.00.a"; dr_mode = "host"; interrupt-parent = <&ps7_scugic_0>; interrupts = < 0 21 4 >; phy_type = "ulpi"; reg = < 0xe0002000 0x1000 >; xlnx,usb-reset = <0xffffffff>; }; ps7_xadc: ps7_xadc@f8007100 { compatible = "xlnx,ps7-xadc-1.00.a"; interrupts = < 0 7 4 >; reg = < 0xf8007100 0x20 >; }; }; };
~/linux-digilent/scripts/dtc/dtc -I dts -O dtb -o devicetree.dtb devicetree.dtsでdtbにコンパイル。
これで必要なファイルがそろったので、SDカードにコピーします。zImageとramdiskは前のままです。
U-Boot 2012.04.01 (Sep 25 2013 - 00:19:21) DRAM: 512 MiB WARNING: Caches not enabled MMC: SDHCI: 0 Using default environment In: serial Out: serial Err: serial Net: zynq_gem Hit any key to stop autoboot: 0 Copying Linux from SD to RAM... Device: SDHCI Manufacturer ID: 1d OEM: 4144 Name: SD Tran Speed: 25000000 Rd Block Len: 512 SD version 1.10 High Capacity: No Capacity: 1.9 GiB Bus Width: 4-bit reading zImage 2527040 bytes read reading devicetree.dtb 6481 bytes read reading ramdisk8M.image.gz 3698068 bytes read ## Starting application at 0x00008000 ... Uncompressing Linux... done, booting the kernel. [ 0.000000] Booting Linux on physical CPU 0 [ 0.000000] Linux version 3.6.0-digilent-13.01-00002-g06b3889 (develop@vm-ubuntu) (gcc version 4.7.2 (Sourcery CodeBench Lite 2012.09-104) ) #1 SMP PREEMPT Wed Sep 25 00:32:40 JST 2013 [ 0.000000] CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d [ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache [ 0.000000] Machine: Xilinx Zynq Platform, model: Xilinx Zynq ZED [ 0.000000] Memory policy: ECC disabled, Data cache writealloc [ 0.000000] PERCPU: Embedded 7 pages/cpu @c1408000 s6976 r8192 d13504 u32768 [ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 130048 [ 0.000000] Kernel command line: console=ttyPS0,115200 root=/dev/ram rw initrd=0x800000,8M earlyprintk rootwait devtmpfs.mount=1 [ 0.000000] PID hash table entries: 2048 (order: 1, 8192 bytes) [ 0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes) [ 0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes) [ 0.000000] Memory: 512MB = 512MB total [ 0.000000] Memory: 506432k/506432k available, 17856k reserved, 0K highmem [ 0.000000] Virtual kernel memory layout: [ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB) [ 0.000000] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) [ 0.000000] vmalloc : 0xe0800000 - 0xfd000000 ( 456 MB) [ 0.000000] lowmem : 0xc0000000 - 0xe0000000 ( 512 MB) [ 0.000000] pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB) [ 0.000000] modules : 0xbf000000 - 0xbfe00000 ( 14 MB) [ 0.000000] .text : 0xc0008000 - 0xc0473a60 (4527 kB) [ 0.000000] .init : 0xc0474000 - 0xc0499b40 ( 151 kB) [ 0.000000] .data : 0xc049a000 - 0xc04d4ae0 ( 235 kB) [ 0.000000] .bss : 0xc04d4b04 - 0xc04ee100 ( 102 kB) [ 0.000000] Preemptible hierarchical RCU implementation. [ 0.000000] Dump stacks of tasks blocking RCU-preempt GP. [ 0.000000] RCU restricting CPUs from NR_CPUS=4 to nr_cpu_ids=2. [ 0.000000] NR_IRQS:512 [ 0.000000] Zynq clock init [ 0.000000] xlnx,ps7-ttc-1.00.a #0 at 0xe0800000, irq=43 [ 0.000000] sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps every 4294967286ms [ 0.000000] Console: colour dummy device 80x30 [ 0.090000] Calibrating delay loop... 1332.01 BogoMIPS (lpj=6660096) [ 0.090000] pid_max: default: 32768 minimum: 301 [ 0.090000] Mount-cache hash table entries: 512 [ 0.090000] CPU: Testing write buffer coherency: ok [ 0.090000] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000 [ 0.090000] hw perfevents: enabled with ARMv7 Cortex-A9 PMU driver, 7 counters available [ 0.090000] Setting up static identity map for 0x32be88 - 0x32bebc [ 0.090000] L310 cache controller enabled [ 0.090000] l2x0: 8 ways, CACHE_ID 0x410000c8, AUX_CTRL 0x72360000, Cache size: 524288 B [ 0.130000] Map SLCR registers [ 0.130000] CPU1: Booted secondary processor [ 0.220000] CPU1: thread -1, cpu 1, socket 0, mpidr 80000001 [ 0.220000] Brought up 2 CPUs [ 0.220000] SMP: Total of 2 processors activated (2664.03 BogoMIPS). [ 0.220000] devtmpfs: initialized [ 0.220000] NET: Registered protocol family 16 [ 0.220000] DMA: preallocated 256 KiB pool for atomic coherent allocations [ 0.220000] xgpiops e000a000.ps7-gpio: gpio at 0xe000a000 mapped to 0xe084a000 [ 0.230000] registering platform device 'pl330' id 0 [ 0.230000] registering platform device 'arm-pmu' id 0 [ 0.230000] registering platform device 'zynq-dvfs' id 0 [ 0.230000] [ 0.230000] ############################################### [ 0.230000] # # [ 0.230000] # Board ZED Init # [ 0.230000] # # [ 0.230000] ############################################### [ 0.230000] [ 0.230000] hw-breakpoint: found 5 (+1 reserved) breakpoint and 1 watchpoint registers. [ 0.230000] hw-breakpoint: maximum watchpoint size is 4 bytes. [ 0.250000] xslcr xslcr.0: at 0xF8000000 mapped to 0xF8000000 [ 0.260000] bio: create slabなんか普通に立ち上がっちゃったので、逆に不安ですが…HDMIにペンギンマークも出ませんし、OLEDにロゴも出てこないので、変更後のハードウェアになってるみたいです。at 0 [ 0.270000] SCSI subsystem initialized [ 0.270000] usbcore: registered new interface driver usbfs [ 0.270000] usbcore: registered new interface driver hub [ 0.270000] usbcore: registered new device driver usb [ 0.270000] Advanced Linux Sound Architecture Driver Version 1.0.25. [ 0.270000] Switching to clocksource xttcpss_timer1 [ 0.280000] NET: Registered protocol family 2 [ 0.280000] TCP established hash table entries: 16384 (order: 5, 131072 bytes) [ 0.280000] TCP bind hash table entries: 16384 (order: 5, 131072 bytes) [ 0.280000] TCP: Hash tables configured (established 16384 bind 16384) [ 0.280000] TCP: reno registered [ 0.280000] UDP hash table entries: 256 (order: 1, 8192 bytes) [ 0.280000] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes) [ 0.280000] NET: Registered protocol family 1 [ 0.280000] RPC: Registered named UNIX socket transport module. [ 0.280000] RPC: Registered udp transport module. [ 0.280000] RPC: Registered tcp transport module. [ 0.280000] RPC: Registered tcp NFSv4.1 backchannel transport module. [ 0.280000] Trying to unpack rootfs image as initramfs... [ 0.280000] rootfs image is not initramfs (no cpio magic); looks like an initrd [ 0.320000] Freeing initrd memory: 8192K [ 0.320000] pl330 dev 0 probe success [ 0.320000] msgmni has been set to 1005 [ 0.320000] io scheduler noop registered [ 0.320000] io scheduler deadline registered [ 0.320000] io scheduler cfq registered (default) [ 0.320000] e0001000.serial: ttyPS0 at MMIO 0xe0001000 (irq = 82) is a xuartps [ 0.830000] console [ttyPS0] enabled [ 0.840000] xdevcfg f8007000.ps7-dev-cfg: ioremap f8007000 to e0850000 with size 1000 [ 0.840000] [drm] Initialized drm 1.1.0 20060810 [ 0.860000] brd: module loaded [ 0.870000] loop: module loaded [ 0.870000] xqspips e000d000.ps7-qspi: master is unqueued, this is deprecated [ 0.880000] xqspips e000d000.ps7-qspi: at 0xE000D000 mapped to 0xE0852000, irq=51 [ 0.890000] libphy: XEMACPS mii bus: probed [ 0.900000] xemacps e000b000.ps7-ethernet: pdev->id -1, baseaddr 0xe000b000, irq 54 [ 0.900000] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver [ 0.910000] usb_hcd_xusbps_probe: No OTG assigned! [ 0.910000] usb_hcd_xusbps_probe: OTG now assigned! [ 0.920000] xusbps-ehci xusbps-ehci.0: Xilinx PS USB EHCI Host Controller [ 0.930000] xusbps-ehci xusbps-ehci.0: new USB bus registered, assigned bus number 1 [ 0.960000] xusbps-ehci xusbps-ehci.0: irq 53, io mem 0x00000000 [ 0.980000] xusbps-ehci xusbps-ehci.0: USB 2.0 started, EHCI 1.00 [ 0.980000] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002 [ 0.990000] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1 [ 1.000000] usb usb1: Product: Xilinx PS USB EHCI Host Controller [ 1.000000] usb usb1: Manufacturer: Linux 3.6.0-digilent-13.01-00002-g06b3889 ehci_hcd [ 1.010000] usb usb1: SerialNumber: xusbps-ehci.0 [ 1.010000] hub 1-0:1.0: USB hub found [ 1.020000] hub 1-0:1.0: 1 port detected [ 1.020000] Initializing USB Mass Storage driver... [ 1.030000] usbcore: registered new interface driver usb-storage [ 1.030000] USB Mass Storage support registered. [ 1.040000] mousedev: PS/2 mouse device common for all mice [ 1.040000] sdhci: Secure Digital Host Controller Interface driver [ 1.050000] sdhci: Copyright(c) Pierre Ossman [ 1.060000] sdhci-pltfm: SDHCI platform and OF driver helper [ 1.060000] mmc0: Invalid maximum block size, assuming 512 bytes [ 1.110000] mmc0: SDHCI controller on e0100000.ps7-sdio [e0100000.ps7-sdio] using ADMA [ 1.120000] usbcore: registered new interface driver usbhid [ 1.130000] usbhid: USB HID core driver [ 1.140000] TCP: cubic registered [ 1.150000] NET: Registered protocol family 17 [ 1.150000] VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 4 [ 1.160000] Registering SWP/SWPB emulation handler [ 1.160000] registered taskstats version 1 [ 1.170000] drivers/rtc/hctosys.c: unable to open rtc device (rtc0) [ 1.170000] mmc0: SD Status: Invalid Allocation Unit size. [ 1.180000] mmc0: new high speed SD card at address b368 [ 1.180000] mmcblk0: mmc0:b368 SD 1.86 GiB [ 1.190000] ALSA device list: [ 1.190000] mmcblk0: p1 [ 1.190000] No soundcards found. [ 1.200000] RAMDISK: gzip image found at block 0 [ 1.510000] EXT4-fs (ram0): couldn't mount as ext3 due to feature incompatibilities [ 1.570000] EXT4-fs (ram0): mounting ext2 file system using the ext4 subsystem [ 1.570000] EXT4-fs (ram0): warning: mounting unchecked fs, running e2fsck is recommended [ 1.580000] EXT4-fs (ram0): mounted filesystem without journal. Opts: (null) [ 1.590000] VFS: Mounted root (ext2 filesystem) on device 1:0. [ 1.590000] devtmpfs: mounted [ 1.600000] Freeing init memory: 148K Starting rcS... ++ Mounting filesystem ++ Setting up mdev ++ Configure static IP 192.168.1.10 ++ Starting telnet daemon ++ Starting http daemon ++ Starting ftp daemon ++ Starting dropbear (ssh) daemon ++ Starting OLED Display insmod: can't read '/lib/modules/3.6.0-digilent-13.01-00002-g06b3889/pmodoled-gpio.ko': No such file or directory ++ Exporting LEDs & SWs rcS Complete zynq> [ 6.820000] xemacps e000b000.ps7-ethernet: Set clk to 124999998 Hz [ 6.820000] xemacps e000b000.ps7-ethernet: link up (1000/FULL)
zynq> pwd /proc/device-tree/axi@0 zynq> ls -l total 0 -r--r--r-- 1 root 0 4 Jan 1 00:07 #address-cells -r--r--r-- 1 root 0 4 Jan 1 00:07 #size-cells -r--r--r-- 1 root 0 44 Jan 1 00:07 compatible -r--r--r-- 1 root 0 4 Jan 1 00:07 name dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-afi@f8008000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-afi@f8009000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-afi@f800a000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-afi@f800b000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-ddrc@f8006000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-dev-cfg@f8007000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-dma@f8003000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-dma@f8004000 dr-xr-xr-x 3 root 0 0 Jan 1 00:07 ps7-ethernet@e000b000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-gpio@e000a000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-iop-bus-config@e0200000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-pl310@f8f02000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-qspi-linear@fc000000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-qspi@e000d000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-scugic@f8f01000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-scutimer@f8f00600 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-scuwdt@f8f00620 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-sdio@e0100000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-slcr@f8000000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-ttc@f8001000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7-usb@e0002000 dr-xr-xr-x 2 root 0 0 Jan 1 00:07 ps7_xadc@f8007100 -r--r--r-- 1 root 0 0 Jan 1 00:07 ranges dr-xr-xr-x 2 root 0 0 Jan 1 00:07 serial@e0001000こう見るとハードマクロのペリフェラル以外が消えてますし、どうやら効いてるみたいです。
まぁ、色々謎のままですが、これでPLが空っぽのLinux環境ができたので、自分の回路を足していくことになります。
ZedBoard Linux (2) [FPGA]
とりあえず標準状態にはなったので、いじるのに関係しそうな所を眺めてみます。
ramdiskのイメージの中身を変更する方法。ramdisk8M.image.gzを解凍し、作業用の./mntフォルダにマウントさせます。変更後、アンマウントして再圧縮します。
それほど頻繁に行うわけでは無いですが、ROM化するまでrootfsをNFSにした方が便利そうです。
u-bootの設定。「~/u-boot-digilent/include/configs」下の「zynq_zed.h」に色々設定が見られます。30行目付近を抜粋。
・0x0000_8000:zImage
・0x0100_0000 :devicetree.dtb
・0x0080_0000: ramdisk8M.image.gz
にロードしてるのがわかります。「sdboot」とかがマクロのように定義され、u-bootのコマンドラインから「run sdboot」とすると定義した処理が行われます(手でひとつずつ打っても同じです)。
u-bootが立ち上がり、なにもせず数秒経つと自動的に上のマクロのどれかが実行されます。どれを実行するかを選択しているのは「modeboot」変数で、「\u-boot-digilent\board\xilinx\zynq_common\board.c」で設定されているようです。80行目付近。
これらの設定は「~/u-boot-digilent/include/autoconf.mk」に集められます。このファイルはmake時に自動生成されます。どんな設定がどんな名前で定義されてるのか、このファイルとREADMEから追いかけるようにしています。
一方、devicetree.dts。標準のdtsの48行目付近。
SDからu-bootをブートして、nfsかtftpでzImageとdevicetree.dtbをダウンロードして、rootファイルシステムをnfsサーバーに置く、などはこの辺を設定すれば良さそうです。
※標準のカーネルだとnfsが無効になってるようなので、再コンパイルは必要。
……「devicetree.dtbは0x0100_0000にあるぞ」はどこでカーネルに教えてるんでしょう?カーネルは知る必要ない?いやいや、そんなことは…というかdevicetreeの仕組み自体よくわかってないので、もっと勉強が必要ですね。
ramdiskのイメージの中身を変更する方法。ramdisk8M.image.gzを解凍し、作業用の./mntフォルダにマウントさせます。変更後、アンマウントして再圧縮します。
$ gunzip ramdisk8M.image.gz $ sudo mount -o loop ramdisk8M.image mnt $ cd mnt mntの中身を変更 $ sudo umount mnt $ gzip ramdisk8M.imageIPアドレスの変更は./etc/init.d/rcS、マウントの設定は./etc/fstab、など、この辺はLinux通りですかね。
それほど頻繁に行うわけでは無いですが、ROM化するまでrootfsをNFSにした方が便利そうです。
u-bootの設定。「~/u-boot-digilent/include/configs」下の「zynq_zed.h」に色々設定が見られます。30行目付近を抜粋。
#define CONFIG_ZED /* Community Board */ /* Default environment */ #define CONFIG_IPADDR 192.168.1.10 #define CONFIG_SERVERIP 192.168.1.50 #undef CONFIG_ZYNQ_XIL_LQSPI /* No NOR Flash available on ZedBoard */ #define CONFIG_SYS_NO_FLASH #define CONFIG_ENV_IS_NOWHERE #undef CONFIG_EXTRA_ENV_SETTINGS #define CONFIG_EXTRA_ENV_SETTINGS \ "ethaddr=00:0a:35:00:01:22\0" \ "kernel_size=0x140000\0" \ "ramdisk_size=0x200000\0" \ "qspiboot=sf probe 0 0 0;" \ "sf read 0x8000 0x100000 0x2c0000;" \ "sf read 0x1000000 0x3c0000 0x40000;" \ "sf read 0x800000 0x400000 0x800000;" \ "go 0x8000\0" \ "sdboot_linaro=echo Copying Linux from SD to RAM...;" \ "mmcinfo;" \ "fatload mmc 0 0x8000 zImage;" \ "fatload mmc 0 0x1000000 devicetree_linaro.dtb;" \ "go 0x8000\0" \ "sdboot=echo Copying Linux from SD to RAM...;" \ "mmcinfo;" \ "fatload mmc 0 0x8000 zImage;" \ "fatload mmc 0 0x1000000 devicetree.dtb;" \ "fatload mmc 0 0x800000 ramdisk8M.image.gz;" \ "go 0x8000\0" \ "jtagboot=echo TFTPing Linux to RAM...;" \ "tftp 0x8000 zImage;" \ "tftp 0x1000000 devicetree.dtb;" \ "tftp 0x800000 ramdisk8M.image.gz;" \ "go 0x8000\0"イメージをコピーしてカーネルにジャンプする部分です。IPアドレスとかサーバーのアドレスもここら辺で変えられるようです。
・0x0000_8000:zImage
・0x0100_0000 :devicetree.dtb
・0x0080_0000: ramdisk8M.image.gz
にロードしてるのがわかります。「sdboot」とかがマクロのように定義され、u-bootのコマンドラインから「run sdboot」とすると定義した処理が行われます(手でひとつずつ打っても同じです)。
u-bootが立ち上がり、なにもせず数秒経つと自動的に上のマクロのどれかが実行されます。どれを実行するかを選択しているのは「modeboot」変数で、「\u-boot-digilent\board\xilinx\zynq_common\board.c」で設定されているようです。80行目付近。
int board_late_init (void) { u32 boot_mode; boot_mode = (XIo_In32(BOOT_MODE_REG) & BOOT_MODES_MASK); switch(boot_mode) { case QSPI_MODE: setenv("modeboot", "run qspiboot"); break; case NAND_FLASH_MODE: setenv("modeboot", "run nandboot"); break; case NOR_FLASH_MODE: setenv("modeboot", "run norboot"); break; case SD_MODE: setenv("modeboot", "run sdboot"); break; case JTAG_MODE: setenv("modeboot", "run jtagboot"); break; default: setenv("modeboot", ""); break; } return 0; }ブートモードジャンパーの値で切り替えてます。通常LinuxはSDからブートしているので、「sdboot」が選ばれるという仕組みです。
これらの設定は「~/u-boot-digilent/include/autoconf.mk」に集められます。このファイルはmake時に自動生成されます。どんな設定がどんな名前で定義されてるのか、このファイルとREADMEから追いかけるようにしています。
一方、devicetree.dts。標準のdtsの48行目付近。
chosen { /*bootargs = "console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=1";*/ bootargs = "console=ttyPS0,115200 root=/dev/ram rw initrd=0x800000,8M earlyprintk rootwait devtmpfs.mount=1"; linux,stdout-path = "/axi@0/serial@e0001000"; };ここでLinuxカーネルのブート引数を設定してます。シリアルポートの設定や、先にコピーしたramdiskのアドレスなども与えています。コメントアウトされてる設定はSDカードをパーティション分けして、rootにマウントさせてます。この方法は大容量のファイルシステムが使えますし、設定を保存しておくこともできます。けど、いきなりバンと電源を切ってしまうようなシステムだとちょっと怖いです。
SDからu-bootをブートして、nfsかtftpでzImageとdevicetree.dtbをダウンロードして、rootファイルシステムをnfsサーバーに置く、などはこの辺を設定すれば良さそうです。
※標準のカーネルだとnfsが無効になってるようなので、再コンパイルは必要。
……「devicetree.dtbは0x0100_0000にあるぞ」はどこでカーネルに教えてるんでしょう?カーネルは知る必要ない?いやいや、そんなことは…というかdevicetreeの仕組み自体よくわかってないので、もっと勉強が必要ですね。
ZedBoard Linux (1) [FPGA]
ZedBoardでLinuxを実行するまでのメモ。
まずは変更を加えずに、標準の環境ができるまでにします。
Digilentのホームページから「ZedBoard_ELHoT」「ZedBoard_Linux_Design」の2つを落としておきます。手順書やソースファイルなどが入っています。
ISE14.6を使用(元のドキュメントでは14.4)。VMware+Ubuntu 12.04 LTS。ホストはWindows7。
ARMのGCCなどを個別に集めても構築できるらしいですが、Xilinxのツールを入れてしまいます。
XilinxのDownloadページから「Software Development Kit」をダウンロード。約1.7GB。でかい。
毎回忘れる、カーネルコンフィグメニューに必要なncurses-devをインストール。
以下はログインするたびに実行。デフォルトで設定するようにしてもOK。
XilinxのSDK環境変数設定。
Linuxのソースを取得、構築。
標準のコンフィグは「~/linux-digilent/arch/arm/configs/digilent_zed_defconfig」です。これを「.config」にコピーしても良いです。
u-bootの作成。
「~/u-boot-digilent/include/configs/zynq_zed.h」がZedBoardの設定ファイルです。必要に応じて編集します。
Device Tree Generatorを導入。
SDK上でハードウェアからdtsファイルを作ってくれるツールです。自分でハードウェアをカスタマイズしたときは、このツールでdtsファイルを作ります。
ちなみに、このフォルダをWindowsの「C:\Xilinx\SDK\2013.2\sw\lib\bsp」にコピーすればWindowsのSDKでもDevice Tree Generatorが使えます。
FSBLを作成。SDKを立ち上げます。
標準の「hw_platform」のフォルダをコピーしておきます。ついでに「system.bit」を「hw_platform」の中にコピーします。本来これらはPlanAheadからExportされます。
このフォルダの中の「system.xml」を元にHardware Platform Specificationを作成します。
New → Others → Xilinx → Hardware Platform Specification を選びます。
コピーしたフォルダの「system.xml」を指定します。
Finishで「hw_platform」フォルダができます。
New → Application Projectで新しく「FSBL」というプロジェクトを作ります。テンプレートとしてFSBLを選びます。設定はデフォルトのまま。
自動的にBuildが始まりますが、gmakeを呼んでいてBuildに失敗するので、makeにシンボリックリンクさせます。ホントはどう対処するのが正しいんだろ?
※ZedBoardではUSBのリセットをFSBLで行うようになっています。手順書に書いてありますがここでは割愛。
ブートイメージを作ります。
SDK上で「FSBL」が選択された状態で、Xilinx Tools → Create Zynq Boot Imageを選択します。
「FSBL.elf」と「system.bit」と「u-boot.elf」を選択し、Createします。
デフォルトでは「~/workspace/FSBL/bootimage/u-boot.bin」というファイルができます。
これを「BOOT.BIN」にリネームして、SDカードにコピーします。
※多分このSDKの作業はWindows上でも同じように動作するはずです。
Device Tree Blobを作成します。
「devicetree.dts」が標準のソースファイルです。コンパイル済みの「devicetree.dtb」をそのまま使っても同じです。
自分でカスタマイズしたハードウェア用にdtsを作るときは、先に入れたDevice Tree Generatorを使うようです。New → Board Support Packageでdevice-treeを選択します。
オプション設定。とりあえずコンソールをUARTに割り当てます。
「ps7_coretex9_0/libsrc/device-tree_xxx/xilinx.dts」が生成されます。「xilinx.dts」と標準の「devicetree.dts」を見比べてみると、生成されたdtsに後で人が色々手を加えてあるようです。dtsの変更はまた今度、今回は標準のdtsを使います。
dtbへのコンパイル。
これまで作った
・BOOT.BIN (FSBL+ビットストリーム+u-boot)
・zImage (Linuxカーネル)
・devicetree.dtb
・ramdisk8M.image.gz (これは標準のまま)
の4つをSDカードにコピーします。ファイル名はコード内で決め打ちなのでこのままです。BOOT.BIN以外はソースを書き換えればファイル名の変更はできます。
後はSDカードをZedBoardに挿して、シリアルポートをつないで、電源を入れれば立ち上がる、はず。
標準ではIPアドレスが192.168.1.10決め打ちです。環境に合わせ、一時的に変更します。
とりあえず動いたので、これから自分の目的に合わせてカスタマイズしていくことになります。
まずは変更を加えずに、標準の環境ができるまでにします。
Digilentのホームページから「ZedBoard_ELHoT」「ZedBoard_Linux_Design」の2つを落としておきます。手順書やソースファイルなどが入っています。
ISE14.6を使用(元のドキュメントでは14.4)。VMware+Ubuntu 12.04 LTS。ホストはWindows7。
ARMのGCCなどを個別に集めても構築できるらしいですが、Xilinxのツールを入れてしまいます。
XilinxのDownloadページから「Software Development Kit」をダウンロード。約1.7GB。でかい。
$ tar xvf Xilinx_SDK_2013.2_0616_1.tar $ cd Xilinx_SDK_2013.2_0616_1 $ sudo sh xsetupデフォルトでは「/opt/Xilinx/SDK/2013.02/」にインストールされます。
毎回忘れる、カーネルコンフィグメニューに必要なncurses-devをインストール。
$ sudo apt-get install ncurses-dev
以下はログインするたびに実行。デフォルトで設定するようにしてもOK。
XilinxのSDK環境変数設定。
$ source /opt/Xilinx/SDK/2013.2/settings32.shついでにARMクロスコンパイル用の環境設定スクリプトも作成&実行。
$ cat armenv.sh #!/bin/bash export CCOMPILER=arm-xilinx-linux-gnueabi-gcc export ARCH=arm export CROSS_COMPILE=arm-xilinx-linux-gnueabi- $ source ./armenv.sh
Linuxのソースを取得、構築。
$ git clone https://github.com/Digilent/linux-digilentこれまた結構でかいので時間かかります。「~/linux-digilent」フォルダができます。
標準のコンフィグは「~/linux-digilent/arch/arm/configs/digilent_zed_defconfig」です。これを「.config」にコピーしても良いです。
$ cd linux-digilent $ cp ./arch/arm/configs/digilent_zed_defconfig .config $ make menuconfig $ make「~/linux-digilent/arch/arm/boot/zImage」にカーネルイメージができます。
u-bootの作成。
$ git clone https://github.com/Digilent/u-boot-digilent「~/u-boot-digilent」フォルダができます。
「~/u-boot-digilent/include/configs/zynq_zed.h」がZedBoardの設定ファイルです。必要に応じて編集します。
$ make zynq_zed_config $ makemakeが終わると「u-boot」ができます。これを「u-boot.elf」とかにリネームしてコピー。
Device Tree Generatorを導入。
SDK上でハードウェアからdtsファイルを作ってくれるツールです。自分でハードウェアをカスタマイズしたときは、このツールでdtsファイルを作ります。
$ git clone git://git.xilinx.com/device-tree.git bsp/device-tree_v0_00_x $ sudo cp -r bsp/device-tree_v0_00_x/ /opt/Xilinx/SDK/2013.2/sw/lib/bsp/「~/bsp/device-tree_v0_00_x」フォルダができるので、SDKの標準リポジトリにコピー。
ちなみに、このフォルダをWindowsの「C:\Xilinx\SDK\2013.2\sw\lib\bsp」にコピーすればWindowsのSDKでもDevice Tree Generatorが使えます。
FSBLを作成。SDKを立ち上げます。
$ xsdk (Xilinx SDK:15612): LIBDBUSMENU-GTK-CRITICAL **: watch_submenu: assertion `GTK_IS_MENU_SHELL(menu)' failedというエラーが出て、SDKのメニューが表示されない。ググったところ、
$ export UBUNTU_MENUPROXY=0としてからSDKを立ち上げればば良いらしいです。先の「~/armenv.sh」に追加記述しておいてもOK。
標準の「hw_platform」のフォルダをコピーしておきます。ついでに「system.bit」を「hw_platform」の中にコピーします。本来これらはPlanAheadからExportされます。
このフォルダの中の「system.xml」を元にHardware Platform Specificationを作成します。
New → Others → Xilinx → Hardware Platform Specification を選びます。
コピーしたフォルダの「system.xml」を指定します。
Finishで「hw_platform」フォルダができます。
New → Application Projectで新しく「FSBL」というプロジェクトを作ります。テンプレートとしてFSBLを選びます。設定はデフォルトのまま。
自動的にBuildが始まりますが、gmakeを呼んでいてBuildに失敗するので、makeにシンボリックリンクさせます。ホントはどう対処するのが正しいんだろ?
$ sudo ln -s /usr/bin/make /usr/bin/gmake再度Build。「FSBL.elf」ができます。
※ZedBoardではUSBのリセットをFSBLで行うようになっています。手順書に書いてありますがここでは割愛。
ブートイメージを作ります。
SDK上で「FSBL」が選択された状態で、Xilinx Tools → Create Zynq Boot Imageを選択します。
「FSBL.elf」と「system.bit」と「u-boot.elf」を選択し、Createします。
デフォルトでは「~/workspace/FSBL/bootimage/u-boot.bin」というファイルができます。
これを「BOOT.BIN」にリネームして、SDカードにコピーします。
※多分このSDKの作業はWindows上でも同じように動作するはずです。
Device Tree Blobを作成します。
「devicetree.dts」が標準のソースファイルです。コンパイル済みの「devicetree.dtb」をそのまま使っても同じです。
自分でカスタマイズしたハードウェア用にdtsを作るときは、先に入れたDevice Tree Generatorを使うようです。New → Board Support Packageでdevice-treeを選択します。
オプション設定。とりあえずコンソールをUARTに割り当てます。
「ps7_coretex9_0/libsrc/device-tree_xxx/xilinx.dts」が生成されます。「xilinx.dts」と標準の「devicetree.dts」を見比べてみると、生成されたdtsに後で人が色々手を加えてあるようです。dtsの変更はまた今度、今回は標準のdtsを使います。
dtbへのコンパイル。
~/linux-digilent/scripts/dtc/dtc -I dts -O dtb -o devicetree.dtb devicetree.dts
これまで作った
・BOOT.BIN (FSBL+ビットストリーム+u-boot)
・zImage (Linuxカーネル)
・devicetree.dtb
・ramdisk8M.image.gz (これは標準のまま)
の4つをSDカードにコピーします。ファイル名はコード内で決め打ちなのでこのままです。BOOT.BIN以外はソースを書き換えればファイル名の変更はできます。
後はSDカードをZedBoardに挿して、シリアルポートをつないで、電源を入れれば立ち上がる、はず。
U-Boot 2012.04.01 (Sep 25 2013 - 00:19:21) DRAM: 512 MiB WARNING: Caches not enabled MMC: SDHCI: 0 Using default environment In: serial Out: serial Err: serial Net: zynq_gem Hit any key to stop autoboot: 0 Copying Linux from SD to RAM... Device: SDHCI Manufacturer ID: 1d OEM: 4144 Name: SD Tran Speed: 25000000 Rd Block Len: 512 SD version 1.10 High Capacity: No Capacity: 1.9 GiB Bus Width: 4-bit reading zImage 2527040 bytes read reading devicetree.dtb 9648 bytes read reading ramdisk8M.image.gz 3694324 bytes read ## Starting application at 0x00008000 ... Uncompressing Linux... done, booting the kernel. [ 0.000000] Booting Linux on physical CPU 0 [ 0.000000] Linux version 3.6.0-digilent-13.01-00002-g06b3889 (develop@vm-ubuntu) (gcc version 4.7.2 (Sourcery CodeBench Lite 2012.09-104) ) #1 SMP PREEMPT Wed Sep 25 00:32:40 JST 2013 [ 0.000000] CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d [ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache [ 0.000000] Machine: Xilinx Zynq Platform, model: Xilinx Zynq ZED [ 0.000000] Memory policy: ECC disabled, Data cache writealloc [ 0.000000] PERCPU: Embedded 7 pages/cpu @c140a000 s6976 r8192 d13504 u32768 [ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 130048 [ 0.000000] Kernel command line: console=ttyPS0,115200 root=/dev/ram rw initrd=0x800000,8M earlyprintk rootwait devtmpfs.mount=1 [ 0.000000] PID hash table entries: 2048 (order: 1, 8192 bytes) [ 0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes) [ 0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes) [ 0.000000] Memory: 512MB = 512MB total [ 0.000000] Memory: 506424k/506424k available, 17864k reserved, 0K highmem [ 0.000000] Virtual kernel memory layout: [ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB) [ 0.000000] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) [ 0.000000] vmalloc : 0xe0800000 - 0xfd000000 ( 456 MB) [ 0.000000] lowmem : 0xc0000000 - 0xe0000000 ( 512 MB) [ 0.000000] pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB) [ 0.000000] modules : 0xbf000000 - 0xbfe00000 ( 14 MB) [ 0.000000] .text : 0xc0008000 - 0xc0473a60 (4527 kB) [ 0.000000] .init : 0xc0474000 - 0xc0499b40 ( 151 kB) [ 0.000000] .data : 0xc049a000 - 0xc04d4ae0 ( 235 kB) [ 0.000000] .bss : 0xc04d4b04 - 0xc04ee100 ( 102 kB) [ 0.000000] Preemptible hierarchical RCU implementation. [ 0.000000] Dump stacks of tasks blocking RCU-preempt GP. [ 0.000000] RCU restricting CPUs from NR_CPUS=4 to nr_cpu_ids=2. [ 0.000000] NR_IRQS:512 [ 0.000000] Zynq clock init [ 0.000000] xlnx,ps7-ttc-1.00.a #0 at 0xe0800000, irq=43 [ 0.000000] sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps every 4294967286ms [ 0.000000] Console: colour dummy device 80x30 [ 0.090000] Calibrating delay loop... 1332.01 BogoMIPS (lpj=6660096) [ 0.090000] pid_max: default: 32768 minimum: 301 [ 0.090000] Mount-cache hash table entries: 512 [ 0.090000] CPU: Testing write buffer coherency: ok [ 0.090000] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000 [ 0.090000] hw perfevents: enabled with ARMv7 Cortex-A9 PMU driver, 7 counters available [ 0.090000] Setting up static identity map for 0x32be88 - 0x32bebc [ 0.090000] L310 cache controller enabled [ 0.090000] l2x0: 8 ways, CACHE_ID 0x410000c8, AUX_CTRL 0x72360000, Cache size: 524288 B [ 0.130000] Map SLCR registers [ 0.130000] CPU1: Booted secondary processor [ 0.220000] CPU1: thread -1, cpu 1, socket 0, mpidr 80000001 [ 0.220000] Brought up 2 CPUs [ 0.220000] SMP: Total of 2 processors activated (2664.03 BogoMIPS). [ 0.220000] devtmpfs: initialized [ 0.220000] NET: Registered protocol family 16 [ 0.220000] DMA: preallocated 256 KiB pool for atomic coherent allocations [ 0.220000] xgpiops e000a000.ps7-gpio: gpio at 0xe000a000 mapped to 0xe084a000 [ 0.230000] registering platform device 'pl330' id 0 [ 0.230000] registering platform device 'arm-pmu' id 0 [ 0.230000] registering platform device 'zynq-dvfs' id 0 [ 0.230000] [ 0.230000] ############################################### [ 0.230000] # # [ 0.230000] # Board ZED Init # [ 0.230000] # # [ 0.230000] ############################################### [ 0.230000] [ 0.230000] hw-breakpoint: found 5 (+1 reserved) breakpoint and 1 watchpoint registers. [ 0.230000] hw-breakpoint: maximum watchpoint size is 4 bytes. [ 0.250000] xslcr xslcr.0: at 0xF8000000 mapped to 0xF8000000 [ 0.270000] bio: create slab立ち上がりました。なんかinsmodでエラーが出ちゃってますが、これは後でどうにかなるでしょう。at 0 [ 0.270000] SCSI subsystem initialized [ 0.270000] usbcore: registered new interface driver usbfs [ 0.270000] usbcore: registered new interface driver hub [ 0.270000] usbcore: registered new device driver usb [ 0.270000] Advanced Linux Sound Architecture Driver Version 1.0.25. [ 0.270000] Switching to clocksource xttcpss_timer1 [ 0.280000] NET: Registered protocol family 2 [ 0.280000] TCP established hash table entries: 16384 (order: 5, 131072 bytes) [ 0.280000] TCP bind hash table entries: 16384 (order: 5, 131072 bytes) [ 0.280000] TCP: Hash tables configured (established 16384 bind 16384) [ 0.280000] TCP: reno registered [ 0.280000] UDP hash table entries: 256 (order: 1, 8192 bytes) [ 0.280000] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes) [ 0.280000] NET: Registered protocol family 1 [ 0.280000] RPC: Registered named UNIX socket transport module. [ 0.280000] RPC: Registered udp transport module. [ 0.280000] RPC: Registered tcp transport module. [ 0.280000] RPC: Registered tcp NFSv4.1 backchannel transport module. [ 0.280000] Trying to unpack rootfs image as initramfs... [ 0.280000] rootfs image is not initramfs (no cpio magic); looks like an initrd [ 0.320000] Freeing initrd memory: 8192K [ 0.320000] pl330 dev 0 probe success [ 0.320000] msgmni has been set to 1005 [ 0.320000] io scheduler noop registered [ 0.320000] io scheduler deadline registered [ 0.320000] io scheduler cfq registered (default) [ 0.320000] e0001000.serial: ttyPS0 at MMIO 0xe0001000 (irq = 82) is a xuartps [ 0.840000] console [ttyPS0] enabled [ 0.840000] xdevcfg f8007000.ps7-dev-cfg: ioremap f8007000 to e0850000 with size 1000 [ 0.850000] [drm] Initialized drm 1.1.0 20060810 [ 0.860000] brd: module loaded [ 0.870000] loop: module loaded [ 0.880000] xqspips e000d000.ps7-qspi: master is unqueued, this is deprecated [ 0.880000] xqspips e000d000.ps7-qspi: at 0xE000D000 mapped to 0xE0852000, irq=51 [ 0.890000] libphy: XEMACPS mii bus: probed [ 0.900000] xemacps e000b000.ps7-ethernet: pdev->id -1, baseaddr 0xe000b000, irq 54 [ 0.910000] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver [ 0.910000] usb_hcd_xusbps_probe: No OTG assigned! [ 0.920000] usb_hcd_xusbps_probe: OTG now assigned! [ 0.920000] xusbps-ehci xusbps-ehci.0: Xilinx PS USB EHCI Host Controller [ 0.930000] xusbps-ehci xusbps-ehci.0: new USB bus registered, assigned bus number 1 [ 0.970000] xusbps-ehci xusbps-ehci.0: irq 53, io mem 0x00000000 [ 0.990000] xusbps-ehci xusbps-ehci.0: USB 2.0 started, EHCI 1.00 [ 0.990000] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002 [ 1.000000] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1 [ 1.010000] usb usb1: Product: Xilinx PS USB EHCI Host Controller [ 1.010000] usb usb1: Manufacturer: Linux 3.6.0-digilent-13.01-00002-g06b3889 ehci_hcd [ 1.020000] usb usb1: SerialNumber: xusbps-ehci.0 [ 1.020000] hub 1-0:1.0: USB hub found [ 1.030000] hub 1-0:1.0: 1 port detected [ 1.030000] Initializing USB Mass Storage driver... [ 1.040000] usbcore: registered new interface driver usb-storage [ 1.040000] USB Mass Storage support registered. [ 1.050000] mousedev: PS/2 mouse device common for all mice [ 1.060000] sdhci: Secure Digital Host Controller Interface driver [ 1.060000] sdhci: Copyright(c) Pierre Ossman [ 1.070000] sdhci-pltfm: SDHCI platform and OF driver helper [ 1.070000] mmc0: Invalid maximum block size, assuming 512 bytes [ 1.080000] No connectors reported connected with modes [ 1.090000] [drm] Cannot find any crtc or sizes - going 1024x768 [ 1.110000] Console: switching to colour frame buffer device 128x48 [ 1.120000] mmc0: SDHCI controller on e0100000.ps7-sdio [e0100000.ps7-sdio] using ADMA [ 1.130000] usbcore: registered new interface driver usbhid [ 1.130000] usbhid: USB HID core driver [ 1.130000] spi_gpio spi_gpio.2: master is unqueued, this is deprecated [ 1.130000] pmodoled-gpio-spi [zed_oled] SPI Probing [ 1.180000] fb0: frame buffer device [ 1.180000] drm: registered panic notifier [ 1.190000] [drm] Initialized analog_drm 1.0.0 20110530 on minor 0 [ 1.200000] mmc0: SD Status: Invalid Allocation Unit size. [ 1.210000] mmc0: new high speed SD card at address b368 [ 1.210000] mmcblk0: mmc0:b368 SD 1.86 GiB [ 1.220000] mmcblk0: p1 [ 1.300000] adv7511 0-0039: Failed to add route DAI IN->TMDS [ 1.310000] adv7511-hdmi-snd adv7511_hdmi_snd.2: adv7511 <-> 75c00000.axi-spdif-tx mapping ok [ 1.320000] TCP: cubic registered [ 1.320000] NET: Registered protocol family 17 [ 1.330000] VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 4 [ 1.330000] Registering SWP/SWPB emulation handler [ 1.340000] registered taskstats version 1 [ 1.340000] drivers/rtc/hctosys.c: unable to open rtc device (rtc0) [ 1.350000] ALSA device list: [ 1.350000] #0: HDMI monitor [ 1.360000] RAMDISK: gzip image found at block 0 [ 1.680000] EXT4-fs (ram0): couldn't mount as ext3 due to feature incompatibilities [ 1.710000] EXT4-fs (ram0): mounting ext2 file system using the ext4 subsystem [ 1.710000] EXT4-fs (ram0): warning: mounting unchecked fs, running e2fsck is recommended [ 1.720000] EXT4-fs (ram0): mounted filesystem without journal. Opts: (null) [ 1.730000] VFS: Mounted root (ext2 filesystem) on device 1:0. [ 1.730000] devtmpfs: mounted [ 1.740000] Freeing init memory: 148K Starting rcS... ++ Mounting filesystem ++ Setting up mdev ++ Configure static IP 192.168.1.10 ++ Starting telnet daemon ++ Starting http daemon ++ Starting ftp daemon ++ Starting dropbear (ssh) daemon ++ Starting OLED Display insmod: can't read '/lib/modules/3.6.0-digilent-13.01-00002-g06b3889/pmodoled-gpio.ko': No such file or directory ++ Exporting LEDs & SWs rcS Complete zynq> [ 6.980000] xemacps e000b000.ps7-ethernet: Set clk to 124999998 Hz [ 6.980000] xemacps e000b000.ps7-ethernet: link up (1000/FULL)
標準ではIPアドレスが192.168.1.10決め打ちです。環境に合わせ、一時的に変更します。
zynq> ifconfig eth0 down zynq> ifconfig eth0 192.168.0.201 zynq> ifconfig eth0 up zynq> [ 358.170000] xemacps e000b000.ps7-ethernet: Set clk to 124999998 Hz [ 358.170000] xemacps e000b000.ps7-ethernet: link up (1000/FULL)
develop@vm-ubuntu:~$ ssh root@192.168.0.201 root@192.168.0.201's password: root zynq>とかやるとリモートでログインできます。ブラウザでhttp://192.168.0.201/を見るとWelcomeページが表示されます。
とりあえず動いたので、これから自分の目的に合わせてカスタマイズしていくことになります。
Vivado HLS を使ってみた。第一回まとめ [FPGA]
今回のRGB→YCbCr変換、最適化はもっとかけられます。変換に3x3のマトリクス演算を行っていますが、ホントは必要ありません。Yの係数を全部足すと1.0になるという条件と、(B-Y)、(R-Y)の形に式を変えると、乗算器は4つで済みます。実際XilinxのIPなどはそうやって作られてます。IPの資料を見ると詳しく載ってます。C側の計算を変える事になりますが、HDLでがんばるより楽でしょう。
実際に使えるようなIPを作ることを目的にしていないので、一番シンプルな計算式を持ってきています。
ということで、簡単なプログラムを対象に、C to HDLをやってみました。
…やべぇ…前からかかってた「HDLなんかもう一行も書きたくないよ~」病に拍車をかける結果となりました…
実はAlteraのC2Hを使った時も同じ事を思ったんですが(笑)
最近(Qsysになってから)C2Hのアップデートが聞こえてきませんね?どうしたのかな?
今回の例は簡単なのでHDLで書いてもたかがしれてますが、ちょっと複雑な処理が入ったらもうHDLなんかやりたくないです。
本当はもっと色々なディレクティブが必要になります、I/Oにも設定が必要です、使えるCの構文にも制限があります、純粋にソフトウェア的な書き方だけでは最適化は難しいかも、思った通りの回路にはなかなかならない場合もある、などなど考慮する点はたくさんあります。
でもCで記述して、誤差とか精度とか詰めて、ソースの最適化かけたときもCのシミュレーションで確認、シミュレーションでOKだったらディレクティブとかいじって最適化、HDLを全く見ずにハードウェア完成。コンパイルも実行時間もHDLより遙かに速いので効率も良いです。これは癖になります。
HLSの新しいバージョンではAXI関連が有効になってます。今回のもAXI Streamにするとほかとの接続が良くなります。XPSへのエクスポートもあるので、Cで作ったモジュールを自動でZynqのAXIチャンネルに接続することもできます。ここでもHDLを排除できます。
AlteraもQsys、SOPC Builderで同様なことが可能です。というかこの辺昔からAlteraの方が強いです。AXIではなくAvalonという独自の接続形態でしたが、非常に便利です。やっとXilinxも追いついてきたという感じがします。
AXI対応はXilinxの方が先行ですかね。AXIは一種の流行なので、特にこだわる理由は無いと思いますが。
なにも書かずにものができあがる、AlteraもXilinxも、多分ほかのベンダーも、みんな目指してるところでしょう。
今はその中間解として、システム自動作成ツール(Qsys、XPS、+IP群)+C系合成ツール(C2H、HLS)+CPU(ARM、Nios、MB)+モデル設計ツール(DSP Builder、SystGen)というところに落ち着いてる感じでしょうか。バラバラなのが難点ですが適材適所で行くしか無いのが現状でしょう。もちろんHDLで書いた方が効率よい部分もあるので、しばらくはごちゃ混ぜ状態が続きそうです。
それにしてもやばい。結構できが良いので本気でHDL書きたくない気分になってます…ライセンス約50万でしたっけ…?う~む…
まぁそんなこと言いながらHDLは書くんですけどね(笑)
※DigiKeyとかで見てみたら、HLS単体だとFloatingでも25万ぐらいでした…うぅう~むぅ…
実際に使えるようなIPを作ることを目的にしていないので、一番シンプルな計算式を持ってきています。
ということで、簡単なプログラムを対象に、C to HDLをやってみました。
…やべぇ…前からかかってた「HDLなんかもう一行も書きたくないよ~」病に拍車をかける結果となりました…
実はAlteraのC2Hを使った時も同じ事を思ったんですが(笑)
最近(Qsysになってから)C2Hのアップデートが聞こえてきませんね?どうしたのかな?
今回の例は簡単なのでHDLで書いてもたかがしれてますが、ちょっと複雑な処理が入ったらもうHDLなんかやりたくないです。
本当はもっと色々なディレクティブが必要になります、I/Oにも設定が必要です、使えるCの構文にも制限があります、純粋にソフトウェア的な書き方だけでは最適化は難しいかも、思った通りの回路にはなかなかならない場合もある、などなど考慮する点はたくさんあります。
でもCで記述して、誤差とか精度とか詰めて、ソースの最適化かけたときもCのシミュレーションで確認、シミュレーションでOKだったらディレクティブとかいじって最適化、HDLを全く見ずにハードウェア完成。コンパイルも実行時間もHDLより遙かに速いので効率も良いです。これは癖になります。
HLSの新しいバージョンではAXI関連が有効になってます。今回のもAXI Streamにするとほかとの接続が良くなります。XPSへのエクスポートもあるので、Cで作ったモジュールを自動でZynqのAXIチャンネルに接続することもできます。ここでもHDLを排除できます。
AlteraもQsys、SOPC Builderで同様なことが可能です。というかこの辺昔からAlteraの方が強いです。AXIではなくAvalonという独自の接続形態でしたが、非常に便利です。やっとXilinxも追いついてきたという感じがします。
AXI対応はXilinxの方が先行ですかね。AXIは一種の流行なので、特にこだわる理由は無いと思いますが。
なにも書かずにものができあがる、AlteraもXilinxも、多分ほかのベンダーも、みんな目指してるところでしょう。
今はその中間解として、システム自動作成ツール(Qsys、XPS、+IP群)+C系合成ツール(C2H、HLS)+CPU(ARM、Nios、MB)+モデル設計ツール(DSP Builder、SystGen)というところに落ち着いてる感じでしょうか。バラバラなのが難点ですが適材適所で行くしか無いのが現状でしょう。もちろんHDLで書いた方が効率よい部分もあるので、しばらくはごちゃ混ぜ状態が続きそうです。
それにしてもやばい。結構できが良いので本気でHDL書きたくない気分になってます…ライセンス約50万でしたっけ…?う~む…
まぁそんなこと言いながらHDLは書くんですけどね(笑)
※DigiKeyとかで見てみたら、HLS単体だとFloatingでも25万ぐらいでした…うぅう~むぅ…
Vivado HLS を使ってみた(その6) [FPGA]
リアルタイムでFullHD 1080p60ぐらいは処理して欲しいですよね。
ソースは(その5)のintバージョン。
1080pで148.5MHzなので、ソリューションの周波数を150MHzにしてみます。
MHzを付けると周波数と認識されるようです。
これで合成すると…(レポートファイル)
1クロックに1つ答えが得られない困るので、ディレクティブ(指示子)を与えます。おぉ、やっと機種依存するところが出てきた(笑)
「Directive」タブを開き、関数(rgb2ycbcr)を右クリックします。
ディレクティブの選択画面が出るので「PIPELINE」を選択します。「Source File」を選ぶと「#pragma HLS PIPELINE」がcppソースに埋め込まれます。「Directive File」を選ぶとディレクティブの別のファイルに保存されます。どちらでも効能は同じ。好みの方にします。
「Directive File」を選んだ場合。ディレクティブのタブに新しく「HLS_PIPELINE」が追加されます。
これで合成開始。
データの流れとリソース。
パイプライン化され、1クロックで1個結果が出てくるようになりました。予想周波数は5.47[ns](約182MHz)。
この辺でいい加減一度ぐらいHDLシミュレーションしておかないと…いままでCのシミュレーションが楽で楽で…改めて思う…HDLのテストベンチってめんどくさい…
中身はほぼ同じで、BMPファイルを流し込んで結果を保存します。ただ、色々書くのが面倒なので、BGRをCbYCrに置き換えたBMPファイルだけ作成します。
ちなみにI/Oピンの情報はレポートファイルの一番下に書かれています。細かい制御方法はXilinxの資料にゆずりますが、今回はI/Oになにも指定していないので、関数の返値(YCbCr)に「ap_ctrl_hs」が、引数(RGB)に「ap_none」が設定されています。Y,Cb,Crそれぞれに「_ap_vld」という信号が付いてますが、この場合「ap_done」と同じ信号になるので見ません。
・「ap_rst」を解除する。
・「ap_start」を'1'にすると動作開始。
・クロック立ち上がり&「ap_ready」が'1'にだったら次のRGBデータをBMPファイルから与える。
・クロック立ち上がり&「ap_done」が'1'だったらYCbCrデータをBMPファイルに書き込む。
という制御だけになります。
シミュレーションの最初と最後だけ出してみます。
カラーコードにしたので、シミュレーションの最後の値が動いていませんが、これで正常です。下の方のsとdがそれぞれのピクセルカウンタです。
HDLシミュレーションで生成されたBMPファイルと、Cの整数シミュレーションで生成されたBMPファイルのバイナリ比較は完全一致でした。Cの浮動小数点モデルで生成したBMPファイルとの比較では所々1bitの差が出ます。
PlanAheadでインプリしてみます。
ソースは(その5)のintバージョン。
1080pで148.5MHzなので、ソリューションの周波数を150MHzにしてみます。
MHzを付けると周波数と認識されるようです。
これで合成すると…(レポートファイル)
================================================================ == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+-------+----------+------------+ | Clock | Target| Estimated| Uncertainty| +---------+-------+----------+------------+ |default | 6.67| 5.47| 0.83| +---------+-------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+---------+ | Latency | Interval | Pipeline| | min | max | min | max | Type | +-----+-----+-----+-----+---------+ | 6| 6| 7| 7| none | +-----+-----+-----+-----+---------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| -| 0| 235| |FIFO | -| -| -| -| |Instance | -| 7| 72| 20| |Memory | -| -| -| -| |Multiplexer | -| -| -| -| |Register | -| -| 231| -| |ShiftMemory | -| -| -| -| +-----------------+---------+-------+--------+-------+ |Total | 0| 7| 303| 255| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 3| ~0 | ~0 | +-----------------+---------+-------+--------+-------+ + Detail: * Instance: +--------------------------------+-----------------------------+---------+-------+----+----+ | Instance | Module | BRAM_18K| DSP48E| FF | LUT| +--------------------------------+-----------------------------+---------+-------+----+----+ |rgb2ycbcr_mul_8ns_10ns_18_3_U1 |rgb2ycbcr_mul_8ns_10ns_18_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_10s_19_3_U5 |rgb2ycbcr_mul_8ns_10s_19_3 | 0| 1| 0| 0| |rgb2ycbcr_mul_8ns_12ns_20_3_U7 |rgb2ycbcr_mul_8ns_12ns_20_3 | 0| 1| 0| 0| |rgb2ycbcr_mul_8ns_12s_20_3_U3 |rgb2ycbcr_mul_8ns_12s_20_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_12s_20_3_U4 |rgb2ycbcr_mul_8ns_12s_20_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_9ns_17_3_U2 |rgb2ycbcr_mul_8ns_9ns_17_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_9s_18_3_U6 |rgb2ycbcr_mul_8ns_9s_18_3 | 0| 1| 0| 0| +--------------------------------+-----------------------------+---------+-------+----+----+ |Total | | 0| 7| 72| 20| +--------------------------------+-----------------------------+---------+-------+----+----+ * Memory: N/A * FIFO: N/A * Shift register: N/A * Expression: +--------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +--------------------+----------+-------+---+----+------------+------------+ |tmp1_fu_171_p2 | + | 0| 0| 19| 19| 19| |tmp_10_fu_272_p2 | + | 0| 0| 21| 21| 21| |tmp_5_fu_236_p2 | + | 0| 0| 21| 21| 21| |p_neg1_fu_320_p2 | - | 0| 0| 21| 1| 21| |p_neg5_fu_295_p2 | - | 0| 0| 21| 1| 21| |p_neg_t1_fu_339_p2 | - | 0| 0| 10| 1| 10| |p_neg_t7_fu_314_p2 | - | 0| 0| 10| 1| 10| |Cb_fu_373_p3 | Select | 0| 0| 10| 1| 1| |Cr_fu_396_p3 | Select | 0| 0| 10| 1| 1| |agg_result_Cb_V | Select | 0| 0| 8| 1| 2| |agg_result_Cr_V | Select | 0| 0| 8| 1| 2| |tmp_13_fu_367_p3 | Select | 0| 0| 10| 1| 10| |tmp_s_fu_358_p3 | Select | 0| 0| 10| 1| 10| |icmp1_fu_413_p2 | icmp | 0| 0| 2| 2| 1| |icmp_fu_390_p2 | icmp | 0| 0| 2| 2| 1| |tmp_14_fu_345_p2 | icmp | 0| 0| 26| 21| 13| |tmp_15_fu_350_p2 | icmp | 0| 0| 26| 21| 13| +--------------------+----------+-------+---+----+------------+------------+ |Total | | 0| 0| 235| 117| 177| +--------------------+----------+-------+---+----+------------+------------+ * Multiplexer: N/A * Register: +---------------------+----+-----+-----------+ | Name | FF | Bits| Const Bits| +---------------------+----+-----+-----------+ |G_cast_reg_462 | 8| 20| 12| |ap_CS_fsm | 3| 3| 0| |p_neg_t1_reg_556 | 10| 10| 0| |p_neg_t7_reg_551 | 10| 10| 0| |tmp1_reg_504 | 19| 19| 0| |tmp3_cast_reg_509 | 21| 21| 0| |tmp5_cast_reg_514 | 21| 21| 0| |tmp_10_reg_535 | 21| 21| 0| |tmp_11_cast_reg_499 | 20| 20| 0| |tmp_11_reg_530 | 9| 9| 0| |tmp_12_reg_541 | 1| 1| 0| |tmp_14_reg_561 | 1| 1| 0| |tmp_15_reg_566 | 1| 1| 0| |tmp_17_reg_546 | 9| 9| 0| |tmp_2_reg_489 | 17| 17| 0| |tmp_4_reg_525 | 1| 1| 0| |tmp_5_reg_519 | 21| 21| 0| |tmp_7_cast_reg_494 | 20| 20| 0| |tmp_reg_484 | 18| 18| 0| +---------------------+----+-----+-----------+ |Total | 231| 243| 12| +---------------------+----+-----+-----------+ ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_clk | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_rst | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+7クロックに1演算という回路になりました。今回はap_start,ap_doneなどが制御信号として働きます。ap_startで演算開始、ap_doneで計算結果が得られる、という制御になります。
1クロックに1つ答えが得られない困るので、ディレクティブ(指示子)を与えます。おぉ、やっと機種依存するところが出てきた(笑)
「Directive」タブを開き、関数(rgb2ycbcr)を右クリックします。
ディレクティブの選択画面が出るので「PIPELINE」を選択します。「Source File」を選ぶと「#pragma HLS PIPELINE」がcppソースに埋め込まれます。「Directive File」を選ぶとディレクティブの別のファイルに保存されます。どちらでも効能は同じ。好みの方にします。
「Directive File」を選んだ場合。ディレクティブのタブに新しく「HLS_PIPELINE」が追加されます。
これで合成開始。
================================================================ == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+-------+----------+------------+ | Clock | Target| Estimated| Uncertainty| +---------+-------+----------+------------+ |default | 6.67| 5.47| 0.83| +---------+-------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+----------+ | Latency | Interval | Pipeline | | min | max | min | max | Type | +-----+-----+-----+-----+----------+ | 6| 6| 1| 1| function | +-----+-----+-----+-----+----------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| -| 0| 235| |FIFO | -| -| -| -| |Instance | -| 7| 72| 20| |Memory | -| -| -| -| |Multiplexer | -| -| -| -| |Register | -| -| 287| -| |ShiftMemory | -| -| 0| 24| +-----------------+---------+-------+--------+-------+ |Total | 0| 7| 359| 279| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 3| ~0 | ~0 | +-----------------+---------+-------+--------+-------+ + Detail: * Instance: +--------------------------------+-----------------------------+---------+-------+----+----+ | Instance | Module | BRAM_18K| DSP48E| FF | LUT| +--------------------------------+-----------------------------+---------+-------+----+----+ |rgb2ycbcr_mul_8ns_10ns_18_3_U1 |rgb2ycbcr_mul_8ns_10ns_18_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_10s_19_3_U5 |rgb2ycbcr_mul_8ns_10s_19_3 | 0| 1| 0| 0| |rgb2ycbcr_mul_8ns_12ns_20_3_U7 |rgb2ycbcr_mul_8ns_12ns_20_3 | 0| 1| 0| 0| |rgb2ycbcr_mul_8ns_12s_20_3_U3 |rgb2ycbcr_mul_8ns_12s_20_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_12s_20_3_U4 |rgb2ycbcr_mul_8ns_12s_20_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_9ns_17_3_U2 |rgb2ycbcr_mul_8ns_9ns_17_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_9s_18_3_U6 |rgb2ycbcr_mul_8ns_9s_18_3 | 0| 1| 0| 0| +--------------------------------+-----------------------------+---------+-------+----+----+ |Total | | 0| 7| 72| 20| +--------------------------------+-----------------------------+---------+-------+----+----+ * Memory: N/A * FIFO: N/A * Shift register: +--------------------+---+----+-----+-----------+ | Name | FF| LUT| Bits| Const Bits| +--------------------+---+----+-----+-----------+ |G_cast_reg_467 | 0| 8| 20| 12| |s_B_V_read_reg_450 | 0| 8| 8| 0| |s_R_V_read_reg_456 | 0| 8| 8| 0| +--------------------+---+----+-----+-----------+ |Total | 0| 24| 36| 12| +--------------------+---+----+-----+-----------+ * Expression: +--------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +--------------------+----------+-------+---+----+------------+------------+ |tmp1_fu_177_p2 | + | 0| 0| 19| 19| 19| |tmp_10_fu_277_p2 | + | 0| 0| 21| 21| 21| |tmp_5_fu_241_p2 | + | 0| 0| 21| 21| 21| |p_neg1_fu_325_p2 | - | 0| 0| 21| 1| 21| |p_neg5_fu_300_p2 | - | 0| 0| 21| 1| 21| |p_neg_t1_fu_344_p2 | - | 0| 0| 10| 1| 10| |p_neg_t7_fu_319_p2 | - | 0| 0| 10| 1| 10| |Cb_fu_378_p3 | Select | 0| 0| 10| 1| 1| |Cr_fu_401_p3 | Select | 0| 0| 10| 1| 1| |agg_result_Cb_V | Select | 0| 0| 8| 1| 2| |agg_result_Cr_V | Select | 0| 0| 8| 1| 2| |tmp_13_fu_372_p3 | Select | 0| 0| 10| 1| 10| |tmp_s_fu_363_p3 | Select | 0| 0| 10| 1| 10| |icmp1_fu_418_p2 | icmp | 0| 0| 2| 2| 1| |icmp_fu_395_p2 | icmp | 0| 0| 2| 2| 1| |tmp_14_fu_350_p2 | icmp | 0| 0| 26| 21| 13| |tmp_15_fu_355_p2 | icmp | 0| 0| 26| 21| 13| +--------------------+----------+-------+---+----+------------+------------+ |Total | | 0| 0| 235| 117| 177| +--------------------+----------+-------+---+----+------------+------------+ * Multiplexer: N/A * Register: +-------------------------------------+----+-----+-----------+ | Name | FF | Bits| Const Bits| +-------------------------------------+----+-----+-----------+ |G_cast_reg_467 | 8| 20| 12| |Y_reg_524 | 8| 8| 0| |ap_CS_fsm | 1| 1| 0| |ap_reg_ppiten_pp0_it1 | 1| 1| 0| |ap_reg_ppiten_pp0_it2 | 1| 1| 0| |ap_reg_ppiten_pp0_it3 | 1| 1| 0| |ap_reg_ppiten_pp0_it4 | 1| 1| 0| |ap_reg_ppiten_pp0_it5 | 1| 1| 0| |ap_reg_ppiten_pp0_it6 | 1| 1| 0| |ap_reg_ppstg_Y_reg_524_pp0_it5 | 8| 8| 0| |ap_reg_ppstg_tmp_11_reg_540_pp0_it5 | 9| 9| 0| |ap_reg_ppstg_tmp_12_reg_551_pp0_it5 | 1| 1| 0| |ap_reg_ppstg_tmp_17_reg_556_pp0_it5 | 9| 9| 0| |ap_reg_ppstg_tmp_4_reg_535_pp0_it5 | 1| 1| 0| |p_neg_t1_reg_566 | 10| 10| 0| |p_neg_t7_reg_561 | 10| 10| 0| |s_B_V_read_reg_450 | 8| 8| 0| |s_R_V_read_reg_456 | 8| 8| 0| |tmp1_reg_509 | 19| 19| 0| |tmp3_cast_reg_514 | 21| 21| 0| |tmp5_cast_reg_519 | 21| 21| 0| |tmp_10_reg_545 | 21| 21| 0| |tmp_11_cast_reg_504 | 20| 20| 0| |tmp_11_reg_540 | 9| 9| 0| |tmp_12_reg_551 | 1| 1| 0| |tmp_14_reg_571 | 1| 1| 0| |tmp_15_reg_576 | 1| 1| 0| |tmp_17_reg_556 | 9| 9| 0| |tmp_2_reg_494 | 17| 17| 0| |tmp_4_reg_535 | 1| 1| 0| |tmp_5_reg_529 | 21| 21| 0| |tmp_7_cast_reg_499 | 20| 20| 0| |tmp_reg_489 | 18| 18| 0| +-------------------------------------+----+-----+-----------+ |Total | 287| 299| 12| +-------------------------------------+----+-----+-----------+ ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_clk | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_rst | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+
データの流れとリソース。
パイプライン化され、1クロックで1個結果が出てくるようになりました。予想周波数は5.47[ns](約182MHz)。
この辺でいい加減一度ぐらいHDLシミュレーションしておかないと…いままでCのシミュレーションが楽で楽で…改めて思う…HDLのテストベンチってめんどくさい…
中身はほぼ同じで、BMPファイルを流し込んで結果を保存します。ただ、色々書くのが面倒なので、BGRをCbYCrに置き換えたBMPファイルだけ作成します。
ちなみにI/Oピンの情報はレポートファイルの一番下に書かれています。細かい制御方法はXilinxの資料にゆずりますが、今回はI/Oになにも指定していないので、関数の返値(YCbCr)に「ap_ctrl_hs」が、引数(RGB)に「ap_none」が設定されています。Y,Cb,Crそれぞれに「_ap_vld」という信号が付いてますが、この場合「ap_done」と同じ信号になるので見ません。
・「ap_rst」を解除する。
・「ap_start」を'1'にすると動作開始。
・クロック立ち上がり&「ap_ready」が'1'にだったら次のRGBデータをBMPファイルから与える。
・クロック立ち上がり&「ap_done」が'1'だったらYCbCrデータをBMPファイルに書き込む。
という制御だけになります。
`timescale 1ns / 1ps `default_nettype none //****************************************** // module bmp_file; integer bfSize; // File Size integer bfOffBits; // Image Offset integer biWidth; // Width integer biHeight; // Height integer biBitCount; // bit/pixel integer imgsize; // Size localparam headersize = 14+40; reg [7:0] tch; reg [7:0] header [0:headersize-1]; integer ifp, ofp, tx; task init; input [8*20:0] ifname; input [8*20:0] ofname; integer i; begin ifp = $fopen(ifname, "rb"); ofp = $fopen(ofname, "wb"); $display("open %s", ifname); $display("%s created", ofname); tx = $fread(header, ifp); //, 0, headersize); $display("header = %d", tx); bfSize = {header[ 5],header[ 4],header[ 3],header[ 2]}; bfOffBits = {header[13],header[12],header[11],header[10]}; biWidth = {header[21],header[20],header[19],header[18]}; biHeight = {header[25],header[24],header[23],header[22]}; biBitCount = {header[29],header[28]}; imgsize = bfSize - bfOffBits; $display(" = %d,%x :(%d,%d,%d)",bfSize,bfOffBits,biWidth,biHeight,biBitCount); for (i = 0; i < headersize; i = i+1) $fwrite(ofp,"%c",header[i]); // copy header info end endtask task done; begin $fclose(ifp); $fclose(ofp); end endtask task sread; output [7:0] B; output [7:0] G; output [7:0] R; begin $fread(B, ifp); $fread(G, ifp); $fread(R, ifp); end endtask task swrite; input [7:0] B; input [7:0] G; input [7:0] R; begin $fwrite(ofp,"%c", B); $fwrite(ofp,"%c", G); $fwrite(ofp,"%c", R); end endtask endmodule // //****************************************** //****************************************** // module tb; localparam ifname = "../H-IIA-F13.bmp"; localparam ofname = "../out_hdl.bmp"; integer bfSize; // File Size integer bfOffBits; // Image Offset integer biWidth; // Width integer biHeight; // Height integer biBitCount; // bit/pixel wire ap_start; wire ap_done; wire ap_idle; wire ap_ready; wire [7:0] agg_result_Y_V; wire agg_result_Y_V_ap_vld; wire [7:0] agg_result_Cb_V; wire agg_result_Cb_V_ap_vld; wire [7:0] agg_result_Cr_V; wire agg_result_Cr_V_ap_vld; wire [7:0] s_R_V; wire [7:0] s_G_V; wire [7:0] s_B_V; reg clock; reg reset; reg start; reg [7:0] R,G,B; wire [7:0] Y,Cb,Cr; rgb2ycbcr tgt ( .ap_clk (clock ), // ap_clk, .ap_rst (reset ), // ap_rst, .ap_start (ap_start ), // ap_start, .ap_done (ap_done ), // ap_done, .ap_idle (ap_idle ), // ap_idle, .ap_ready (ap_ready ), // ap_ready, .agg_result_Y_V (agg_result_Y_V ), // agg_result_Y_V, .agg_result_Y_V_ap_vld (agg_result_Y_V_ap_vld ), // agg_result_Y_V_ap_vld, .agg_result_Cb_V (agg_result_Cb_V ), // agg_result_Cb_V, .agg_result_Cb_V_ap_vld (agg_result_Cb_V_ap_vld ), // agg_result_Cb_V_ap_vld, .agg_result_Cr_V (agg_result_Cr_V ), // agg_result_Cr_V, .agg_result_Cr_V_ap_vld (agg_result_Cr_V_ap_vld ), // agg_result_Cr_V_ap_vld, .s_R_V (s_R_V ), // s_R_V, .s_G_V (s_G_V ), // s_G_V, .s_B_V (s_B_V ) // s_B_V, ); bmp_file bmp(); assign ap_start = start; assign s_R_V = R; assign s_G_V = G; assign s_B_V = B; assign Y = agg_result_Y_V; assign Cb = agg_result_Cb_V; assign Cr = agg_result_Cr_V; integer s,d; initial begin reset <= 1'b1; start <= 1'b0; # 100; @(posedge clock); reset <= 1'b0; @(posedge clock); start <= 1'b1; bmp.init(ifname,ofname); for (s = 0; s < bmp.imgsize; s = s+3) begin wait (ap_ready); bmp.sread(B,G,R); @(posedge clock); end start <= 1'b0; # 500; end initial begin wait (start); for (d = 0; d < bmp.imgsize; d = d+3) begin wait (ap_done); @(posedge clock); bmp.swrite(Cb,Y,Cr); end bmp.done; #300; $stop; end // Clock localparam period = (1000.0/148.5); initial clock <= 1'b0; always #(period/2) clock <= ~clock; endmodule // //****************************************** `default_nettype wire…行間から「HDLめんどくさい」が漏れてる…
シミュレーションの最初と最後だけ出してみます。
カラーコードにしたので、シミュレーションの最後の値が動いていませんが、これで正常です。下の方のsとdがそれぞれのピクセルカウンタです。
HDLシミュレーションで生成されたBMPファイルと、Cの整数シミュレーションで生成されたBMPファイルのバイナリ比較は完全一致でした。Cの浮動小数点モデルで生成したBMPファイルとの比較では所々1bitの差が出ます。
PlanAheadでインプリしてみます。
Number of BUFGs 1 out of 32 3% Number of DSP48E1s 7 out of 220 3% Number of External IOBs 57 out of 200 28% Number of LOCed IOBs 0 out of 57 0% Number of Slices 102 out of 13300 1% Number of Slice Registers 212 out of 106400 1% Number used as Flip Flops 212 Number used as Latches 0 Number used as LatchThrus 0 Number of Slice LUTS 245 out of 53200 1% Number of Slice LUT-Flip Flop pairs 307 out of 53200 1%HLSの予測値にかなり近い値になりました。レポートされた周波数は150MHz制約に対してSlack 1.63[ns]、約198MHzという結果になってます。
Vivado HLS を使ってみた(その5) [FPGA]
試しに精度の計算をコンパイラ任せにしてみます。ap_fixedではなくintで書き直します。
レポートファイル。
中間値のbit幅が自動計算されてます。ap_fixedの時は18bitだった変数が自動で必要な幅になってます。また、リソースも減ってます。丸めやサチュレーション関連の回路でしょうか。
PlanAheadでインプリしたところ。
精度をユーザがちゃんと制御したい、自動計算だと中間精度のbit数がふくれあがってしまう、などの場合はfixedなどで精度を制御する必要がありますが、普通はintでコンパイラ任せの方がなんか良さそうです。第一楽ですし。
#define fw 12 #define frc 4096 /* pow(2, fw) */ #define dat 256 /* pow(2, dw) */ YCbCr rgb2ycbcr(RGB s) { YCbCr d; const int cY [3] = { 0.2126f*frc, 0.7152f*frc, 0.0722f*frc }; const int cCb[3] = {-0.1146f*frc, -0.3854f*frc, 0.5000f*frc }; const int cCr[3] = { 0.5000f*frc, -0.4542f*frc, -0.0458f*frc }; const int op5 = 0.5f * dat * frc; const int llim = 0; const int ulim = dat-1; int R,G,B; int Y,Cb,Cr; R = s.R; G = s.G; B = s.B; Y = (cY [0] * R + cY [1] * G + cY [2] * B ) /frc; Cb = (cCb[0] * R + cCb[1] * G + cCb[2] * B + op5) /frc; Cr = (cCr[0] * R + cCr[1] * G + cCr[2] * B + op5) /frc; if (Y < llim) Y = llim; if (ulim < Y ) Y = ulim; if (Cb < llim) Cb = llim; if (ulim < Cb) Cb = ulim; if (Cr < llim) Cr = llim; if (ulim < Cr) Cr = ulim; d.Y = Y ; d.Cb = Cb; d.Cr = Cr; return d; }より簡単に。
レポートファイル。
================================================================ == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+--------+----------+------------+ | Clock | Target | Estimated| Uncertainty| +---------+--------+----------+------------+ |default | 100.00| 23.29| 12.50| +---------+--------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+---------+ | Latency | Interval | Pipeline| | min | max | min | max | Type | +-----+-----+-----+-----+---------+ | 0| 0| 1| 1| none | +-----+-----+-----+-----+---------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| 7| 0| 235| |FIFO | -| -| -| -| |Instance | -| -| -| -| |Memory | -| -| -| -| |Multiplexer | -| -| -| -| |Register | -| -| -| -| |ShiftMemory | -| -| -| -| +-----------------+---------+-------+--------+-------+ |Total | 0| 7| 0| 235| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 3| 0| ~0 | +-----------------+---------+-------+--------+-------+ + Detail: * Instance: N/A * Memory: N/A * FIFO: N/A * Shift register: N/A * Expression: +-----------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +-----------------------+----------+-------+---+----+------------+------------+ |tmp_11_cast_fu_281_p2 | * | 1| 0| 0| 8| 12| |tmp_1_fu_136_p2 | * | 1| 0| 0| 8| 12| |tmp_2_fu_142_p2 | * | 1| 0| 0| 8| 9| |tmp_6_fu_179_p2 | * | 1| 0| 0| 8| 10| |tmp_7_cast_fu_189_p2 | * | 1| 0| 0| 8| 12| |tmp_7_fu_287_p2 | * | 1| 0| 0| 8| 9| |tmp_fu_126_p2 | * | 1| 0| 0| 8| 10| |tmp1_fu_152_p2 | + | 0| 0| 19| 19| 19| |tmp_10_fu_321_p2 | + | 0| 0| 21| 21| 21| |tmp_5_fu_219_p2 | + | 0| 0| 21| 21| 21| |p_neg1_fu_335_p2 | - | 0| 0| 21| 1| 21| |p_neg5_fu_233_p2 | - | 0| 0| 21| 1| 21| |p_neg_t1_fu_355_p2 | - | 0| 0| 10| 1| 10| |p_neg_t7_fu_253_p2 | - | 0| 0| 10| 1| 10| |Cb_fu_389_p3 | Select | 0| 0| 10| 1| 1| |Cr_fu_419_p3 | Select | 0| 0| 10| 1| 1| |agg_result_Cb_V | Select | 0| 0| 8| 1| 2| |agg_result_Cr_V | Select | 0| 0| 8| 1| 2| |tmp_13_fu_375_p3 | Select | 0| 0| 10| 1| 10| |tmp_s_fu_273_p3 | Select | 0| 0| 10| 1| 10| |icmp1_fu_437_p2 | icmp | 0| 0| 2| 2| 1| |icmp_fu_407_p2 | icmp | 0| 0| 2| 2| 1| |tmp_14_fu_383_p2 | icmp | 0| 0| 26| 21| 13| |tmp_15_fu_413_p2 | icmp | 0| 0| 26| 21| 13| +-----------------------+----------+-------+---+----+------------+------------+ |Total | | 7| 0| 235| 173| 251| +-----------------------+----------+-------+---+----+------------+------------+ * Multiplexer: N/A * Register: N/A ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+こちらの方が若干小さいです。
中間値のbit幅が自動計算されてます。ap_fixedの時は18bitだった変数が自動で必要な幅になってます。また、リソースも減ってます。丸めやサチュレーション関連の回路でしょうか。
PlanAheadでインプリしたところ。
Number of DSP48E1s 5 out of 220 2% Number of External IOBs 55 out of 200 27% Number of LOCed IOBs 0 out of 55 0% Number of Slices 60 out of 13300 1% Number of Slice Registers 1 out of 106400 1% Number used as Flip Flops 1 Number used as Latches 0 Number used as LatchThrus 0 Number of Slice LUTS 171 out of 53200 1% Number of Slice LUT-Flip Flop pairs 171 out of 53200 1%こちらだと乗算器が減ってます。詳しくは見てませんが、シフトなどでまかなえる部分は最適化されたんだと思います。速度は約23[ns]。だいたい同じです。
精度をユーザがちゃんと制御したい、自動計算だと中間精度のbit数がふくれあがってしまう、などの場合はfixedなどで精度を制御する必要がありますが、普通はintでコンパイラ任せの方がなんか良さそうです。第一楽ですし。
Vivado HLS を使ってみた(その4) [FPGA]
浮動小数点のままではさすがにでかいし、精度過剰なので固定小数点化します。
「ap_fixed<>」が固定小数点を扱う型です。bit数とか丸めモードとかサチュレーションモードとか設定できるようになってます。今は適当。floatの代わりに置き換えています。
まずはシミュレーション。fw(小数点以下の精度)が8bitぐらいだと明らかにおかしな画になりますが、12bitぐらいだと見た目は大丈夫そうです。出力されたBMPファイルをバイナリ比較すると1bitぐらい違います。
C Synthesisをかけてみます。
レポートファイル。
データフローとリソース。
なんとまあ、組み合わせ回路になってしまいました。そりゃそうか。できたHDLを見てみると、ap_startがap_done,ready、各_ap_vldに直結されてます。
実際このままPlanAheadに放り込んでZynqでインプリしたところ、
#define fw 12 YCbCr rgb2ycbcr(RGB s) { YCbCr d; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> cY [3] = { 0.2126f, 0.7152f, 0.0722f }; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> cCb[3] = {-0.1146f, -0.3854f, 0.5000f }; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> cCr[3] = { 0.5000f, -0.4542f, -0.0458f }; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> op5 = 0.5f * dat; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> llim = 0.0f; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> ulim = (float)(dat-1); ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> R,G,B; ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> Y,Cb,Cr; R = s.R; G = s.G; B = s.B; Y = cY [0] * R + cY [1] * G + cY [2] * B; Cb = cCb[0] * R + cCb[1] * G + cCb[2] * B + op5; Cr = cCr[0] * R + cCr[1] * G + cCr[2] * B + op5; if (Y < llim) Y = llim; if (ulim < Y ) Y = ulim; if (Cb < llim) Cb = llim; if (ulim < Cb) Cb = ulim; if (Cr < llim) Cr = llim; if (ulim < Cr) Cr = ulim; d.Y = Y; d.Cb = Cb; d.Cr = Cr; return d; }
「ap_fixed<>」が固定小数点を扱う型です。bit数とか丸めモードとかサチュレーションモードとか設定できるようになってます。今は適当。floatの代わりに置き換えています。
まずはシミュレーション。fw(小数点以下の精度)が8bitぐらいだと明らかにおかしな画になりますが、12bitぐらいだと見た目は大丈夫そうです。出力されたBMPファイルをバイナリ比較すると1bitぐらい違います。
C Synthesisをかけてみます。
レポートファイル。
=============================================================== == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+--------+----------+------------+ | Clock | Target | Estimated| Uncertainty| +---------+--------+----------+------------+ |default | 100.00| 24.80| 12.50| +---------+--------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+---------+ | Latency | Interval | Pipeline| | min | max | min | max | Type | +-----+-----+-----+-----+---------+ | 0| 0| 1| 1| none | +-----+-----+-----+-----+---------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| 7| 0| 372| |FIFO | -| -| -| -| |Instance | -| -| -| -| |Memory | -| -| -| -| |Multiplexer | -| -| -| -| |Register | -| -| -| -| |ShiftMemory | -| -| -| -| +-----------------+---------+-------+--------+-------+ |Total | 0| 7| 0| 372| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 3| 0| ~0 | +-----------------+---------+-------+--------+-------+ + Detail: * Instance: N/A * Memory: N/A * FIFO: N/A * Shift register: N/A * Expression: +--------------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +--------------------------+----------+-------+---+----+------------+------------+ |r_V_13_fu_246_p2 | * | 1| 0| 0| 18| 8| |r_V_17_fu_342_p2 | * | 1| 0| 0| 18| 7| |r_V_8_fu_140_p2 | * | 1| 0| 0| 18| 10| |r_V_9_fu_154_p2 | * | 1| 0| 0| 18| 8| |r_V_fu_130_p2 | * | 1| 0| 0| 18| 7| |rhs_V_2_cast_fu_256_p2 | * | 1| 0| 0| 18| 10| |rhs_V_4_cast_fu_360_p2 | * | 1| 0| 0| 18| 10| |p_Val2_11_fu_336_p2 | + | 0| 0| 18| 18| 18| |p_Val2_17_fu_440_p2 | + | 0| 0| 18| 18| 18| |p_Val2_5_fu_232_p2 | + | 0| 0| 18| 18| 18| |r_V_10_fu_164_p2 | + | 0| 0| 28| 28| 28| |r_V_11_fu_174_p2 | + | 0| 0| 28| 28| 28| |r_V_15_fu_272_p2 | + | 0| 0| 28| 28| 28| |Cb_V_fu_466_p3 | Select | 0| 0| 18| 1| 12| |Cr_V_fu_480_p3 | Select | 0| 0| 18| 1| 12| |Y_V_fu_452_p3 | Select | 0| 0| 18| 1| 12| |qb_assign_1_fu_222_p2 | and | 0| 0| 2| 1| 1| |qb_assign_3_fu_326_p2 | and | 0| 0| 2| 1| 1| |qb_assign_5_fu_430_p2 | and | 0| 0| 2| 1| 1| |r_1_fu_306_p2 | icmp | 0| 0| 10| 9| 1| |r_2_fu_410_p2 | icmp | 0| 0| 10| 9| 1| |r_fu_202_p2 | icmp | 0| 0| 2| 1| 1| |tmp_2_fu_460_p2 | icmp | 0| 0| 22| 18| 12| |tmp_4_fu_474_p2 | icmp | 0| 0| 22| 18| 12| |tmp_9_fu_446_p2 | icmp | 0| 0| 22| 18| 12| |brmerge_i62_i1_fu_320_p2 | or | 0| 0| 2| 1| 1| |brmerge_i62_i2_fu_424_p2 | or | 0| 0| 2| 1| 1| |brmerge_i62_i_fu_216_p2 | or | 0| 0| 2| 1| 1| |r_V_16_fu_278_p2 | xor | 0| 0| 40| 28| 29| |r_V_21_fu_382_p2 | xor | 0| 0| 40| 28| 29| +--------------------------+----------+-------+---+----+------------+------------+ |Total | | 7| 0| 372| 402| 337| +--------------------------+----------+-------+---+----+------------+------------+ * Multiplexer: N/A * Register: N/A ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+
データフローとリソース。
なんとまあ、組み合わせ回路になってしまいました。そりゃそうか。できたHDLを見てみると、ap_startがap_done,ready、各_ap_vldに直結されてます。
実際このままPlanAheadに放り込んでZynqでインプリしたところ、
Number of DSP48E1s 9 out of 220 4% Number of External IOBs 55 out of 200 27% Number of LOCed IOBs 0 out of 55 0% Number of Slices 18 out of 13300 1% Number of Slice Registers 0 out of 106400 0% Number used as Flip Flops 0 Number used as Latches 0 Number used as LatchThrus 0 Number of Slice LUTS 60 out of 53200 1% Number of Slice LUT-Flip Flop pairs 60 out of 53200 1%DSPリソースがHLSの計算より多い。多分0.5倍が最適化されずに乗算器が実装されたんでしょう。速度は約21[ns]と若干速いですがほぼ予想値通りです。