アフィリエイト広告
アフィリエイト広告

VerilogHDL 波形から回路をつくる

今回の例題はこれ。

波形から回路をつくる

図 1. ある回路から出力された信号

問題の波形は、図 1 のとおり。4MHz のクロックから発生された信号です。このような信号を発生させる回路をつくれ、との指令です。

こんな波形、見たことありますねぇ。シフトレジスタ?リングカウンタ?

そうそう、最近つくったことがある回路、デコーダですよ。
7 セグメント LED のダイナミック点灯をやったとき、デジットを制御するための 2to4 ラインデコーダの出力波形が、こんなでした (過去記事)。そのときはアクティブローでしたので、波形は HIGH、LOW が逆転してましたが、同じことです。
なので答えは、デコーダです。

いや、シフトレジスタもリングカウンタも正解ですし。

シフトレジスタによる回路構成

デコーダに関しては、すでに 7 セグメントデコーダで類似した回路記述をやりました。例題サイトの解説にもあるように、いくつかの回路が考えられますが、例題に倣ってシフトレジスタを考えてみたいと思います。
シフトレジスタを選んだもうひとつの理由は、「連接演算子 { } 」にあります。4 ビット加算器のシミュレーションをやったときに出てきているのですが、使い方がよくわかっていませんでしたので、もう少し勉強してみましょう。

図 2. シフトレジスタによる回路構成

クロックは 4MHz (周期 250ns)、出力信号は 1ms 幅なので、イネーブル信号も 1ms 幅です。したがって、クロックを 4000 分周する必要があります。4000 分周回路からイネーブル信号を出力し、イネーブル付きのシフトレジスタを制御します。

回路記述

4000 分周回路と、シフトレジスタを接続した回路の記述です。

  1. module SHIFTREGISTER(
  2.   input wire CLK, RES,
  3.   output reg [3:0] Q
  4. );
  5.   wire EN;
  6.   reg [11:0] NUMBER_OF_CLOCKS;
  7.   parameter CLOCKS_PER_EN = 12'd3999;

モジュール名、入出力ポート、内部信号、パラメータの宣言です。
4000 は 2 進数で何ビットですか? もう、スマホの電卓ですぐに計算できますね。

N = log(4000) / log(2) ≒ 12

NUMBER_OF_CLOCKS は、カウントしたクロックの数。3999 クロック目でイネーブル信号 EN を出力して 0 に戻します。パラメータ CLOCKS_PER_EN は EN を出力するカウント数、12’d3999 です。

メモ:対数の計算

対数の計算方法ですが、手計算だとこんなふう。対数表は、必要ですけど。
まぁ、頭の体操です (^_^;)

log(4000) = log(22 x 103) = 2 x log(2) + 3 x log(10)
log(10) = 1 , log(2) = 0.301 なので
log(4000)/log(2) = (2 x 0.301 + 3 x 1) / 0.301 = 11.97   
  1. // 4000 Divider
  2.   always @(posedge CLK or negedge RES) begin
  3.     if(1'b0 == RES)
  4.       NUMBER_OF_CLOCKS <= 12'd0;
  5.     else if(CLOCKS_PER_EN == NUMBER_OF_CLOCKS)
  6.       NUMBER_OF_CLOCKS <= 12'd0;
  7.     else
  8.       NUMBER_OF_CLOCKS <= NUMBER_OF_CLOCKS + 12'd1;
  9.   end
  10.   assign EN = (CLOCKS_PER_EN == NUMBER_OF_CLOCKS);

4000 分周器の回路。リセットは非同期です。21 行目が、桁上がり、ここでは EN を出力する記述。

  1. // Shift Register
  2.   always @(posedge CLK or negedge RES) begin
  3.     if(1'b0 == RES)
  4.       Q <= 4'b0001;
  5.     else if(1'b1 == EN)
  6.       Q <= {Q[2:0], Q[3]};
  7.   end
  8. endmodule

シフトレジスタの記述は、初めまして、です。

25~26 行目。リセットが入ったとき、出力を 0b0001 にしています。これ、本来のシフトレジスタなら 0b0000 になるべきです。ここに 0b0001 を入れるってのは、リングカウンタを構成させる、ってことだな。リングカウンタもジョンソンカウンタも、シフトレジスタでつくりますから。

そして、27~28 行目。EN が入ったとき、出力 Q を「 {Q[2:0], Q[3]} 」にしています。Q[2:0] とは、Q の bit2~bit0 を指す。たとえば 0b0010 ならば「010」。Q[3] は、Q の bit3、つまり「0」です。
{} は連接演算子で、ビット列をつないで多ビット信号としてあつかいます。

{ Q[2:0], Q[3] } = { 010, 0 } = 0100

ということで、0b0010 が 0b0100 になった。1 がシフトした、ってことです。

ちなみに、通常のシフトレジスタの場合は、シリアル入力を SERIAL として

Q <= { Q[2:0], SERIAL };

のように記述します。

連接演算子は左辺でも利用できます。4 ビット加算器のシミュレーションにでてきた

assign {carry, X} = A + B;

がそれ。
carry は 1 ビット、A、B、X はそれぞれ 4 ビットです。A と B を加算したとき、桁上がりがあると 5 ビットになる。左辺では、carry に先頭の 1 ビット (桁上がりビット)、X に 4 ビット (加算された値) が格納される。左辺と右辺のビット数は、同じにするのが吉です。

テストベンチ

今回は、テストベンチにも新しいしかけをみつけました。

  1. `timescale 1ns / 1ns
  2. module SHIFTREGISTER_TEST;
  3.   reg CLK, RES;
  4.   wire [3:0] Q;
  5.   parameter OSC_PERIOD = 250; // 4MHz
  6.   always begin
  7.     CLK = 0; #(OSC_PERIOD /2);
  8.     CLK = 1; #(OSC_PERIOD /2);
  9.   end

4MHz のクロックの周期は 250ns。なので、`timescale を 1ns としました。
クロック周期を 250 としてパラメータ宣言し、クロックを発生させています。

  1.   SHIFTREGISTER SHIFTREGISTER(CLK, RES, Q);
  2.   //defparam SHIFTREGISTER.CLOCKS_PER_EN = 39;

シフトレジスタ回路をインスタンス化しています。

16 行目。コメントアウトしていますが、defparam は、下位モジュールのパラメータを上書きする記述です。ここのコメントを外すと、回路記述で宣言したパラメータ CLOCKS_PER_EN = 12’3999 が上書きされて、12’d39 になっちゃう。つまり、本来なら 3999 クロック数えるところが、39 クロックで EN を出力させることになる。
シミュレーションをちゃちゃっと進める、1 つの方法ってことでしょうか。あるいは、シミュレーションの流れを変化させるおまじないかもしれません (^_^;)

なお、つぎのように、下位モジュール接続 (インスタンス化) 時にパラメータ割り当てする記述スタイルもあります。

  1.  SHIFTREGISTER #(.CLOCKS_PER_EN(39)) SHIFTREGISTER(CLK, RES, Q);

#() 内に、パラメータ名を指定する場合はドットをつけます。数字だけでもいいんですが、複数のパラメータがあるときは順番を間違えてはいけません。
このあたり、記述スタイルもいろいろあるようですが、どれかに決めておくのがよさそうです。

  1.   initial begin
  2.     RES = 0;
  3.     repeat(2) @(posedge CLK);
  4.     RES = 1;
  5.     repeat(40000) @(posedge CLK);
  6.     RES = 0;
  7.     repeat(2) @(posedge CLK);
  8.     $finish;
  9.   end

入力を発生させます。
RES=0 でスタートし、2 クロック待って RES=1。40000 クロック (=10ms) のあいだ動作させて、RES=0 に戻す。2 クロック待って終了。

  1.   initial begin
  2.     $dumpfile("shiftregister_test.vcd");
  3.     $dumpvars(0, SHIFTREGISTER_TEST);
  4.   end
  5. endmodule

毎度の VCD ファイル出力です。

シミュレーション結果

図 3.シフトレジスタのシミュレーション結果

シミュレーション結果です。

図 3 は、3999 カウント目で EN を出力した、設計通りのシミュレーションです。
EN が 1ms ごとに出力され、出力 Q がシフトしていっています。

図 4. パラメータを変更したシミュレーション結果

図 4 は、パラメータ割り当てを 39 に変更したシミュレーション結果です。
39 カウント目で、EN が出力され、Q が変化している部分を拡大しています。クロックの周期が 250ns になっていることも、確認できます。

後記

前回の、クロック同期回路のシミュレーションでは、動作の確認にすこしばかり苦労しました。が、今回「パラメータ割り当て」という方法を知ったので、前回のようなときに役立ちそうです。
まだまだ、まぁ C 言語なんかでも同様ですが、さまざまなこと、知らないこと、理解不能なデープな機能もあることでしょう。自分なりに、うまく利用できたら、と思っています。

さて、例題にさせていただいたサイトでは、次は「期末考査」のようです。基本的なことは、これまでやってきたとおりですので、俺は、パスしま〜す。テスト、きら〜い (^_^;)

タイトルとURLをコピーしました