カウンタとシフトレジスタを書く

📌 このページの概要と目標

概要: カウンタやシフトレジスタは動きがあるので動作を理解するのが大変です。各回路の動作原理とVerilog記述を確実に習得してください。

目標:

  • N進カウンタのビット数設計と記述ができる
  • 2のN乗カウンタの記述の特徴を理解できる
  • アップダウンカウンタを記述できる
  • GRAYコードカウンタ(function+always)を記述できる
  • 分周回路(T-FF従属接続)を記述できる
  • シリアルパラレル変換回路を記述できる
  • レジスタ配列を宣言・記述できる

1. N進カウンタの記述

もふねこ

「カウンタ」や「シフトレジスタ」は、クロックごとに値が変わるから、頭の中で動きをイメージするのが少し難しいかも🐾
でも、デジタル回路のあちこちで使われる超重要パーツだから、ここでマスターしちゃおう!

6進カウンタを例にN進カウンタの記述について説明します。

graph LR subgraph 6進カウンタ CNT[COUNT6

► CK] end RB[RB] --> CNT CK[CK] --> CNT CNT -->|3| Q[Q
3bit] style CNT fill:#e8f5e9,stroke:#388e3c,stroke-width:2px;

ステップ1:出力Qのビット数を決める

出力Qのビット数は以下の条件式を満たす最小のkの値になります:

条件式6進カウンタの場合
2^k ≥ N を満たす最小のk2^3 = 8 ≥ 6 なので k = 3(3ビット)
出力Qの範囲0 ≤ Q ≤ 2^k - 1 → 0 から 7

ステップ2:N進カウンタのVerilog記述

6進カウンタの場合は0から5までカウント後、再び0にするための記述が必要になります。

module COUNT6 ( CK, Q, RB );
input  CK, RB;
output [2:0] Q;
reg    [2:0] Q;

always @( posedge CK or negedge RB )
begin
    if ( RB == 1'b0 )
        Q <= 3'b000;
    else if ( Q == 3'd5 )      // N-1(=5)に達したら0に戻す
        Q <= 3'b000;
    else
        Q <= Q + 3'b001;
end
endmodule
💡 N進カウンタのポイント
  • RBが0のとき → 非同期リセット(CKに関係なくQ=0)
  • CK立ち上がりでQ == N-1(=5)のとき → Q=0 に戻す
  • それ以外 → Q+1(カウントアップ)

2. 2のN乗カウンタの記述

2のN乗カウンタの場合は記述が異なります。2の3乗である8進カウンタを例に説明します。

比較項目6進カウンタ(一般N進)8進カウンタ(2の3乗)
リセット条件のelse必要(Q==5 でリセット)不要
オーバーフロー動作N-1の次は手動でリセット7+1=8 → 3ビットで自動的に0へ
module COUNT8 ( CK, Q, RB );
input  CK, RB;
output [2:0] Q;
reg    [2:0] Q;

always @( posedge CK or negedge RB )
begin
    if ( RB == 1'b0 )
        Q <= 3'b000;
    else
        Q <= Q + 3'b001;   // 7+1=8 → 最上位ビット欠落 → 0に戻る
end
endmodule
📌 2のN乗カウンタの原理 3ビットで7+1=8を表現できないため最上位ビットが欠落し、自動的に0になります。
2のN乗カウンタの記述ではリセット動作のelse項は不要です。

3. アップダウンカウンタの記述

入力UPの値によりカウントアップ・カウントダウンの両方に対応した回路です。

graph LR subgraph アップダウンカウンタ CNT[UPDOWN8

► CK] end RB[RB] --> CNT UP[UP] --> CNT CK[CK] --> CNT CNT -->|3| Q[Q
3bit] style CNT fill:#e8f5e9,stroke:#388e3c,stroke-width:2px;
UP入力動作
1カウントアップ(Q+1)
0カウントダウン(Q-1)
module UPDOWN8 ( CK, UP, Q, RB );
input  CK, UP, RB;
output [2:0] Q;
reg    [2:0] Q;

always @( posedge CK or negedge RB )
begin
    if ( RB == 1'b0 )
        Q <= 3'b000;
    else if ( UP == 1'b1 )
        Q <= Q + 3'b001;
    else
        Q <= Q - 3'b001;
end
endmodule

4. GRAYコードカウンタの記述

もふねこ

「グレイコード」って聞いたことある?🐾
普通のカウントと違って、1回に1ビットしか変化しないから、通信やセンサのノイズ対策(グリッチ防止)にすごく役立つ書き方なんだ!

GRAYコードカウンタは、クロックの立ち上がりごとに1ビットだけ変化するカウンタです。記述はfunctionとalways文で構成されます。

module GRAY_COUNT ( CLK, Q, RB );
input  CLK, RB;
output [2:0] Q;
reg    [2:0] Q;

// function GRAY: 現在のカウント値(IN)から次のカウント値を返す
function [2:0] GRAY;
input [2:0] IN;
begin
    case ( IN )
        3'b000: GRAY = 3'b001;   // 0 → 1
        3'b001: GRAY = 3'b011;   // 1 → 3
        3'b011: GRAY = 3'b010;   // 3 → 2
        3'b010: GRAY = 3'b110;   // 2 → 6
        3'b110: GRAY = 3'b111;   // 6 → 7
        3'b111: GRAY = 3'b101;   // 7 → 5
        3'b101: GRAY = 3'b100;   // 5 → 4
        3'b100: GRAY = 3'b000;   // 4 → 0
        default: GRAY = 3'b000;
    endcase
end
endfunction

always @( posedge CLK or negedge RB )
begin
    if ( RB == 1'b0 )
        Q <= 3'b000;
    else
        Q <= GRAY( Q );   // 現在のQを引数に渡し、次の値を取得
end
endmodule
💡 GRAYコードカウンタのポイント
  • function GRAY:引き数INは現在のカウント値、戻り値GRAYは次のカウント値
  • case文の内容を変えることで、任意パターンカウンタを記述可能
  • 各クロックで変化するビット数が1ビットのみ → グリッチが少ない

5. 分周回路の記述

分周回路はTフリップフロップを従属接続し、TフリップフロップのQの反転(~Q)を次のT-FFのクロックに入力します。

graph LR subgraph 分周回路 direction LR T0[T-FF
► CLK] T1[T-FF
► Q0_n] T2[T-FF
► Q1_n] CLK[CLK] --> T0 T0 -->|Q0| Q0[Q 0
CLK/2] T0 -.->|~Q0| T1 T1 -->|Q1| Q1[Q 1
CLK/4] T1 -.->|~Q1| T2 T2 -->|Q2| Q2[Q 2
CLK/8] end style T0 fill:#e3f2fd,stroke:#1976d2; style T1 fill:#e3f2fd,stroke:#1976d2; style T2 fill:#e3f2fd,stroke:#1976d2;
構成周波数
1段目 T-FF(CLK入力)CLK / 2
2段目 T-FF(1段目Q入力)CLK / 4
3段目 T-FF(2段目Q入力)CLK / 8
module DIVIDER3 ( CLK, Q, RB );
input  CLK, RB;
output [2:0] Q;
reg    [2:0] Q;

// assign文でビット選択を1ビット信号に変換(論理合成対応)
wire Q0, Q1;
assign Q0 = Q[0];
assign Q1 = Q[1];

// 各T-FFは共通のCLKではないため、3つのalways文で記述
always @( negedge CLK or negedge RB )
begin
    if ( RB == 1'b0 ) Q[0] <= 1'b0;
    else              Q[0] <= ~Q[0];
end

always @( negedge Q0 or negedge RB )
begin
    if ( RB == 1'b0 ) Q[1] <= 1'b0;
    else              Q[1] <= ~Q[1];
end

always @( negedge Q1 or negedge RB )
begin
    if ( RB == 1'b0 ) Q[2] <= 1'b0;
    else              Q[2] <= ~Q[2];
end
endmodule
⚠️ ビット選択をイベント式に使う際の注意 @(negedge Q[1]) のようにビット選択をイベント式に記述すると論理合成ツールでエラーになる場合があります。
assign Q1 = Q[1]; でワイヤ型の1ビット信号に置き換えてから使用してください。

6. シフトレジスタ(シリアルパラレル変換)の記述

3ビットのシリアル入力パラレル出力(SIPO)シフトレジスタです。CLKの立ち上がりで1ビットのシリアルデータを入力し、3ビットのパラレルデータを出力します。

graph LR subgraph シリアルパラレル変換 SIPO direction LR DFF0[D-FF
Q 0] DFF1[D-FF
Q 1] DFF2[D-FF
Q 2] DIN[DIN] --> DFF0 DFF0 -->|シフト| DFF1 DFF1 -->|シフト| DFF2 DFF0 --> Q0[Q 0] DFF1 --> Q1[Q 1] DFF2 --> Q2[Q 2] end style DFF0 fill:#fff3e0,stroke:#f57c00; style DFF1 fill:#fff3e0,stroke:#f57c00; style DFF2 fill:#fff3e0,stroke:#f57c00;
クロック数DIN入力Q[2]Q[1]Q[0]
1回目 ↑××00
2回目 ↑×0
3回目 ↑×
module SIPO3 ( CLK, DIN, Q, RB );
input  CLK, DIN, RB;
output [2:0] Q;
reg    [2:0] Q;

// CLKは各FFに共通なので、always文1つで記述可能
always @( posedge CLK or negedge RB )
begin
    if ( RB == 1'b0 )
        Q <= 3'b000;
    else
        Q <= { Q[1:0], DIN };   // 連接演算で1ビット左シフト
end
endmodule
💡 シリアルパラレル変換の連接演算
  • {Q[1:0], DIN} でQの下位2ビットとDINを連接 → 3ビット
  • 3ビットをQ(3ビット)に代入すると最上位ビットが欠落 → 1ビット左シフト
  • 連接演算を使うことでシフト動作を1行で記述できる

7. レジスタ配列の記述

レジスタとは複数のフリップフロップの集合体です。ここでは4ビット×2個のレジスタ配列を説明します。

graph TD subgraph レジスタ配列 direction TB FILE0[FILE 0
4bit] FILE1[FILE 1
4bit] end DIN[DIN 4bit] -->|WE=1 & AIN=0| FILE0 DIN -->|WE=1 & AIN=1| FILE1 FILE0 -->|AOUT=0| DOUT[DOUT 4bit] FILE1 -->|AOUT=1| DOUT style FILE0 fill:#f3e5f5,stroke:#9c27b0; style FILE1 fill:#f3e5f5,stroke:#9c27b0;
操作条件動作
書き込みCLK↑かつWE=1AINで選択したFFにDINを書き込む
読み出し常時AOUTで選択したFFの値をDOUTに出力
module REGISTER2 ( CLK, DIN, DOUT, AIN, AOUT, WE );
input  CLK;
input  [3:0] DIN;
output [3:0] DOUT;
input  AIN, AOUT, WE;

// レジスタ配列の宣言: reg [ビット幅-1:0] 信号名 [配列数-1:0]
reg [3:0] FILE [1:0];

// 書き込み制御: always文
always @( posedge CLK )
begin
    if ( WE == 1'b1 )
        FILE[AIN] <= DIN;
end

// 読み出し: assign文(組み合わせ回路)
assign DOUT = FILE[AOUT];
endmodule
📌 レジスタ配列の記法
  • reg [3:0] FILE [1:0]; → 4ビット幅のFILEというレジスタが2組
  • 書き込み(順序回路動作):always文
  • 読み出し(組み合わせ回路動作):assign文

📝 C6 まとめ: カウンタとシフトレジスタの記述

回路種類記述の特徴ポイント
N進カウンタif(Q==N-1) Q<=0; else Q<=Q+1;ビット数: 2^k ≥ N の最小k
2のN乗カウンタelse Q<=Q+1;(リセット条件不要)オーバーフローで自動的に0に戻る
アップダウンカウンタif(UP) Q<=Q+1; else Q<=Q-1;UP入力で方向を切り替え
GRAYコードカウンタfunction + always文1クロックで1ビットのみ変化
分周回路T-FF従属接続(複数always文)Q[n]をQ[n+1]のクロックに
シリアルパラレル変換Q <= {Q[1:0], DIN};連接演算で1行シフト
レジスタ配列reg [3:0] FILE [1:0];書き込み=always / 読み出し=assign