Slide 1

Slide 1 text

SystemVerilog を使用した Xilinx FPGA開発 〜Zynq-7000〜 @tethys_seesaa

Slide 2

Slide 2 text

Table of Contents • Vivadoの設計言語としてのSystemVerilog対応 • 合成可能なSystemVerilog記述 • Zynq-7000をターゲットとしたHW/SW開発について

Slide 3

Slide 3 text

VivadoのSystemVerilog対応話

Slide 4

Slide 4 text

VivadoのSystemVerilog対応の利点(1/5) • typedef対応 • VHDLのsubtypeのようなもの。 • バグの入りにくいスッキリとしたコード記述ができる。

Slide 5

Slide 5 text

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を入れる

Slide 6

Slide 6 text

VivadoのSystemVerilog対応の利点(2/5) • typedefとpacked配列の合わせ技 • packed構造体 • VHDLのrecordのようなもの • コード行を比較的、減らせる

Slide 7

Slide 7 text

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 ...

Slide 8

Slide 8 text

VivadoのSystemVerilog対応の利点(3/5) • typedefとenumの合わせ技 • VHDLのtypeみたいなもの • 例えばステートマシンでいちいち`defineで文字列にエン コード値を割り振らなくても良い • シミュレータからの波形も文字列で表示される • ただし、これを用いて設計する場合、Vivadoだと論理合成のデ フォルト(auto)はエンコード値がワンホットに割り当てられるこ とが多いので注意。

Slide 9

Slide 9 text

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; ...

Slide 10

Slide 10 text

VivadoのSystemVerilog対応の利点(4/5) • package, import • packageにて、これまでのtypedef等を共通の定義としてまとめること ができる。 • まとめたpackageは、importで各moduleで使用できる • VHDLのpackageのようなもの • Vivadoでは論理合成時にちょっとクセがある • 後述します • Vivadoだと、importのimportはどうも対応していないようだ

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

VivadoのSystemVerilog対応の利点(5/5) • interface • 信号をまとめることができ、Master/Slaveでそれぞれメソッドを記述 することが可能 • modportでMaster/Slaveの指定ができる。 • ただし、今回のプロジェクトには未採用 • Block Designを使用すると、採用する機会が無かった

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

VivadoのSystemVerilog対応まとめ • VHDLなみの型を意識しつつ、シンプルに記述できる • バグが発生しにくいコードが書ける • typedefを駆使して、packageを使用する • ただし、あくまでVerilog HDL • バス幅表記とか • interfaceにも対応している • ここは個人的に意外だった

Slide 16

Slide 16 text

ここからはVivadoを使用した 開発の話

Slide 17

Slide 17 text

このZynq-7000システムを支えるボード • Zynq-7000 All Programmable SoC ZC706 評価キット • 一家に一台は欲しいZC706ボード

Slide 18

Slide 18 text

本当に起こっていた事実

Slide 19

Slide 19 text

開発フロー • Block Design • SystemVerilo g/Verilog • その他IP HW開発、 Vivado(RH EL6.x) Petalinux SW開発、 OSビルド (Ubuntu 14.04) ILA等 FPGA動作 確認 (Windows 7)

Slide 20

Slide 20 text

ハードウェア開発

Slide 21

Slide 21 text

採用した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) • 無償

Slide 22

Slide 22 text

CDC対策(1)~あるある対応~ • 今回のプロジェクトでは、ほとんどのCDC部分の通信は、ある 時間で局所的にまとまって発生する。 • 常にやり取りするわけではない。 • Dual Port SRAM(Block RAM)で対応 • 一定量たまったら、送信先クロックドメインに信号送る • 1bit 2段 F/Fで受けて、応答を返す(ハンドシェイク) • Dual Port SRAMは、Vivadoの合成ツールにてRAM推論させる ようにしました。 • Block Memory Generator等を使用しない

Slide 23

Slide 23 text

// 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];

Slide 24

Slide 24 text

CDC対策(2)~ちょっと面倒な対応~ • Block RAMほど、ワード数の多いモジュールは必要ない • データが受信されたら、すぐにデータを取り出したい • もちろん、空っぽの時はデータを取り出さないように制御する • SystemVerilogにて、FIFOのF/F記述 • read/write ポインタはグレイコードでクロックドメイン渡し • グレイコードの性質を利用して、満タンおよび空っぽ状態をシンプル に表現 • 150行程度のコード • SystemVerilogの .* 接続を活用

Slide 25

Slide 25 text

FIFO構造 • 「FPGAの部屋」より拝借 • 同期FIFOと非同期FIFO • http://marsee101.blog19.fc2.com/blog -entry-1085.html ココだけ、次のスライドでコード説明

Slide 26

Slide 26 text

// 空っぽ 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;

Slide 27

Slide 27 text

Block Designの作成(比較的簡単) • 時間をかければ、なんとなく慣れる • 初心者に優しい

Slide 28

Slide 28 text

されどBlock Design • ブロックが多くなると… • 配線がミスしていないか、チェックするのが大変難しい • Validate Designが問題なくとも、バグが無いことを保証するわけではない • 目視チェックしかない! • RTLシミュレーション…あきらめました • IPごとにVerilogだったりVHDLだったり • Vivadoから出力されるシミュレータスクリプトが前時代的 • Incisiveだとirunコマンドで出して欲しい • RTLシミュレーションは、Zynq VIPで今後何とかなるかもしれない • EDAベンダのシミュレータじゃないと面倒(工数のかかる)ところがいろいろ ありそう

Slide 29

Slide 29 text

けっこう面倒なBlock Design • Block Designに入れるブロックが多くなると問題が発 生しやすくなった • メンテナンスで開発進行に影響が出るケースがあった • 開発には別のプロジェクトを作成して実行 • 専用プロジェクトで生成したBlock Design(bdファイル)をイ ンポートして、論理合成・配置配線を行う • IPも専用のディレクトリで管理 • ディレクトリ構成が深く、かつ複雑怪奇

Slide 30

Slide 30 text

IPのアサインおよび設定~AXI DMA~ • シンプルな設定 • アーキ検討でここまで絞り込んだ • Block Designで自動配線 • 何故か割り込みをZynqにつないでくれない • 理由はなんとなく想像がつく • 後工程のDevice Tree作成でようやく気づく • Validate Designで指摘してくれせんかね •

Slide 31

Slide 31 text

IPのアサインおよび設定~MyIP~ • AXI DMAからAXI Streamバス経由 で来るデータとUser Designとの受 け渡し • VivadoのAXI Peripheral生成ツール にて作成 • 中のVerilogを編集してUser Design I/Fを作る • AXI Stream側は手動結線 • さらにバスを開き、tstrbとtlastを手動結 線する必要がある • tkeep信号の意味?

Slide 32

Slide 32 text

IPのアサインおよび設定 ~MyIP レジスタ~ • PLのレジスタ設定をAXI Lite I/Fを持つモジュールに集約 • Block Designにおく • VivadoのAXI Peripheral生成ツールにて作成 • 中のVerilogを編集する

Slide 33

Slide 33 text

IPのアサインおよび設定 ~10GbE PCS/PMA~ • Licenseは「Purchase(有償)」だが、ZC706ボードでは無償 • …のハズ?

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

IPのアサインおよび設定 ~GbE PCS/PMA~ • Licenseは「Included(無償)」

Slide 36

Slide 36 text

IPのアサインおよび設定 ~GbE PCS/PMA~ • Include Shared Logic in Core • Shared Logicも含める • ZC706ボードのSFPポート1個し か使用しないため

Slide 37

Slide 37 text

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のバージョンに気をつけましょう

Slide 38

Slide 38 text

VivadoのフローとSystemVerilog(1/2) • IP設定が固まったら、基本はtclモード(CUI)でVivadoを実行 • 合成 • 配置配線 • bitファイル生成 • SDK Export • tclモードには2つのモードがある • プロジェクトモード • 非プロジェクトモード

Slide 39

Slide 39 text

VivadoのフローとSystemVerilog(2/2) • Cadence Incisive等のシミュレータは、typedef等、共通ファイ ルは共通のコンパイルスコープで実施される。 • よって、package, import等は明確に記述しなくても良い • Vivado非プロジェクトモードも同様 • Block Designはプロジェクトモードで作られる • packageで指定しないと、typedefで指定した型がVivadoで見つけてく れない • 合成以前に、エラボレーションでエラーとなる • やむなく、package, importを明示して使用した

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

ソフトウェア開発

Slide 42

Slide 42 text

OS/ソフトウェア • OSはPetalinux 2016.2 • ブートおよびソフトウェア実行はUARTコンソールより実行 • C++/Cアプリ用テンプレートを生成し、作成

Slide 43

Slide 43 text

メモリ/レジスタダンプ用アプリ #include #include #include #include #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

Slide 44

Slide 44 text

メモリ/レジスタライトアプリ #include #include #include #include #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 ; }

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

スクリプト一発で完了させるつもりが… • Device Tree生成で、dtsiファイルに同じモジュールのエントリ が2つ生成される • たぶん、Vivado 2016.2のバグ • 仕方ないのでビルド前の段階で毎回手修正

Slide 47

Slide 47 text

まとめ • Zynq-7000をスクラッチで開発できた

Slide 48

Slide 48 text

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