Google ColaboratoryでいつでもどこでもFPGA開発(シミュレーション編)
前回Google Colaboratory上にVivadoをインストールして実行できることを確認した。 今回はColab上でシミュレーションを行い、WaveDromを用いてVCDファイルを表示するところまで行った。
Vivadoでシミュレーションを実行
Vivadoはすでにインストールされているものとして、シミュレーション用のTcl/Verilog HDLファイルを作成します。今回は面倒くさいので、writefile
マジックを使って必要なファイルを作成します。
%%writefile sim.tcl
create_project proj_sim proj_sim -part xc7z020clg400-1 -force
add_files -norecurse ./adder.v
update_compile_order -fileset sources_1
set_property SOURCE_SET sources_1 [get_filesets sim_1]
add_files -fileset sim_1 -norecurse ./test.v
update_compile_order -fileset sim_1
launch_simulation
%%writefile test.v `timescale 1ns/1ns `default_nettype none module test; reg clk = 1'b0; reg rst = 1'b0; reg [3:0] in0, in1; wire [3:0] out; initial begin $dumpvars; $dumpfile("dump.vcd"); end always #5 clk = ~clk; initial begin repeat(5) @(posedge clk); rst <= 1'b1; in0 <= 4'h0; in1 <= 4'h0; repeat(5) @(posedge clk); rst <= 1'b0; repeat(5) @(posedge clk); in0 <= 4'h1; in1 <= 4'h2; repeat(5) @(posedge clk); $finish; end adder u_adder(.in0(in0), .in1(in1), .out(out)); endmodule `default_nettype wire
%%writefile adder.v `default_nettype none module adder( input wire [3:0] in0, input wire [3:0] in1, output wire [3:0] out ); assign out = in0 + in1; endmodule `default_nettype wire
あとは、Vivadoを用いてシミュレーションするだけ波形ファイルが得られます。とりあえず、作業しやすいようにカレントディレクトリにVCDファイルを移動します。
!source /opt/Xilinx/Vivado/2018.2/settings64.sh && vivado -mode batch -source sim.tcl !cp /content/proj_sim/proj_sim.sim/sim_1/behav/xsim/dump.vcd sample.vcd
VCDファイルの表示
次に、VCDファイルをWaveDromのフォーマットにするために、pythonでなんちゃってVCDの読込みと解析を行ってフォーマット変換するものを作りました。VCDReader
クラスはいくつか自分が波形を表示する上で、必要そうな関数を適当に実装しています(使いやすいかは微妙ですが。あと、あまり効率的にファイルをリードしないので、大きなファイルとか長い時間を表示すると多分死ぬ。バグもたくさんあるはず。
このVCDReader
を用いて、Colabで先ほどのVCDファイルを読み込みます。
import nbwavedrom from VCDReader import * # ファイルの読込み vcd = VCDReader('sample.vcd') # この時点でヘッダの解析は行う # モジュールの構成を表示 print(vcd.tree()) # [m] test # └── [m] u_adder # オブジェクト(モジュール)の取得 u_adder = vcd.get_objects('test.u_adder')[0] print('name: {}, type: {}'.format(u_adder.name, u_adder.type)) # name: u_adder, type: module # u_adderの信号名の取得 print([s.name for s in vcd.get_signals(u_adder)]) # ['in0[3:0]', 'in1[3:0]', 'out[3:0]'] # 信号(in0[3:0])の値を取得 print(vcd.get_values(vcd.get_signals(u_adder)[0])[0]) # [(0, 'bx'), (45000, 'b0'), (145000, 'b1')] # クロックのフォーマットを変更 clk = vcd.get_signals('test.clk')[0] clk.format = 'clk' # クロック周期の取得 cyc = vcd.get_cycle(clk) print('{} * {}'.format(cyc, vcd.header['timescale'])) # 5000 * 1ps (周期と言っているが、半周期分です) # 波形の描画 (Noneはすべてのモジュールと信号を対象) nbwavedrom.draw(vcd.to_wavedrom(None, clk))
サンプルコード等
作るだけで力尽きたので、GithubのVCDReaderのREADMEは後日書きます。 Gistにサンプル置きましたが、波形は表示されないのね