FC2カウンター FPGAの部屋 キャラクタ・ディスプレイ・コントローラのシミュレーション
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

キャラクタ・ディスプレイ・コントローラのシミュレーション

キャラクタ・ディスプレイ・コントローラを作っていて、木(気)になることがあった。
それは、HSYNC, VSYNCの間隔の確認はできるが、表示文字の確認はシミュレーションでは難しいことだ。出力信号のパターンを見て、これは'A'かな? これは'B'かな? と推測するしかない。
そこで、テストベンチを工夫して'*'文字のパターンでキャラクタを表示してみることにした。
下にテストベンチを示す。なお、このテストベンチを作るにあたっては、Verilog Simulater & FPGA & CPU のページVerilogチュートリアルを参考にさせていただきました。ありがとうございました。

//
`timescale 1ns/100ps

module CharDispCtrler_tb;
    `include "disp_timing_parameters.vh"
    
    reg clk = 1'b0;
    reg reset = 1'b1;
    reg [12:0] processor_addr = 13'b0000000000000;
    reg [9:0] processor_din = 10'b0000000000;
    wire [9:0] processor_dout;
    reg processor_we = 1'b0;
    wire VGA_RED;
    wire VGA_GREEN;
    wire VGA_BLUE;
    wire VGA_HSYNC;
    wire VGA_VSYNC;
    integer i, j, k, m, n;
    reg [8*100:1] str_buffer;

    parameter PERIOD = 40;
    parameter real DUTY_CYCLE = 0.5;
    parameter OFFSET = 0;

    initial    // Clock process for clk
    begin
        #OFFSET;
        forever
        begin
            clk = 1'b0;
            #(PERIOD-(PERIOD*DUTY_CYCLE)) clk = 1'b1;
            #(PERIOD*DUTY_CYCLE);
        end
    end

    CharDispCtrler UUT (
        .clk(clk),
        .reset(reset),
        .processor_addr(processor_addr),
        .processor_din(processor_din),
        .processor_dout(processor_dout),
        .processor_we(processor_we),
        .VGA_RED(VGA_RED),
        .VGA_GREEN(VGA_GREEN),
        .VGA_BLUE(VGA_BLUE),
        .VGA_HSYNC(VGA_HSYNC),
        .VGA_VSYNC(VGA_VSYNC));

    initial begin // キャラクタを書き込む
        #(PERIOD*2);
        
        FB_Write(0, 7'h41, 3'b111); // A
        FB_Write(1, 7'h42, 3'b111); // B
        FB_Write(2, 7'h43, 3'b111); // C
        FB_Write(3, 7'h44, 3'b111); // D
    end
    
    task FB_Write;
        input [12:0] addr;
        input [6:0] ascii_code;
        input [2:0] color_code;
        begin
            @(posedge clk); // 次のクロックへ
            #1;
            processor_addr = addr;
            processor_din = {color_code, ascii_code};
            processor_we = 1'b1;
            @(posedge clk); // 次のクロックへ
            #1;
            processor_we = 1'b0;
        end
    endtask
    
    initial begin // キャラクタを*で表示する
        
        #(PERIOD*2);
        reset = 1'b0;
        
        @(posedge VGA_VSYNC); // 垂直同期の立ち上がりまでWAIT
        
        for(i=0; i<= V_FRONT_PORCH-1-10; i=i+1) // V_FRONT_PORCHの10ライン前まで数える(表示範囲を広げるため)
            @(posedge VGA_HSYNC); // 水平同期の立ち上がりまでWAIT
            
        for(j=0; j<= H_FRONT_PORCH-1-10; j=j+1) // H_FRONT_PORCHの10ピクセルクロック前まで数える(表示範囲を広げるため)
            @(posedge clk);
            
        for(k=0; k<(8*5); k=k+1) begin // 8行(1キャラクタ)*5行
            for(m=0; m<(8*12); m=m+1) begin // 8ピクセル(1キャラクタ)*12列
                #1; // wait
                if(VGA_RED==1'b1 || VGA_GREEN==1'b1 || VGA_BLUE==1'b1)
                    $write("*");
                else
                    $write(" ");
                @(posedge clk); // 次のクロックへ
            end
            $write("¥n");
            
            @(posedge VGA_HSYNC) ; // 水平同期の立ち上がりまでWAIT
            for(n=0; n<= H_FRONT_PORCH-1-10; n=n+1) // H_FRONT_PORCHの10ピクセルクロック前まで数える(表示範囲を広げるため)
                @(posedge clk);
        end
        $stop; // 終了
    end
endmodule


テストベンチの説明をすると、FB_Writeタスクで、最初に'A', 'B', 'C', 'D' キャラクタをアドレスを変えて、フレームバッファに入力する。次のinitial 文で、VGA_RED, VGA_GREEN, VGA_BLUE のいずれかの色が出力されたときに '*' キャラクタを表示する。なければスペースを表示する。最初、$display で表示していたが、改行されてしまった。Verilogチュートリアルを見たところ $write が改行しないということで使った見たら、うまくいった。

実際にやってみるときには、"キャラクタ・ディスプレイ・コントローラのまとめ"からその他のVerlogファイルをダウンロードして、上のテストベンチでシミュレーションしてみてほしい。
今回は初めてVeritak でデバック、シミュレーションを行った。シンタックス エラーのときに、Syntax error というメッセージだけ表示される。ModelSimで詳しくエラー情報が出ても結局どこだかわからないことがあるが、もう少しエラー情報があるとよいと思った?
良いところは、ModelSimはwaveに入れた波形しか表示できない。新しく入れた信号はもう一度シミュレーションする必要がある。Veritak は、すべての信号を保存してあるので、Waveform Viewer にドラック・アンド・ドロップすると、すぐに見ることができる。これは、キャラクタ・ディスプレイ・コントローラのように20msくらいシミュレーションする必要がある回路にとっては、とてもありがたい機能だ。
Veritak でシミュレーションした結果を示す。
CharDispCtler_Veritak_1_071017.png

上のウインドウに '*' で'A', 'B', 'C', 'D' キャラクタが表示されているのがわかると思う。
キャラクタがつぶれているときは、Document Viewer のフォントを等幅フォントに変えると正常に見えるようになる。具体的には、メニューバーのユーティリティ -> Document Viewer フォント設定 -> Document Viewer を選ぶとフォントを選ぶダイアログが出てくるので、そこで等幅フォント、たとえばMSゴシックを選ぶと良い。

2007/10/18 注意: $write("¥n"); の¥が全角になっています。半角だとどうしても、いつの間にか消えてしまいます。全角を半角に直してお使いください。
  1. 2007年10月17日 20:42 |
  2. シミュレーション
  3. | トラックバック:0
  4. | コメント:7

コメント

Veritakだ!

>ModelSimはwaveに入れた波形しか表示できない。新しく入れた信号はもう一度シミュレーションする必要がある。

一応、VCDファイルをダンプしておくという手もありますがツール付属のビューワで簡単に追加して見れるとやっぱり便利ですよね。

でも常に全信号を記録しているってことはメモリの使用量が大きそうだけど、どうなんでしょう?
  1. 2007/10/18(木) 10:16:43 |
  2. URL |
  3. アイン #xRS1q4qQ
  4. [ 編集 ]

marseeさん、
随分簡単に書けていますね。それでいて視覚的に動いているかどうか即座に分かるので素晴らしいです。

opencores にプロの方達が書いたものがあります。たとえば、PCIコアなど、IPの中身をご存知のものもあると思いますので、一度ご覧になると、雰囲気の参考になると思いますが、同じように多くのtask で、構成されていると思います。少しいじくるとすぐにxx Errorとメッセージが出てきます。一種のアサーションと考えることもできますね。

>Syntax error というメッセージだけ表示される
はい、認知しています。コンパイラは、
 1)プりプロセッサ
 2)構文解析
 3)意味解析
 4)インスタンス展開 Elaboration
 5)コード生成
の順に進みますが、Veritakの場合、1)の構文解析の部分は、ほとんど自動的なパーサまかせで、ご指摘の通り弱いです。(2)以降は、手書きしていますので、それなりのメッセージが出てきます。
これについては、将来のVersionで改善しようと思います。ご指摘ありがとうございした。

アインさん、
大きいです。波形自体は圧縮しているので、それ自体は、VCDの1/10程度にはなっているとは思いますが、Veritakの場合、コンパイラとシミュレータが分離していないので、その部分で大きく見えます。

  1. 2007/10/18(木) 12:12:54 |
  2. URL |
  3. たっく #-
  4. [ 編集 ]

コメントありがとうございます。

>アインさん
waveにデザイン全部の信号を入れれば、ModelSimでもOKですが、わずらわしいですね。

>たっくさん
お褒めいただいてありがとうございます。
私は、やはりメインがVHDLなので、VHDLの焼き直しです。Verilogはいろいろなシステムタスクがあって便利ですね。VHDLでもCの関数を使えるようなライブラリ(パッケージ)はありますが、だいぶ重くなるようです。
それでも Verilog は、カプセル化できていないようで、心理的な抵抗があります。task に出力信号が書けないところなどです。その点はVHDLの procedure の方が出力信号を書けるので、すっきりしていると思っています。これはSystem Verilogでは解決されるのでしょうか? 調べてみようと思っています。

OPENCORESのコードを見ていると本当に勉強になりますね。この前もたっくさんのところからAVR_coreのコードをダウンロードさせていただいて、XilinxのブロックROMを使用するように直したのですが、その際に、私の懸案事項が解決するコードが書いてありました。そのうち、調べてブログに書こうと思っています。いろいろなプロジェクトをOPENCORESで見て勉強すると、本当に勉強になりそうですね。

Syntax error の件は将来、改善できるようにToDoリストに入れていただけるそうで、ありがとうございます。
  1. 2007/10/18(木) 16:25:01 |
  2. URL |
  3. marsee #-
  4. [ 編集 ]

カプセル化

カプセル化をするということであれば、

プロセッサモデルとモニターモデルのモジュールを作って

「initial begin // キャラクタを書き込む」のブロックととtask FB_Write;をプロセッサモデルのモジュールに、
「initial begin // キャラクタを*で表示する」のブロック をモニタモデルのモジュールに入れて
トップで接続とすればスッキリすると思います。

SystemVerilogは実際に仕事で使ったことはないのですが
恐らく、先の2つのモデルをクラスで記述して、
さらにprocessor_*およびVGA_*を
processor_ifやVGA_ifなどといったインタフェースにまとめて
トップで接続といった感じになるのではないでしょうか。

MentorからダウンロードできるAVMクックブックなどはSystemVerilogの勉強になると思います。
  1. 2007/10/18(木) 18:10:23 |
  2. URL |
  3. アイン #xRS1q4qQ
  4. [ 編集 ]

アインさん、ありがとうございました。AVMクックブック調べてみます。

task PS2_SigGen を再利用しようとすると、入力しか定義がなく、出力はmoduleで定義されたreg を使用するので、再利用が難しいですよね?task PS2_SigGen をほかで使おうとすると、出力するreg 信号名をあわせる必要がありますね?
VHDLの procedure では出力も下位モジュールのように定義できるので、再利用もしやすいかな?と考えています。
  1. 2007/10/18(木) 19:37:20 |
  2. URL |
  3. marsee #-
  4. [ 編集 ]

marseeさんの意図が分かりました。
SVでは、Pass by referenceがtask/function/moduleの引数に使えます。C++の参照(VHDLのSharedVariable?)のようなものですが、LRM上は、logic(reg)のpropertyがまったく同じで、static定義であるならば実体をどこまでも参照して使えるはずです。 (残念ながらVeritakでのサポートは、よくて来年の今頃になります。ごめんなさい。)
  1. 2007/10/18(木) 21:47:33 |
  2. URL |
  3. たっく #-
  4. [ 編集 ]

用語の使用方法を間違っていました。
モジュール化でいいのかな?
ライブラリに登録して、使い回ししたいと思っていました。
すみません、あまり良くわかりませんが、SystemVerilog は参照渡しができるんでしたら、それでOKだと思います。
  1. 2007/10/18(木) 22:11:21 |
  2. URL |
  3. marsee #-
  4. [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/625-640a2a16
この記事にトラックバックする(FC2ブログユーザー)