別件記事で古の8080用の2相ノンオーバラップクロックもどきを制作。標準ロジックIC2個、合計7ゲートのロジックを組むのが老人には辛いっす。この際FPGAでやれば自分で配線せずとも出来るじゃん。ということでFPGAにしてみることにしましたが、そのためには回路をHDLで書かないとなりません。久しぶりにVerilog?
※かえらざるMOS回路 投稿順 INDEX
Icarus Verilog
とりあえず手元にあるFPGAは、超お求めやすい価格だったのだけれど使い方が良く分からない(マニュアルが簡体字中国語。。。)ものなんであります。米国製のFPGAであればツールチェーンにVerilogやVHDLのシミュレータやら何やらも含まれていたりするのですが、これはFPGAに書き込むビットファイルは生成できますがシミュレータは含まれません。そこで代表的なフリーのVerilogコンパイラ/シミュレータであるIcarus Verilogを使用することにいたしました。使うの相当久しぶりだな。
Icarus Verilogのドキュメントのページはこちら(数年前にあった「公式ページ」は無くなったみたいデス。でも上記のリンクの方が情報多くなってる気がしないでもない。)
今回作成予定の回路
今回FPGA上に実装しようとしている回路は以下のようなものです。
-
- ボード上に24MHzのオシレータが搭載されているのでそれをクロック源とする
- 24MHzを12分周すれば2MHzになる(オリジナル8080の速度?)
- 2MHzのクロック出力は2相ノンオーバラップとする
- ついでに2MHzを2,4、8、16分周して1MHz、500kHz、250kHz、125kHzの単相クロックも出力できるようにする
別件の2相ノンオーバラップクロック生成回路では、物理的な遅延を使ってノンオーバラップ期間を確保していたのですが、今回は12分周のところで各フェースのON期間を5、OFF期間を7として、確実に「1」の隙間(とりあえず40nsくらい。後で調整はなんとでもなる筈)を作るという目標です。
後はVerilog書くだけなので以下のようです。まず全体回路。このレベルの入出力をFPGAから外に接続の予定。
module counter12 ( input wire CLK_IN, input wire RST_N, output wire [3:0]CLK_OUT, output wire P1, output wire P2 ); freqdiv12 FD12 ( CLK_IN, RST_N, P1, P2 ); freqdiv16 FD16 ( P1, RST_N, CLK_OUT ); endmodule
内部に配置したfreqdiv12が元クロックの12分周回路、freqdiv16が2分周4段で16分周回路です。最初は12分周回路。この中で2フェーズノンオーバラップを作製。
module freqdiv12 ( input wire CLK_IN, input wire RST_N, output wire P1, output wire P2 ); reg [3:0] count; reg phase1; reg phase2; initial begin count=4'b0000; phase1=0; phase2=0; end always @(posedge CLK_IN) begin if (RST_N==1) count <= 4'b0000; else if (count == 4'b1011) count <= 4'b0000; else count <= count + 1; end always @(posedge CLK_IN) begin if (count == 4'b0000) phase1 <= 1; else if (count == 4'b0101) phase1 <= 0; end always @(posedge CLK_IN) begin if (count == 4'b0110) phase2 <= 1; else if (count == 4'b1011) phase2 <= 0; end assign P1 = phase1; assign P2 = phase2; endmodule
つづいて16分周回路。こんなもんで良いか?
module freqdiv16 ( input wire CLK_IN, input wire RST_N, output wire [3:0]CLK_OUT ); reg [3:0] count; initial begin count=4'b0000; end always @(posedge CLK_IN) begin if (RST_N==1) count <= 4'b0000; else if (count == 4'b1111) count <= 4'b0000; else count <= count + 1; end assign CLK_OUT = count; endmodule
ここまでが後で論理合成にかける予定の部分。
テストベンチ
通例手抜きのテストベンチが以下に。ボード上のクロック源は24MHzなんだけれども、計算メンドイという理由で25MHzクロックにしてるぞ。まあ遅延とか全然なシミュレーションなので雰囲気だけね~。いい加減な。
`timescale 1 ns / 100 ps module counter8_tb; reg CLK, RST; wire [3:0] OUT; wire P1; wire P2; parameter STEP = 40; counter12 dut(CLK, RST, OUT, P1, P2); initial begin $dumpfile("counter12.vcd"); $dumpvars(-1, dut); $monitor("%d CLK=%d RST=%d OUT=%d P1=%d P2=%d", $stime, CLK, RST, OUT, P1, P2); end always begin CLK=0; #(STEP/2); CLK=1; #(STEP/2); end initial begin #0 RST = 1'b1; #STEP RST = 1'b0; #(STEP*600) $finish; end endmodule
エラボレーション(ビルド)してシミュレーション
上記で作成のファイル4つをエラボレーション(ビルド。)
$ iverilog -o counter12.out counter12_tb.v counter12.v freqdiv12.v freqdiv16.v
生成されたオブジェクト counter12.outのシミュレーション実行
$ vvp counter12.out >counter12_out.txt
上記でテキスト出力されたシミュレーション結果の頭のところが以下に。
VCD info: dumpfile counter12.vcd opened for output. 0 CLK=0 RST=1 OUT= 0 P1=0 P2=0 20 CLK=1 RST=1 OUT= 0 P1=1 P2=0 40 CLK=0 RST=0 OUT= 0 P1=1 P2=0 60 CLK=1 RST=0 OUT= 0 P1=1 P2=0 80 CLK=0 RST=0 OUT= 0 P1=1 P2=0 100 CLK=1 RST=0 OUT= 0 P1=1 P2=0 120 CLK=0 RST=0 OUT= 0 P1=1 P2=0 140 CLK=1 RST=0 OUT= 0 P1=1 P2=0 160 CLK=0 RST=0 OUT= 0 P1=1 P2=0 180 CLK=1 RST=0 OUT= 0 P1=1 P2=0 200 CLK=0 RST=0 OUT= 0 P1=1 P2=0 220 CLK=1 RST=0 OUT= 0 P1=1 P2=0 240 CLK=0 RST=0 OUT= 0 P1=1 P2=0 260 CLK=1 RST=0 OUT= 0 P1=0 P2=0 280 CLK=0 RST=0 OUT= 0 P1=0 P2=0 300 CLK=1 RST=0 OUT= 0 P1=0 P2=1
また結果を波形ビューワー gtkwave で観察したところが以下に。
2相クロックの間に「隙間」は空いておる、と。