aster_ismの工作室

FPGAとかマイコンとか

Google ColaboratoryでいつでもどこでもFPGA開発(シミュレーション編)

前回Google Colaboratory上にVivadoをインストールして実行できることを確認した。 今回はColab上でシミュレーションを行い、WaveDromを用いてVCDファイルを表示するところまで行った。

f:id:aster_ism:20190303102752p:plain

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

f:id:aster_ism:20190303102752p:plain

サンプルコード等

作るだけで力尽きたので、GithubのVCDReaderのREADMEは後日書きます。 Gistにサンプル置きましたが、波形は表示されないのね

github.com

colab.research.google.com