なんせ、初心者が独学で勉強しようとすると、とんでもなく、しょうもないことでハマります。 みなさんには、何をくだらんこと書いてるか、と思われるでしょうが、とりあえず、晒しときます。
always の中で出力に代入する。
例えば、カウンタなどを書くとき、always の中で、出力線に代入したいですよね。最初は次のように書いてました。
module hoge(CLK, RSTn, out); input CLK, RSTn; output [7:0]out; always @(posedge CLK or negedge RSTn) begin if (!RSTn) out<=0; else out <= out + 1; end endmodule // hoge
当然、out には <=で代入できない、って怒られます。次にしたのは、こんな感じです。
module hoge(CLK, RSTn, out); input CLK, RSTn; output [7:0]out; reg [7:0]outreg; always @(posedge CLK or negedge RSTn) begin if (!RSTn) outreg<=0; else outreg <= outreg + 1; end assign out = outreg; endmodule // hoge
あまりにも冗長でムダですよね。今は次のように書いています。 なんか、参考書を読み飛ばしていたみたいですね。しかし、間違いを指摘 してもらえるのは、コンパイラのワケわからんエラーメッセージのみなのです。正解は、
IO線を、同じ名前で、reg として再宣言するのでした。
module hoge(CLK, RSTn, out); input CLK, RSTn; output [7:0]out; reg [7:0]out; always @(posedge CLK or negedge RSTn) begin if (!RSTn) out<=0; else out <= out + 1; end endmodule // hoge△目次
CPLDに同期回路を実装するときに、回路記述に次の制約を設けるのが良いようです。
always のセンシティブリスト(@ の中身)は、posedge clk or negedge reset
CPLDには、クロックピン(GCLK)とリセットピン(RSTn)が出ていて、 always の posedge clk と negedge reset に優先的に割り当てられ、 クロックに同期した回路、負論理の非同期リセットが作りやすいようになっています。 逆に、それ以外の回路は作りにくいです。
よって、モジュールのパターンとしては、次のようになります。なんとかこのパターンに合わせこみます。
module hoge(CLK, RSTn, ...); input CLK, RSTn; reg register; always @(posedge CLK or negedge RSTn) begin if (!RSTn) register<=0; // 全てレジスタのリセット else begin // クロックが入ったときの動作 end end endmodule // hoge
では、次のようなことをしたいときはどうするのでしょう?
全体のリセットではなく、外部からのシグナルで特定のレジスタを リセットしたいときはどうするのでしょう?外部のリセットピンを 正論理で、RST_EXT としたときに、
always @(posedge RST_EXT)とすると、RSTn に割り当てられない、とコンパイラに 文句を言われることがあります。そんな時は、同期リセットにしてしまいましょう(同期リセットで良いなら)。
always @(posedge CLK) begin if (RST_EXT) // リセット else // それ以外 end
シリアル信号のサンプリングなど、しかも速度を可変したいとき、 チップの GCLK(Global Clock)とは異なるスピードで動作させたいとき、 always @(posedge local_clk)としてしまいがちですが、 あくまでも、always @(posedge GCLK)したいと思います。
そんな時は、ローカルサンプリングクロック local_clk は、 レジスタのイネーブルに突っ込んでしまいたいと思います。
しかし、これでは、local_clk の幅が、GCLK の2倍以上あるときは、動作しない気がする。デューティ比最小のディバイダを使かいましょう。
always @(posedge GCLK or negedge nRST) begin if (!nRST) count <= 0; else if (local_clk) if (count == 'd76) count <= 0; else count <= count + 1; end // always△目次
分周器が必要になることがよくあるのですが、アップカウンタとダウンカウンタとどっちが得なんでしょうかね。
アップカウンタ | ダウンカウンタ | |
---|---|---|
Verilog 記述 | reg [7:0]count; always @(posedge CLK) begin if (count == 'd100) count <= 0; else count <= count + 1; end | always @(posedge CLK) begin if (count == 8'b0) count <= 'd100; else count <= count - 1; end end |
Xilinx WebPack ISE 5.2, XC9500 Fitting |
Design Statistics # IOs : 9 Macro Statistics : # Xors : 7 # 1-bit xor2 : 7 Cell Usage : # BELS : 39 # AND2 : 11 # AND3 : 2 # AND4 : 2 # AND5 : 1 # AND6 : 1 # AND7 : 1 # INV : 14 # XOR2 : 7 # FlipFlops/Latches : 8 # FD : 8 # IO Buffers : 9 # IBUF : 1 # OBUF : 8 |
Design Statistics # IOs : 9 Macro Statistics : # Xors : 7 # 1-bit xor2 : 7 Cell Usage : # BELS : 88 # AND2 : 11 # AND8 : 4 # INV : 63 # OR2 : 3 # XOR2 : 7 # FlipFlops/Latches : 8 # FD : 8 # IO Buffers : 9 # IBUF : 1 # OBUF : 8 |
100 進カウンタの場合、ダウンカウンタは INV を大量に消費しているようです。直感的にはダウンカウンタの方が得に見えたのですが、実際合成してみると違うもんですね。
△目次パラ-シリ変換なんかで、パラレルデータが準備できたら、ラッチをかけます。私は次のように書いてみました。トランスペアレントしてもよいなら(ツツヌケ時は処理しないようにしないといけない)、使える方法だと思います。
よくわからないうちは、ラッチトリガを、シフトレジスタでエッジサンプリングしたりなどしてしまいました。エッジサンプリングは、外部入力に対して行うものですよね。
module sender(GCLK, nRST, RD, RE, RBSY); input GCLK, nRST; input [7:0]RD; // send data read from port input RE; // send data enable output RBSY; // data read busy reg re_i; reg [7:0]rd_i; always @(posedge GCLK or negedge nRST) begin if (!re_i) // …(3) rd_i <= RD; // …(2) end // always always @(posedge GCLK or negedge nRST) begin if (count > 7'b1001_000) // 処理終了 re_i <= 0; else // 処理中 re_i <= re_i | RE; // …(1), (3), (4) end // always assign RBSY = re_i; // …(5) endmodule // sender△目次
私の使っているスタイルを紹介します。あんまり一般的ではないかもしれないですが、プログラマー出身の人なら、一部納得していただける部分もあると思います。
// モジュールの説明をコメントする。 module hogehoge(GCLK, nRST, in_1, in_2, out_1, out_2); input GCLK, nRST; // 外部インターフェイスになる信号は大文字、nXXX は負論理 input in_1; // モジュール信号線は、原則 1行1宣言。ここに、信号の説明をコメントする。 // clk_i ←代入漏れなどをおこさないようにalways の中で代入されるレジスタを先頭に書く always @(posedge GCLK or negedge nRST) begin // とにかく、posedge GCLK or negedge nRST) if (!nRST) // 最初は、非同期リセット時の動作。 clk_i <= 0; // 全レジスタをリセットする else // GCLK 時の動作 clk_i <= {clk_i[0], clk}; end // always(clk_i) ←どのalways の終わりかを書く(代入されるレジスタで識別) endmodule // hogehoge ←どの module の終わりかを明示する△目次
メモリやバスアクセスなど、双方向入出力では、inout を使います。
module hogehoge(BUS, rw); input rw; // write = 1, read = 0; inout [15:0]BUS; reg [15:0]BUS_i; assign BUS = (rw)?BUS_i:16'bz; endmodule△目次
AD変換ボードのサンプリングクロックみたいに、10MHzの基準クロックから、10MHz, 5MHz, 2MHz, 1MHz,,,,,5Hz, 2Hz, 1Hzのサンプリングクロックを作りたいときはどうすれば良いでしょう?
やってみました。
Verilog では、定数のことを、ビット幅を指定して、
ところが、この意味は、32ビット幅なんですね。比較したり、レジスタにロードしたりする場合 はうまく動いても、シフトレジスタに突っ込むときに、ビット幅を忘れていて、ハマッたことがあるのでメモしておきます。
// 正解 always @(posedge OCLK) if (CLK_M) CLK_MPX[7:0] <= 8'b0000_0001; else CLK_MPX[7:0] <= {CLK_MPX[6:0], 1'b0};
// ダメ always @(posedge OCLK) if (CLK_M) CLK_MPX[7:0] <= 8'b0000_0001; else CLK_MPX[7:0] <= {CLK_MPX[6:0], 0};△目次
読者より、フィードバックフォームでリクエストを頂きました。
通常の非同期回路だと、フリップフロップのクロックがエッジ検出になっているの ですが、同期式回路だと、フリップフロップのクロックは、システムクロックになっ ていて、入力の立ち上がり・立ち下がりに反応させることができません。私も最初 は悩みました。
答えは、何段かのシフトレジスタで入力をサンプリングしてエッジを検出する、で す。
2007/04/02 誤記修正。ご指摘感謝。
module counter(CLK, RST, A, COUNT); input CLK; input RST; input A; output [7:0] COUNT; reg [1:0] reg_a; reg [7:0] COUNT; always @(posedge CLK) begin if (RST) begin // 同期リセット reg_a <= 2'b00; end else begin reg_a <= {reg_a[0], A}; // 入力 A をシフトレジスタに入れる end end always @(posedge CLK) begin if (RST) begin // 同期リセット COUNT <= 8'b0; end else begin if (reg_a == 2'b01) begin // 立ち上がり!!! COUNT <= COUNT + 1; end end end endmodule
この方法は、エンコーダの A/B 相判別、チャタリング/ノイズフィルタ、 RS-232C の入力、などにも応用できます。
△目次レジスタと組み合わせ論理を使ったステートマシンでは、定番の書き方があります。
ここで、モジュールから値を出力するときに、(*1)のようなレジスタの出力がモ ジュールの出力になっているものと、(*2)のようにレジスタの出力の後ろに組み合 わせ論理をつけた出力の2通りの方法を取ることができます。(*3)のように、入力 もデコードして出力を生成するのが、ミーリィマシンといいますがハザードやメタ ステーブルの問題が出やすく、ほとんど使われません。
module hoge(CLK, nRESET, IN, OUT1, OUT2); input CLK; input RESET; input IN; output OUT1; output OUT2; reg OUT1; reg [1:0] reg_state; always @(posedge CLK or negedge nRESET) begin if (!nRESET) begin // 非同期リセット(Lレベル) OUT2 <= 0; end else begin case(reg_state) 4'b00: begin reg_state <= 4'b01; end; 4'b01: begin reg_state <= 4'b11; OUT1 <= 1'b1; // (*1) レジスタから直接出力している end; 4'b11: begin reg_state <= 4'b10; end; 4'b10: begin reg_state <= 4'b00; end; default: begin // デフォルトを付けとく。or full case reg_state <= 4'b00; end; endcase end end assign OUT2 = reg_state[0] & OUT1; // (*2) レジスタの出力の後ろに組み合わせ論理を通して出力している assign OUT3 = OUT1 & IN; // (*3) 入力がレジスタを通らず、組み合わせ論理を通って出力 endmodule
私としては、FPGAやCPLDに回路を実装するというときは、(*1)のレジスタ出力型を おすすめします。よっぽど気合いをいれる場合は(*2)を使うかも。
△目次フィードバックフォームより質問を受けました。私のやりかたを紹介します。
回路には大きく分けて、順序回路(レジスタを含み、状態を有するもの)と組み合わせ論理があります。順序回路は、クロックにしたがって動作します。クロック以外の信号が変わっても動作しないので、always@(posedge CLK) が基本です。非同期リセットをするときは always@(posedge CLK and negedge nRST) です。組み合わせ回路は、全ての入力をセンシティブリストに列挙した always文を使っても書けますが、私は、組み合わせ回路=function 文や assign 文で書きます。always=順序回路、assign=組み合わせ回路、のほうがわかりやすいと思っているし、組み合わせ回路を作るつもりで、always のセンシティブリストに入力を列挙しわすれたりしたりするからです。
代入は、always の中での代入は、クロック以外で変化してもらっては困る場合、reg に代入します。その場合は、ノンブロッキング代入 <= を使います。ブロックしてもらっては、クロックで一斉に変化する、というのが順序回路だからです。
順序回路の場合は、wire への assign になるので、ブロッキング代入 = を使います。a = b; c = a; という場合でも、信号伝搬の速度で、この順序に代入されるので、ブロッキング代入です。
順序回路 | 組み合わせ回路 | |
信号線 | reg | wire |
構文 | always@(posedge CLK {or negedge nRST}) | assign a = b; function b... if 文、case 文 |
代入 | <= | = |