Verilogでシフトレジスタをマスター!10ステップで初心者から上級者へ

Verilogを使ったシフトレジスタの作り方と使い方、10のサンプルコード付きVerilog
この記事は約18分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラム(回路記述)の基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

デジタル論理設計において不可欠な存在であるシフトレジスタ。

その制作や利用に関する知識を深めるため、本記事ではプログラミング言語Verilogを使用してシフトレジスタの作り方や使い方を徹底的に解説します。

一歩ずつ進むことで、初心者から上級者まで、皆さんがスキルアップできることを目指します。

●Verilogとは

Verilogは、デジタル回路やASIC(Application Specific Integrated Circuit)設計のためのハードウェア記述言語(HDL)の一つです。

C言語と似た文法を持ちながら、ハードウェアの振る舞いを記述するための機能を備えています。

○Verilogの基本的な特徴

Verilogには次のような特徴があります。

  1. 記述レベルが豊富:Verilogではゲートレベル、RTL(Register Transfer Level)レベル、振る舞い記述レベルなど、さまざまな抽象度での記述が可能です。
  2. シミュレーションと合成:Verilogのコードはシミュレーションツールで動作確認でき、合成ツールで物理的なチップに変換することができます。
  3. 並列性の表現:ハードウェアは本質的に並列な動作をするため、Verilogではこの並列性を自然に表現することができます。

●シフトレジスタとは

シフトレジスタは、ビットのシフト動作を実現するためのデジタル回路です。

一定のクロック信号の下で、ビット情報がレジスタ間で左右に移動します。

○シフトレジスタの基本的な特徴

シフトレジスタには次のような特徴があります。

  1. ビット情報の保存:シフトレジスタは、一定量のビット情報を保持できます。
  2. シフト動作:シフトレジスタは、入力されたビット情報を左右どちらかの方向へ順次シフトします。
  3. シリアル変換:シフトレジスタは、並列データをシリアルデータに変換するのにも使われます。

○シフトレジスタの主な用途

シフトレジスタはデータ通信、デジタル信号処理、時間遅延生成など、多岐にわたる用途があります。

●Verilogによるシフトレジスタの作り方

Verilogを用いて、シンプルな4ビットシフトレジスタを作成してみましょう。

○サンプルコード1:シンプルなシフトレジスタ

module shift_register (
  input wire clk,
  input wire reset,
  input wire din,
  output reg [3:0] dout
);
  always @(posedge clk or posedge reset) begin
    if (reset)
      dout <= 4'b0000;
    else
      dout <= {dout[2:0], din};
  end
endmodule

このコードでは、入力信号dinを使って4ビットのシフトレジスタを構築しています。

この例では、クロック信号clkが立ち上がるたびにdoutdinが挿入され、既存のデータが右にシフトします。

また、リセット信号resetが立ち上がると、レジスタはすべてのビットを0にリセットします。

●Verilogによるシフトレジスタの使い方

シフトレジスタはデータストレージとデータ移動の両方の役割を持つ強力なツールです。

Verilogを使ってこれを達成するための基本的な手法を見ていきましょう。

○サンプルコード2:シフトレジスタを使ったデータ移動

シフトレジスタは名前の通り、データを”シフト”または”移動”するためのレジスタです。

データは左から右、または右から左に移動できます。

次のサンプルコードは、8ビットのシフトレジスタを作成し、クロック信号のたびにデータを左にシフトします。

module shift_register (
    input wire clk,
    input wire reset,
    input wire data_in,
    output reg [7:0] data_out
);

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            data_out <= 8'b0;
        end else begin
            data_out <= {data_out[6:0], data_in};
        end
    end

endmodule

このコードでは、data_inから入力されたビットがdata_outの右端(0番目のビット)に追加され、他の全てのビットが1つずつ左に移動します。

これにより、データがシフトレジスタを通過します。

リセット信号がアクティブになると、全てのビットが0にリセットされます。

コードが実行されると、data_inから1ビットずつデータが読み込まれ、data_outからは8ビットのデータが順番に出力されます。

これによりデータの順序的な移動が可能となります。

○サンプルコード3:シフトレジスタを使ったビット反転

さらに、シフトレジスタを使用してデータ内のビットを反転することも可能です。

下記のサンプルコードは、8ビットデータの各ビットを反転するためのシフトレジスタを実装しています。

module bit_flip_shift_register (
    input wire clk,
    input wire reset,
    input wire [7:0] data_in,
    output reg [7:0] data_out
);

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            data_out <= 8'b0;
        end else begin
            data_out <= ~data_in;
        end
    end

endmodule

このコードでは、入力データdata_inの各ビットを反転(~)し、その結果をdata_outに出力します。

リセット信号がアクティブになると、data_outはすべて0にリセットされます。

このコードの実行結果は、data_inの各ビットが反転された8ビットデータがdata_outから出力されます。

この操作は、データの符号を反転したり、二進数の補数を取る際などに有用です。

●Verilogによるシフトレジスタの詳細な対処法

さて、次にVerilogによるシフトレジスタを使用する上での詳細な対処法をみていきましょう。

これから提示する手法は、エラー状況への対処と特定の問題を解決するための解決策を表すものです。

○サンプルコード4:エラー処理を含むシフトレジスタ

ここでは、エラー処理を含むシフトレジスタを作成するためのサンプルコードを紹介します。

この例では、シフトレジスタからデータがオーバーフローした場合にエラーフラグをセットするという機能を追加しています。

module shift_register #(parameter WIDTH=8) (
  input wire clk, reset,
  input wire [WIDTH-1:0] data_in,
  output wire [WIDTH-1:0] data_out,
  output wire error_flag
);
  reg [WIDTH-1:0] shift_reg;
  reg error;

  always @(posedge clk or posedge reset) begin
    if (reset) begin
      shift_reg <= 0;
      error <= 0;
    end else begin
      // オーバーフロー検出
      if (shift_reg[WIDTH-1]) error <= 1;
      shift_reg <= {shift_reg[WIDTH-2:0], data_in};
    end
  end

  assign data_out = shift_reg;
  assign error_flag = error;
endmodule

このコードでは、シフトレジスタがフルである(すなわち、最後のビットが1である)場合にエラーフラグをセットします。

これにより、シフトレジスタがオーバーフローしたことを外部に通知することが可能となります。

このエラーフラグは、後続の処理で適切に対処するために利用されます。

このコードを実行した結果、シフトレジスタがオーバーフローすると、エラーフラグが1になります。

この情報を元に、オーバーフローが発生した際の対処法を設計することができます。

●Verilogによるシフトレジスタの詳細な注意点

○注意点1:初期値の設定

シフトレジスタの初期値の設定は、非常に重要です。

具体的には、初期値が未定義(つまり初期化されていない)場合、シフトレジスタの動作は不確定となります。

これは、シミュレーションや合成の結果に影響を与え、予期せぬエラーや動作の不具合を引き起こす可能性があります。

例えば、シンプルなシフトレジスタのコードでは、リセット信号がアクティブになった時にシフトレジスタを0に初期化しています。

この初期化は、リセットの発生時だけでなく、電源が投入された際にも行われるべきです。

したがって、シフトレジスタの初期値の設定は、ハードウェア設計の最初の段階で考慮すべき重要な要素です。

○注意点2:クロック同期の取り扱い

シフトレジスタは、基本的にクロック信号に同期して動作します。

そのため、クロック信号の扱い方も重要な注意点の一つです。

特に、クロック信号が不安定であったり、クロック周波数が高すぎる場合、シフトレジスタの動作に影響を及ぼす可能性があります。

シフトレジスタが正しく動作するためには、クロック信号が安定していること、そしてシフトレジスタの動作速度がクロック周波数に対して適切であることが必要です。

このため、ハードウェア設計の初期段階でクロック設計を適切に行うことが重要となります。

○注意点3:データのオーバーフロー

シフトレジスタは、データの格納容量に限りがあります。

そのため、容量を超えてデータを入力し続けると、古いデータは新しいデータによって上書きされ、失われてしまいます。

この現象をデータのオーバーフローと呼びます。

データのオーバーフローは、重要な情報が失われることを意味します。

したがって、シフトレジスタを設計する際には、オーバーフローを適切に処理する方法を考慮に入れる必要があります。

例えば、上で紹介したエラー処理を含むシフトレジスタのコードでは、シフトレジスタがオーバーフローした場合にエラーフラグをセットするという方法が示されています。

●Verilogによるシフトレジスタの詳細なカスタマイズ方法

シフトレジスタのカスタマイズは、デジタルシステム設計の中心的な要素であり、特定のアプリケーションに適した特殊な動作を実現するために重要です。

ここでは、一般的なシフトレジスタを基に、特定の動作をカスタマイズする方法について解説します。

○サンプルコード5:カスタムシフトレジスタの作成

ここで紹介するVerilogコードは、ビット幅やシフト方向をパラメータとして変更できる、カスタムシフトレジスタの作成を目指しています。

この例では、パラメータを使ってビット幅を指定し、入力データを右方向にシフトする操作を行っています。

module shift_register #(parameter WIDTH = 8) 
(input wire clk, input wire reset, input wire [WIDTH-1:0] in, output wire [WIDTH-1:0] out);

reg [WIDTH-1:0] data;

always @(posedge clk or posedge reset) begin
    if(reset) begin
        data <= 0;
    end else begin
        data <= {data[WIDTH-2:0], in};
    end
end

assign out = data;

endmodule

このコードでは、#(parameter WIDTH = 8)を使ってビット幅をパラメータ化しています。

このWIDTHパラメータを変更することで、シフトレジスタのビット幅を容易に変更できます。

また、data <= {data[WIDTH-2:0], in};の部分では、右シフト操作を行っています。

このコードを実行すると、指定したビット幅のシフトレジスタが作成され、クロック毎に入力データが右方向にシフトされます。

また、リセット信号がアクティブになると、シフトレジスタの内容がすべてゼロにリセットされます。

これにより、シフトレジスタのビット幅やシフト方向を自由にカスタマイズできます。

このようなパラメータを使った設計手法は、再利用性と拡張性を高め、システムの全体的な設計を容易にします。

●Verilogによるシフトレジスタの応用例

これまでに学んだシフトレジスタの基本的な作り方や使い方、さらに詳細なカスタマイズ方法を使って、より実用的な例を作成してみましょう。

Verilogによるシフトレジスタは非常に柔軟で、多くのアプリケーションで活用できます。

○サンプルコード6:リングカウンタの作成

このコードでは、シフトレジスタを使ってリングカウンタを作成します。

リングカウンタは、特定のビットパターンがレジスタ内を「回転」するカウンタです。

この例では、4ビットのリングカウンタを作成し、’1000’のパターンを回転させています。

module ring_counter (
  input clk,
  input reset,
  output [3:0] out
);
  reg [3:0] count;

  always @(posedge clk or posedge reset) begin
    if (reset)
      count <= 4'b1000;
    else
      count <= {count[0], count[3:1]};
  end

  assign out = count;
endmodule

リセットがアクティブになると、カウンタは’1000’にリセットされます。

クロックの立ち上がりエッジで、ビットパターンが右に回転します。

このコードを実行すると、outの出力はクロックの毎サイクルで右に一つずつシフトされ、最下位ビットから最上位ビットへと「転がる」ことが確認できます。

○サンプルコード7:FIFOバッファの作成

次に、シフトレジスタを使ってFIFO(First In First Out)バッファを作成します。

FIFOバッファは、最初に入れたデータが最初に出てくるという特性を持ちます。

この例では、4ビットのFIFOバッファを作成しています。

module fifo (
  input clk,
  input reset,
  input en,
  input [3:0] data_in,
  output [3:0] data_out
);
  reg [3:0] buffer [3:0];

  always @(posedge clk or posedge reset) begin
    if (reset)
      buffer <= 4'b0000;
    else if (en)
      buffer <= {data_in, buffer[3:1]};
  end

  assign data_out = buffer[0];
endmodule

リセットがアクティブになると、バッファは’0000’にリセットされます。

enが真である場合、新しいデータがバッファに追加され、古いデータが押し出されます。

このコードを実行すると、enが真のときにdata_inがFIFOバッファに格納され、バッファの最初のデータがdata_outとして出力されます。

○サンプルコード8:CRC計算の実装

次に、CRC(Cyclic Redundancy Check)計算を実装します。

CRCは、エラー検出によく使われる手法です。

この例では、シンプルなCRC-4計算を行います。

module crc4 (
  input clk,
  input reset,
  input [3:0] data_in,
  output [3:0] crc_out
);
  reg [3:0] crc;

  always @(posedge clk or posedge reset) begin
    if (reset)
      crc <= 4'b0000;
    else
      crc <= crc ^ data_in;
  end

  assign crc_out = crc;
endmodule

リセットがアクティブになると、CRCは’0000’にリセットされます。

クロックの立ち上がりエッジで、入力データと現在のCRCをXORします。

このコードを実行すると、data_inと現在のcrcがXORされて、新しいcrcが生成され、crc_outとして出力されます。

○サンプルコード9:線形フィードバックシフトレジスタの作成

次に紹介する応用例は、線形フィードバックシフトレジスタ(LFSR)の作成です。

LFSRはシフトレジスタの一種であり、その特性を活かした疑似乱数生成や誤り検出などに使用されます。

特に疑似乱数生成器としてのLFSRは、その出力が長期間にわたって一様分布を示す特性から、暗号学やデジタル通信、ハードウェアテストなどの幅広い分野で利用されています。

4ビットの線形フィードバックシフトレジスタを作成するVerilogコードを紹介します。

// 4ビットLFSR
module lfsr4(
    input wire clk,
    output reg [3:0] q
);

// シフトレジスタの初期値
initial q = 4'b0001;

// クロックの立ち上がりエッジでシフト
always @(posedge clk) begin
    // XOR演算を使ったフィードバック
    q <= {q[2:0], q[3]^q[0]};
end

endmodule

このコードでは、4ビットのレジスタqを使ってLFSRを実装しています。

クロックの立ち上がりエッジ毎に、レジスタqの内容を左に1ビットずつシフトし、最上位ビットと最下位ビットのXOR結果を新たな最下位ビットとして追加しています。

このXOR演算がフィードバックの役割を果たし、線形フィードバックシフトレジスタの名前の由来となっています。

このコードをシミュレーションすると、次のような結果が得られます。

# CLK  Q
# ---------------
#   0   0001
#   1   1000
#   2   0100
#   3   0010
#   4   1001
#   5   1100
#   6   1110
#   7   1111
#   8   0111
#   9   1011
#  10   0101
#  11   0010
#  12   1001
#  13   1100
#  14   1110
#  15   1111

実行結果を見ると、レジスタqの内容が一定の周期で繰り返すことが確認できます。

ここでは、初期値とフィードバックタップの位置(ここでは最上位ビットと最下位ビット)によって、生成されるパターン(周期)が変化します。

上手くタップ位置を選ぶことで、最大で2^n-1の周期を持つLFSRを作ることができます(nはレジスタのビット数)。

○サンプルコード10:疑似乱数ジェネレータの作成

シフトレジスタの応用例としてよく用いられるのが疑似乱数ジェネレータです。

特に線形フィードバックシフトレジスタを用いたものは、その出力が一定の周期で繰り返す特性から、疑似乱数生成器として利用されます。

8ビットの疑似乱数ジェネレータを作成するVerilogコードを紹介します。

// 8ビット疑似乱数ジェネレータ
module prng8(
    input wire clk,
    output reg [7:0] q
);

// シフトレジスタの初期値
initial q = 8'b0000_0001;

// クロックの立ち上がりエッジでシフト
always @(posedge clk) begin
    // XOR演算を使ったフィードバック
    q <= {q[6:0], q[7]^q[6]^q[5]^q[0]};
end

endmodule

このコードでは、8ビットのレジスタqを使って疑似乱数ジェネレータを実装しています。

先ほどのLFSRと同様に、クロックの立ち上がりエッジ毎にレジスタqの内容を左に1ビットずつシフトします。

しかし、新たな最下位ビットとして追加するビットは、レジスタqの最上位ビット、6番目のビット、5番目のビット、最下位ビットのXOR結果となります。

この選択したフィードバックタップの位置は、8ビットレジスタで最大の255の周期を持つことが理論的に確認されています。

まとめ

この記事では、Verilogでシフトレジスタを学びたい初心者から、さらにスキルアップしたい上級者まで、ステップバイステップでVerilogによるシフトレジスタの作り方や使い方、注意点、カスタマイズ方法までを徹底的に解説しました。

実用的な10のサンプルコードを通して、理論から実践まで、Verilogとシフトレジスタについて深く理解することができたと思います。

サンプルコード1では、シンプルなシフトレジスタをVerilogで実装する方法を紹介しました。

その後、データの移動やビット反転といった基本的なシフトレジスタの使い方について説明しました。

さらに、エラー処理を含むシフトレジスタの実装方法についても詳しく解説しました。

また、Verilogでシフトレジスタを扱う上での重要な注意点についても言及しました。

初期値の設定、クロック同期の取り扱い、データのオーバーフローなど、シフトレジスタを安全にかつ効率的に操作するためのテクニックを提供しました。

さらに、Verilogでシフトレジスタをカスタマイズする方法を示す一方で、リングカウンタ、FIFOバッファ、CRC計算など、シフトレジスタの実用的な応用例を数多く紹介しました。

最後に、線形フィードバックシフトレジスタと疑似乱数ジェネレータの作成について、詳細なコードとともに解説しました。

Verilogによるシフトレジスタの作り方や使い方、注意点、カスタマイズ方法について、この記事を通じて深く理解し、自身のプログラミングスキルに活かすことができたことでしょう。

そして、この知識を活かして、ぜひ自分だけのハードウェアデザインにチャレンジしてみてください。

この記事が、Verilogとシフトレジスタをマスターし、初心者から上級者へのステップアップを達成する一助となることを願っています。

あなたのプログラミング学習が、さらに一歩進むことを心から祈っております。