間があいてしまいましたが。

…これって、「あいだ」があくんでしょうか、「ま」があくんでしょうか。

K.I.T.T.っぽく光ったまま、おうちでやる時間がとれなくて放ったらかしになってましたが。

実はシリアル通信で "Hello, Michael."とかボタンを押すとなんか適当なことをしゃべるようなことをハードウェアのみでやろうかと思ってたんですが、どう考えてもループ制御(あるいはインライン展開的ななにか)になりそうだったので、これはもしやソフトでやるべきコトなのでは、などと反省。

じゃあ他にネタとしてなにをやろうかと思ったときに、ADコンバータなんかもオモシロいかもなぁと思いつつも、結局これもソフトがあったほうが便利。DCカットしたスピーカーをつけて音を鳴らしたらオモシロいかしらと思ったけれどこれもソフトのお仕事。うーん。

なかなかハードウェアだけでなにかやる(完結させる)というのはしんどいものですね。

それはさておき。ちょっとオモシロいページを見つけたのでご紹介。村田製作所のサイトから、EMICON-FUN!
静電容量の電圧特性」。

たとえば1uF/10Vのコンデンサがあったとして、2Vの電圧をかけたとき、どれだけ電荷がたまってるんだっけ、なんて思ったのがきっかけで。

一般にはW=1/2CV2ということになってますが、それはあくまで理想コンデンサの場合。実際には静電容量自体が電圧特性を持っているので、1uFだと思っていても実行容量はその6割程度しかなかったということが容易に起こるのです。もちろんこれは容量の許容誤差の話ではありません。

電圧による静電容量の変化をDCバイアス特性と呼んでいますが、Y5VとかX5Rとか、あるいはB特性、F特性、CH特性なんて風に温度特性でクラス分けされていますが、これがほぼそのままDCバイアス特性に関連していると思ってよさそうです。

回路設計では何気なく0.1uF/10V程度のパスコンをICの電源端子の直近と、あとは1uF程度のを適当にばらまいておけばいいや、なんてことをやってる人が多いと思いますが、これだと電源特性もEMI特性もコントロールできません。

いい基板を作るためにはやはりそれなりにセオリーがあるのです。が、実際にはデジアナ混在やら基板材料やら銅箔厚さやらサイズ、形、基板上を流れる信号の周波数などなど数多くの要素が絡み合ってしまうため、万能薬がない(ように見える)のが現実。

特に最近は高周波信号を使うことが多くなってきていますから、EMIを減らすための設計というのはますます重要になってきており、そうなると受動部品一つ一つの特性の深い理解が必要になってくるんですよね。

アナログ回路から入った人はそこらへんがわかっているんだけれど、デジタル回路から入った人は「デジタルというのは信号の扱いを分類しただけのことで、基板上を流れる信号はアナログ」だということをわかっていない人もいます。

また、回路というのは電流が回ってくる路(みち)だということを常に意識していないと、いい加減な基板を作ってしまうことにもなります。そうなると後々苦労するんですよね。

…いや、MICO32のハードルが高いからごまかしているとかそういうわけでは、決して…決して…。

ナイトライダーを作ってみる。

さてそれではナイトライダー…っぽいLED Flasherです。
どうやって作ろうか方針を練ります。

光が移動していく速度は、とりあえずLEDが8個なので片道1秒とすれば125msecになります。まずはその線でいってみましょう。
LEDの輝度は、1000と30と1くらいでいいかしら。8個中3個が同時点灯という形で。

制御方法ですが、16ステップのシーケンサを作ってしまえばいいかな。4bitのフラグレジスタを用意して、125msecごとにフラグ値の処理を行い、フラグを+1する、という感じ。
125msecごとに起動するのは50MHzのクロックだと50000000/8=6250000なので、24bitのカウンタを用意して5F5E10hと比較すればいいかな。フラグが立ったらカウンタをリセット。

パターンはオール0から始まって、

0000 0001
0000 0012
0000 0123
0000 1230

0001 2300
0012 3000
0123 0000
1230 0000

2300 0000
3000 0000
1000 0000
2100 0000

3210 0000
0321 0000
0032 1000
0003 2100

0000 3210
0000 0321
0000 0032
0000 0003

ん、20パターンですねぇ。ちょっと美しくないけれどこれでやってみましょうか。

最初の100行ほどが変わるだけなので、その部分だけ貼り付けます。


// vim: set fenc=utf-8 filetype=verilog expandtab ts=4 sts=4 sw=4:
//
// generate 1000Hz clock from 50MHz osc input.
//
module LED_TOP(clk, reset,
    LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8);
    input clk;
    input reset;
    output LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8;
    wire c1kout;

    reg [7:0] count1;
    reg [7:0] count2;
    reg [7:0] count3;
    reg [7:0] count4;
    reg [7:0] count5;
    reg [7:0] count6;
    reg [7:0] count7;
    reg [7:0] count8;

    reg [9:0] duty1 = 1000;
    reg [9:0] duty2 = 0;
    reg [9:0] duty3 = 0;
    reg [9:0] duty4 = 0;
    reg [9:0] duty5 = 0;
    reg [9:0] duty6 = 0;
    reg [9:0] duty7 = 0;
    reg [9:0] duty8 = 0;

    reg  _out1;
    reg  _out2;
    reg  _out3;
    reg  _out4;
    reg  _out5;
    reg  _out6;
    reg  _out7;
    reg  _out8;
    
assign LED1 = _out1;
assign LED2 = _out2;
assign LED3 = _out3;
assign LED4 = _out4;
assign LED5 = _out5;
assign LED6 = _out6;
assign LED7 = _out7;
assign LED8 = _out8;

 reg [23:0] seq; // sequence counter
 reg [4:0] step; // sequence step flag

CLK10000HZ c1k(
    .clk(clk),
    .reset(reset),
    .clk1m(c1kout));

// sequencer
always @ (posedge clk or negedge reset) begin
    if (reset == 0)
    begin
        seq <= 0;
        step <= 0;
    end else
    if (seq < 24'h5F5E10 - 1)
    begin
        seq <= seq + 1;
    end
    else
    begin
        seq <= 0;
        case (step)
            5'b00000 : begin duty1 <=  1000; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b00001 : begin duty1 <=  30; duty2 <=  1000; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b00010 : begin duty1 <=  1; duty2 <=  30; duty3 <=  1000; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b00011 : begin duty1 <=  0; duty2 <=  1; duty3 <=  30; duty4 <=  1000; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b00100 : begin duty1 <=  0; duty2 <=  0; duty3 <=  1; duty4 <=  30; duty5 <=  1000; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b00101 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  1; duty5 <=  30; duty6 <=  1000; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b00110 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  1; duty6 <=  30; duty7 <=  1000; duty8 <=  0; step <=  step + 1; end
            5'b00111 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  1; duty7 <=  30; duty8 <=  1000; step <=  step + 1; end
            5'b01000 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  1; duty8 <=  30; step <=  step + 1; end
            5'b01001 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  1; step <=  step + 1; end
            5'b01010 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  1000; step <=  step + 1; end
            5'b01011 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  1000; duty8 <=  30; step <=  step + 1; end
            5'b01100 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  1000; duty7 <=  30; duty8 <=  1; step <=  step + 1; end
            5'b01101 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  1000; duty6 <=  30; duty7 <=  1; duty8 <=  0; step <=  step + 1; end
            5'b01110 : begin duty1 <=  0; duty2 <=  0; duty3 <=  0; duty4 <=  1000; duty5 <=  30; duty6 <=  1; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b01111 : begin duty1 <=  0; duty2 <=  0; duty3 <=  1000; duty4 <=  30; duty5 <=  1; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b10000 : begin duty1 <=  0; duty2 <=  1000; duty3 <=  30; duty4 <=  1; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b10001 : begin duty1 <=  1000; duty2 <=  30; duty3 <=  1; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b10010 : begin duty1 <=  30; duty2 <=  1; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  step + 1; end
            5'b10011 : begin duty1 <=  1; duty2 <=  0; duty3 <=  0; duty4 <=  0; duty5 <=  0; duty6 <=  0; duty7 <=  0; duty8 <=  0; step <=  0; end
        endcase
    end
end

case文のところはわかりやすく記述したかったので、貼り付けたらかえって見辛くなってしまいましたが、1000のところだけを追いかければ「行って戻って」を繰り返すような記述になっているのがわかると思います。

撮影してみました。


両端での折り返しはこんな感じでいいんですかね。あと、肉眼だとわかるんですが、ビデオだと露出の関係かLEDの輝度の違いがわかりづらいですね。しかも撮影したビデオはフォーカスが合ってるのに、これはなんだかぼやけてしまっています。再エンコードされたからかしら。

明るさを変えてみる-その2。

シミュレーションではちゃんと波形が出ていたので、変だなぁ変だなぁと思いながらピンアサインを眺めたりしてたんですが。

なぜかいきなりパッとひらめきました。

出力端子の論理が逆じゃん!

指定した時間分だけ出力端子をHにするように書いていたんですが、LEDはLでドライブされるんですね。Brevia2の回路上もそうなってます。
ということで、コードを修正して試してみると、ちゃんと明るさが変わりました。が、変化の度合いが少ないため、クロックをさらに細かくしてデューティを0.01msec単位で変更できるようにしました。

twincle_led.v:

// vim: set fenc=utf-8 filetype=verilog expandtab ts=4 sts=0 sw=4:
//
// generate 1000Hz clock from 50MHz osc input.
//
module LED_TOP(clk, reset,
    LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8);
    input clk;
    input reset;
    output LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8;
    wire c1kout;

    reg [15:0] count1;
    reg [15:0] count2;
    reg [15:0] count3;
    reg [15:0] count4;
    reg [15:0] count5;
    reg [15:0] count6;
    reg [15:0] count7;
    reg [15:0] count8;

    reg [15:0] duty1 = 1;
    reg [15:0] duty2 = 5;
    reg [15:0] duty3 = 10;
    reg [15:0] duty4 = 30;
    reg [15:0] duty5 = 70;
    reg [15:0] duty6 = 100;
    reg [15:0] duty7 = 500;
    reg [15:0] duty8 = 1000;

    reg  _out1;
    reg  _out2;
    reg  _out3;
    reg  _out4;
    reg  _out5;
    reg  _out6;
    reg  _out7;
    reg  _out8;
    
assign LED1 = _out1;
assign LED2 = _out2;
assign LED3 = _out3;
assign LED4 = _out4;
assign LED5 = _out5;
assign LED6 = _out6;
assign LED7 = _out7;
assign LED8 = _out8;

CLK10000HZ c1k(
    .clk(clk),
    .reset(reset),
    .clk1m(c1kout));

// LED1
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out1 <= 0;
        count1 <= 0;
    end else
    if (count1 < 100 - 1) // 10msec周期
    begin
        count1 <= count1 + 1;
        if (count1 < duty1) // デューティ時間はONする
            _out1 <= 0;
        else
            _out1 <= 1;
    end
    else
    begin
        count1 <= 0;
    end
end

// LED2
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out2 <= 0;
        count2 <= 0;
    end else
    if (count2 < 100 - 1) // 10msec周期
    begin
        count2 <= count2 + 1;
        if (count2 < duty2) // デューティ時間はONする
            _out2 <= 0;
        else
            _out2 <= 1;
    end
    else
    begin
        count2 <= 0;
    end
end

// LED3
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out3 <= 0;
        count3 <= 0;
    end else
    if (count3 < 100 - 1) // 10msec周期
    begin
        count3 <= count3 + 1;
        if (count3 < duty3) // デューティ時間はONする
            _out3 <= 0;
        else
            _out3 <= 1;
    end
    else
    begin
        count3 <= 0;
    end
end

// LED4
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out4 <= 0;
        count4 <= 0;
    end else
    if (count4 < 100 - 1) // 10msec周期
    begin
        count4 <= count4 + 1;
        if (count4 < duty4) // デューティ時間はONする
            _out4 <= 0;
        else
            _out4 <= 1;
    end
    else
    begin
        count4 <= 0;
    end
end

// LED5
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out5 <= 0;
        count5 <= 0;
    end else
    if (count5 < 100 - 1) // 10msec周期
    begin
        count5 <= count5 + 1;
        if (count5 < duty5) // デューティ時間はONする
            _out5 <= 0;
        else
            _out5 <= 1;
    end
    else
    begin
        count5 <= 0;
    end
end

// LED6
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out6 <= 0;
        count6 <= 0;
    end else
    if (count6 < 100 - 1) // 10msec周期
    begin
        count6 <= count6 + 1;
        if (count6 < duty6) // デューティ時間はONする
            _out6 <= 0;
        else
            _out6 <= 1;
    end
    else
    begin
        count6 <= 0;
    end
end

// LED7
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out7 <= 0;
        count7 <= 0;
    end else
    if (count7 < 100 - 1) // 10msec周期
    begin
        count7 <= count7 + 1;
        if (count7 < duty7) // デューティ時間はONする
            _out7 <= 0;
        else
            _out7 <= 1;
    end
    else
    begin
        count7 <= 0;
    end
end

// LED8
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out8 <= 0;
        count8 <= 0;
    end else
    if (count8 < 100 - 1) // 10msec周期
    begin
        count8 <= count8 + 1;
        if (count8 < duty8) // デューティ時間はONする
            _out8 <= 0;
        else
            _out8 <= 1;
    end
    else
    begin
        count8 <= 0;
    end
end

endmodule

clk1000hz.vのほうは parameter CNT = 250; に変更。これでやってみたのが下の写真です。


ちゃんと輝度が変わってきていますね。一番上のLEDはフル点灯です。

これを動かしたらKnight 2000のLED Flasherみたいにできるかな、というのが先週のLチカの直後に思い浮かんだので、次はこれをやってみましょう。

ということで巫女さんと遊ぶのはもうちょっと後にしましょう。

明るさを変えてみる。

今まではクロックをカウンタに放り込んでそのままLEDに出力してましたが、これをPWMにしてデューティを変えてやったら輝度を調整できるんじゃないか、などという悪魔だか天使だかのささやきが。

テレビや8mmフィルムなどの映像では、昔から24Hzでコマが切り替わるようになっていました。そのくらいの速度であれば、脳がコマの切り替わりを認識できずに連続した映像だと錯覚するということなんですが、CRTの短残光モニタだと60Hzでは蛍光灯の周波数と干渉してちらつきがでたりするので、75Hz以上が普通にありました。LCDだと現在は60Hzくらいが普通です。

となると、100Hz程度あれば、フリッカーに気づかれずに細かい点滅ができるかもしれません。

100Hzだと、1周期は10msec。今回は1msecクロックを作っていますから、0msec(消灯)から10msec(点灯)までの間を1msecごとに区切れば、輝度を制御できるかもしれません。


// LED1
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out1 <= 0;
        count1 <= 0;
    end else
    if (count1 < 100 - 1)
    begin
        count1 <= count1 + 1;
    end
    else begin
        count1 <= 0;
        _out1 <= ~_out1;
    end
end

上はLED1の点滅回路部分です。
ここで周期を10msec固定にし、ON時間を0msecから10msecの間に設定できるようにレジスタを作れば、なんとなくできそうな気がします。


    reg [15:0] duty1 = 1;

// LED1
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out1 <= 0;
        count1 <= 0;
    end else
    if (count1 < 10 - 1) // 10msec周期
    begin
        count1 <= count1 + 1;
        if (count1 < duty1) // デューティ時間はONする
            _out1 <= 1;
        else
            _out1 <= 0;
    end
    else
    begin
        count1 <= 0;
    end
end

こんな感じにしてみました。
同じコトを他の7個のLEDにも行って、いざプログラム。


……。

微妙に明るさが変わっていっているのはわかりますが、あまり違いがわかりません。

なにか間違ったかなぁ。
このまま突き進んでも意味がないので、ちょっと頭を冷やしてシミュレーションしてみましょう。

たくさんLチカ。

Lチカはできたんですが、どうせなら8個あるLEDを全部、適当にばらばらな間隔で点滅させてみようか、などと思い立ち、ちょっと遠回り。巫女さんと遊ぶのはもうちょっと先にしましょう。

方針としては、1msecクロックはそのままで、カウンタを8個に増やし、それぞれのLEDごとに判定ループを作ります。

まずはCLK1000HZ.vを再掲。CNT - 1に修正してあります。


// vim: set fenc=utf-8 filetype=verilog expandtab ts=4 sts=0 sw=4:
//
// generate 1000Hz clock from 50MHz osc input.
//

module CLK1000HZ (clk, reset, clk1m);
    parameter CNT = 25000;

    input clk;
    input reset;
    output clk1m;
    reg clk1m;
    reg [15:0] count;

    always @ (posedge clk or negedge reset) begin
        if (reset == 0)
        begin
            count <= 0;
            clk1m <= 0;
        end else
        if (count < CNT - 1)
        begin
            count <= count + 1;
        end
        else begin
            count <= 0;
            clk1m <= ~clk1m;
        end
    end
endmodule
1行目の // vim: はvimで編集するときにこの設定でやってよね、というおまじないないので、vimを使わない人は不要です。
16行目の if (reset == 0) は if (!reset) でもいいかもです。

次はtwincle_led.vです。これがトップモジュールになり、LEDを駆動します。


// vim: set fenc=utf-8 filetype=verilog expandtab ts=4 sts=0 sw=4:
//
// generate 1000Hz clock from 50MHz osc input.
//
module LED_TOP(clk, reset,
    LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8);
    input clk;
    input reset;
    output LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8;
    wire c1kout;

    reg [15:0] count1;
    reg [15:0] count2;
    reg [15:0] count3;
    reg [15:0] count4;
    reg [15:0] count5;
    reg [15:0] count6;
    reg [15:0] count7;
    reg [15:0] count8;
    
    reg  _out1;
    reg  _out2;
    reg  _out3;
    reg  _out4;
    reg  _out5;
    reg  _out6;
    reg  _out7;
    reg  _out8;
    
assign LED1 = _out1;
assign LED2 = _out2;
assign LED3 = _out3;
assign LED4 = _out4;
assign LED5 = _out5;
assign LED6 = _out6;
assign LED7 = _out7;
assign LED8 = _out8;

CLK1000HZ c1k(
    .clk(clk),
    .reset(reset),
    .clk1m(c1kout));

// LED1
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out1 <= 0;
        count1 <= 0;
    end else
    if (count1 < 100 - 1)
    begin
        count1 <= count1 + 1;
    end
    else begin
        count1 <= 0;
        _out1 <= ~_out1;
    end
end

// LED2
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out2 <= 0;
        count2 <= 0;
    end else
    if (count2 < 50 - 1)
    begin
        count2 <= count2 + 1;
    end
    else begin
        count2 <= 0;
        _out2 <= ~_out2;
    end
end

// LED3
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out3 <= 0;
        count3 <= 0;
    end else
    if (count3 < 300 - 1)
    begin
        count3 <= count3 + 1;
    end
    else begin
        count3 <= 0;
        _out3 <= ~_out3;
    end
end

// LED4
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out4 <= 0;
        count4 <= 0;
    end else
    if (count4 < 1000 - 1)
    begin
        count4 <= count4 + 1;
    end
    else begin
        count4 <= 0;
        _out4 <= ~_out4;
    end
end

// LED5
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out5 <= 0;
        count5 <= 0;
    end else
    if (count5 < 20 - 1)
    begin
        count5 <= count5 + 1;
    end
    else begin
        count5 <= 0;
        _out5 <= ~_out5;
    end
end

// LED6
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out6 <= 0;
        count6 <= 0;
    end else
    if (count6 < 2500 - 1)
    begin
        count6 <= count6 + 1;
    end
    else begin
        count6 <= 0;
        _out6 <= ~_out6;
    end
end

// LED7
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out7 <= 0;
        count7 <= 0;
    end else
    if (count7 < 100 - 1)
    begin
        count7 <= count7 + 1;
    end
    else begin
        count7 <= 0;
        _out7 <= ~_out7;
    end
end

// LED8
always @ (posedge c1kout or negedge reset) begin
    if (reset == 0)
    begin
        _out8 <= 0;
        count8 <= 0;
    end else
    if (count8 < 720 - 1)
    begin
        count8 <= count8 + 1;
    end
    else begin
        count8 <= 0;
        _out8 <= ~_out8;
    end
end

endmodule
見てわかるとおり、LEDを1から8まで増やし、それに応じてカウンタと出力信号も増やしています。

ところでVerilogの記法って、なにかスタンダードがあるんでしょうかね。たとえばモジュール定義の信号名は大文字にするとかCamelCaseにするとか、モジュール名はどう書く、とか。ここらへん、今ひとつわかりませんでした。
そしてピン定義ファイル LED.lpf。

BLOCK RESETPATHS ;
BLOCK ASYNCPATHS ;
IOBUF ALLPORTS IO_TYPE=LVTTL33 ;
LOCATE COMP "LED1" SITE "46" ;
LOCATE COMP "LED2" SITE "45" ;
LOCATE COMP "LED3" SITE "44" ;
LOCATE COMP "LED4" SITE "43" ;
LOCATE COMP "LED5" SITE "40" ;
LOCATE COMP "LED6" SITE "39" ;
LOCATE COMP "LED7" SITE "38" ;
LOCATE COMP "LED8" SITE "37" ;
LOCATE COMP "clk" SITE "21" ;
LOCATE COMP "reset" SITE "19" ;
IOBUF PORT "clk" IO_TYPE=LVTTL33 PULLMODE=NONE ;
IOBUF PORT "reset" IO_TYPE=LVTTL33 PULLMODE=UP ;
なにやら大文字小文字が混ざっていて気持ち悪い部分がありますが、ここではキニシナイことにしましょう。

ソースの修正が終わったらコンパイルとプログラムです。
そうしたら、オレンジ色でこんなメッセージが表示されました。

@W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[7] to a constant 0 @W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[8] to a constant 0 @W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[9] to a constant 0 @W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[10] to a constant 0 @W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[11] to a constant 0 @W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[12] to a constant 0 @W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[13] to a constant 0 @W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[14] to a constant 0 @W: CL190 :"D:\Users\chiyo\lattice_projects\LED\twincle_led.v":45:0:45:5|Optimizing register bit count1[15] to a constant 0

カウンタループの中での判定値が16bit幅では余るため、上位ビットは0に固定しましたよ、というメッセージです。こんな最適化もしてくれるんですね。 

プログラムしてみると、8個のLEDがワザワサと動き始めました。

そしてLチカ。

シミュレーションでクロックの間引きがちゃんと動作することがわかりましたから、あとはその出力をポートにつなげればLEDがチカチカするはず。

それではトップモジュールを追加…の前にやっておくことがあります。

せっかく作ったテストベンチですが、これはシミュレーション用のモノなので、FPGAを作成するには不要です。なので、テストベンチのプロパティを変更しておきます。

clk1000hz_test.vを右クリックしてPropertiesを開くと、どの段階でインクルードするかの指定ができます。


そこでSimulationのみを指定してやると、シミュレーションの時には使用され、Synthesis、つまり実際の論理合成では使用しないという設定ができます。

トップモジュールの追加

さてそれではトップモジュールの追加です。英語では"a top level module"というようですが。

Diamondの左側のFile ListタブでInput Filesを右クリック、Add → New File...します。Verilogを選択してファイル名を入力します。ここではtwincle_ledとでもしましょう。


ファイルが追加されたら、Verilogを記述します。


// vim: set fenc=utf-8 filetype=verilog expandtab ts=4 sts=0 sw=4:
//
// twincle LED.
//
module Twincle(clk, reset, LED1);
 input clk;
 input reset;
 output LED1;
 wire out;
 
 reg [15:0] count; // clk1mを数えるためのカウンタ
 reg _out;
 
assign LED1 = _out;

CLK1000HZ clk(
  .clk(clk),
  .reset(reset),
  .clk1m(out));

always @ (posedge out or negedge reset) begin
    if (reset == 0)
    begin
        _out = 0;
        count <= 0;
    end else
    if (count < 100 - 1)
    begin
        count <= count + 1;
    end
    else begin
       count <= 0;
       _out <= ~_out;
    end
end
endmodule
LED1はボード上のLED1を制御するための出力です。それから1msecクロックを伝えるためのwire clk、1msecを数えるためのカウンタ、そしてLED1に直結するregの_outを定義します。 次に1msecクロックをインスタンス化し、最後にalwaysでリセット時の初期化とカウントループを作ってやります。


ここまでやったら一度コンパイルします。ここでコンパイルに通らないと、後のステップが通りません。

ピンを割り当てる

さて、無事コンパイルに通ったら、次は物理的なピンアサインです。

Brevia2ボードのピンアサインはBrevia2のユーザガイドに載ってますが、ここではSpreadsheet Viewから設定してみます。

ツールバーの左端、星形と鉛筆のアイコンをクリックするとSpreadsheet Viewが開きます。


親切なことに、この段階ですでにトップモジュールの入出力が表示されています。

Brevia2ボードはすでにできあがっているボードですから、ピンアサインはすでに決まっています。今回使用するピンは以下の通りです。
  • clk : 21ピン
  • reset : 19ピン
  • LED1 : 46ピン
さらにこれらは3.3VのTTLレベルです。ということでそれを設定していきます。
ここで先ほどのコンパイルが通っていないと、Spreadsheet Viewは編集できません。

入力が終われば次のようになります。


ちなみにピンアサインはlpfファイルを直接編集することでも指定できます。

プログラムしてRun!

それではいよいよ仕上げです。
左側のProcessペインで、一番下のJEDECファイルにチェックを入れます。その状態でRunボタンを押すと…


裏でごにょごにょやったあとで、ファイルが生成されます。ここで生成されたJEDECファイルをFPGAにプログラムすることで、ハードウェアを動かすことができるようになるのです。

さてそれではボードをminiUSBケーブルでPCに繋いで、いよいよプログラムボタンを押します。ちなみにProgrammerのボタンはツールバーの真ん中よりやや左にある、ICに下矢印のアイコンです。


このボタンを押すと、Programmerのケーブルを指定するダイアログが出ます。


OKを押すと、初回に限り次のようなダイアログが出ることがあります。


ここでは「アクセスを許可する」を押して先に進むとなにかを裏で始めてるようですが、少しすると戻ってきます。
そこであらためてProgrammerタブのプログラムボタンを押します。


Programmerの矢印がオレンジだったのに対して、プログラムボタンは緑です。


こんな感じのプログレスバーが出て、しばらくすると書き込み終了です。

すると自動的にLEDが点滅を始めます。

プログラムでは、100msec点灯したら100msec消灯、つまり1周期が200msecなので、1秒間に5回の点滅をしているはずです。

もしそうなっていなかったら…LEDが点きっぱなしに見えたら…。

clk1000hz.vのparameter CNTの値が10に変更したままになっていませんか。そうすると実際に点滅しているのに、あまりにも周期が速すぎて人間には点滅に見えません。

ということで、ながながと書いてきましたが、これでようやく第一歩が踏み出せた気がします。

Lチカ、終了~。

急がば回れのシミュレーション。

ひとつひとつ、ちゃんと確認しながら進んでいくのは大事よね。というのもあるし、Diamondってどうやって使うのというのにも慣れなくてはいけないので、シミュレーションをしてみます。

まず元のソースclk1000hz.vの7行目、CNT = 25000; を CNT = 10; くらいにしておきます。半周期で25000クロックも刻まれても困るので。

そうしたら、テストベンチファイルの作成です。
左ペインのFile Listタブを選択し、Input Filesで右クリック、Add → New File...でclk1000hz_test.vというファイルを作成します。



// vim: set fenc=utf-8 filetype=verilog expandtab ts=4 sts=0 sw=4:
//
// testbench for clk1000hz.
//
//

`timescale 1ns/1ns

module CLK_TEST;
reg clk, reset;
wire out;

parameter CYCLE = 20; // 20ns (50MHz)
CLK1000HZ c1k(
  .clk(clk),
  .reset(reset),
  .clk1m(out));

always #5 clk = ~clk;

initial begin
  $monitor("%t: clk=%b, reset=%b, out=%b", $time, clk, reset, out);

                clk = 0; reset = 1;
        #10     reset = 0;
        #10     reset = 1;
  #1000 
  $finish;
end
endmodule
そうしたら上記のようなソースを入力します。
いろいろ細かい作法やら考え方、キーワードなどはその手の本を見てもらうとして、テストベンチでは入力はreg、出力はwireだそうです。そして実行する内容はinitialブロックに記述、と。
ここでは初期化としてclk=0とreset=1を、10サイクル後にリセットをおろし、その10サイクル後にリセットを解除。そのまま1000サイクルまでシミュレーションしておしまい、ということにしておきます。

次にツールバーから波形と鉛筆のアイコンをクリックします。


Simulation Wizardというウィザードです。最初に説明ダイアログが出ますから、これはそのままNextをクリックします。
次に、Simulation Project Nameを入力するように求められます。


ここではSimulatorが選べるようになって…いません。無償版ですからね。Active-HDLしか選べません。なので、asimulなどという名前を適当につけてみました。すると、そんなプロジェクトはないけれど作りますかというメッセージが出るので、Yesをクリックします。

次にProcess Stageを選択するようになっていますが、ここも選択できるのはひとつだけなのでそのままNext。するとソースファイルを指定するように出てきますので、これもそのままNextです。


次はParse HDL files for simulationというダイアログ。ここではトップモジュールを指定できるようになっていますが、現在は一つしか選べません。ここもNext。


最後に諸設定を確認するダイアログが出ます。


左下のチェックボックスはすべてチェックした状態でFinishしてみます。
するとActive-HDL 9.2というのが立ち上がり、シミュレーションをして…


なにも波形が出てきません。左ペインの信号のところもステートが不明になっていたりします。それはそうです、テストベンチのコンパイルをしてないですから。

Active-HDLのDesignからCompile Allをクリックして再度シミュレータを走らせると、ちゃんと出ました。


波形も出ています。が、よくよく数えてみるとclkが11個でoutが切り替わっています。どうやらなにか間違っているようです。

よくよくソースを見てみれば、if (count < CNT)ではダメですね。ここはCNT-1でないと。
ソースを修正してコンパイルし、再度シミュレーションしてみました。


どうやらよさそうですね。やっぱり急がば回れは重要です。

SWDなら3線でいいとはいうものの。

 安価で優秀なデバッグプローブはないかしら、と探したら、 Raspberry Pi Debug Probe というのがあったんですが、これは3線がにゅるんと出てるだけです。 もちろんSWDなら3線を繋げばいいのではありますが、汎用的に考えるなら5x2のリボンケーブルコネクタが欲し...