Verilogのテストベンチ(bdfファイルとかRAMとかの話)
大学の学生実験でFPGAボード上で簡単なCPUを作っている(easyではない).
使っているツールはALTERA社のQuartusというツール.これでANDとかORとかDフリップフロップとかをぽちぽちして回路を作る.
ぽちぽちの回路はBlock Diagramというらしい〜
Quartusにはmodelsimというシミュレータもあるけど、とりあえず今回はicarus-verilog http://iverilog.icarus.com/を用いてテストをしてみました.
QuartusはWindowsとLinuxにはあるのにMacにはなくて悲しい.
Verilog HDLでつくったモジュールとBlock Diagramでつくったモジュールが混在した回路でも問題なくテストできました.
適当に回路を作る
3進カウンタをつくりました.ぽちぽち.
次にこれをVerilog HDLに書き出します.
メニューから File -> Create/Update -> Create HDL Design from Current File ... を選択してVerilog HDLを選んで,保存.
複数のモジュールを使用している際はすべてのモジュールをそれぞれ書き出します.
もちろんもともとVerilog HDLでモジュールをつくっている場合はそのままでOK.
書き出されたファイルは以下のようなものです.
// Copyright (C) 1991-2015 Altera Corporation. All rights reserved. // Your use of Altera Corporation's design tools, logic functions // and other software and tools, and its AMPP partner logic // functions, and any output files from any of the foregoing // (including device programming or simulation files), and any // associated documentation or information are expressly subject // to the terms and conditions of the Altera Program License // Subscription Agreement, the Altera Quartus Prime License Agreement, // the Altera MegaCore Function License Agreement, or other // applicable license agreement, including, without limitation, // that your use is for the sole purpose of programming logic // devices manufactured by Altera and sold by Altera or its // authorized distributors. Please refer to the applicable // agreement for further details. // PROGRAM "Quartus Prime" // VERSION "Version 15.1.1 Build 189 12/02/2015 SJ Lite Edition" // CREATED "Mon Jul 04 04:11:37 2016" module counter( clear, clock, Q1, Q2 ); input wire clear; input wire clock; output wire Q1; output wire Q2; wire SYNTHESIZED_WIRE_0; wire SYNTHESIZED_WIRE_3; reg SYNTHESIZED_WIRE_4; reg DFF_inst1; assign Q1 = SYNTHESIZED_WIRE_4; assign Q2 = DFF_inst1; assign SYNTHESIZED_WIRE_3 = 1; always@(posedge clock or negedge clear or negedge SYNTHESIZED_WIRE_3) begin if (!clear) begin SYNTHESIZED_WIRE_4 <= 0; end else if (!SYNTHESIZED_WIRE_3) begin SYNTHESIZED_WIRE_4 <= 1; end else begin SYNTHESIZED_WIRE_4 <= SYNTHESIZED_WIRE_0; end end always@(posedge clock or negedge clear or negedge SYNTHESIZED_WIRE_3) begin if (!clear) begin DFF_inst1 <= 0; end else if (!SYNTHESIZED_WIRE_3) begin DFF_inst1 <= 1; end else begin DFF_inst1 <= SYNTHESIZED_WIRE_4; end end assign SYNTHESIZED_WIRE_0 = ~(SYNTHESIZED_WIRE_4 | DFF_inst1); endmodule
テストを書く.
テストについては「Verilog testbench」とかでしらべたら山のようにでてくるのでググる.
とりあえず上の回路に対応するベンチを書いてみました.
//counter_test.v module counter_test; //テストベンチの入力はreg //テストベンチの出力はwire //複数行にかいてもいい.バスのときはreg[15:0] Q;とか. reg clear, clock; wire q2,q1; parameter STEP = 100; //常に実行される always begin clear = 1'b1; #(STEP/2) clock = !clock; end //最初に一度だけ実行される initial begin clear = 1'b0; clock = 1'b1; //monitorは引き数の値が1つでも変わった時に値を表示するように設定する.(alwaysにはかかないものらしい) $monitor("Counter: %b%b", q2, q1); #1000 $finish; //1000STEPで停止 end //インスタンスの作成(回路の呼び出し)counter_instance はなんでもいい //counter の部分は呼び出したいモジュールのVerilogファイルの1行目にあるモジュール名を入れる. //引数はVerilogファイルの冒頭にあるinputの/outputにある順番にかく. counter counterinstance(clear,clock,q1,q2); endmodule
コンパイル
使用したモジュールすべてのVerilogファイルと、テストベンチのファイルをすべてまとめてコンパイルします.
iverilog -o counter counter.v counter_test.v
たくさんある場合は
iverilog -o simple2 simple2.v ./lib/phase/*.v ./lib/selector/*.v ./lib/other/*.v ./lib/component/*.v ./lib/calc/*.v ./lib/cable/*.v ./lib/7SEG/*.v dummy_ram.v simple2_test.v
こんなかんじ.
- oの後に指定したファイル名でコンパイルされたファイルが出力されます.
vvp counter
または
./counter
と入力すればシミュレーションがはじまります.
Counter: 00 Counter: 01 Counter: 10 Counter: 00 Counter: 01 Counter: 10 Counter: 00 Counter: 01 Counter: 10 Counter: 00 Counter: 01
無事にシミュレーションできました.
ハマった点としては,ぽちぽちでカウンタを作る際に、DFFのpresetを1にしておけば,DFFの出力の初期値は0だと思っていたのですが、出力されたVerilogのコードを読んだところ,初期値については何もかかれておらず,テストするとxx(値不定)となってしまいました.
そのため上のテストベンチではinitialブロック内でclear = 0として一度クリアしています.
Verilog、入門のページみながら適当にかいたりしているけど難しい・・・・・・・・・・・・・・
RAMについて
CPUをつくる実験ではRAMを使ったりします.
RAMはQuartus上ではVerilogに変換できないので,自分でダミーのVerilogファイルを書きます.
module ram( input [15:0] data, input wren, input wire [11:0] address, input clock, output reg [15:0] q ); //16bit幅 4096word (* ram_style = "BLOCK" *) reg [15:0] bram[0:4095]; initial begin bram[12'd0] = 16'b1000000000000001; bram[12'd1] = 16'b1000000100000011; bram[12'd2] = 16'b1100100000000000; bram[12'd3] = 16'b1100000011010000; bram[12'd4] = 16'b1000000100000101; bram[12'd5] = 16'b1100100000000000; bram[12'd6] = 16'b1100000011010000; end always @(posedge clock) begin if(wren) bram[address] <= data; else q <= bram[address]; end endmodule
こんなかんじでダミーのRAMをつくればOK.(説明が雑)
wrenが1のときにclockがはいったらdataの内容をbram[address]に書き込んで,wrenが0のときはbram[address]の内容をqに入れて出力というのをかいただけ.
とりあえずこれでデバッグできるようになったので嬉しいです〜〜〜
ぽちぽちのときは値不定なんてほとんどないと思うんだけどVerilogでかくとif文の条件分岐とかが足りてないと値不定になってしまってなるほどなあというかんじ.
ここ間違ってるとか表記おかしいとかあったら教えてください.