Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SystemVerilog を使用したXilinx FPGA開発

tethys_seesaa
September 23, 2017

SystemVerilog を使用したXilinx FPGA開発

2017年9月24日(日)に開催した
RTLを語る会(14)~FPGAの現実~の発表スライド(公開版)

tethys_seesaa

September 23, 2017
Tweet

More Decks by tethys_seesaa

Other Decks in Technology

Transcript

  1. typedef logic[7:0] Byte_t; // 幅表記が統一できる Byte_t [7:0] ad, dat; logic

    [7:0] be; always_ff @(posedge rst, posedge clk) if(rst) begin ad <= '0; // logic型0のaggregationも兼ねる be <= '0; end … … always_comb dat[6] = '1; // バイト6に0xFFを入れる
  2. typedef logic[7:0] Byte_t; typedef struct packed unsigned { Byte_t [7:0]

    ad; logic [7:0] be; logic frame, irdy; } Pci_t; ... Pci_t pd; ... always_ff @(posedge rst, posedge clk) if(rst) begin pd <= '0; // ほとんどのメンバ信号は0 pd.frame <= '1; // このメンバ信号だけ1を入れる end ...
  3. typedef enum logic [7:0] { RESET = '0, // Vivadoのデフォルトだと無意味かも

    INIT, ... ERROR } St_t; St_t cur, nxt; always_ff @(posedge rst, posedge clk) if(rst) cur <= RESET; else cur <= nxt; ...
  4. VivadoのSystemVerilog対応の利点(4/5) • package, import • packageにて、これまでのtypedef等を共通の定義としてまとめること ができる。 • まとめたpackageは、importで各moduleで使用できる •

    VHDLのpackageのようなもの • Vivadoでは論理合成時にちょっとクセがある • 後述します • Vivadoだと、importのimportはどうも対応していないようだ
  5. package common_pack; typedef logic[7:0] Byte_t; ... // SystemVerilogではfunctionの入力はinput文を省略可 function Byte_t

    get_data(Byte_t data); return data; endfunction ... endpackage ... import common_pack::*; module module_a( ... endmodule
  6. interface bus_t (); logic sel; logic rw; addr_t addr; //typedefで型指定

    data_t rdat; //typedefで型指定 data_t wdat; // Master methods function data_t get_rdata(); return rdat; endfunction task automatic mst_rst(); sel <= ‘0; rw <= ‘0; addr <= ‘0; wdat <= ‘0; endtask // Slave methods function logic get_sel(); return sel; endfunction function data_t get_wdata(); return wdat; endfunction // modportでMaster/Slave決め modport mst( import get_rdata, mst_rst, output sel, rw, addr, wdat, input rdat ); modport slv( import get_sel, get_wdata, input sel, rw, addr, wdat, output rdat ); endinterface
  7. module top(); // interfaceのインスタンス // interfaceの配列はダメっぽい bus_t bus_0(); bus_t bus_1();

    ... // module_0にbus_0をslaveとして接続 module_0 mod_0( .bus_0(bus_0), .*); ... endmodule module module_0( ... bus_t.slv bus_0 ); data_t dat; // Slave methods呼び出し always_ff @(posedge RST, posedge CLK) if(RST) dat <= ‘0; else if(bus_0.get_sel()) dat <= bus_0.get_wdata(); ... endmodule
  8. 開発フロー • Block Design • SystemVerilo g/Verilog • その他IP HW開発、

    Vivado(RH EL6.x) Petalinux SW開発、 OSビルド (Ubuntu 14.04) ILA等 FPGA動作 確認 (Windows 7)
  9. 採用したIP(すべて無償) • Zynqシステム(Block Design) • AXI DMA(Scatter/Gatherではないノーマル) x 2 •

    AXI Peripheralテンプレート • Vivadoのユーティリティ • AXI4 Lite Slave • AXI4 Stream Master/Slave • 10GbE PCS/PMA(10GbE用PHY) • ZC706ボード(Zynq-7000?)だと無償 • GbE PCS/PMA(GbE用PHY) • 無償
  10. CDC対策(1)~あるある対応~ • 今回のプロジェクトでは、ほとんどのCDC部分の通信は、ある 時間で局所的にまとまって発生する。 • 常にやり取りするわけではない。 • Dual Port SRAM(Block

    RAM)で対応 • 一定量たまったら、送信先クロックドメインに信号送る • 1bit 2段 F/Fで受けて、応答を返す(ハンドシェイク) • Dual Port SRAMは、Vivadoの合成ツールにてRAM推論させる ようにしました。 • Block Memory Generator等を使用しない
  11. // VivadoにDual Port RAM(BlockRAM)推論させるVerilog記述 reg [31:0] mem [0:1023] // RAM定義

    always @(posedge WCLK) if(WEN) mem[WADDR] <= WDAT; always @(posedge RCLK) RDAT <= mem[RADDR];
  12. CDC対策(2)~ちょっと面倒な対応~ • Block RAMほど、ワード数の多いモジュールは必要ない • データが受信されたら、すぐにデータを取り出したい • もちろん、空っぽの時はデータを取り出さないように制御する • SystemVerilogにて、FIFOのF/F記述

    • read/write ポインタはグレイコードでクロックドメイン渡し • グレイコードの性質を利用して、満タンおよび空っぽ状態をシンプル に表現 • 150行程度のコード • SystemVerilogの .* 接続を活用
  13. // 空っぽ always_comb begin rbinnext = rbin + (rinc &

    ~rempty); rgraynext = (rbinnext>>1) ^ rbinnext; rempty_val = (rgraynext == rq2_wptr); end always_ff @(posedge RST, posedge RCLK) if (RST) rempty <= ’1; else rempty <= rempty_val; //満タン always_comb begin wbinnext = wbin + (winc & ~wfull); wgraynext = (wbinnext>>1) ^ wbinnext; wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZ E-1], wq2_rptr[ADDRSIZE-2:0]}); end always_ff @(posedge RST, posedge WCLK) if (RST) wfull <= ’0; else wfull <= wfull_val;
  14. されどBlock Design • ブロックが多くなると… • 配線がミスしていないか、チェックするのが大変難しい • Validate Designが問題なくとも、バグが無いことを保証するわけではない •

    目視チェックしかない! • RTLシミュレーション…あきらめました • IPごとにVerilogだったりVHDLだったり • Vivadoから出力されるシミュレータスクリプトが前時代的 • Incisiveだとirunコマンドで出して欲しい • RTLシミュレーションは、Zynq VIPで今後何とかなるかもしれない • EDAベンダのシミュレータじゃないと面倒(工数のかかる)ところがいろいろ ありそう
  15. けっこう面倒なBlock Design • Block Designに入れるブロックが多くなると問題が発 生しやすくなった • メンテナンスで開発進行に影響が出るケースがあった • 開発には別のプロジェクトを作成して実行

    • 専用プロジェクトで生成したBlock Design(bdファイル)をイ ンポートして、論理合成・配置配線を行う • IPも専用のディレクトリで管理 • ディレクトリ構成が深く、かつ複雑怪奇
  16. IPのアサインおよび設定~AXI DMA~ • シンプルな設定 • アーキ検討でここまで絞り込んだ • Block Designで自動配線 •

    何故か割り込みをZynqにつないでくれない • 理由はなんとなく想像がつく • 後工程のDevice Tree作成でようやく気づく • Validate Designで指摘してくれせんかね •
  17. IPのアサインおよび設定~MyIP~ • AXI DMAからAXI Streamバス経由 で来るデータとUser Designとの受 け渡し • VivadoのAXI

    Peripheral生成ツール にて作成 • 中のVerilogを編集してUser Design I/Fを作る • AXI Stream側は手動結線 • さらにバスを開き、tstrbとtlastを手動結 線する必要がある • tkeep信号の意味?
  18. IPのアサインおよび設定 ~10GbE PCS/PMA~ • Include Shared Logic in example design

    • Shared Logicをコアの外に出す (ややこしい) • example designをベースに改造す る • ZC706では無償だが、デフォル ト有償 • ライセンスチェックを行うため、 画面遷移で1分ほど待たされる
  19. IPのアサインおよび設定 ~GbE PCS/PMA~ • Include Shared Logic in Core •

    Shared Logicも含める • ZC706ボードのSFPポート1個し か使用しないため
  20. GbE PCS/PMAにおけるわたしの凡ミス • Vivado 2016.2に含まれている同IP • v15.2 • Vivado 2016.4に含まれている同IP

    • v16.0 • プロジェクト開始時、2016.4のドキュメントを読んでいた • MDIO関係の、1bitのあるレジスタの設定値の定義が反転していた • 2016.2と2016.4で異なる! • 実機デバッグにて発覚するまで1日浪費してしまった • ドキュメントは使うIPのバージョンに気をつけましょう
  21. VivadoのフローとSystemVerilog(1/2) • IP設定が固まったら、基本はtclモード(CUI)でVivadoを実行 • 合成 • 配置配線 • bitファイル生成 •

    SDK Export • tclモードには2つのモードがある • プロジェクトモード • 非プロジェクトモード
  22. VivadoのフローとSystemVerilog(2/2) • Cadence Incisive等のシミュレータは、typedef等、共通ファイ ルは共通のコンパイルスコープで実施される。 • よって、package, import等は明確に記述しなくても良い • Vivado非プロジェクトモードも同様

    • Block Designはプロジェクトモードで作られる • packageで指定しないと、typedefで指定した型がVivadoで見つけてく れない • 合成以前に、エラボレーションでエラーとなる • やむなく、package, importを明示して使用した
  23. Vivado実行スクリプト #!/bin/sh # 以下のようにシェルスクリプトを実行 # ./run.sh clean; ./run.sh work fpga_top

    if [ $# -lt 1 ]; then echo "usage: ./run.sh workdir top_name " exit 1 fi if [ $1 ="clean"]; then TRASH=$(ls -a -I . -I .. -I run.sh -I top.xdc -I pin.xdc) rm -rf ${TRASH} exit 0 fi # set environment source ../env/current.env source /opt/Xilinx/Vivado/2016.2/settings64.sh # Block Design用プロジェクトからのエクスポート vivado -mode tcl -source ../tcl/gen_bd.tcl # ワークディレクトリとトップモジュール設定 export WORKDIR=$1 export TOPMODULE=$2 # Vivado実行 vivado -mode tcl -source ../tcl/syn.tcl
  24. メモリ/レジスタダンプ用アプリ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #define

    MAP_LENG 0x00100000 void main (int argc, char **argv){ int fd , i; unsigned int *addr; unsigned int offset, count; fd = open( "/dev/mem", O_RDWR ); if ( fd == -1 ){ printf ( "Can't open /dev/mem. ¥n" ); return ; } offset = strtol ( argv[1], NULL, 16 ); count = strtol ( argv[2], NULL, 10 ); addr = mmap ( NULL, MAP_LENG, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset & 0xFFFF0000) ; if ( addr == MAP_FAILED ) { printf( "Error: mmap()¥n" ); } printf (" usage: memdump ADRS(hex) LEN(dec) ¥n" ); for( i=0; i<count; i++){ printf ( "%08X: %08X¥n", ( (offset & 0xFFFFFFFC) + (i * 4) ), ( addr[((offset & 0x0000FFFC) /4 ) + i] ) ); } printf ( "¥n" ); return ; }
  25. メモリ/レジスタライトアプリ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #define

    MAP_LENG 0x00100000 void main (int argc, char **argv){ int fd , i; unsigned int *addr; unsigned int offset, data; fd = open( "/dev/mem", O_RDWR ); if ( fd == -1 ){ printf ( "Can't open /dev/mem. ¥n" ); return ; } offset = strtol ( argv[1], NULL, 16 ); data = strtol ( argv[2], NULL, 16 ); addr = mmap ( NULL, MAP_LENG, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset & 0xFFFF0000) ; if ( addr == MAP_FAILED ) { printf( "Error: mmap()¥n" ); } printf (" usage: memwrite ADRS(hex) LEN(dec) ¥n" ); addr[(offset & 0x0000FFFC) / 4] = data; printf ( "%08X: %08X¥n", ( offset & 0xFFFFFFFC ), ( addr[(offset & 0x0000FFFC) /4] ) ); printf ( "¥n" ); return ; }
  26. Petalinuxブート用SDカードイメージ作成 #!/bin/sh PETAPROJ='petaprj' VIVPROJ='fpgaprj' BD_NAME='design_1_wrapper' VIVPROJ_DIR='/home/tethys/work' VIVPROJ_SDK_DIR=${VIVPROJ_DIR}/${VIVPROJ}.sdk source /opt/Xilinx/Vivado/2016.2/settings64.sh source

    /opt/Xilinx/petalinux-v2016.2-final/settings.sh #Create Project petalinux-create --force --type project ¥ --template zynq ¥ --name ${PETAPROJ} #Import HDF cd ${PETAPROJ} petalinux-config --get-hw-description=${VIVPROJ_SDK_DIR} #Create new app #petalinux-create -t apps --template c --name myapp #Choice Apps petalinux-config -c rootfs #Build Petalinux LANG=C petalinux-build #Make bood.bin petalinux-package ¥ --boot ¥ --fsbl ./images/linux/zynq_fsbl.elf ¥ --fpga ./images/linux/${BD_NAME}.bit ¥ --u-boot
  27. Reference 1. 小林優, (2016). FPGAプログラミング大全. ISBN 4798047538. 2. FPGAマガジン, (2013).

    高速Ethernet x FPGA. ISBN 478984613X 3. FPGAマガジン, (2016). ARMコアFPGA x Linux初体験. ISBN 4789846229 4. Clifford E. Cummings, (2001). Simulation and Synthesis Techniques for Asynchronous FIFO Design. Sunburst Design, Inc. http://www.sunburst- design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf 5. zakii, (2011). RAMのRTL記述. http://zakii.la.coocan.jp/hdl/44_ram_rtl.htm 6. ikwzm, (2016). VivadoをGUIを使わずに実行するためのTclスクリ プト達. http://qiita.com/ikwzm/items/a0120079d2f7f86a5904