情報画像学実験II

実験II-1. 論理回路

回路作成例・解説


第4週目-課題1

図1 に記述例を示す。

module COUNTER10(
    input CLK,
    output reg [3:0] OUT=0
        // 10=(1010)2を数えるためには 4ビット
        // 4ビットのレジスタが必要
    );
    
    always@(posedge CLK) begin
        if(OUT==9) begin    // 9の次は 0
            OUT <= 0;
        end else begin    // それ以外の場合は1増加    
            OUT <= OUT+1;
        end
    end
endmodule
図1. 10進カウンタの Verilog 記述例

第4週目-課題2

図2 に記述例を示す。

module T_FLIPFLOP(
    input G12,
    output reg M5,
    input B8
    );
     
     always@(posedge B8) begin    
        if(G12) begin
            M5 <= ~M5;
        end
    end
endmodule
図2. Tフリップフロップの Verilog 記述例

押しボタン押下時に LED が薄暗く光っているのが確認できたはずである。なお、LED の点滅を確認するのは不可能である。50MHz のクロック信号とはつまり、1秒間に 50,000,000 回立ち上がり、50,000,000 回立ち下がる信号である。一方、人間の目が認識できるのはせいぜい秒間 100フレーム程度である。クロック信号の高速な変化を認識することはできない。また、実は LED は点滅していない。 LED もまた、50MHz の高速な信号値の変化に対応できないのである。


第4週目-課題3

図3 に記述例を示す。

module FREQ1HZ(
    input B8,
    output reg M5
    );

    reg [24:0] R;
    
    always@(posedge B8) begin
        R <= (R > 25*1000*1000) ? 0 : R + 1;    
        M5 <= (R == 0) ? ~M5 : M5;
    end
endmodule
図3. 1秒間に1回LEDが点滅する回路の記述例

クロックは 1秒間に 50,000,000 回立ち上がる。よって、25,000,000 進カウンタ R を設け、R の値が 0 になる毎に LED の状態 Y を反転させることにした。log(25,000,000) = 24.6 より R のビット幅は25ビット以上とする必要がある。


第4週目-課題4

図4に 10進カウンタ回路の Verilog 記述例を示す。ここでは、第2週、第3週に作成したモジュール DECODER7 を利用する。

押しボタンが押されたことを検出するためには、押しボタンに割り当てられた入力信号の立ち上がりを検出すればよい。フリップフロップのクロック入力を用いれば立ち上がりを検出できるように思うだろう。しかし、これを ISE は許していない。クロックは非常にデリケートな信号であり、そのため、クロック専用の特殊な入力からしか入力できないように制限されている。押しボタンスイッチに割り当てようとしても、エラーが出てコンフィギュレーションデータを作成できないはずである。 そこで、以下のように立ち上がりを検出する。まず、入力 G12 の値を、G12B に保存することとする。1クロック後、G12B に保存された古い G12値が 0 であり、かつ新しい G12 値が 1 であるとき、G12 の立ち上がりを検出しカウンタの値を 1増加させている。

module COUNTER10_7SEG(
    input G12,
    output L14,
    output H12,
    output N14,
    output N11,
    output P12,
    output L13,
    output M12,
    output N13,
    output F12,
    output J12,
    output M13,
    output K14,
    input B8
    );
    
    reg [3:0] COUNT;
    reg G12B;
    
    DECODER7 U0 (.I3(COUNT[3]), .I2(COUNT[2]), .I1(COUNT[1]), .I0(COUNT[0]),    
        .A(L14), .B(H12), .C(N14), .D(N11), .E(P12), .F(L13), .G(M12));
    
    always@(posedge B8) begin    // G12 の古い値を G12B に保存
        G12B <= G12;
    end
    
    always@(posedge B8) begin
        if(G12B==0 && G12==1) begin    // 立ち上がり検出時
            COUNT <= (COUNT == 9) ? 0 : COUNT + 1;
        end
    end
    
    assign N13 = 1;    // 右下のドットは消灯する
    assign F12 = 0;    // 最下位の7セグメントLEDだけ点灯させる
    assign J12 = 1;
    assign M13 = 1;
    assign K14 = 1;
endmodule
図4. 7セグメントLED を用いた 10進カウンタ回路の Verilog 記述例

第4週目-課題5

マニュアルの Figure 8 のとおりに波形を作成すればよい。Refresh period = 1ms to 16ms とある。ここでは 1ms で作成する。50,000進カウンタを作成し、その値が、0, 12,500, 25,000, 37,500 のときに、7セグメントLED に与える値を変化させている。図5 に記述例を示す。

module DISPLAY1234(
    output L14,
    output H12,
    output N14,
    output N11,
    output P12,
    output L13,
    output M12,
    output N13,
    output reg F12,
    output reg J12,
    output reg M13,
    output reg K14,
    input B8
    );
    
    reg [3:0] CATHODES;
    reg [18:0] CNT;
    
    DECODER7 U0 (.I3(CATHODES[3]), .I2(CATHODES[2]),
        .I1(CATHODES[1]), .I0(CATHODES[0]),
        .A(L14), .B(H12), .C(N14), .D(N11), .E(P12), .F(L13), .G(M12));    
    
    always@(posedge B8) begin
        CNT <= (CNT >= 49999) ? 0 : CNT+1;
    end
    
    always@(posedge B8) begin
        if(CNT == 0) begin    // 1000の位に1を表示
             F12 <= 1;
             J12 <= 1;
             M13 <= 1;
             K14 <= 0;
             CATHODES <= 1;
        end else if(CNT == 12500) begin    // 100の位に1を表示
             F12 <= 1;
             J12 <= 1;
             M13 <= 0;
             K14 <= 1;
             CATHODES <= 2;
        end else if(CNT == 25000) begin    // 10の位に1を表示
             F12 <= 1;
             J12 <= 0;
             M13 <= 1;
             K14 <= 1;
             CATHODES <= 3;
        end else if(CNT == 37500) begin    // 1の位に1を表示
             F12 <= 0;
             J12 <= 1;
             M13 <= 1;
             K14 <= 1;
             CATHODES <= 4;
        end
    end
    
    assign N13 = 1;
endmodule
図5. 7セグメントLED に 1234 と表示する回路の Verilog 記述例

第4週目-課題6

課題4で作成した押しボタンが押下される毎に1増加するカウンタと、課題5で作成した 7セグメントLED に値を表示する回路を組み合わせればよい。しかし、カウンタの出力値は2進数である。例えばカウンタの値が 1234 の場合は 10011010010 という値を出力する。一方、7セグメントLED は10進数を入力として求める。正確に言えば、2進化10進表現、すなわち 1234 の場合は 0001(=1), 0010(=2), 0011(=3), 0100(=4) のように千百十一の各位の値をそれぞれ2進数で表した値を入力とする必要がある。よって、2進表現された値を2進化10進表現される値に変換する必要がある。この変換器は除算器を用いて構成できる。図6に回路構成例を示す。


図6. 7セグメントLED を用いた 10,000進カウンタ回路の概要図例1

問題は ISE では除算記号を用いることができない。そこで、ここでは除算器を作成することにする。加減算だけで除算器を作る一番簡単な方法は減算を繰り返すことである。被除数から除数を引くという操作を「被除数」が除数より小さな値になるまでになるまで繰り返し行い、減算を行った回数を商、最終的な「被除数」を剰余とすればよい。ただ、これでは時間がかかるのでもう少し効率的に計算を行うことが望まれる。

図7 に2進数における除算の例を示す。やり方は10進数における除算の筆算と基本的に同じである。除数 (例では10) と2のべき乗 (8, 4, 2, 1) の積 (80, 40, 20, 10) と被除数 (95) を比較し、被除数の方が大きい場合、その差を新たな被除数とする操作を「2のべき乗」の値をどんどん小さくしながら繰り返し実行する。また、この操作を行ったとき、商のうち、「2のべき乗」に対応するビット (例では 8, 1 に対応するビット、すなわち第3, 0 ビット) を 1 とする。最後に「被除数」として残った値 (例では5) を剰余とする。


図7. 除算例: 95÷10

このような方針で構成された、入力値を 10で除する回路の構成例を図8 に示す。

module DIVIDE10(
    input [7:0] DIVIDEND,
    output reg [4:0] QUOTIENT,
    output [3:0] REMAINDER,
    input CLK
    );

    reg [7:0] CNT=0;    // 演算制御用のカウンタ
    reg [7:0] CURRENT_DIVIDEND;    // 現在の「被除数」
    reg [7:0] NEXT_DIVIDEND;
    reg [7:0] DIVISOR;    // 除数と2のべき乗の積
    reg Q;
    
    always @(*) begin
       if(CURRENT_DIVIDEND >= DIVISOR) begin
               // 「被除数」と「除数と2のべき乗の積」を比較    
           NEXT_DIVIDEND <= CURRENT_DIVIDEND - DIVISOR;
           Q <= 1;   // 商の対応するビットを1にする準備
       end else begin
           NEXT_DIVIDEND <= CURRENT_DIVIDEND;
           Q <= 0;   // 商の対応するビットを0にする準備
       end
    end

    always@(posedge CLK) begin
       CNT <= CNT + 1;
    end
    always@(posedge CLK) begin
       if(CNT == 0) begin    // 初期化
           CURRENT_DIVIDEND <= DIVIDEND;
           DIVISOR <= 160;
               // 被除数は高々 255 であるため、
               // 被除数より小さくなる最大の「除数と2のべき乗の積」は    
               // 160 である
           QUOTIENT <= 0;
       end else begin
           if(DIVISOR != 5) begin    // もしも演算が終了していないとき
               CURRENT_DIVIDEND <= NEXT_DIVIDEND;
               DIVISOR <= {1'b0, DIVISOR[7:1]};    // DIVISOR /= 2  
               QUOTIENT <= {QUOTIENT[3:0], Q};
                   // 「2のべき乗」に対応する商のビット値を Q にする
                   // 商の各ビット値は上位から決まるので、
                   // 値が決まる毎に、Q値を商の最下位に与え、
                   // すでに決定している値を1ビット上位にシフトする
           end
       end
    end
    assign REMAINDER = CURRENT_DIVIDEND[3:0];
        // 最後に残った「被除数」を剰余とする
endmodule
図8. 入力値を10で除する回路の Verilog 記述例

最終的に求めたい 10,000進カウンタの構成例を図9に示す。ややわかりにくいが除算器の構成方針は図8と同じである。

module COUNTER10000_7SEG(    // 回路全体
    input G12,
    output L14,
    output H12,
    output N14,
    output N11,
    output P12,
    output L13,
    output M12,
    output N13,
    output F12,
    output J12,
    output M13,
    output K14,
    input B8
    );
    
    wire [13:0] COUNT;
    wire [3:0] NUM3;
    wire [3:0] NUM2;
    wire [3:0] NUM1;
    wire [3:0] NUM0;
    
    COUNTER10000 U1 (    // 課題4改
    .G12(G12), .COUNT(COUNT), .B8(B8));
    
    BIN2BCD U2(    // 2進数から2進化10進表現への変換
        .COUNT(COUNT), .NUM3(NUM3), .NUM2(NUM2), .NUM1(NUM1), .NUM0(NUM0), .B8(B8));

    DISPLAY7SEGLED U3(    // 課題5改
        .NUM3(NUM3), .NUM2(NUM2), .NUM1(NUM1), .NUM0(NUM0),
        .L14(L14), .H12(H12), .N14(N14), .N11(N11),
        .P12(P12), .L13(L13), .M12(M12), .N13(N13),
        .F12(F12), .J12(J12), .M13(M13), .K14(K14),
        .B8(B8));
endmodule

module COUNTER10000(
    input G12,
    output reg [13:0] COUNT ,
    input B8
    );
    
    reg G12B;
    
    always@(posedge B8) begin    // G12 の古い値を G12B に保存
        G12B <= G12;
    end
    
    always@(posedge B8) begin
        if(G12B==0 && G12==1) begin    // 立ち上がり検出時
            COUNT <= (COUNT == 9999) ? 0 : COUNT + 1;
        end
    end
endmodule

module BIN2BCD(
    input [13:0] COUNT,
    output reg [3:0] NUM3,
    output reg [3:0] NUM2,
    output reg [3:0] NUM1,
    output [3:0] NUM0,
    input B8
    );
    
    reg [13:0] CURRENT_DIVIDEND;
    reg [13:0] DIVISOR;
    reg [13:0] NEXT_DIVIDEND;
    
    reg [16:0] CNT = 6250;
        // CNT は 12500進カウンタ
        // 7セグメントデコーダの状態変化とタイミングをずらすために
        // CNT を 12500÷2 で初期化
    
    reg Q;
    
    always@(posedge B8) begin
        CNT <= (CNT >= 12499) ? 0 : CNT+1;
    end
    
    always @(*) begin
        if(CURRENT_DIVIDEND >= DIVISOR) begin
            NEXT_DIVIDEND <= CURRENT_DIVIDEND - DIVISOR;
            Q <= 1;
        end else begin
            NEXT_DIVIDEND <= CURRENT_DIVIDEND;
            Q <= 0;
        end
    end
    
    always@(posedge B8) begin
        if(CNT == 12499) begin    // 演算開始前の初期化
            CURRENT_DIVIDEND <= COUNT;
            DIVISOR <= 8000;    // 最初に ÷1000 を実行
        end else if(
            CNT ==  0 || CNT ==  1 || CNT ==  2 ||
            CNT ==  4 || CNT ==  5 || CNT ==  6 ||
            CNT ==  8 || CNT ==  9 || CNT == 10)
        begin    // 除算実行中
            CURRENT_DIVIDEND <= NEXT_DIVIDEND;
            DIVISOR <= {1'b0, DIVISOR[13:1]};
        end else if(CNT == 3) begin    // ÷1000 終了、÷100 開始
            CURRENT_DIVIDEND <= NEXT_DIVIDEND;
            DIVISOR <= 800;
        end else if(CNT == 7) begin    // ÷100 終了、÷10 開始
            CURRENT_DIVIDEND <= NEXT_DIVIDEND;
            DIVISOR <= 80;
        end else if(CNT == 11) begin    // ÷10 終了、演算終了
            CURRENT_DIVIDEND <= NEXT_DIVIDEND;
            DIVISOR <= 0;
        end
    end
    
    always@(posedge B8) begin
        if(CNT == 0 || CNT == 1 || CNT == 2 || CNT == 3
                || CNT == 4 || CNT == 5 || CNT == 6 || CNT == 7
                || CNT == 8 || CNT == 9 || CNT == 10 || CNT == 11) begin
            NUM3 <= {NUM3[2:0], NUM2[3]};
            NUM2 <= {NUM2[2:0], NUM1[3]};
            NUM1 <= {NUM1[2:0], Q};
        end
    end
    assign NUM0 = CURRENT_DIVIDEND[3:0];    // ÷10の剰余
endmodule

module DISPLAY7SEGLED(
    input [3:0] NUM3,
    input [3:0] NUM2,
    input [3:0] NUM1,
    input [3:0] NUM0,
    output L14,
    output H12,
    output N14,
    output N11,
    output P12,
    output L13,
    output M12,
    output N13,
    output reg F12,
    output reg J12,
    output reg M13,
    output reg K14,
    input B8
    );
    
    reg [3:0] CATHODES;
    reg [18:0] CNT;
    
    DECODER7 U0 (.I3(CATHODES[3]), .I2(CATHODES[2]),
        .I1(CATHODES[1]), .I0(CATHODES[0]),
        .A(L14), .B(H12), .C(N14), .D(N11), .E(P12), .F(L13), .G(M12));
    
    always@(posedge B8) begin
        CNT <= (CNT >= 49999) ? 0 : CNT+1;
    end
    
    always@(posedge B8) begin
        if(CNT == 0) begin
             F12 <= 1;
             J12 <= 1;
             M13 <= 1;
             K14 <= 0;
             CATHODES <= NUM3;
        end else if(CNT == 12500) begin
             F12 <= 1;
             J12 <= 1;
             M13 <= 0;
             K14 <= 1;
             CATHODES <= NUM2;
        end else if(CNT == 25000) begin
             F12 <= 1;
             J12 <= 0;
             M13 <= 1;
             K14 <= 1;
             CATHODES <= NUM1;
        end else if(CNT == 37500) begin
             F12 <= 0;
             J12 <= 1;
             M13 <= 1;
             K14 <= 1;
             CATHODES <= NUM0;
        end
    end
    
    assign N13 = 1;
endmodule
図9. 7セグメントLED を用いた 10,000進カウンタ回路の Verilog 記述例1

別解を示す。上例では2進数のカウンタと、2進化10進表現への変換器を用いて回路構成した。しかし、2進化10進表現のカウンタを用いる方法もある。図10に別解の構成を示す。この回路は千百十一各位に対応する4個の10進カウンタを持つ。各10進カウンタは桁上げ信号を出力として持ち、9→0 への変化を知らせることができるものとする。1の位に対応するカウンタは押しボタンが押下されることにより1増加する。それ以外のカウンタは下位桁から桁上げ信号が出力されたとき、1増加する。


図10. 7セグメントLED を用いた 10,000進カウンタ回路の概要図例2

この構成に基づく Verilog 記述を図11 に示す。

module COUNTER10000_7SEG(    // 回路全体
    input G12,
    output L14,
    output H12,
    output N14,
    output N11,
    output P12,
    output L13,
    output M12,
    output N13,
    output F12,
    output J12,
    output M13,
    output K14,
    input B8
    );
    
    wire [3:0] NUM3;
    wire [3:0] NUM2;
    wire [3:0] NUM1;
    wire [3:0] NUM0;
    
    COUNTER10000BCD U1 (
    .G12(G12), .NUM3(NUM3), .NUM2(NUM2), .NUM1(NUM1), .NUM0(NUM0), .B8(B8));
    
    DISPLAY7SEGLED U3(
        .NUM3(NUM3), .NUM2(NUM2), .NUM1(NUM1), .NUM0(NUM0),
        .L14(L14), .H12(H12), .N14(N14), .N11(N11),
        .P12(P12), .L13(L13), .M12(M12), .N13(N13),
        .F12(F12), .J12(J12), .M13(M13), .K14(K14),
        .B8(B8));
endmodule

module COUNTER10000BCD(
    input G12,
    output [3:0] NUM3,
    output [3:0] NUM2,
    output [3:0] NUM1,
    output [3:0] NUM0,
    input B8
    );
    
    wire [2:0] CARRY;
    wire BUTTON_DOWN;
    reg G12B;
    
    always@(posedge B8) begin
        G12B <= G12;
    end

    assign BUTTON_DOWN = (G12B==0 && G12==1);    // ボタンの押下判定    

    COUNTER10 U0 (.CIN(BUTTON_DOWN), .NUM(NUM0), .COUT(CARRY[0]), .B8(B8));    
    COUNTER10 U1 (.CIN(CARRY[0]), .NUM(NUM1), .COUT(CARRY[1]), .B8(B8));
    COUNTER10 U2 (.CIN(CARRY[1]), .NUM(NUM2), .COUT(CARRY[2]), .B8(B8));
    COUNTER10 U3 (.CIN(CARRY[2]), .NUM(NUM3), .B8(B8));
        // 10進カウンタを4個数珠つなぎにする
endmodule

module COUNTER10(
    input CIN,
    output reg [3:0] NUM,
    output reg COUT,
    input B8
    );
    
    always@(posedge B8) begin
        if(CIN) begin
            if(NUM >= 9) begin
                NUM <= 0;
                COUT <= 1;    // 9→0 変化時は桁上げ
            end else begin
                NUM <= NUM + 1;
                COUT <= 0;
            end
        end else begin
            NUM <= NUM;
            COUT <= 0;
        end
    end
endmodule

module DISPLAY7SEGLED(
    input [3:0] NUM3,
    input [3:0] NUM2,
    input [3:0] NUM1,
    input [3:0] NUM0,
    output L14,
    output H12,
    output N14,
    output N11,
    output P12,
    output L13,
    output M12,
    output N13,
    output reg F12,
    output reg J12,
    output reg M13,
    output reg K14,
    input B8
    );
    
    reg [3:0] CATHODES;
    reg [18:0] CNT;
    
    DECODER7 U0 (.I3(CATHODES[3]), .I2(CATHODES[2]),
        .I1(CATHODES[1]), .I0(CATHODES[0]),
        .A(L14), .B(H12), .C(N14), .D(N11), .E(P12), .F(L13), .G(M12));
    
    always@(posedge B8) begin
        CNT <= (CNT >= 49999) ? 0 : CNT+1;
    end
    
    always@(posedge B8) begin
        if(CNT == 0) begin
             F12 <= 1;
             J12 <= 1;
             M13 <= 1;
             K14 <= 0;
             CATHODES <= NUM3;
        end else if(CNT == 12500) begin
             F12 <= 1;
             J12 <= 1;
             M13 <= 0;
             K14 <= 1;
             CATHODES <= NUM2;
        end else if(CNT == 25000) begin
             F12 <= 1;
             J12 <= 0;
             M13 <= 1;
             K14 <= 1;
             CATHODES <= NUM1;
        end else if(CNT == 37500) begin
             F12 <= 0;
             J12 <= 1;
             M13 <= 1;
             K14 <= 1;
             CATHODES <= NUM0;
        end
    end
    
    assign N13 = 1;
endmodule
図11. 7セグメントLED を用いた 10,000進カウンタ回路の Verilog 記述例2

図9よりすっきりした回路になっている。一般に回路上で除算を行うのは難しいので、可能であれば除算は避けるべきである。ただし、これをもって回路は C言語などより劣ると考えるのは早計である。C言語で書かれたプログラムもまた、最終的には回路上で動作することを忘れてはいけない。プログラムソース上からはわかりにくくなっているだけで、除算が多ければ、やはり計算時間が長くなる。ちなみに本実験は使用しなかったが、ISE は除算器を IP コアとして提供しており、ユーザが一から記述する必要はない。


第3週目の回路作成例・解説に戻る
実験II-1トップページに戻る
難波担当実験・演習のページに戻る

難波 一輝 (助教・北神・難波研究室)
工学部1号棟4階409号室、内線3255、043-290-3255、namba@ieee.org