Verilog-HDL 文法(6):シミュレーション記述(2) 2015/09/27 |
[CategoryTop] [Prev] [Next] |
[目次]・シミュレーション記述例(その2) + モチーフ回路はSRAM + テストベンチ ●シミュレーション記述例(その2) ◆モチーフ回路はSRAM ・今回はモチーフ回路をSRAMとしてテストベンチ記述/シミュレーション実行を行 いたいと思います。SRAMは通常CB(Custom Block)として設計されることがほとん どですが、Simulation時には、動作モデルを作成して対応します。 ・下記はReadアドレスとWriteアドレスが別に用意されている、いわゆる2ポート SRAMの動作概念図です。(*1) [Fig.1 SRAMモデルイメージ] ┌───────────────────────────────────────┐ └───────────────────────────────────────┘ ・また、クロックとデータの関係は以下のようになっていると仮定します。普通は ディレイありますが、今回は無しということで。 [Fig.2 Write/Readシーケンス] ┌───────────────────────────────────┐ └───────────────────────────────────┘ ・このSRAMをシミュレーション可能な記述にすると下記のようになります。 [List.1 SRAMシミュレーションモデル] ┌───────────────────────────────────┐ `timescale 1ns/1ns `define M_ADRW 4 // Address Bit Width `define M_DATW 16 // Data Bit Width `define M_DLY 1 // Delay Unit └───────────────────────────────────┘ ┌───────────────────────────────────┐ `timescale 1ns/1ns module mem_a16_d16 ( wclk, rclk, din, dout, waddr, raddr, we, re ); parameter ADRW = `M_ADRW; parameter DATW = `M_DATW; parameter DLY = `M_DLY; input wclk; // Write Clock input rclk; // Read Clock input [(DATW-1):0] din; // Input Data output [(DATW-1):0] dout; // Output Data input [(ADRW-1):0] waddr; // Write Address input [(ADRW-1):0] raddr; // Read Address input we; // Write Enable input re; // Read Enable reg [(DATW-1):0] mem [0:{ADRW{1'b1}}]; // Memory本体(Reg配列) reg [(ADRW-1):0] waddrReg; reg weReg; always @(posedge wclk) begin weReg <= #(DLY) we; waddrReg <= #(DLY) waddr; if (weReg) mem[waddrReg] <= #(DLY) din; end reg [(ADRW-1):0] raddrReg; reg reReg; always @(posedge rclk) begin reReg <= #(DLY) re; raddrReg <= #(DLY) raddr; end assign dout = (reReg)? mem[raddrReg] : {(DATW-1){1'bx}}; endmodule └───────────────────────────────────┘ ・上記RTLのポイントは、メモリセルのArray部にレジスタ配列を使用している点で す。この記述はシミュレーションのみで、合成ツールやATPGツールには適用でき ません(*2)。 ・また、上記のSRAM動作モデルは、re/weが同時にassertされた場合や、waddrと raddrが同じ値だった場合にSRAMが何を出力するか(通常はZかX)が記述されてい ません。今回は省略していますが、本来であればこれらの動作についてもモデ ル内で記述します。 ・また、これは合成対象にRTLにおいても意識する話ですが、FF/LATCH相当のレジ スタに値を代入する場合、必ずディレイを入れます。これは合成時にはツールか ら無視されますが、検証時にシミュレータによって実行結果に差が出ないように するためです。 ◆テストベンチ ・テストの観点から言えば、ここではマーチパターン等を書くべきですが、とりあ えずは通常動作のシミュレーションを実施してみます。 ・テストベンチの大まかな動作としては下記です。 + 最初に全てのデータを読み出す(Xが読み出されるはず) + 次にアドレス展開したデータを書き込みます --> アドレス 0010 には 0010_0010_0010_0010 を書き込む + 最後に書き込んだデータを読み出します 具体的な記述例と動作結果を示します。 [List.2 SRAMシミュレーション テストベンチ] ┌───────────────────────────────────┐ `timescale 1ns/1ns module mem_a16_d16_test; parameter CYC = 100; parameter ADRW = `M_ADRW; parameter DATW = `M_DATW; reg clk; // Clock (Synchronous) reg [(DATW-1):0] din; // Write data reg [(ADRW-1):0] waddr; // Write address reg [(ADRW-1):0] raddr; // Read address reg we; // Write enable reg re; // Read enable wire [(DATW-1):0] dout; // Read Data (Observe) always #(CYC/2) clk = ~clk; // clock integer idx; // ==== event write sequence ==== event write_seq; always @(write_seq) begin #(0) we = 1'b1; din = {4{waddr}}; #(CYC) we = 1'b0; $display("Din [%4b]=%16b", waddr, din); #(CYC) waddr = waddr + 1'b1; end // ==== event preread sequence ==== event preread_seq; always @(preread_seq) begin #(0) re = 1'b1; #(CYC) re = 1'b0; $display("Dout[%4b]=%16b", raddr, dout); #(CYC) raddr = raddr + 1'b1; end // ==== event postread sequence ==== event postread_seq; always @(postread_seq) begin #(0) re = 1'b1; #(CYC) re = 1'b0; if (dout == {4{raddr}}) $display("Dout[%4b]=%16b : PASS", raddr, dout); else $display("Dout[%4b]=%16b : FAIL", raddr, dout); #(CYC) raddr = raddr + 1'b1; end // ==== simple write/read ==== initial begin // ---- initialize ---- #(0) clk = 1'b0; waddr = {ADRW{1'b0}}; raddr = {ADRW{1'b0}}; we = 1'b0; re = 1'b0; #(CYC*2); // ---- pre read ---- $display("==== PreRead Sequence ===="); for (idx={ADRW{1'b0}}; idx<={ADRW{1'b1}}; idx=idx+1'b1) begin -> preread_seq; #(CYC*3) ; end #(CYC*2); // ---- write ---- $display("==== Write Sequence ===="); for (idx={ADRW{1'b0}}; idx<={ADRW{1'b1}}; idx=idx+1'b1) begin -> write_seq; #(CYC*3) ; end #(CYC*2); // ---- post read ---- $display("==== PostRead Sequence ===="); for (idx={ADRW{1'b0}}; idx<={ADRW{1'b1}}; idx=idx+1'b1) begin -> postread_seq; #(CYC*3) ; end #(CYC) $finish; end // ==== memory instance ==== mem_a16_d16 mem_a16_d16 ( .wclk (clk), .rclk (clk), .din (din), .dout (dout), .waddr(waddr), .raddr(raddr), .we (we), .re(re) ); endmodule ─────────────────────────────────── [実行結果] ==== PreRead Sequence ==== Dout[0000]=xxxxxxxxxxxxxxxx Dout[0001]=xxxxxxxxxxxxxxxx Dout[0010]=xxxxxxxxxxxxxxxx Dout[0011]=xxxxxxxxxxxxxxxx Dout[0100]=xxxxxxxxxxxxxxxx Dout[0101]=xxxxxxxxxxxxxxxx Dout[0110]=xxxxxxxxxxxxxxxx Dout[0111]=xxxxxxxxxxxxxxxx Dout[1000]=xxxxxxxxxxxxxxxx Dout[1001]=xxxxxxxxxxxxxxxx Dout[1010]=xxxxxxxxxxxxxxxx Dout[1011]=xxxxxxxxxxxxxxxx Dout[1100]=xxxxxxxxxxxxxxxx Dout[1101]=xxxxxxxxxxxxxxxx Dout[1110]=xxxxxxxxxxxxxxxx Dout[1111]=xxxxxxxxxxxxxxxx ==== Write Sequence ==== Din [0000]=0000000000000000 Din [0001]=0001000100010001 Din [0010]=0010001000100010 Din [0011]=0011001100110011 Din [0100]=0100010001000100 Din [0101]=0101010101010101 Din [0110]=0110011001100110 Din [0111]=0111011101110111 Din [1000]=1000100010001000 Din [1001]=1001100110011001 Din [1010]=1010101010101010 Din [1011]=1011101110111011 Din [1100]=1100110011001100 Din [1101]=1101110111011101 Din [1110]=1110111011101110 Din [1111]=1111111111111111 ==== PostRead Sequence ==== Dout[0000]=0000000000000000 : PASS Dout[0001]=0001000100010001 : PASS Dout[0010]=0010001000100010 : PASS Dout[0011]=0011001100110011 : PASS Dout[0100]=0100010001000100 : PASS Dout[0101]=0101010101010101 : PASS Dout[0110]=0110011001100110 : PASS Dout[0111]=0111011101110111 : PASS Dout[1000]=1000100010001000 : PASS Dout[1001]=1001100110011001 : PASS Dout[1010]=1010101010101010 : PASS Dout[1011]=1011101110111011 : PASS Dout[1100]=1100110011001100 : PASS Dout[1101]=1101110111011101 : PASS Dout[1110]=1110111011101110 : PASS Dout[1111]=1111111111111111 : PASS └───────────────────────────────────┘ ・今回のテストベンチでは定型的な動作の記述に event を使用しています。event は event型変数として定義されます。 event イベント変数名; イベントの呼び出しは -> イベント変数名; です。このイベント変数がアサートされたときに、どのような動きをさせるかに ついてはalways文で記述します。 always @(イベント変数名) begin 動作記述 end ・eventの場合は、呼び出した側で実行が即次行へ移ります。なので、eventで規定 した動作を行わせてから、次の動作を行いたい場合は、待ち時間を記述する必要 があります。 ・例えば今回の例「preread_seq」では3サイクルを想定しているので、呼び出し側 も3サイクルを待たせます。 ─┬─ re = 1'b0; | <呼び出し側> Cycle | -> preread_seq; ─┼─ re = 1'b1; | #(CYC*3); // 3サイクル待たせる Cycle | ─┼─ raddr = raddr + 1'b1; | Cycle | ─┴─ | ・定型的動作の記述については、その他に task文 もありますが、これについては 次回説明したいと思います。 (*1)ちなみに、このタイプのモデルを考えるときは、Behavior(動作)モデルがRTLで記 述できるだけではNGです。必ずネットリストのイメージが作れるように書きましょ う。テストのときに必要となるからです。 (*2)特定EDAベンダのツールでは適用できますが、特殊性を含んだものは、横展開時に 必ず困ることになるので、汎用の観点からモデル適用可否の判断をしましょう。 [Revision Table] |Revision |Date |Comments |----------|-----------|----------------------------------------------------- |1.00 |2005-11-20 |初版 |1.01 |2005-11-24 |モデル注意事項追記 [end] Copyright(C) 2015 Altmo
|