Artix-7で一周したら止まるLチカを書く

最初に書いたLチカは色を変えながらずっとループするものだった。その後、すぐにUARTを書こうとして文字の出力が止まらなくなった。今日は少し反省したので、謙虚にLEDを0から3まで順番に点灯させていって最後はすべて消して止まる、という回路を書こうと思う。文字の出力が止まらなくなったので一歩下がってわかりやすい例でちゃんと止まるものを書くべきだと思ったのだ。もっと毎日がっつり時間をかけないとものにならないのでは?

とりあえず以下のようなコードを書いた。これは、2Hzで以下のように動作する。

  • LED0を点ける
  • LED1を点ける
  • LED2を点ける
  • LED3を点ける
  • すべて消して終了

それだけだ。これは動いた。これに関してはさほど苦労しなかった。ただし、いくつかの軽微なエラーにでくわした。

例えば、「multiple driver nets」というのにでくわした。これは、同じシグナルに複数のalways文から入力してしまっているということらしい。教科書を流し読みしただけだったので、これが禁止されていることを理解していなかった。

forums.xilinx.com

理解しないと覚えづらいたち(・・)なのでこれが禁止されている理由について考えてみたい。上のリンクで回答している人も説明してくれればいいのに……。それとも何かの背景知識があると自明なはずなのだろうか。Verilogでは回路の繋ぎ方を書いている、と思っていいのなら、このエラーは複数の順序回路の出力が一つのレジスタに入っているということを意味している。これは……片方が1でもう片方が0の時に不定になる? というかそもそもレジスタへの入力は一つなので繋ぐことができないのかな。本の索引でalwaysが乗ってるページを前からしらみつぶしに見ていたところ、真ん中の方のコラムに「二つのalwaysブロックを書いた結果レジスタが二つ生成されてそれらがセレクタを通して出力されてシミュレータでは動いたが実機では動かなかい」例が載っていた。一つしか宣言していなくてもalways文の数だけ生成されて、あとでセレクタがそれを繋ぐようになるということ? これは合成結果の回路を見ればわかるのかな。これっぽいけど、合成自体がエラーで落ちたんだよな。本が出てから後でこういう場合はエラーで落とすようになったということだろうか。

どうも電子工作の経験が皆無なことがハンデになっている気がしなくもない。あまりちゃんと理解できている感触がない。

ところで、以下のコードでalways @(...)コメントアウトしている部分を使ったところ正しく動かなかった(リセット後3回に2回は何も起きない、残りは2秒ほどすべてのLEDが点いて消える)のだけど、これは何でだろう。どうも、挙動を完全に理解できていないまま動かしていると前に進んでいる感があまりないな……。

module blink_for_a_while(
    input  wire      CLK100MHZ,
    input  wire      reset,
    output wire[2:0] led0,
    output wire[2:0] led1,
    output wire[2:0] led2,
    output wire[2:0] led3
);

    localparam CLK_PER_BLINK = 50000000;
    localparam CLK_COUNTER_W = 1+$clog2(CLK_PER_BLINK);

    reg[CLK_COUNTER_W-1:0] clock_counter = {CLK_COUNTER_W{1'b0}};

    reg      turn_led = 1'b0;
    reg[2:0] led_addr = 3'b0;

    reg[2:0] led0_state = 3'b000;
    reg[2:0] led1_state = 3'b000;
    reg[2:0] led2_state = 3'b000;
    reg[2:0] led3_state = 3'b000;

    assign led0 = led0_state;
    assign led1 = led1_state;
    assign led2 = led2_state;
    assign led3 = led3_state;

    always @(posedge CLK100MHZ) begin
        turn_led <= 1'b0;
        if (reset) begin
            clock_counter <= {CLK_COUNTER_W{1'd0}};
        end else if (clock_counter == CLK_PER_BLINK) begin
            clock_counter <= {CLK_COUNTER_W{1'd0}};
            turn_led      <= 1'b1;
        end else begin
            clock_counter <= clock_counter + 1'b1;
        end
    end

//    always @(turn_led or reset) begin
    always @(posedge CLK100MHZ) begin
        if (reset) begin
            led_addr   <= 3'd0;
            led0_state <= 3'b000;
            led1_state <= 3'b000;
            led2_state <= 3'b000;
            led3_state <= 3'b000;
        end else if (turn_led == 1'b1) begin
            if (led_addr != 4) begin
                led_addr <= led_addr + 1;
                case (led_addr)
                    2'd0: led0_state <= 3'b111;
                    2'd1: led1_state <= 3'b111;
                    2'd2: led2_state <= 3'b111;
                    2'd3: led3_state <= 3'b111;
                endcase
            end else begin
                led0_state <= 3'b000;
                led1_state <= 3'b000;
                led2_state <= 3'b000;
                led3_state <= 3'b000;
            end
        end
    end
endmodule
## This file is a general .xdc for the Arty A7-35 Rev. D
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project

## Clock signal
set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { CLK100MHZ }]; #IO_L12P_T1_MRCC_35 Sch=gclk[100]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports { CLK100MHZ }];

## RGB LEDs
set_property -dict { PACKAGE_PIN E1    IOSTANDARD LVCMOS33 } [get_ports { led0[2] }]; #IO_L18N_T2_35 Sch=led0_b
set_property -dict { PACKAGE_PIN F6    IOSTANDARD LVCMOS33 } [get_ports { led0[1] }]; #IO_L19N_T3_VREF_35 Sch=led0_g
set_property -dict { PACKAGE_PIN G6    IOSTANDARD LVCMOS33 } [get_ports { led0[0] }]; #IO_L19P_T3_35 Sch=led0_r
set_property -dict { PACKAGE_PIN G4    IOSTANDARD LVCMOS33 } [get_ports { led1[2] }]; #IO_L20P_T3_35 Sch=led1_b
set_property -dict { PACKAGE_PIN J4    IOSTANDARD LVCMOS33 } [get_ports { led1[1] }]; #IO_L21P_T3_DQS_35 Sch=led1_g
set_property -dict { PACKAGE_PIN G3    IOSTANDARD LVCMOS33 } [get_ports { led1[0] }]; #IO_L20N_T3_35 Sch=led1_r
set_property -dict { PACKAGE_PIN H4    IOSTANDARD LVCMOS33 } [get_ports { led2[2] }]; #IO_L21N_T3_DQS_35 Sch=led2_b
set_property -dict { PACKAGE_PIN J2    IOSTANDARD LVCMOS33 } [get_ports { led2[1] }]; #IO_L22N_T3_35 Sch=led2_g
set_property -dict { PACKAGE_PIN J3    IOSTANDARD LVCMOS33 } [get_ports { led2[0] }]; #IO_L22P_T3_35 Sch=led2_r
set_property -dict { PACKAGE_PIN K2    IOSTANDARD LVCMOS33 } [get_ports { led3[2] }]; #IO_L23P_T3_35 Sch=led3_b
set_property -dict { PACKAGE_PIN H6    IOSTANDARD LVCMOS33 } [get_ports { led3[1] }]; #IO_L24P_T3_35 Sch=led3_g
set_property -dict { PACKAGE_PIN K1    IOSTANDARD LVCMOS33 } [get_ports { led3[0] }]; #IO_L23N_T3_35 Sch=led3_r

## Buttons
set_property -dict { PACKAGE_PIN D9    IOSTANDARD LVCMOS33 } [get_ports { reset }]; #IO_L6N_T0_VREF_16 Sch=btn[0]