FC2カウンター FPGAの部屋 2012年10月
fc2ブログ

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

FPGAの部屋

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

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する16(ARMのソフトからキャラクタを書けた)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する15(ChipScope での評価)”の続き。

実は、アナデバのリファレンス・デザインを見ていたが、HDMIの出力画像はARMのソフトウェアから書いてあった。それだと、”ZedBoardにビットマップ・ディスプレイ・コントローラを追加する6(完成とは言えない)”の様にキャッシュ?のために表示がおかしくなるはずなのに正常に表示されている。その違いはどこなのか、ソフトウェアを調べてみた。

そうすると、メモリに書いた後に、Xil_DCacheFlush(); を実行していました。これだ~~~と思いましたね。。。

そこで早速、自分のコードに、Xil_DCacheFlush(); を追加した。
最初に、DDR3 SDRAMの画像フレームバッファをクリアしてから、Xil_DCacheFlush(); を実行すると、画面がすべて真っ黒になった。やはり、Dキャッシュからcast out しなければダメなのか。。。

1.1ドット描画する部分に、Xil_DCacheFlush(); を入れたらキャラクタをVGA画面分描画するのがとっても遅かった。約68秒掛かった。(drawn_disp.c の // 1番目の時間計測 を参照)

2.1キャラクタ描画してから、Xil_DCacheFlush(); を入れたらキャラクタをVGA画面分描画するのに、約2秒掛かった。(drawn_disp.c の // 2番目の時間計測 を参照)

3.全部のキャラクタを書き終えてから、Xil_DCacheFlush(); を入れたらキャラクタをVGA画面分描画するのに、1秒以下位だった。手計測なので、良く計測できない。(drawn_disp.c の // 3番目の時間計測 を参照)(:上の2つと違って、キャラクタをWriteするCコードを実行する時間が基本的に入っていないので、値が正しくない可能性がある。但し、Cコード実行の途中でキャッシュが溢れて一部のデータはメモリに書かれているかもしれない?)

これらの結果からXil_DCacheFlush(); で経過した時間を計算することにする。先ずは、Xil_DCacheFlush(); の呼び出しと実行にかかる時間を y とする。それ以外の時間を x とする。x は、本来のビットマップ・ディスプレイ上にキャラクタをWriteする時間となる。
1.で得られたキャラクタWriteの経過時間 68sec で式を立てる。ここでは、Xil_DCacheFlush(); をピクセル1ドット毎に行なっているので、Xil_DCacheFlush(); を行う回数は 640 x 480 回となる。よって、(1) 式が導ける。

x + 640 x 480 x y = 68sec ----- (1)


次に、2.で得られたキャラクタWriteの経過時間 2sec で式を立てる。ここでは、Xil_DCacheFlush(); を1文字ごとに行なっている。キャラクタの総数は 80 x 60 文字なので、Xil_DCacheFlush(); を呼び出す回数は 80 x 60 回となる。よって、(2) 式が導ける。

x + 80 x 60 x y = 2 ----- (2)


これで2つの式が出来たので、x と y について解くことが出来る。先ずは、y から求めてみる。
式 (1) から式 (2) を引くと x が消去できる。

(640 x 480 - 80 x 60) x y = 68 - 2
y = 21 x 10の-6乗 = 21 usec


Xil_DCacheFlush(); の実行時間は 21usec となった。これはかなり遅いということが出来ると思う。ARM Cortex - A9のIPC (Instructions Per Clock) を知りませんが、仮に1と仮定すると、クロック周波数は667MHz ですから、1命令の実行時間は約1.5nsec となる。21usec では、21usec / 1.5 nsec = 14,000命令実行できることになる。これはあくまでもCPUの動作周波数で実行できる命令数なので、1st cache に入っている命令だけだが、かなり多くの命令を実行しているということがわかると思う。
次に、x を求めてみる。今度は y を消去するために、式(1) から、8 x 8倍した式(2) を引くことにする。そうすると下の式が導ける。

-63x = -60
x = 60/63 = 0.95 sec


よって、Xil_DCacheFlush(); の実行時間を除いたキャラクタWriteの実行時間は、0.95sec となった。これは3.での結果と合致すると言うことが出来る。最後にキャッシュをメモリに吐き出すので、1回のみXil_DCacheFlush(); を実行する事になり、Xil_DCacheFlush(); の実行コストが最小になる。

#計算が間違っていたら、お知らせください。

下に、キャラクタのWriteが終了した時点の写真を示す。
ZedBoad_BitMap_DispCnt_100_121101.jpg

drawn_disp.c のCコードを下に示す。

/*
 * drawn_disp.c
 *
 *  Created on: 2012/09/25
 *      Author: Masaaki
 */

#include "xparameters.h"
#include "xgpio.h"

#define VIDEO_BUFFER_START_ADDRESS    0x10000000

void Xil_DCacheFlush(void);

// char_draw: アドレスとキャラクタコードを受け取ってそのキャラクタを描画する
// addr : 描画するキャラクタのスタートアドレス
// char_code : 描画するキャラクタのコード
// char_color : 描画するキャラクタのカラー、32ビットで表される、0RGBと8ビットずつで表す
// 戻り値:次のキャラクタの描画先頭アドレスを示す。
unsigned int *char_draw(unsigned int *addr, unsigned char char_code, unsigned int char_color){
    int i,j;
    unsigned int char_pattern;
    unsigned int char_code_int;
    unsigned int *cal_char_addr;
    unsigned int *return_addr;

    char_code_int = (unsigned int)(char_code);
    return_addr = addr + 8;
    cal_char_addr = (unsigned int *)(XPAR_CHAR_ROM_AXI_LITE_0_S_AXI_RNG00_BASEADDR+((char_code_int<<3)<<2)); // キャラジェネROMのデータは32ビット幅で下の8ビットだけ有効
    for(i=0; i<8; i++){
        char_pattern = *(volatile unsigned int *)(cal_char_addr); // キャラクタのパターンを読み出し
        for(j=0; j<8; j++){
            if(char_pattern & 0x1) // 7ビット目が1の時はドットを描画
                *(volatile unsigned int *)((unsigned int)addr ^ 4) = char_color;
            else
                *(volatile unsigned int *)((unsigned int)addr ^ 4) = 0; // 黒を描画
//            Xil_DCacheFlush();    // 1番目の時間計測
            addr++;
            char_pattern >>= 1; // キャラクタのパターンを1ビット右シフト
        }
        addr -= 8; // 行の最初のアドレスに戻す
        addr += 640; // アドレスを1行下にする
        cal_char_addr++;
    }
//    Xil_DCacheFlush();    // 2番目の時間計測

    return return_addr;
}

int main()
{
    static XGpio GPIOInstance_Ptr;
    int xStatus;
    unsigned char char_code;
    unsigned int * ddr2_addr;
    unsigned int coler_code;
    unsigned int char_cnt;
    int i, j;

    // AXI GPIO Initialization
    xStatus = XGpio_Initialize(&GPIOInstance_Ptr,XPAR_AXI_GPIO_0_DEVICE_ID);
    if(XST_SUCCESS != xStatus)
        print("GPIO INIT FAILED\n\r");
    // AXI GPIO Set the Direction(Output setting)
    XGpio_SetDataDirection(&GPIOInstance_Ptr, 1, 0);
    // init_doneに1を出力
    XGpio_DiscreteWrite(&GPIOInstance_Ptr, 1, 1);

    for (ddr2_addr=(unsigned int *)VIDEO_BUFFER_START_ADDRESS; ddr2_addr<(unsigned int *)((unsigned int)VIDEO_BUFFER_START_ADDRESS + (unsigned int)0x12C000); ddr2_addr++){
        *(volatile unsigned int *)((unsigned int)ddr2_addr ^ 4) = 0;
    }
    Xil_DCacheFlush();

    ddr2_addr = (unsigned int *)VIDEO_BUFFER_START_ADDRESS;
    char_cnt = 0;
    for(j=0; j<8; j++){
        for(i=1; i<8; i++){
            switch(i){
                case 1 :
                    coler_code = 0xff;
                    break;
                case 2 :
                    coler_code = 0xff00;
                    break;
                case 3 :
                    coler_code = 0xffff;
                    break;
                case 4 :
                    coler_code = 0xff0000;
                    break;
                case 5 :
                    coler_code = 0xff00ff;
                    break;
                case 6 :
                    coler_code = 0xffff00;
                    break;
                case 7 :
                    coler_code = 0xffffff;
            }

            for(char_code=0x21; char_code<0x80; char_code++){
                if(char_code >= 0x80) // キャラクタコードが上限に達したら、一番下に戻す
                    char_code = 0x21;
                if (char_cnt!=0 && char_cnt%80==0)
                    ddr2_addr = (unsigned int *)((unsigned int)ddr2_addr + 640*4*7); // 1行書き終わったので、下の行に、アドレスは640ドットx1ピクセル4バイトx7行
                ddr2_addr = char_draw(ddr2_addr, char_code, coler_code); // キャラクタを描画
                char_cnt++;
            }
        }
    }

    Xil_DCacheFlush();    // 3番めの時間計測
    return 0;
}

  1. 2012年10月31日 05:49 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力1(アナデバのリファレンス・デザイン)

ZedBoardに付いているHDMIポートから画像を出力してみたい。ZedBoard.orgのCommunity Projects のリンクからANALOG DEVICESのReference Design をダウンロードして、サンプルを試してみた。

ANALOG DEVICESのReference Designは、”ADV7511 XILINX KC705, VC707, ZC702 AND ZED REFERENCE DESIGN”のWebページだ。
WebページにあるDOWNLOADSのZED Reference Design Source Code をクリックするとXPSプロジェクトと3つのカスタムIPコア、axi_clkgen_v1_00_a、axi_hdmi_tx_16b_v1_00_a、axi_spdif_tx_v1_00_a をダウンロードすることが出来る。

アナデバのHDMI出力リファレンス・デザインのブロック図がここにある。
XPSプロジェクトの構成は、ZynqからAXI-StreamバスでVDMAに画像データを渡して、VDMAがaxi_hdmi_tx_16b_0 にストリームで画像データを供給する。ARMプロセッサへの接続ポートは、S_AXI_HP0を使用し、64ビット幅であった。
SPDIFは、音声を伝送する規格だ。

S/PDIF(Sony Philips Digital InterFace、エスピーディーアイエフ)とは、映像・音響機器などで音声信号をデジタル転送するための規格である。データ転送の方式および接続端子の種類を規定している。


SPDIF(axi_spdif_tx_0) はARMプロセッサから、S_AXI_HP2ポートを通して32ビットバス幅の通常のMemory Maped アクセスで接続されている。XPSプロジェクトを下に示す。
ZedBoard_HDMI_1_121029.png

ZedBoard_HDMI_2_121029.png

さて、リファレンス・デザインを動作させて見よう。予めダウンロードしたファイルのcf_adv7511_zed\cf_adv7511_zed\sw にコンパイル済みのファイルがあるので、それを使用する。
ZedBoard_HDMI_3_121029.png

リファレンス・デザインの実行方法はXMDを起動してコマンドを入れるわけだが、WebページのコマンドはZC702用だったので、ZedBoard用に一部ファイル名を変更した。それを下に示す。

fpga -debugdevice devicenr 2 -f cf_adv7511_zed.bit
connect arm hw
source ps7_init.tcl
ps7_init
dow cf_adv7511_zed.elf
run
disconnect 64
exit


ZedBoardの設定ピンのMI02~MI06はすべてGNDだ。さて、ISE14.3のXMDを起動してリファレンス・デザインを実行してみよう。

・スタートメニューのアクセサリからコマンドプロンプトを起動した。

・ISE14.3のXilinx\14.3\ISE_DS に行って、setting32.bat(64ビット環境では、setting64.bat) を実行した。これでISE14.3のXMDが動作する。
ZedBoard_HDMI_4_121029.png

・cf_adv7511_zed\cf_adv7511_zed\sw フォルダにcd する。

・ZedBoardの電源ON

・XMDを起動した。
ZedBoard_HDMI_5_121029.png

・fpga -debugdevice devicenr 2 -f cf_adv7511_zed.bit を実行した。
ZedBoard_HDMI_6_121029.png

・connect arm hw を実行した。
ZedBoard_HDMI_7_121029.png

・source ps7_init.tcl を実行した。
ZedBoard_HDMI_8_121029.png

・ps7_init を実行した。
ZedBoard_HDMI_9_121029.png

・dow cf_adv7511_zed.elf を実行した。
ZedBoard_HDMI_10_121029.png

・run を実行した。
ZedBoard_HDMI_11_121029.png

この時点でHDMIに切り替えるとサンプル画像と音が出ていた。
ZedBoard_HDMI_14_121029.jpg

・disconnect 64 を実行した。
ZedBoard_HDMI_12_121029.png

・exit を実行した。
ZedBoard_HDMI_13_121029.png
  1. 2012年10月29日 05:18 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

009 RE:CYBORG を見てきました

009 RE:CYBORG を見てきました。
サイボーグ009はとっても好きで漫画やテレビでアニメを見ていました。期待して見に行ったんですが、ラストは意味が良くわからなかったです。003はとても色っぽくなってました。石ノ森章太郎さんの漫画のイメージじゃないみたいです。
映像はとっても良かったです。3Dで見てきたんですが、とっても綺麗でしかも奥行きが自然でした。今までの3D映画は奥行きが3段階とか多くても5段階くらいにしか見えなかったんですが、自然な奥行きに見えました。
  1. 2012年10月28日 19:29 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Raspberry Pi を動かしてみました

Raspberry Pi を動かしてみました。
Interface 2012年12月号を見ながら、Win32DiskImagerをダウンロードして書き込もうとしたんですが、Disk Lockダイアログが出て、2012-09-18-wheezy-raspbian.imgを8GBのSDカードに書き込むことができませんでした。
私のパソコンでのSDカードのドライブはL:でしたが、それをフォーマットしてからWin32DiskImagerでイメージファイルをWriteすると無事にWriteすることが出来ました。相当悩んじゃいました。
Raspberry_Pi_3_121027.png

ツイッターで教えて頂いた所によると、今回はイメージファイルを上書きしてしまうので良いのですが、正規のフォーマッター(SD/SDHC/SDXC用SDフォーマッター3.1)でフォーマットしないとダメだそうです。

イメージファイルが書けたので、Raspberry Pi にHDMIケーブル、キーボード、マウス、電源用のマイクロUSBケーブル、LANケーブルを接続しました。
Raspberry_Pi_4_121027.jpg

Raspberry Pi にSDカードを入れて電源用のマイクロUSBケーブルをつなぐとブートして、Raspi-config画面が出てきました。
Raspberry_Pi_5_121027.jpg

最初に、SDカードのルートパーティションの拡張を行いました。
Raspberry_Pi_6_121027.jpg

こんな画面が出て終了しました。
Raspberry_Pi_7_121027.jpg

次にキーボードの設定を行いました。
Raspberry_Pi_8_121027.jpg

Generic 105-key (Intl) PCを選択して、
Raspberry_Pi_9_121027.jpg

Ohter を選択、
Raspberry_Pi_10_121027.jpg

Japaneseを選択、
Raspberry_Pi_11_121027.jpg

Japanese - Japanese (OADC 109A) を選択しました。
Raspberry_Pi_12_121027.jpg

後の設定はInterface の本の通りに、設定しました。(デフォルト、デフォルト、Yes)

memory_split を見ると、Interfaceとは違っていて、デフォルト値が、ARMの240MB、VideoCoreに16MBでした。512MB DRAMが載っているのに256MBしか使えないようでした。(メモリの容量はやはりツイッターで教えてもらったんですが、SAMSUNG のK4P4G324EB を見ると4Gbで512MBということがわかりました)
Raspberry_Pi_14_121027.jpg

デフォルト値で良しとしました。

sshは本のとおりにEnable としました。

boot_behaviour をYesにして、スタート時にX Windowが立ち上がるようにすると、リブート時に起動しませんでした。これはデフォルトにしておいて、コマンドラインでstartx した方が良いようです。
Raspberry_Pi_15_121027.jpg

TABキーでFinishを選択してリブートしました。(最初はリブートを選択肢なかったのか?リブートしなかった気がしますが。。。)
Raspberry_Pi_16_121027.jpg

piでログインします。(パスワードはraspberry でした)
startx でX Window を開始しました。
Raspberry_Pi_17_121027.jpg

FPGAの部屋をMidoriというブラウザで表示してみましたが、日本語フォントが入っていないので、日本語は表示されません。yahoo.comとか英語のページだったら表示に問題はありません。
Raspberry_Pi_18_121027.jpg
  1. 2012年10月28日 05:54 |
  2. マイコン関連
  3. | トラックバック:0
  4. | コメント:0

Raspberry Pi が届きました

昨日、Raspberry Pi が届きました。
メモリ512MBのRaspberry Pi のはずです。まだ試していません。
クリアケースも買いました。良い感じです。
Raspberry_Pi_1_121026.jpg

クリアケースの型番は764-4382です。日本のRSだと980円で高いです。UKでRaspberry Pi と一緒に頼んだので、3.99ユーロでした。日本の半額以下ですね。
Raspberry Pi の拡大写真です。
Raspberry_Pi_2_121026.jpg

ケースに入った写真です。なかなかかっこいいです。
Raspberry_Pi_3_121026.jpg

ケースの裏側です。Raspberry Pi のマークが付いています。ゴム足も付いていて滑りにくいです。
Raspberry_Pi_4_121026.jpg

ケースは蓋になっていて、外して下のトレイだけに出来ます。その状態だとむき出しの基板と同じ状態でGPIOなどを使えます。便利だと思います。

Interface でもARMコンピュータでI/Oということで、Raspberry Pi の記事がたくさん載ってました。参考にさせていただきたいと思います。

Interface 12月号関連企画 『ARMコンピュータでI/O』に申し込みました。勉強してきたいと思います。参加される方、よろしくお願いします。
  1. 2012年10月26日 05:57 |
  2. マイコン関連
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.4(OVLチェッカのVerilog コード)

前の記事は、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.1(BFMシミュレーション7)”だが、全体の関連は、”AXI4 Slave IPの作製”を参照のこと。

OVLチェッカはOVL(Open Verification Library)を使用して、AXIバスの信号が規格から外れていないか?をチェックするチェッカだ。OVLだけでなく、Verilogコードも書いてチェックしてあるが、まだまだルールが足りないと思っている。OVLでもどのようにチェッカを使うか迷う場面が少なくない。汎用性を持たせようと思うと、並列のトランザクションに対応する必要がある。それにはまだ対応していないが、OVLチェッカだけでは難しいのではないか?と考えている。例えば、AWVALIDとAWREADYの組みとWVALID, WREADY, WLASTの組みのカウント数が合っているか?をチェックするチェッカが必要なのではないか?と思っている。

現在のOVL_Checker.v を下に貼っておく。

// OVL_Checker.v
// 2012/10/25:書き込みトランザクションの時に、MasterがAWREADYを待って、WVALIDをアサートしていけないというルールがあったので、修正した。
//

`default_nettype none

`timescale 100ps / 1ps

`include "std_ovl_defines.h"

module OVL_Checker (
    input    wire    ACLK,
    input    wire    ARESETN,

    input wire [0:0] S_AXI_AWID,
    input wire [31:0] S_AXI_AWADDR,
    input wire [7:0] S_AXI_AWLEN,
    input wire [2:0] S_AXI_AWSIZE,
    input wire [1:0] S_AXI_AWBURST,
    input wire [1:0] S_AXI_AWLOCK,
    input wire [3:0] S_AXI_AWCACHE,    // Normal Non-cacheable Non-bufferable
    input wire [2:0] S_AXI_AWPROT,
    input wire [3:0] S_AXI_AWREGION,
    input wire [3:0] S_AXI_AWQOS,
    input wire [0:0] S_AXI_AWUSER,
    input wire S_AXI_AWVALID,
    input wire [0:0] S_AXI_WID,
    input wire [31:0] S_AXI_WDATA,
    input wire [3:0] S_AXI_WSTRB,
    input wire S_AXI_WLAST,
    input wire [0:0] S_AXI_WUSER,
    input wire S_AXI_WVALID,
    input wire S_AXI_BREADY,
    input wire [0:0] S_AXI_ARID,
    input wire [31:0] S_AXI_ARADDR,
    input wire [7:0] S_AXI_ARLEN,
    input wire [2:0] S_AXI_ARSIZE,
    input wire [1:0] S_AXI_ARBURST,
    input wire [1:0] S_AXI_ARLOCK,
    input wire [3:0] S_AXI_ARCACHE, // Normal Non-cacheable Non-bufferable
    input wire [2:0] S_AXI_ARPROT,
    input wire [3:0] S_AXI_ARREGION,
    input wire [3:0] S_AXI_ARQOS,
    input wire [0:0] S_AXI_ARUSER,
    input wire S_AXI_ARVALID,
    input wire S_AXI_RREADY,

    input wire S_AXI_AWREADY,
    input wire S_AXI_WREADY,
    input wire [0:0] S_AXI_BID,
    input wire [1:0] S_AXI_BRESP,
    input wire [0:0] S_AXI_BUSER,
    input wire S_AXI_BVALID,
    input wire S_AXI_ARREADY,
    input wire [0:0] S_AXI_RID,
    input wire [31:0] S_AXI_RDATA,
    input wire [1:0] S_AXI_RRESP,
    input wire S_AXI_RLAST,
    input wire [0:0] S_AXI_RUSER,
    input wire S_AXI_RVALID
);

    wire [`OVL_FIRE_WIDTH-1:0] fire_wr_data, fire_rd_data;
    wire [`OVL_FIRE_WIDTH-1:0] fire_aw_hcheck, fire_ar_hcheck;
    wire [`OVL_FIRE_WIDTH-1:0] fire_awvalid_cont, fire_wd_sig_assert;
    wire [`OVL_FIRE_WIDTH-1:0] fire_ar_never;
    reg        [7:0]    countw, countr;
    
    parameter    idle_wts =        3'b001,
                wr_data_tran =    3'b010,
                wr_resp_tran =    3'b100;
    reg    [2:0]    wr_tran_cs;
    
    parameter    idle_rts =        1'b0,
                rd_data_tran =    1'b1;
    reg    rd_trans_cs;
    
    
    // Wirte Transaction データが+1されていることをチェックする(BFM Check)
    ovl_increment #(
        `OVL_ERROR,            // severity_level
        32,                        // width
        1,                        // value
        `OVL_ASSERT,            // property_type
        "Error: Write data has not been incremented", // msg
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock_edge
        `OVL_ACTIVE_HIGH,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) wr_data_check (
        ACLK,                    // clock
        ~ARESETN | (S_AXI_AWVALID & S_AXI_AWREADY),    // reset, Write のアドレス転送でリセット
        S_AXI_WVALID & S_AXI_WREADY,                    // enable
        S_AXI_WDATA,            // test_expr
        fire_wr_data            // fire    parameter
    );
    
    // Read Transaction データが+1されていることをチェックする(BFM Check)
    ovl_increment #(
        `OVL_ERROR,            // severity_level
        32,                        // width
        1,                        // value
        `OVL_ASSERT,            // property_type
        "Error: Read data has not been incremented", // msg
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock_edge
        `OVL_ACTIVE_HIGH,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) rd_data_check (
        ACLK,                    // clock
        ~ARESETN | (S_AXI_ARVALID & S_AXI_ARREADY),    // reset, Read のアドレス転送でリセット
        S_AXI_RVALID & S_AXI_RREADY,                    // enable
        S_AXI_RDATA,            // test_expr
        fire_rd_data            // fire    parameter
    );
    
    
    // S_AXI_AWVALID とS_AXI_AWREADY のハンドシェークのテスト
    ovl_handshake #(
        `OVL_ERROR,            // severity_level
        1,                    // min_ack_cycle
        32,                    // max_ack_cycle
        1,                    // req_drop
        1,                    // deassert_count
        1,                    // max_ack_length
        `OVL_ASSERT,        // property_type
        "Error: Handshake Error of S_AXI_AWREADY and S_AXI_AWVALID",
        `OVL_COVER_DEFAULT,    // coverage_level
        `OVL_POSEDGE,        // clock_edge
        `OVL_ACTIVE_LOW,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) aw_handshake_check (
        ACLK,                    // clock
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_AWVALID,            // req
        S_AXI_AWREADY,            // ack
        fire_aw_hcheck            // fire parameter
    );
    
    // S_AXI_ARVALID とS_AXI_ARREADY のハンドシェークのテスト
    ovl_handshake #(
        `OVL_ERROR,            // severity_level
        1,                    // min_ack_cycle
        32,                    // max_ack_cycle
        1,                    // req_drop
        1,                    // deassert_count
        1,                    // max_ack_length
        `OVL_ASSERT,        // property_type
        "Error: Handshake Error of S_AXI_ARREADY and S_AXI_ARVALID",
        `OVL_COVER_DEFAULT,    // coverage_level
        `OVL_POSEDGE,        // clock_edge
        `OVL_ACTIVE_LOW,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) ar_handshake_check (
        ACLK,                    // clock
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_ARVALID,            // req
        S_AXI_ARREADY,            // ack
        fire_ar_hcheck            // fire parameter
    );
    
    // Write, S_AXI_AWREADY がアサートされるとき、次のクロックでS_AXI_AWVALIDがディアサートされる
    ovl_transition #(
        `OVL_ERROR,                // severity_level
        1,                        // width
        `OVL_ASSERT,            // property_type
        "Error : Assert of S_AXI_AWVALID",
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock edge
        `OVL_ACTIVE_LOW,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) awvalid_cont_assert (
        ACLK,                    // clock_edge
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_AWVALID,            // test_expr
        S_AXI_AWREADY & S_AXI_AWVALID,    // start_state
        ~S_AXI_AWVALID,            // next_state
        fire_awvalid_cont
    );
    
    // Write, S_AXI_WVALID, S_AXI_WLAST, S_AXI_WREADYがアサートされるとき、次のクロックで、それらの信号がディアサートされる
    ovl_transition #(
        `OVL_ERROR,                // severity_level
        1,                        // width
        `OVL_ASSERT,            // property_type
        "Error : Assert of S_AXI_WVALID or S_AXI_WLAST or S_AXI_WREADY",
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock edge
        `OVL_ACTIVE_LOW,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) wd_sig_assert (
        ACLK,                    // clock_edge
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_AWVALID,            // test_expr
        S_AXI_WVALID & S_AXI_WLAST & S_AXI_WREADY,    // start_state
        ~(S_AXI_WVALID | S_AXI_WLAST | S_AXI_WREADY),            // next_state
        fire_wd_sig_assert
    );
    
    // Read, S_AXI_RREADY がアサートされるときは、 S_AXI_RVALID, S_AXI_RREADY, S_AXI_RLAST はアサートされない
    ovl_never #(
        `OVL_ERROR,                // severity_level
        `OVL_ASSERT,            // property_type
        "Read, Assert Error of S_AXI_RREADY",
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock edge
        `OVL_ACTIVE_LOW,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) ar_never_assert (
        ACLK,                    // clock_edge
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_ARREADY & (S_AXI_RVALID | S_AXI_RREADY | S_AXI_RLAST),
        fire_ar_never
    );
    
    // Write の転送数をカウントして、 S_AXI_WLAST の出力を確認するアサーション
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            countw <= 0;
        else begin
            if (S_AXI_AWVALID & S_AXI_AWREADY) begin // countw へロード
                countw <= S_AXI_AWLEN;
            end else if (S_AXI_WVALID & S_AXI_WREADY) begin // データ転送
                if (countw==0) begin // データ転送終了
                    if (~S_AXI_WLAST) begin // countw==0 でS_AXI_WLASTが立たない
                        $display("%m: at time %t Error: countw==0 でS_AXI_WLASTが立たない",$time);
                    end
                end
            end
        end
    end
    
    // Read の転送数をカウントして、 S_AXI_RLAST の出力を確認するアサーション
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            countr <= 0;
        else begin
            if (S_AXI_ARVALID & S_AXI_ARREADY) begin // countw へロード
                countr <= S_AXI_ARLEN;
            end else if (S_AXI_RVALID & S_AXI_RREADY) begin // データ転送
                if (countr==0) begin // データ転送終了
                    if (~S_AXI_RLAST) begin // countw==0 でS_AXI_WLASTが立たない
                        $display("%m: at time %t Error: countr==0 でS_AXI_WLASTが立たない",$time);
                    end
                end
            end
        end
    end
    
    // Write 動作用ステートマシン
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            wr_tran_cs <= idle_wts;
        else begin
            case (wr_tran_cs)
                idle_wts :
                    if (S_AXI_AWREADY & (S_AXI_BREADY)) // エラー
                        $display("%m: at time %t S_AXI_AWREADY がアサートされた時に、BREADY信号がアサートされた",$time);
                    else if (S_AXI_AWVALID & S_AXI_AWREADY) // アドレス転送終了
                        wr_tran_cs <= wr_data_tran;
                wr_data_tran :
                    if (S_AXI_BREADY) // エラー
                        $display("%m: at time %t Write データ転送中に、S_AXI_BREADY がアサートされた",$time);
                    else if (S_AXI_WVALID & S_AXI_WREADY & S_AXI_WLAST) // データ転送終了
                        wr_tran_cs <= wr_resp_tran;
                wr_resp_tran :
                    if (S_AXI_AWREADY | S_AXI_WVALID | S_AXI_WREADY | S_AXI_WLAST) // エラー
                        $display("%m: at time %t Write Response Channel 転送時に関連しない信号がアサートされた",$time);
                    else if (S_AXI_BVALID & S_AXI_BREADY) // Write Response Channel 転送終了
                        wr_tran_cs <= idle_wts;
            endcase
        end
    end
    
    // Read 動作用ステートマシン
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            rd_trans_cs <= idle_rts;
        else begin
            case (rd_trans_cs)
                idle_rts :
                    if (S_AXI_ARREADY & (S_AXI_RVALID | S_AXI_RREADY | S_AXI_RLAST)) // エラー
                        $display("%m: at time %t S_AXI_ARREADY がアサートされた時に、その他のVALID, READY信号がアサートされた",$time);
                    else if (S_AXI_ARVALID & S_AXI_ARREADY) // アドレス転送終了
                        rd_trans_cs <= rd_data_tran;
                rd_data_tran :
                    if (S_AXI_ARREADY) // エラー
                        $display("%m: at time %t Read データ転送中に、S_AXI_ARREADY がアサートされた",$time);
                    else if (S_AXI_RVALID & S_AXI_RREADY & S_AXI_RLAST) // データ転送終了
                        rd_trans_cs <= idle_rts;
            endcase
        end
    end
                        
endmodule    

`default_nettype wire



OVLは実行オプションが重要なので、コマンドラインでのコマンドを下に示す。

Command Line: fuse -intstyle ise -incremental -i K:/HDL/OVL/std_ovl_v2p6 -d OVL_VERILOG -d OVL_ASSERT_ON -d OVL_FINISH_OFF -lib unisims_ver -lib unimacro_ver -lib xilinxcorelib_ver -lib secureip -o H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_141/pcores/cdc_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb_isim_beh.exe -prj H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_141/pcores/cdc_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb_beh.prj work.CDC_axi_slave_tb work.glbl {-L accellera_ovl_vlog=K:\HDL\Xilinx\13.4\ISE_DS\ISE\verilog\hdp\nt\accellera_ovl_vlog}
Running: K:\HDL\Xilinx\14.1\ISE_DS\ISE\bin\nt\unwrapped\fuse.exe -intstyle ise -incremental -i K:/HDL/OVL/std_ovl_v2p6 -d OVL_VERILOG -d OVL_ASSERT_ON -d OVL_FINISH_OFF -lib unisims_ver -lib unimacro_ver -lib xilinxcorelib_ver -lib secureip -o H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_141/pcores/cdc_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb_isim_beh.exe -prj H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_141/pcores/cdc_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb_beh.prj work.CDC_axi_slave_tb work.glbl -L accellera_ovl_vlog=K:\HDL\Xilinx\13.4\ISE_DS\ISE\verilog\hdp\nt\accellera_ovl_vlog

  1. 2012年10月25日 05:59 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.3(AXI Master BFMのVerilogコード)

前の記事は、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.1(BFMシミュレーション7)”だが、全体の関連は、”AXI4 Slave IPの作製”を参照のこと。

今回はAXI Master BFMを貼っておく。AXI Master BFMと言っても、汎用的に使えるものではなく、トランザクションの並列実行に対応していない。キャラクタ・ディスプレイ・コントローラ AXI Slave IPの簡易的な単体テストとしては十分に使えると思う。

AXI Master BFMの Write の task がAXI_Master_1Seq_Write だ。このtaskは、(Write Address, Write Data), Write Response をシーケンシャルにオーバーラップせずに行う。今回の修正で、(Write Address, Write Data)はfork ~ join を使用して、オーバーラップするように変更した。AXI_Master_1Seq_Writeは、8個の引数を持つ。wait_clk_bready、wmax_wait 以外の信号はAXIバスの信号なので、説明は省く。wait_clk_bready はWriteのデータ転送が終了して、BREADYをアサートするまでのWaitクロック数を設定する。Writeデータ転送時にランダムな数のWaitが入るが、wmax_wait は、そのWait の最大値を指示する。

AXI Master BFMの Read の task が、AXI_Master_1Seq_Read で、Read Address, Read Data をシーケンシャルに行う。

下に、AXI Master BFM を示す。(2013/12/14:AWCACHE、ARCACHEを 3 に変更しました、input に DELAYを入れるように変更しました。)

// AXI4 bus Master Bus Fucntion Mode
// 2012/10/24 : 修正、S_AXI_AWREADYが1になるのを確認してからS_AXI_WVALIDを1にしていたのでは、AXIバスの非標準となる。
// よって、AXI_MASTER_WAC とAXI_MASTER_WDC をfork ~ join で並列に実行する
// 2013/12/14 : input に DELAYを入れるように変更
//

`default_nettype none

`timescale 100ps / 1ps

module AXI4_Master_BFM #(
    parameter DELAY    = 10 )
(
    input    wire    ACLK,

    output reg [0:0] S_AXI_AWID = 0,
    output reg [31:0] S_AXI_AWADDR = 0,
    output reg [7:0] S_AXI_AWLEN = 0,
    output reg [2:0] S_AXI_AWSIZE = 0,
    output reg [1:0] S_AXI_AWBURST = 0,
    output reg [1:0] S_AXI_AWLOCK = 0,
    output reg [3:0] S_AXI_AWCACHE = 3,    // Normal Non-cacheable bufferable
    output reg [2:0] S_AXI_AWPROT = 0,
    output reg [3:0] S_AXI_AWREGION = 0,
    output reg [3:0] S_AXI_AWQOS = 0,
    output reg [0:0] S_AXI_AWUSER = 0,
    output reg S_AXI_AWVALID = 0,
    output reg [0:0] S_AXI_WID = 0,
    output reg [31:0] S_AXI_WDATA = 0,
    output reg [3:0] S_AXI_WSTRB = 0,
    output reg S_AXI_WLAST = 0,
    output reg [0:0] S_AXI_WUSER = 0,
    output reg S_AXI_WVALID = 0,
    output reg S_AXI_BREADY = 0,
    output reg [0:0] S_AXI_ARID = 0,
    output reg [31:0] S_AXI_ARADDR = 0,
    output reg [7:0] S_AXI_ARLEN = 0,
    output reg [2:0] S_AXI_ARSIZE = 0,
    output reg [1:0] S_AXI_ARBURST = 0,
    output reg [1:0] S_AXI_ARLOCK = 0,
    output reg [3:0] S_AXI_ARCACHE = 2, // Normal Non-cacheable bufferable
    output reg [2:0] S_AXI_ARPROT = 0,
    output reg [3:0] S_AXI_ARREGION = 0,
    output reg [3:0] S_AXI_ARQOS = 0,
    output reg [0:0] S_AXI_ARUSER = 0,
    output reg S_AXI_ARVALID = 0,
    output reg S_AXI_RREADY = 0,

    input wire S_AXI_AWREADY,
    input wire S_AXI_WREADY,
    input wire [0:0] S_AXI_BID,
    input wire [1:0] S_AXI_BRESP,
    input wire [0:0] S_AXI_BUSER,
    input wire S_AXI_BVALID,
    input wire S_AXI_ARREADY,
    input wire [0:0] S_AXI_RID,
    input wire [31:0] S_AXI_RDATA,
    input wire [1:0] S_AXI_RRESP,
    input wire S_AXI_RLAST,
    input wire [0:0] S_AXI_RUSER,
    input wire S_AXI_RVALID
);

    reg     [7:0]    awlen_hold = 0;
    reg     [0:0]    wid_hold = 0;
    reg     axi_w_transaction_active = 0;
    reg     axi_r_transaction_active = 0;
    reg     [7:0]    arlen_hold = 0;

    reg     S_AXI_AWREADY_d;
    reg     S_AXI_WREADY_d;
    reg     [0:0] S_AXI_BID_d;
    reg     [1:0] S_AXI_BRESP_d;
    reg     [0:0] S_AXI_BUSER_d;
    reg     S_AXI_BVALID_d;
    reg     S_AXI_ARREADY_d;
    reg     [0:0] S_AXI_RID_d;
    reg     [31:0] S_AXI_RDATA_d;
    reg     [1:0] S_AXI_RRESP_d;
    reg     S_AXI_RLAST_d;
    reg     [0:0] S_AXI_RUSER_d;
    reg     S_AXI_RVALID_d;

    always @* S_AXI_AWREADY_d <= #DELAY S_AXI_AWREADY;
    always @* S_AXI_WREADY_d <= #DELAY S_AXI_WREADY;
    always @* S_AXI_BID_d <= #DELAY S_AXI_BID;
    always @* S_AXI_BRESP_d <= #DELAY S_AXI_BRESP;
    always @* S_AXI_BUSER_d <= #DELAY S_AXI_BUSER;
    always @* S_AXI_BVALID_d <= #DELAY S_AXI_BVALID;
    always @* S_AXI_ARREADY_d <= #DELAY S_AXI_ARREADY;
    always @* S_AXI_RID_d <= #DELAY S_AXI_RID;
    always @* S_AXI_RDATA_d <= #DELAY S_AXI_RDATA;
    always @* S_AXI_RRESP_d <= #DELAY S_AXI_RRESP;
    always @* S_AXI_RLAST_d <= #DELAY S_AXI_RLAST;
    always @* S_AXI_RUSER_d <= #DELAY S_AXI_RUSER;
    always @* S_AXI_RVALID_d <= #DELAY S_AXI_RVALID;

    // Write Channel
    // wait_clk_bready : 0 - bready の Wait は無し、0以外 - bready の Wait は wait_clk_bready の値の Wait が入る
    // wmax_wait : 0 - wvalid の Wait は無し、0以外 - wmax_wait を最大値とするランダムな値の Wait が wvalid に入る
    task AXI_Master_1Seq_Write;    // Write Address; Write Data, Write Response をシーケンシャルにオーバーラップせずに行う。
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        input    [31:0]    wdata;
        input    [7:0]    wait_clk_bready;
        input    [7:0]    wmax_wait;
        begin
            fork
                AXI_MASTER_WAC(awid, awaddr, awlen, awsize, awburst);
                AXI_MASTER_WDC(wdata, wmax_wait);
            join
            AXI_MASTER_WRC(wait_clk_bready);
        end
    endtask

    // Write Address Channel
    task AXI_MASTER_WAC;
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        begin
            S_AXI_AWID        = awid;
            S_AXI_AWADDR    = awaddr;
            S_AXI_AWLEN        = awlen;
            S_AXI_AWSIZE    = awsize;
            S_AXI_AWBURST    = awburst;
            S_AXI_AWVALID    = 1'b1;

            if (axi_w_transaction_active == 1'b0) begin // AXI Write トランザクションが開始されている場合は戻る
                axi_w_transaction_active = 1'b1; // AXIトランザクション開始

                awlen_hold        = awlen; // Write Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ

                while (~S_AXI_AWREADY_d) begin    // S_AXI_AWREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end

                #DELAY;
                S_AXI_AWID         = 0;
                S_AXI_AWADDR    = 0;
                S_AXI_AWLEN     = 0;
                S_AXI_AWSIZE     = 0;
                S_AXI_AWBURST     = 0;
                S_AXI_AWVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
        end
    endtask

    // Write Data Channel
    // wmax_wait : 0 - wvalid の Wait は無し、0以外 - wmax_wait を最大値とするランダムな値の Wait が wvalid に入る
    task AXI_MASTER_WDC;    // WDATA は+1する
    // とりあえず、WSTRBはオール1にする
        input    [31:0]    wdata;
        input    [7:0]    wmax_wait;    // Write時の最大wait数
        integer    i, j, val;
        begin
            i = 0; j = 0;
            S_AXI_WSTRB = 4'b1111;

            while (~S_AXI_AWVALID) begin    // S_AXI_AWVALID が1になるまで待つ
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end

            while (i<=awlen_hold) begin
                if (wmax_wait == 0) // wmax_wait が0の時は$random を実行しない
                    val = 0;
                else
                    val = $unsigned($random) % (wmax_wait+1);

                if (val == 0) begin // waitなし
                    S_AXI_WVALID = 1'b1;
                end else begin // waitあり
                    S_AXI_WVALID = 1'b0;
                    for (j=0; j<val; j=j+1) begin
                        @(posedge ACLK);    // 次のクロックへ
                        #DELAY;
                    end
                    S_AXI_WVALID = 1'b1; // wait終了
                end

                if (i == awlen_hold)
                    S_AXI_WLAST = 1'b1;
                else
                    S_AXI_WLAST = 1'b0;
                S_AXI_WDATA = wdata;
                wdata = wdata + 1;

                @(posedge ACLK);    // 次のクロックへ

                while (~S_AXI_WREADY_d) begin    // S_AXI_WREADY が0の時は1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                #DELAY;

                i = i + 1;
            end
            S_AXI_WVALID = 1'b0;
            S_AXI_WLAST = 1'b0;
            S_AXI_WSTRB = 4'b0000;
        end
    endtask

    // Write Response Channel
    // wait_clk_bready : 0 - bready の Wait は無し、0以外 - bready の Wait は wait_clk_bready の値の Wait が入る
    task AXI_MASTER_WRC;    // wait_clk_bready
        input    [7:0]    wait_clk_bready;
        integer    i;
        begin
            for (i=0; i<wait_clk_bready; i=i+1) begin
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end

            S_AXI_BREADY = 1'b1;


            @(posedge ACLK);    // 次のクロックへ

            while (~S_AXI_BVALID_d) begin // S_AXI_BVALID が1になるまでWait
                #DELAY;
                @(posedge ACLK);    // 次のクロックへ
            end
            #DELAY;

            S_AXI_BREADY = 1'b0;

            axi_w_transaction_active = 1'b0; // AXIトランザクション終了
            @(posedge ACLK);
            #DELAY;
        end
    endtask

    // Read Channel
    task AXI_Master_1Seq_Read; // Read Address, Read Data をシーケンシャルに行う。
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        input    [7:0]    rmax_wait;    // Read時の最大wait数
        begin
            AXI_MASTER_RAC(arid, araddr, arlen, arsize, arburst);
            AXI_MASTER_RDC(rmax_wait);
        end
    endtask

    // Read Address Channel
    task AXI_MASTER_RAC;
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        begin
            S_AXI_ARID         = arid;
            S_AXI_ARADDR    = araddr;
            S_AXI_ARLEN        = arlen;
            S_AXI_ARSIZE    = arsize;
            S_AXI_ARBURST    = arburst;
            S_AXI_ARVALID     = 1'b1;

            if (axi_r_transaction_active == 1'b0) begin // AXI Read トランザクションが開始されている場合は戻る
                arlen_hold    =arlen; // Read Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ

                while (~S_AXI_ARREADY_d) begin    // S_AXI_ARREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end

                #DELAY;
                S_AXI_ARID         = 0;
                S_AXI_ARADDR    = 0;
                S_AXI_ARLEN     = 0;
                S_AXI_ARSIZE     = 0;
                S_AXI_ARBURST     = 0;
                S_AXI_ARVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
                axi_r_transaction_active = 1'b1; // AXIトランザクション開始
            end
        end
    endtask

    // Read Data Channel
    task AXI_MASTER_RDC; // S_AXI_RLAST がアサートされるまでS_AXI_RREADY をアサートする
        input    [7:0]    rmax_wait;    // Read時の最大wait数
        integer i, val;
        begin
            while (~(S_AXI_RLAST_d & S_AXI_RVALID_d & S_AXI_RREADY)) begin // S_AXI_RLAST & S_AXI_RVALID & S_AXI_RREADY で終了
                if (rmax_wait == 0) begin // rmax_wait が0の時は$random を実行しない
                    val = 0;
                    S_AXI_RREADY = 1'b1;
                end else begin
                    val = $unsigned($random) % (rmax_wait+1);
                    if (val == 0)
                        S_AXI_RREADY = 1'b1;
                    else
                        S_AXI_RREADY = 1'b0;
                end
                #DELAY;

                for (i=0; i<val; i=i+1) begin // ランダム値でWait、val=0の時はスキップ
                    @(posedge ACLK);    // 次のクロックへ
                    #DELAY;
                end

                S_AXI_RREADY = 1'b1;
                @(posedge ACLK);    // 次のクロックへ
                while (~S_AXI_RVALID_d) begin // S_AXI_RVALID が1になるまでWait
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                #DELAY;
            end
            #DELAY;

            S_AXI_RREADY = 1'b0;
            axi_r_transaction_active = 1'b0; // AXIトランザクション終了
            @(posedge ACLK);
            #DELAY;
        end
    endtask

endmodule

`default_nettype wire


  1. 2012年10月25日 05:50 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.2(テストベンチのVerilogコード)

前の記事は、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.1(BFMシミュレーション7)”だが、全体の関連は、”AXI4 Slave IPの作製”を参照のこと。

今回はテストベンチのVerilogコードを下に貼っておく。(2013/12/14:修正、DELAYをAXI4 Master BFMへ移動しました)

// CDC_axi_slave_tb.v

`default_nettype none

`timescale 100ps / 1ps

module CDC_axi_slave_tb;

    parameter DELAY    = 10;
    parameter NUMBER_OF_TEST = 100;    // テストする数

    parameter    ASIZE_BT_4    = 3'd2;    // 32 bit width
    parameter    ASIZE_BT_2 = 3'd1;    // 16 bit width
    parameter    ASIZE_BT_1 = 3'd0;    // 8 bit width

    parameter    ABURST_FIXED    = 2'd0;
    parameter    ABURST_INCR    = 2'd1;
    parameter    ABURST_WRAP    = 2'd2;

    parameter    BRESP_OKAY        = 2'b00;
    parameter    BRESP_EXOKAY    = 2'b01;
    parameter    BRESP_SLVERR    = 2'b10;
    parameter    BRESP_DECERR    = 2'b11;

    // Inputs
    wire ACLK;
    wire ARESETN;
    wire [0:0] S_AXI_AWID;
    wire [31:0] S_AXI_AWADDR;
    wire [7:0] S_AXI_AWLEN;
    wire [2:0] S_AXI_AWSIZE;
    wire [1:0] S_AXI_AWBURST;
    wire [1:0] S_AXI_AWLOCK;
    wire [3:0] S_AXI_AWCACHE;
    wire [2:0] S_AXI_AWPROT;
    wire [3:0] S_AXI_AWREGION;
    wire [3:0] S_AXI_AWQOS;
    wire [0:0] S_AXI_AWUSER;
    wire S_AXI_AWVALID;
    wire [0:0] S_AXI_WID;
    wire [31:0] S_AXI_WDATA;
    wire [3:0] S_AXI_WSTRB;
    wire S_AXI_WLAST;
    wire [0:0] S_AXI_WUSER;
    wire S_AXI_WVALID;
    wire S_AXI_BREADY;
    wire [0:0] S_AXI_ARID;
    wire [31:0] S_AXI_ARADDR;
    wire [7:0] S_AXI_ARLEN;
    wire [2:0] S_AXI_ARSIZE;
    wire [1:0] S_AXI_ARBURST;
    wire [1:0] S_AXI_ARLOCK;
    wire [3:0] S_AXI_ARCACHE;
    wire [2:0] S_AXI_ARPROT;
    wire [3:0] S_AXI_ARREGION;
    wire [3:0] S_AXI_ARQOS;
    wire [0:0] S_AXI_ARUSER;
    wire S_AXI_ARVALID;
    wire S_AXI_RREADY;
    wire pixclk;

    // Outputs
    wire S_AXI_AWREADY;
    wire S_AXI_WREADY;
    wire [0:0] S_AXI_BID;
    wire [1:0] S_AXI_BRESP;
    wire [0:0] S_AXI_BUSER;
    wire S_AXI_BVALID;
    wire S_AXI_ARREADY;
    wire [0:0] S_AXI_RID;
    wire [31:0] S_AXI_RDATA;
    wire [1:0] S_AXI_RRESP;
    wire S_AXI_RLAST;
    wire [0:0] S_AXI_RUSER;
    wire S_AXI_RVALID;
    wire TMDS_tx_clk_p;
    wire TMDS_tx_clk_n;
    wire TMDS_tx_2_G_p;
    wire TMDS_tx_2_G_n;
    wire TMDS_tx_1_R_p;
    wire TMDS_tx_1_R_n;
    wire TMDS_tx_0_B_p;
    wire TMDS_tx_0_B_n;

    // Instantiate the Unit Under Test (UUT)
    CDC_axi_slave uut (
        .ACLK(ACLK),
        .ARESETN(ARESETN),
        .S_AXI_AWID(S_AXI_AWID),
        .S_AXI_AWADDR(S_AXI_AWADDR),
        .S_AXI_AWLEN(S_AXI_AWLEN),
        .S_AXI_AWSIZE(S_AXI_AWSIZE),
        .S_AXI_AWBURST(S_AXI_AWBURST),
        .S_AXI_AWLOCK(S_AXI_AWLOCK),
        .S_AXI_AWCACHE(S_AXI_AWCACHE),
        .S_AXI_AWPROT(S_AXI_AWPROT),
        .S_AXI_AWREGION(S_AXI_AWREGION),
        .S_AXI_AWQOS(S_AXI_AWQOS),
        .S_AXI_AWUSER(S_AXI_AWUSER),
        .S_AXI_AWVALID(S_AXI_AWVALID),
        .S_AXI_AWREADY(S_AXI_AWREADY),
        .S_AXI_WID(S_AXI_WID),
        .S_AXI_WDATA(S_AXI_WDATA),
        .S_AXI_WSTRB(S_AXI_WSTRB),
        .S_AXI_WLAST(S_AXI_WLAST),
        .S_AXI_WUSER(S_AXI_WUSER),
        .S_AXI_WVALID(S_AXI_WVALID),
        .S_AXI_WREADY(S_AXI_WREADY),
        .S_AXI_BID(S_AXI_BID),
        .S_AXI_BRESP(S_AXI_BRESP),
        .S_AXI_BUSER(S_AXI_BUSER),
        .S_AXI_BVALID(S_AXI_BVALID),
        .S_AXI_BREADY(S_AXI_BREADY),
        .S_AXI_ARID(S_AXI_ARID),
        .S_AXI_ARADDR(S_AXI_ARADDR),
        .S_AXI_ARLEN(S_AXI_ARLEN),
        .S_AXI_ARSIZE(S_AXI_ARSIZE),
        .S_AXI_ARBURST(S_AXI_ARBURST),
        .S_AXI_ARLOCK(S_AXI_ARLOCK),
        .S_AXI_ARCACHE(S_AXI_ARCACHE),
        .S_AXI_ARPROT(S_AXI_ARPROT),
        .S_AXI_ARREGION(S_AXI_ARREGION),
        .S_AXI_ARQOS(S_AXI_ARQOS),
        .S_AXI_ARUSER(S_AXI_ARUSER),
        .S_AXI_ARVALID(S_AXI_ARVALID),
        .S_AXI_ARREADY(S_AXI_ARREADY),
        .S_AXI_RID(S_AXI_RID),
        .S_AXI_RDATA(S_AXI_RDATA),
        .S_AXI_RRESP(S_AXI_RRESP),
        .S_AXI_RLAST(S_AXI_RLAST),
        .S_AXI_RUSER(S_AXI_RUSER),
        .S_AXI_RVALID(S_AXI_RVALID),
        .S_AXI_RREADY(S_AXI_RREADY),
        .pixclk(pixclk),
        .TMDS_tx_clk_p(TMDS_tx_clk_p),
        .TMDS_tx_clk_n(TMDS_tx_clk_n),
        .TMDS_tx_2_G_p(TMDS_tx_2_G_p),
        .TMDS_tx_2_G_n(TMDS_tx_2_G_n),
        .TMDS_tx_1_R_p(TMDS_tx_1_R_p),
        .TMDS_tx_1_R_n(TMDS_tx_1_R_n),
        .TMDS_tx_0_B_p(TMDS_tx_0_B_p),
        .TMDS_tx_0_B_n(TMDS_tx_0_B_n)
    );

    // clk_gen のインスタンス
    clk_gen #(
        .CLK_PERIOD(100),    // 10nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ACLKi (
        .clk_out(ACLK)
    );

    clk_gen #(
        .CLK_PERIOD(250),    // 25nsec, 40MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) PIXCLKi (
        .clk_out(pixclk)
    );

    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b0),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ARESETN)
    );

    // AXI4_BFM のインスタンス
    AXI4_Master_BFM #(.DELAY(DELAY)) MBFMi(
        .ACLK(ACLK),
        .S_AXI_AWID(S_AXI_AWID),
        .S_AXI_AWADDR(S_AXI_AWADDR),
        .S_AXI_AWLEN(S_AXI_AWLEN),
        .S_AXI_AWSIZE(S_AXI_AWSIZE),
        .S_AXI_AWBURST(S_AXI_AWBURST),
        .S_AXI_AWLOCK(S_AXI_AWLOCK),
        .S_AXI_AWCACHE(S_AXI_AWCACHE),
        .S_AXI_AWPROT(S_AXI_AWPROT),
        .S_AXI_AWREGION(S_AXI_AWREGION),
        .S_AXI_AWQOS(S_AXI_AWQOS),
        .S_AXI_AWUSER(S_AXI_AWUSER),
        .S_AXI_AWVALID(S_AXI_AWVALID),
        .S_AXI_AWREADY(S_AXI_AWREADY),
        .S_AXI_WID(S_AXI_WID),
        .S_AXI_WDATA(S_AXI_WDATA),
        .S_AXI_WSTRB(S_AXI_WSTRB),
        .S_AXI_WLAST(S_AXI_WLAST),
        .S_AXI_WUSER(S_AXI_WUSER),
        .S_AXI_WVALID(S_AXI_WVALID),
        .S_AXI_WREADY(S_AXI_WREADY),
        .S_AXI_BID(S_AXI_BID),
        .S_AXI_BRESP(S_AXI_BRESP),
        .S_AXI_BUSER(S_AXI_BUSER),
        .S_AXI_BVALID(S_AXI_BVALID),
        .S_AXI_BREADY(S_AXI_BREADY),
        .S_AXI_ARID(S_AXI_ARID),
        .S_AXI_ARADDR(S_AXI_ARADDR),
        .S_AXI_ARLEN(S_AXI_ARLEN),
        .S_AXI_ARSIZE(S_AXI_ARSIZE),
        .S_AXI_ARBURST(S_AXI_ARBURST),
        .S_AXI_ARLOCK(S_AXI_ARLOCK),
        .S_AXI_ARCACHE(S_AXI_ARCACHE),
        .S_AXI_ARPROT(S_AXI_ARPROT),
        .S_AXI_ARREGION(S_AXI_ARREGION),
        .S_AXI_ARQOS(S_AXI_ARQOS),
        .S_AXI_ARUSER(S_AXI_ARUSER),
        .S_AXI_ARVALID(S_AXI_ARVALID),
        .S_AXI_ARREADY(S_AXI_ARREADY),
        .S_AXI_RID(S_AXI_RID),
        .S_AXI_RDATA(S_AXI_RDATA),
        .S_AXI_RRESP(S_AXI_RRESP),
        .S_AXI_RLAST(S_AXI_RLAST),
        .S_AXI_RUSER(S_AXI_RUSER),
        .S_AXI_RVALID(S_AXI_RVALID),
        .S_AXI_RREADY(S_AXI_RREADY)
    );

    // OVL Checker
    OVL_Checker OVLi (
        .ACLK(ACLK),
        .ARESETN(ARESETN),
        .S_AXI_AWID(S_AXI_AWID),
        .S_AXI_AWADDR(S_AXI_AWADDR),
        .S_AXI_AWLEN(S_AXI_AWLEN),
        .S_AXI_AWSIZE(S_AXI_AWSIZE),
        .S_AXI_AWBURST(S_AXI_AWBURST),
        .S_AXI_AWLOCK(S_AXI_AWLOCK),
        .S_AXI_AWCACHE(S_AXI_AWCACHE),
        .S_AXI_AWPROT(S_AXI_AWPROT),
        .S_AXI_AWREGION(S_AXI_AWREGION),
        .S_AXI_AWQOS(S_AXI_AWQOS),
        .S_AXI_AWUSER(S_AXI_AWUSER),
        .S_AXI_AWVALID(S_AXI_AWVALID),
        .S_AXI_AWREADY(S_AXI_AWREADY),
        .S_AXI_WID(S_AXI_WID),
        .S_AXI_WDATA(S_AXI_WDATA),
        .S_AXI_WSTRB(S_AXI_WSTRB),
        .S_AXI_WLAST(S_AXI_WLAST),
        .S_AXI_WUSER(S_AXI_WUSER),
        .S_AXI_WVALID(S_AXI_WVALID),
        .S_AXI_WREADY(S_AXI_WREADY),
        .S_AXI_BID(S_AXI_BID),
        .S_AXI_BRESP(S_AXI_BRESP),
        .S_AXI_BUSER(S_AXI_BUSER),
        .S_AXI_BVALID(S_AXI_BVALID),
        .S_AXI_BREADY(S_AXI_BREADY),
        .S_AXI_ARID(S_AXI_ARID),
        .S_AXI_ARADDR(S_AXI_ARADDR),
        .S_AXI_ARLEN(S_AXI_ARLEN),
        .S_AXI_ARSIZE(S_AXI_ARSIZE),
        .S_AXI_ARBURST(S_AXI_ARBURST),
        .S_AXI_ARLOCK(S_AXI_ARLOCK),
        .S_AXI_ARCACHE(S_AXI_ARCACHE),
        .S_AXI_ARPROT(S_AXI_ARPROT),
        .S_AXI_ARREGION(S_AXI_ARREGION),
        .S_AXI_ARQOS(S_AXI_ARQOS),
        .S_AXI_ARUSER(S_AXI_ARUSER),
        .S_AXI_ARVALID(S_AXI_ARVALID),
        .S_AXI_ARREADY(S_AXI_ARREADY),
        .S_AXI_RID(S_AXI_RID),
        .S_AXI_RDATA(S_AXI_RDATA),
        .S_AXI_RRESP(S_AXI_RRESP),
        .S_AXI_RLAST(S_AXI_RLAST),
        .S_AXI_RUSER(S_AXI_RUSER),
        .S_AXI_RVALID(S_AXI_RVALID),
        .S_AXI_RREADY(S_AXI_RREADY)
    );


    // test

    // Write Channel

    // wr_data生成、+1する
    reg        [15:0]    wcount;
    wire    [31:0]    wdata;

    always @(posedge ACLK) begin
        if (~ARESETN)
            wcount <= 0;
        else begin
            if (S_AXI_WVALID & S_AXI_WREADY)
                wcount <= wcount + 16'd1;
        end
    end
    assign wdata = {{16{1'b0}}, wcount};


    reg    WriteTaskBusy = 1'b0;
    integer wt_cnt;
    initial begin
        // Wait 100 ns for global reset to finish
        #1000;
        #5000;    // 500nsec Wait, PLL Locked

        @(posedge ACLK);    // 次のクロックへ
        #DELAY;

        for (wt_cnt=0; wt_cnt<NUMBER_OF_TEST; wt_cnt=wt_cnt+1) begin
            WriteTaskBusy = 1'b1;
            MBFMi.AXI_Master_1Seq_Write(0, 32'h100, 8'd7, ASIZE_BT_4, ABURST_INCR, wdata, 0, 2);
            MBFMi.AXI_Master_1Seq_Write(0, 32'h200, 8'd3, ASIZE_BT_4, ABURST_INCR, wdata, 1, 3);
            MBFMi.AXI_Master_1Seq_Write(0, 32'h300, 8'd1, ASIZE_BT_4, ABURST_INCR, wdata, 1, 4);
            MBFMi.AXI_Master_1Seq_Write(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR, wdata, 1, 5);

            WriteTaskBusy = 1'b0;
            while (ReadTestBusy) begin // Read 終了待ち
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
        end
    end

    // Read Channel
    reg    ReadTestBusy = 1'b0;
    integer rd_cnt;
    initial begin
        // Wait 100 ns for global reset to finish
        #1000;
        #5000;    // 500nsec Wait, PLL Locked

        for (rd_cnt=0; rd_cnt<NUMBER_OF_TEST; rd_cnt=rd_cnt+1) begin

            ReadTestBusy = 1'b1;
            #1000;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;

            MBFMi.AXI_Master_1Seq_Read(0, 32'h100, 8'd7, ASIZE_BT_4, ABURST_INCR, 2);
            MBFMi.AXI_Master_1Seq_Read(0, 32'h200, 8'd3, ASIZE_BT_4, ABURST_INCR, 3);
            MBFMi.AXI_Master_1Seq_Read(0, 32'h300, 8'd1, ASIZE_BT_4, ABURST_INCR, 4);
            MBFMi.AXI_Master_1Seq_Read(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR, 5);

            ReadTestBusy = 1'b0;
            while (WriteTaskBusy) begin // Write の終了待ち
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
        end
    end

endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire

  1. 2012年10月25日 05:33 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.1(BFMシミュレーション7)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8(BFMシミュレーション6)”の続き。

書き込みトランザクションの時に、MasterがAWREADYを待って、WVALIDをアサートしていけないというルールがあったのを忘れていたので、AXI Slave IP用のBFMが間違っていたことがわかったので修正する。ちなみにWriteだけ変更している。Readは変更していない。

”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする”シリーズの”BFMシミュレーション”~”BFMシミュレーション6”まで間違っているが、全部修正するのが大変なので、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8(BFMシミュレーション6)”のみ状態でのみ書きなおす。

テストベンチ (CDC_axi_slave_tb.v) を変更した。任意の回数ループするトランザクションは、8バースト、4バースト、2バースト、単発転送とする。それぞれデータ転送の最大のWait数を変えることにした。Readの値をovl_increment でチェックするため、Write トランザクションとRead トランザクションは、最後のトランザクションで同期する。Write トランザクションよりRead トランザクションの方が、遅延して開始する。

Writeの一部分を下に示す。
AXI_Slave_BFM_1_121025.png

Readの一部を下に示す。
AXI_Slave_BFM_2_121025.png

Writeの1トランザクションの拡大を下に示す。
AXI_Slave_BFM_3_121025.png

Readの1トランザクションの拡大を下に示す。
AXI_Slave_BFM_4_121025.png

テストベンチAXI Master BFMOVLチェッカはそれぞれ1つずつブログ記事を作って紹介する。
  1. 2012年10月25日 05:28 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

ISE14.1, 14.2 で”ERROR:MapLib:979 - LUT5 symbol”が出る

AXIバスに非標準のHDLを書いてしまったので、キャラクタ・ディスプレイ・コントローラAXI4スレーブIPを駆動するAXI4マスタIPを書き換えたりしている。単体シミュレーションは通ったのだが、ISE14.1 (Project Navigator) でインプリメントするところで、BitGenがエラーになってしまう。エラーは、”ERROR:MapLib:979 - LUT5 symbol”とそれに類似しているエラーがたくさん出る。ISE14.1だったので、ISE14.2でやってみても同様にエラーだった。
bitgen_error_1_121023.png

アンサーを見ると、”11.3 Virtex-5 FPGA MAP - Global Optimization オプションの組み合わせに関連したロジック破損”があってMAPのプロパティで出てくる”-global_opt speed”などのオプションでエラーが出ているとのことだった。見たところオプションは入っていなかった。
bitgen_error_2_121023.png

MAPオプションを変えてやってみたが同様だった。
bitgen_error_3_121023.png

考えてみたら、MAPでエラーが出るのではなく、BitGenでエラーが出る。なぜ、BitGenでMAPエラー?ということだが、エラーチェックのためか?BitGenでは論理合成からひと通りやっているようだ。なぜMAPでひかからなくてBitGenで?という話もある。BitGenのDRCエラーチェックを外してみたが、同様にエラーだった。
bitgen_error_6_121023.png

いよいよ、エラーを解消する手段が無い。。。実は以前、XPSプロジェクト単体でやっていた時にMAPでエラーが出ていたが、ISEのプロジェクトの下にXPSプロジェクトをおいたら大丈夫になったことがあった

次にPlanAhead14.1 でやってみることにした。
PlanAheadではエラーは無かった。
bitgen_error_7_121023.png
  1. 2012年10月23日 05:55 |
  2. Xilinx ISEについて
  3. | トラックバック:0
  4. | コメント:0

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する15(ChipScope での評価)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する14(ソースの公開)”の続き。

(2012/10/21: 注: この記事は、AXI4バスのHDLコードにミスがあったので、全面的に書き直しました。)

前回はソースを公開したが、今回は、DDR3 SDRAMのフレーム・バッファにキャラクタを書き込むchar_write_axi_master IPのChipScope による評価を行う。
まずは、DDR3 SDRAMのフレーム・バッファにキャラクタを書き込む全体のタイミングチャートのキャプチャ図を下に示す。
ZedBoad_BitMap_DispCnt_96_121020.png

Bus/Signal の黄色の部分がAXIバスの信号だ。その下の信号がIPの内部信号だ。キャラクタをキャラクタROMから読んで、AXIバスでDDR3 SDRAMにWriteし終わるまでに掛かった時間は306クロックだ。クロックの周波数は100MHz、クロック周期は10nsec だから、306クロック x 10nsec = 3.06usec となる。結構遅いかも?知れない。
char_gen_8raster.v がキャラクタROMからビットマップデータを読んでFIFOのピクセルデータを入力するまでがピンクのカーソルまでだ。ここで99クロックかかっている。990nsec、約1usec かかっている事になる。AXIバスでの転送にの残りの約2usec かかっていることになる。

次にAXIバスのトランザクションを詳しく見てみよう。
Master(自作IP)がawvalid をアサートしてから、Slave (ARM, PS) がM_AXI_AWREADYをアサートするのは2クロック後だ。
ZedBoad_BitMap_DispCnt_97_121020.png

Master がwvalid をアサートしてから、Slave がM_AXI_WREADY をアサートし始めるのは5クロック後となる。
ZedBoad_BitMap_DispCnt_98_121020.png

64ビット幅データがWaitすることなく4バーストでデータ転送を終了している。そのデータ転送の最後ににwlast がアサートされている。
最後のデータ転送からSlave がM_AXI_BVALIDをアサートするまでに14クロックかかっている。これでAXIバスのWriteのトランザクションは終了する。
ZedBoad_BitMap_DispCnt_99_121020.png

この14クロックは長い気がする。Writeは通常はFIFOなどのキューにためて完了することが多いと思うのだが、それだけではなくDDR3 SDRAMに書いてから完了しているのだろうか?次のWriteが、このIPでは初められなくてスループットが落ちている。1回のWriteトランザクションに23クロック、230nsec かかっている。案外High Performance ポートは遅いというか、完了するのが長いようだ。
最も、AXIバスはトランザクションを重ね合わせることができるので、Master の方でトランザクションを重ね合わせれば良いはずだ。自分がサボっていると証明されてしまったが、HPポートのWriteの完了が遅いことは考慮しておく必要があると思う。
  1. 2012年10月20日 05:49 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する14(ソースの公開)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する13(char_wirte_axi_master IPを追加4)”の続き。

ビットマップ・ディスプレイ・コントローラのAXI4 IPのソースは、”ビットマップ・ディスプレイ・コントローラの作製19(HDLソースの公開)”で公開してあって、そこからほとんど修正していない。そこで、ビットマップ・ディスプレイ・コントローラは省略とする。
DDR3 SDRAMのフレーム・バッファにキャラクタを書き込むchar_write_axi_master IPのソースを公開する。
先ずは、char_write_axi_master.vhd から下に示す。char_write_axi_master.vhd は、char_gen_8raster.v が作ったキャラクタのピクセルデータが揃ったら、1ラスタずつ、64ビット幅4バーストでキャラクタのピクセルデータをDDR3 SDRAMに転送する。それを8ラスタ行う。

-------------------------------------------------------------------------------
--
-- AXI Master
--
-- VHDL-Standard:   VHDL'93
----------------------------------------------------------------------------
--
-- Structure:
--   char_write_axi_master
--
----------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;

package m_seq_pack is
    function M_SEQ24_F(mseq24in : std_logic_vector
        )return std_logic_vector;
end package m_seq_pack;
package body m_seq_pack is
    function M_SEQ24_F(mseq24in : std_logic_vector
        )return std_logic_vector is
            variable mseq24 : std_logic_vector(23 downto 0);
            variable xor_result : std_logic;
    begin
        xor_result := mseq24in(23) xor mseq24in(3) xor mseq24in(2) xor mseq24in(0);
        mseq24 := mseq24in(22 downto 0) & xor_result;
        return mseq24;
    end M_SEQ24_F;
end m_seq_pack;


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

library work;
use work.m_seq_pack.all;

--library unisim;
--use unisim.vcomponents.all;

entity char_write_axi_master is
  generic(
    C_M_AXI_SUPPORTS_THREADS            : integer := 0;
    C_M_AXI_THREAD_ID_WIDTH             : integer := 1;
    C_M_AXI_ADDR_WIDTH                     : integer := 32;
    C_M_AXI_DATA_WIDTH                     : integer := 64;
    C_INTERCONNECT_M_AXI_WRITE_ISSUING    : integer := 8;
    C_INTERCONNECT_M_AXI_READ_ISSUING    : integer := 8;
    C_M_AXI_SUPPORTS_READ                : integer := 0;
    C_M_AXI_SUPPORTS_WRITE                : integer := 1;
    C_M_AXI_SUPPORTS_USER_SIGNALS        : integer := 0;
    C_M_AXI_AWUSER_WIDTH                : integer := 1;
    C_M_AXI_ARUSER_WIDTH                : integer := 1;
    C_M_AXI_WUSER_WIDTH                 : integer := 1;
    C_M_AXI_RUSER_WIDTH                 : integer := 1;
    C_M_AXI_BUSER_WIDTH                 : integer := 1;
    C_M_AXI_SUPPORTS_NARROW_BURST        : integer := 0;
    C_M_AXI_TARGET                        : std_logic_vector(31 downto 0) := x"00000000";
    C_M_AXI_BURST_LEN                    : integer := 16;
    C_OFFSET_WIDTH                        : integer := 9;
    
    DISP_INTERVAL_DIV                     : integer := 20000000 -- 0.2sec 間隔でキャラクタを書くための分周値
  );
  port(
    -- System Signals
    ACLK    : in std_logic;
    ARESETN : in std_logic;

    -- Master Interface Write Address
    M_AXI_AWID    : out std_logic_vector(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_AWADDR  : out std_logic_vector(C_M_AXI_ADDR_WIDTH-1 downto 0);
    M_AXI_AWLEN   : out std_logic_vector(8-1 downto 0);
    M_AXI_AWSIZE  : out std_logic_vector(3-1 downto 0);
    M_AXI_AWBURST : out std_logic_vector(2-1 downto 0);
    M_AXI_AWLOCK  : out std_logic;
    M_AXI_AWCACHE : out std_logic_vector(4-1 downto 0);
    M_AXI_AWPROT  : out std_logic_vector(3-1 downto 0);
    -- AXI3    M_AXI_AWREGION:out std_logic_vector(4-1 downto 0);
    M_AXI_AWQOS   : out std_logic_vector(4-1 downto 0);
    M_AXI_AWUSER  : out std_logic_vector(C_M_AXI_AWUSER_WIDTH-1 downto 0);
    M_AXI_AWVALID : out std_logic;
    M_AXI_AWREADY : in  std_logic;

    -- Master Interface Write Data
    -- AXI3   M_AXI_WID(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_WDATA  : out std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
    M_AXI_WSTRB  : out std_logic_vector(C_M_AXI_DATA_WIDTH/8-1 downto 0);
    M_AXI_WLAST  : out std_logic;
    M_AXI_WUSER  : out std_logic_vector(C_M_AXI_WUSER_WIDTH-1 downto 0);
    M_AXI_WVALID : out std_logic;
    M_AXI_WREADY : in  std_logic;

    -- Master Interface Write Response
    M_AXI_BID    : in  std_logic_vector(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_BRESP  : in  std_logic_vector(2-1 downto 0);
    M_AXI_BUSER  : in  std_logic_vector(C_M_AXI_BUSER_WIDTH-1 downto 0);
    M_AXI_BVALID : in  std_logic;
    M_AXI_BREADY : out std_logic;

    -- Master Interface Read Address
    M_AXI_ARID    : out std_logic_vector(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_ARADDR  : out std_logic_vector(C_M_AXI_ADDR_WIDTH-1 downto 0);
    M_AXI_ARLEN   : out std_logic_vector(8-1 downto 0);
    M_AXI_ARSIZE  : out std_logic_vector(3-1 downto 0);
    M_AXI_ARBURST : out std_logic_vector(2-1 downto 0);
    M_AXI_ARLOCK  : out std_logic_vector(2-1 downto 0);
    M_AXI_ARCACHE : out std_logic_vector(4-1 downto 0);
    M_AXI_ARPROT  : out std_logic_vector(3-1 downto 0);
    -- AXI3   M_AXI_ARREGION:out std_logic_vector(4-1 downto 0);
    M_AXI_ARQOS   : out std_logic_vector(4-1 downto 0);
    M_AXI_ARUSER  : out std_logic_vector(C_M_AXI_ARUSER_WIDTH-1 downto 0);
    M_AXI_ARVALID : out std_logic;
    M_AXI_ARREADY : in  std_logic;

    -- Master Interface Read Data 
    M_AXI_RID    : in  std_logic_vector(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_RDATA  : in  std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
    M_AXI_RRESP  : in  std_logic_vector(2-1 downto 0);
    M_AXI_RLAST  : in  std_logic;
    M_AXI_RUSER  : in  std_logic_vector(C_M_AXI_RUSER_WIDTH-1 downto 0);
    M_AXI_RVALID : in  std_logic;
    M_AXI_RREADY : out std_logic;

    init_done    : in std_logic;
    
    -- Example Design only
    wr_error : out std_logic;
    rd_error : out std_logic
);

end char_write_axi_master;

-------------------------------------------------------------------------------
-- Architecture
-------------------------------------------------------------------------------
architecture implementation of char_write_axi_master is
constant    RESP_OKAY        : std_logic_vector := "00";
constant    RESP_EXOKAY        : std_logic_vector := "01";
constant    RESP_SLVERR        : std_logic_vector := "10";
constant    RESP_DECERR        : std_logic_vector := "11";

constant    DDR3_START_ADDR    : std_logic_vector := x"10000000";

constant    ROW_ALL_PIXELS : integer := 640;
constant    COULMN_ALL_PIXELS : integer := 480;

constant    SVGA_ADDR_LIMIT : integer := ROW_ALL_PIXELS*COULMN_ALL_PIXELS*4; -- SVGAのアドレスリミット(横ピクセル数 * 縦ピクセル数 * 1ピクセルを表すバイト数)

signal reset_1d, reset_2d, reset : std_logic;
type wr_main_transaction_state is (idle_wr_main, char_bitmap_trans_start, char_bitmap_trans_wait, write_state, wait_state);
signal wr_main_cs, wr_main_1d : wr_main_transaction_state;
type write_transaction_state is (idle_wr, awvalid_assert, data_write_hold, bready_assert, wr_tran_end);
signal wrt_cs : write_transaction_state;
type write_wvalid_state is (idle_wvalid, wvalid_assert, wvalid_hold);
signal wrt_wv_cs : write_wvalid_state;

signal awvalid, wvalid, bready : std_logic;
signal wr_addr, wr_addr_old, wr_addr_sc : std_logic_vector(31 downto 0);
signal awlen, write_count : std_logic_vector(7 downto 0);
signal wlast : std_logic;
signal disp_count : integer;
signal disp_ena : std_logic;
signal wr_tran_count : std_logic_vector(2 downto 0);
signal char_code         : std_logic_vector(6 downto 0);
signal color_code         : std_logic_vector(31 downto 0);
signal char_code_ena     : std_logic;
signal raster_data         : std_logic_vector(63 downto 0);
signal raster_valid        : std_logic;
signal raster_ready        : std_logic;
signal fifo_empty        : std_logic;

component char_gen_8raster
    port(
        clk             : in std_logic;
        reset            : in std_logic;
        char_code        : in std_logic_vector(6 downto 0);
        color_code        : in std_logic_vector(31 downto 0);
        char_code_ena    : in std_logic; -- アスキーキャラクタコードのイネーブル、1パルス
        raster_valid    : out std_logic; -- ラスタデータのRead時のReady信号
        raster_data        : out std_logic_vector(63 downto 0);
        raster_ready    : in std_logic;
        fifo_empty        : out std_logic
    );
end component;

begin
    -- ARESETN をACLK で同期化
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            reset_1d <= not ARESETN or not init_done;
            reset_2d <= reset_1d;
        end if;
    end process;
    reset <= reset_2d;
    
    -- インプリメントでは0.2秒に1回表示する
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                disp_count <= 0;
                disp_ena <= '0';
            else
                if disp_count = DISP_INTERVAL_DIV then
                    disp_count <= 0;
                    disp_ena <= '1';
                else
                    disp_count <= disp_count + 1;
                    disp_ena <= '0';
                end if;
            end if;
        end if;
    end process;
    
    -- Write Transaction State Machine
    -- Writeを0.2秒に1回行う。Readは無し
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                wr_main_cs <= idle_wr_main;
                char_code_ena <= '0';
            else
                case (wr_main_cs) is
                    when idle_wr_main =>
                        wr_main_cs <= char_bitmap_trans_start;
                    when char_bitmap_trans_start => -- キャラクタコードからビットマップデータへの変換をスタート
                        char_code_ena <= '1';
                        wr_main_cs <= char_bitmap_trans_wait;
                    when char_bitmap_trans_wait => -- キャラクタコードからビットマップデータへの変換が終了するまでWait
                        char_code_ena <= '0';
                        if raster_valid='1' then
                            wr_main_cs <= write_state;
                        end if;
                    when write_state =>
                        if wrt_cs = wr_tran_end and wr_tran_count=7 then -- 1キャラクタ描画終了
                            wr_main_cs <= wait_state;
                        end if;
                    when wait_state =>
                        if disp_ena='1' then
                            wr_main_cs <= idle_wr_main;
                        end if;
                end case;
            end if;
        end if;
    end process;
    
    -- writeの数を数える
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                wr_tran_count <= (others => '0');
            else
                if wrt_cs = wr_tran_end then
                    wr_tran_count <= wr_tran_count + 1;
                elsif wr_main_cs = wait_state then
                    wr_tran_count <= (others => '0');
                end if;
            end if;
        end if;
    end process;
    
    -- color_code の処理
    color_code(31 downto 24) <= (others => '0');
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                color_code(23 downto 0) <= (others => '1');
            else
                if wr_main_cs=write_state and wrt_cs = wr_tran_end and wr_tran_count=7 then -- 1キャラクタ描画終了
                    color_code(23 downto 0) <= M_SEQ24_F(color_code(23 downto 0));
                end if;
            end if;
        end if;
    end process;
    -- color_code(23 downto 0) <= (others => '1');

    char_gen_8raster_i : char_gen_8raster port map(
        clk             => ACLK,
        reset            => reset,
        char_code        => char_code,
        color_code        => color_code,
        char_code_ena    => char_code_ena,
        raster_valid    => raster_valid,
        raster_data        => raster_data,
        raster_ready    => raster_ready,
        fifo_empty        => fifo_empty
    );
    
    -- Write
    M_AXI_AWID        <= "0";
    M_AXI_AWSIZE     <= "011";    -- 8 bytes fixed
    M_AXI_AWBURST    <= "01";    -- INCR
    M_AXI_AWLOCK    <= '0';    -- Normal Access
    M_AXI_AWCACHE    <= "0010";    -- Normal Non-cacheable Non-bufferable
    -- M_AXI_AWCACHE    <= "0011";    -- Normal Non-cacheable Bufferable, Zynq-7020ではBRESPが10でSLVERRになってしまい設定してはならない。
    M_AXI_AWPROT    <= "000";    -- Data access, Secure access, Unprivileged access
    M_AXI_AWQOS        <= "0000";    -- default
    M_AXI_AWUSER    <= "0";
    M_AXI_WSTRB        <= (others => '1');
    M_AXI_WUSER        <= "0";
    M_AXI_AWLEN        <= CONV_STD_LOGIC_VECTOR(3, 8); -- 1ライン8ピクセル-1(64ビット幅なので4バースト)
    awlen            <= CONV_STD_LOGIC_VECTOR(3, 8); -- 1ライン8ピクセル-1(64ビット幅なので4バースト)
    
    -- AXI4バス Write Transaction State Machine
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                wrt_cs <= idle_wr;
                awvalid <= '0';
                bready <= '0';
            else 
                case(wrt_cs) is
                    when idle_wr =>
                        if wr_main_cs = write_state then
                            wrt_cs <= awvalid_assert;
                            awvalid <= '1';
                        end if;
                    when awvalid_assert =>
                        if M_AXI_AWREADY='1' then
                            if wrt_wv_cs=wvalid_hold or (wrt_wv_cs=wvalid_assert and write_count=0 and M_AXI_WREADY='1') then
                                wrt_cs <= bready_assert;
                                bready <= '1';
                            else
                                wrt_cs <= data_write_hold;
                            end if;
                            awvalid <= '0';
                        end if;
                    when data_write_hold =>
                        if wrt_wv_cs=wvalid_hold or (wrt_wv_cs=wvalid_assert and write_count=0 and M_AXI_WREADY='1') then
                            wrt_cs <= bready_assert;
                            bready <= '1';
                        end if;
                    when bready_assert =>
                        if M_AXI_BVALID='1' then
                            wrt_cs <= wr_tran_end;
                            bready <= '0';
                        end if;
                    when wr_tran_end =>
                        wrt_cs <= idle_wr;
                end case;
            end if;
        end if;
    end process;
    -- AXI4 バス Write Transaction WVALID State Machine
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                wrt_wv_cs <= idle_wvalid;
                wvalid <= '0';
            else 
                case(wrt_wv_cs) is
                    when idle_wvalid =>
                        if wrt_cs=awvalid_assert then
                            wrt_wv_cs <= wvalid_assert;
                            wvalid <= '1';
                        end if;
                    when wvalid_assert =>
                        if write_count=0 and M_AXI_WREADY='1' then -- 終了
                            wrt_wv_cs <= wvalid_hold;
                            wvalid <= '0';
                        end if;
                    when wvalid_hold =>
                        if wrt_cs=bready_assert then
                            wrt_wv_cs <= idle_wvalid;
                        end if;
                end case;
            end if;
        end if;
    end process;
    
    M_AXI_AWVALID    <= awvalid;
    M_AXI_WVALID    <= wvalid;
    M_AXI_BREADY    <= bready;
    
    -- wr_addr の処理、Write Transaction が終了するたびにキャラクタの下のラインをアドレスするために640を足す。
    -- wr_addr_old : キャラクタのベースアドレス(左上)、wr_add_sc : キャラクタの1行の左上のアドレス
    -- 1キャラクタ=4バイと、よってアドレスは*4
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                wr_addr <= DDR3_START_ADDR;
                wr_addr_old <= DDR3_START_ADDR;
                wr_addr_sc <= DDR3_START_ADDR;
            else
                if wrt_cs = wr_tran_end then
                    if wr_tran_count=7 then -- 1キャラクタ描画終了
                        if wr_addr_old >= DDR3_START_ADDR+(SVGA_ADDR_LIMIT-(ROW_ALL_PIXELS*4*8)+(ROW_ALL_PIXELS-8)*4) then -- 1画面描画終了, 640*4*(480-8)+(640-8)*4、1キャラクタは8行なので、総数ピクセルから8行分のピクセルを除いて、最後のキャラクタまでのピクセルを足す
                            wr_addr <= DDR3_START_ADDR;
                            wr_addr_old <= DDR3_START_ADDR;
                            wr_addr_sc <= DDR3_START_ADDR;
                        elsif (wr_addr_sc+(ROW_ALL_PIXELS-8)*4) - wr_addr_old > 0 then -- 次の行に行かない場合
                            wr_addr <= wr_addr_old + 8*4; -- 次のキャラクタのアドレス
                            wr_addr_old <= wr_addr_old + 8*4;
                        else -- 1行キャラクタを書き終わったので、次のキャラクタ行に移動する
                            wr_addr <= wr_addr_sc + ROW_ALL_PIXELS *8 *4; -- x8行 x4バイト
                            wr_addr_old <= wr_addr_sc + ROW_ALL_PIXELS *8 * 4;
                            wr_addr_sc <= wr_addr_sc + ROW_ALL_PIXELS *8 * 4;
                        end if;
                    else -- 同一キャラクタの次の行を描画
                        wr_addr <= wr_addr + ROW_ALL_PIXELS*4;
                    end if;
                end if;
            end if;
        end if;
    end process;
    M_AXI_AWADDR <= wr_addr;
    
    -- write_data の処理、char_gen_8raster のFIFOからデータを出力する
    raster_ready <= '1' when wvalid='1' and M_AXI_WREADY='1' else '0';
    M_AXI_WDATA <= raster_data;
    
    -- write_count の処理
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                write_count <= (others => '0');
            else
                if wrt_wv_cs=idle_wvalid then
                    write_count <= awlen;
                elsif wrt_wv_cs=wvalid_assert and M_AXI_WREADY='1' then
                    write_count <= write_count - 1;
                end if;
            end if;
        end if;
    end process;
    
    -- wlastの処理
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                wlast <= '0';
            else
                if wrt_wv_cs=idle_wvalid and awlen=0 then -- awlen が0の時はデータ転送の最初からwlast をアサートする
                    wlast <= '1';
                elsif wrt_wv_cs=wvalid_assert and write_count=1 and M_AXI_WREADY='1' then -- awlen が0で無い時はwrite_count が1でM_AXI_WREADY='1'の時、つまりwrite_count が0の時にwlastをアサートする
                    wlast <= '1';
                elsif wrt_wv_cs=wvalid_assert and write_count=0 and M_AXI_WREADY='1' then -- データ転送が終了なのでwlast を0にする
                    wlast <= '0';
                end if;
            end if;
        end if;
    end process;
    M_AXI_WLAST <= wlast;
    
    -- wr_error の処理、M_AXI_BRESPがRESP_OKAY以外の時にwr_errorを点灯する
    process(ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                wr_error <= '0';
            else
                if wrt_cs=bready_assert and M_AXI_BVALID='1' and M_AXI_BRESP/=RESP_OKAY then
                    wr_error <= '1';
                end if;
            end if;
        end if;
    end process;
    
    -- char_code の処理
    process(ACLK) begin -- キャラクタコードを+1して表示
        if ACLK'event and ACLK='1' then
            if reset='1' then
                char_code <= "0100001"; -- キャラクタの!
            else
                if wrt_cs = wr_tran_end and wr_tran_count=7 then -- 1キャラクタ描画終了
                    if char_code="1111110" then -- キャラクタの~
                        char_code <= "0100001"; -- キャラクタの!
                    else
                        char_code <= char_code + 1;
                    end if;
                end if;
            end if;
        end if;
    end process;
    
    -- Readは無し
    M_AXI_ARID        <= "0";
    M_AXI_ARLEN        <= (others => '0');
    M_AXI_ARSIZE    <= "010";    -- 4bytes
    M_AXI_ARBURST    <= "01";    -- INCR
    M_AXI_ARLOCK    <= "00";    -- Normal Access
    M_AXI_ARCACHE    <= "0010";    -- Normal Non-cacheable Non-bufferable
    M_AXI_ARPROT    <= "000";    -- Data access, Secure access, Unprivileged access
    M_AXI_ARQOS        <= "0000";    -- default
    M_AXI_ARUSER    <= "0";
    M_AXI_ARADDR    <= (others => '0');
    M_AXI_ARVALID    <= '0';
    M_AXI_RREADY    <= '0';
    rd_error        <= '0';
end implementation;


(2012/10/20、17:23:修正、AXIプロトコル違反になっていたので、修正しました。ご指摘ありがとうございました。)
(2012/10/21:修正、M_AXI_AWCACHE <= "0011"; -- Normal Non-cacheable Bufferable だとM_AXI_BRESPが”01”、SLVERRとなってしまった。BufferableはZynq-7020では使えないようだ。M_AXI_AWCACHE <= "0010"; -- Normal Non-cacheable Non-bufferableに戻した)


次に、char_gen_8raster.v を下に示す。char_gen_8raster.v は、キャラクタROMのビットマップデータを元にピクセルデータを作成してFIFOに入力する。

// キャラクタコードのラスタデータ出力
//
// char_code_ena 1パルスでFIFOにキャラクタコードのラスタデータを読み込む
// すべてのラスタデータを読み込めたら、raster_validが立つ
// 上位モジュールは、ラスタデータを1つ読み終えたら、 raster_ready を立てる
//

`default_nettype none

module char_gen_8raster (
    input    wire    clk,
    input    wire    reset,
    input    wire    [6:0]    char_code,        // アスキーキャラクタコード
    input    wire    [31:0]    color_code,        // カラーコード
    input    wire    char_code_ena,            // アスキーキャラクタコードのイネーブル、1パルス
    output    reg        raster_valid,            // ラスタデータ有効(1パルス)
    output    wire    [63:0]    raster_data,    // ラスタデータ
    input    wire    raster_ready,            // ラスタデータのRead時のReady信号
    output    wire    fifo_empty
);

    parameter [2:0]    IDLE_MAIN =            3'b000,
                    CHAR_ROM_READ =        3'b001,
                    CHAR_ROM_VALID =    3'b011,
                    CONVERT_FORMAT =    3'b010,
                    NEXT_LINE_ST =        3'b110,
                    RASTER_VALID_WAIT =    3'b111,
                    RASTER_VALID_ST =    3'b101;
                    
    reg        [2:0]    mainsm_cs;
    reg        [7:0]    char_shift_reg;
    reg        [6:0]    char_code_hold;
    reg        [31:0]    color_code_hold;
    reg        [2:0]    shift_count;
    reg        [2:0]    row_count;
    reg        [31:0]    bitmap_pixel_data;
    wire    cgfifo_wr_en;
    wire    cgfifo_full;
    wire    cgfifo_underflow;
    wire    [5:0] cgfifo_data_count;
    wire    [7:0]    char_raster_8bit;
    
    // キャラクタ・ジェネレータROMのインスタンス
    char_gen_rom char_gen_rom_inst (
        .clk(clk),
        .reset(reset),
        .char_addr(char_code_hold),
        .row_addr(row_count),
        .dout(char_raster_8bit)
    );
    
    // char_code, color_code をラッチする
    always @(posedge clk) begin
        if (reset) begin
            char_code_hold <= 0;
            color_code_hold <= 0;
        end else begin
            if (mainsm_cs == IDLE_MAIN && char_code_ena) begin
                char_code_hold <= char_code;
                color_code_hold <= color_code;
            end
        end
    end
    
    // main state machine
    always @(posedge clk) begin
        if (reset) begin
            mainsm_cs <= IDLE_MAIN;
            raster_valid <= 1'b0;
        end else begin
            case (mainsm_cs)
                IDLE_MAIN : begin // char_code_ena が1になるとスタート, 0
                    raster_valid <= 1'b0;
                    if (char_code_ena)    // キャラクタのラスタ・ビットマップからRGBビットマップへの変換スタート
                        mainsm_cs <= CHAR_ROM_READ;
                end
                CHAR_ROM_READ : // char_gen_rom のReadのために1クロックWaitする, 1
                    mainsm_cs <= CHAR_ROM_VALID;
                CHAR_ROM_VALID : // この時点で、 char_gen_rom のdoutが確定される, 3
                    mainsm_cs <= CONVERT_FORMAT;
                CONVERT_FORMAT : // キャラクタのラスタ・ビットマップからRGBビットマップへの変換(1ラスタ分), 2
                    if (shift_count == 0) begin
                        if (row_count == 7) // 8ラスタを終了
                            mainsm_cs <= RASTER_VALID_WAIT;
                        else // 次のラスタへ
                            mainsm_cs <= NEXT_LINE_ST;
                    end
                NEXT_LINE_ST : // 次のラスタへの準備, 6
                    mainsm_cs <= CHAR_ROM_READ;
                RASTER_VALID_WAIT : // 1キャラクタ分の変換が終了, char_gen_fifo が32になるまで待機, 7
                    if (cgfifo_data_count >= 32)
                        mainsm_cs <= RASTER_VALID_ST;
                RASTER_VALID_ST : begin // キャラクタがすべてchar_gen_fifo に入ったので、AXIトランザクション開始, 5
                    raster_valid <= 1'b1;
                    mainsm_cs <= IDLE_MAIN;
                end
            endcase
        end
    end                    
    
    // char_gen_rom の出力データ用シフトレジスタ
    always @(posedge clk) begin
        if (reset)
            char_shift_reg <= 0;
        else begin
            if (mainsm_cs == CHAR_ROM_VALID)
                char_shift_reg <= char_raster_8bit[7:0];
            else if (mainsm_cs == CONVERT_FORMAT)
                char_shift_reg <= {1'b0, char_shift_reg[7:1]};
        end
    end
    
    // ピクセルのシフト数のカウント
    always @(posedge clk) begin
        if (reset)
            shift_count <= 3'd7;
        else begin
            if (mainsm_cs == CONVERT_FORMAT && shift_count != 0)
                shift_count <= shift_count - 3'd1;
            else if (mainsm_cs == NEXT_LINE_ST || mainsm_cs == RASTER_VALID_ST)
                shift_count <= 3'd7;
        end
    end
            
    // row_count 
    always @(posedge clk) begin
        if (reset)
            row_count <= 3'd0;
        else begin
            if (mainsm_cs == NEXT_LINE_ST && row_count != 7)
                row_count <= row_count + 3'd1;
            else if (mainsm_cs == IDLE_MAIN)
                row_count <= 3'd0;
        end
    end
    
    // char_gen_fifo へのデータの入力
    always @* begin
        if (char_shift_reg[0])
            bitmap_pixel_data <= color_code_hold;
        else
            bitmap_pixel_data <= 0;
    end
    
    assign cgfifo_wr_en = (mainsm_cs == CONVERT_FORMAT) ? 1'b1 : 1'b0;
    
    // char_gen_fifo のインスタンス
    char_gen_fifo char_gen_fifo_inst (
        .rst(reset), // input rst
        .wr_clk(clk), // input wr_clk
        .rd_clk(clk), // input rd_clk
        .din(bitmap_pixel_data), // input [31 : 0] din
        .wr_en(cgfifo_wr_en), // input wr_en
        .rd_en(raster_ready), // input rd_en
        .dout(raster_data), // output [63 : 0] dout
        .full(cgfifo_full), // output full
        .empty(fifo_empty), // output empty
        .underflow(cgfifo_underflow), // output underflow
        .rd_data_count(cgfifo_data_count) // output [5 : 0] rd_data_count
    );

endmodule                


次に、char_gen_8raster.v で使用しているFIFO、char_gen_fifo のxco ファイルを下に示す。

##############################################################
#
# Xilinx Core Generator version 14.2
# Date: Sat Oct 13 07:46:30 2012
#
##############################################################
#
# This file contains the customisation parameters for a
# Xilinx CORE Generator IP GUI. It is strongly recommended
# that you do not manually alter this file as it may cause
# unexpected and unsupported behavior.
#
##############################################################
#
# Generated from component: xilinx.com:ip:fifo_generator:9.2
#
##############################################################
#
# BEGIN Project Options
SET addpads = false
SET asysymbol = true
SET busformat = BusFormatAngleBracketNotRipped
SET createndf = false
SET designentry = Verilog
SET device = xc7z020
SET devicefamily = zynq
SET flowvendor = Other
SET formalverification = false
SET foundationsym = false
SET implementationfiletype = Ngc
SET package = clg484
SET removerpms = false
SET simulationfiles = Behavioral
SET speedgrade = -1
SET verilogsim = true
SET vhdlsim = false
# END Project Options
# BEGIN Select
SELECT Fifo_Generator xilinx.com:ip:fifo_generator:9.2
# END Select
# BEGIN Parameters
CSET add_ngc_constraint_axi=false
CSET almost_empty_flag=false
CSET almost_full_flag=false
CSET aruser_width=1
CSET awuser_width=1
CSET axi_address_width=32
CSET axi_data_width=64
CSET axi_type=AXI4_Stream
CSET axis_type=FIFO
CSET buser_width=1
CSET clock_enable_type=Slave_Interface_Clock_Enable
CSET clock_type_axi=Common_Clock
CSET component_name=char_gen_fifo
CSET data_count=false
CSET data_count_width=6
CSET disable_timing_violations=false
CSET disable_timing_violations_axi=false
CSET dout_reset_value=0
CSET empty_threshold_assert_value=4
CSET empty_threshold_assert_value_axis=1022
CSET empty_threshold_assert_value_rach=1022
CSET empty_threshold_assert_value_rdch=1022
CSET empty_threshold_assert_value_wach=1022
CSET empty_threshold_assert_value_wdch=1022
CSET empty_threshold_assert_value_wrch=1022
CSET empty_threshold_negate_value=5
CSET enable_aruser=false
CSET enable_awuser=false
CSET enable_buser=false
CSET enable_common_overflow=false
CSET enable_common_underflow=false
CSET enable_data_counts_axis=false
CSET enable_data_counts_rach=false
CSET enable_data_counts_rdch=false
CSET enable_data_counts_wach=false
CSET enable_data_counts_wdch=false
CSET enable_data_counts_wrch=false
CSET enable_ecc=false
CSET enable_ecc_axis=false
CSET enable_ecc_rach=false
CSET enable_ecc_rdch=false
CSET enable_ecc_wach=false
CSET enable_ecc_wdch=false
CSET enable_ecc_wrch=false
CSET enable_read_channel=false
CSET enable_read_pointer_increment_by2=false
CSET enable_reset_synchronization=true
CSET enable_ruser=false
CSET enable_tdata=false
CSET enable_tdest=false
CSET enable_tid=false
CSET enable_tkeep=false
CSET enable_tlast=false
CSET enable_tready=true
CSET enable_tstrobe=false
CSET enable_tuser=false
CSET enable_write_channel=false
CSET enable_wuser=false
CSET fifo_application_type_axis=Data_FIFO
CSET fifo_application_type_rach=Data_FIFO
CSET fifo_application_type_rdch=Data_FIFO
CSET fifo_application_type_wach=Data_FIFO
CSET fifo_application_type_wdch=Data_FIFO
CSET fifo_application_type_wrch=Data_FIFO
CSET fifo_implementation=Independent_Clocks_Block_RAM
CSET fifo_implementation_axis=Common_Clock_Block_RAM
CSET fifo_implementation_rach=Common_Clock_Block_RAM
CSET fifo_implementation_rdch=Common_Clock_Block_RAM
CSET fifo_implementation_wach=Common_Clock_Block_RAM
CSET fifo_implementation_wdch=Common_Clock_Block_RAM
CSET fifo_implementation_wrch=Common_Clock_Block_RAM
CSET full_flags_reset_value=1
CSET full_threshold_assert_value=63
CSET full_threshold_assert_value_axis=1023
CSET full_threshold_assert_value_rach=1023
CSET full_threshold_assert_value_rdch=1023
CSET full_threshold_assert_value_wach=1023
CSET full_threshold_assert_value_wdch=1023
CSET full_threshold_assert_value_wrch=1023
CSET full_threshold_negate_value=62
CSET id_width=4
CSET inject_dbit_error=false
CSET inject_dbit_error_axis=false
CSET inject_dbit_error_rach=false
CSET inject_dbit_error_rdch=false
CSET inject_dbit_error_wach=false
CSET inject_dbit_error_wdch=false
CSET inject_dbit_error_wrch=false
CSET inject_sbit_error=false
CSET inject_sbit_error_axis=false
CSET inject_sbit_error_rach=false
CSET inject_sbit_error_rdch=false
CSET inject_sbit_error_wach=false
CSET inject_sbit_error_wdch=false
CSET inject_sbit_error_wrch=false
CSET input_data_width=32
CSET input_depth=64
CSET input_depth_axis=1024
CSET input_depth_rach=16
CSET input_depth_rdch=1024
CSET input_depth_wach=16
CSET input_depth_wdch=1024
CSET input_depth_wrch=16
CSET interface_type=Native
CSET output_data_width=64
CSET output_depth=32
CSET overflow_flag=false
CSET overflow_flag_axi=false
CSET overflow_sense=Active_High
CSET overflow_sense_axi=Active_High
CSET performance_options=First_Word_Fall_Through
CSET programmable_empty_type=No_Programmable_Empty_Threshold
CSET programmable_empty_type_axis=No_Programmable_Empty_Threshold
CSET programmable_empty_type_rach=No_Programmable_Empty_Threshold
CSET programmable_empty_type_rdch=No_Programmable_Empty_Threshold
CSET programmable_empty_type_wach=No_Programmable_Empty_Threshold
CSET programmable_empty_type_wdch=No_Programmable_Empty_Threshold
CSET programmable_empty_type_wrch=No_Programmable_Empty_Threshold
CSET programmable_full_type=No_Programmable_Full_Threshold
CSET programmable_full_type_axis=No_Programmable_Full_Threshold
CSET programmable_full_type_rach=No_Programmable_Full_Threshold
CSET programmable_full_type_rdch=No_Programmable_Full_Threshold
CSET programmable_full_type_wach=No_Programmable_Full_Threshold
CSET programmable_full_type_wdch=No_Programmable_Full_Threshold
CSET programmable_full_type_wrch=No_Programmable_Full_Threshold
CSET rach_type=FIFO
CSET rdch_type=FIFO
CSET read_clock_frequency=1
CSET read_data_count=true
CSET read_data_count_width=6
CSET register_slice_mode_axis=Fully_Registered
CSET register_slice_mode_rach=Fully_Registered
CSET register_slice_mode_rdch=Fully_Registered
CSET register_slice_mode_wach=Fully_Registered
CSET register_slice_mode_wdch=Fully_Registered
CSET register_slice_mode_wrch=Fully_Registered
CSET reset_pin=true
CSET reset_type=Asynchronous_Reset
CSET ruser_width=1
CSET synchronization_stages=2
CSET synchronization_stages_axi=2
CSET tdata_width=64
CSET tdest_width=4
CSET tid_width=8
CSET tkeep_width=4
CSET tstrb_width=4
CSET tuser_width=4
CSET underflow_flag=true
CSET underflow_flag_axi=false
CSET underflow_sense=Active_High
CSET underflow_sense_axi=Active_High
CSET use_clock_enable=false
CSET use_dout_reset=true
CSET use_embedded_registers=false
CSET use_extra_logic=true
CSET valid_flag=false
CSET valid_sense=Active_High
CSET wach_type=FIFO
CSET wdch_type=FIFO
CSET wrch_type=FIFO
CSET write_acknowledge_flag=false
CSET write_acknowledge_sense=Active_High
CSET write_clock_frequency=1
CSET write_data_count=false
CSET write_data_count_width=7
CSET wuser_width=1
# END Parameters
# BEGIN Extra information
MISC pkg_timestamp=2012-06-23T13:35:37Z
# END Extra information
GENERATE
# CRC: a331f18e


(2012/12/16:追加)char_gen_rom.v を追加で貼っておきます。ダウンロードできるファイルの中にはあったと思うのですが、ソースファイルはブログに貼ったことがないみたいです。
自作のキャラクタROMです。8x8の四角を塗りつぶして作りました。すごい手間が掛かってますよ~。

// キャラクタジェネレータ用ROM

`default_nettype none
`timescale 1ns / 1ps

module char_gen_rom(clk, reset, char_addr, row_addr, dout);
    input clk;
    input reset;
    input [6:0] char_addr;
    input [2:0] row_addr;
    output [7:0] dout;
    
    wire clk;
    wire reset;
    wire [6:0] char_addr;
    wire [2:0] row_addr;
    wire [7:0] dout;
    
    wire [10:0] addr;
    
    assign addr = {1'b0, char_addr, row_addr};
    
    RAMB16_S9 #(
        .INIT_00(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_01(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_02(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_03(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_04(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_05(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_06(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_07(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_08(256'h0014147F147F1414000000000012243600080008080808080000000000000000), // #,",!, 
        .INIT_09(256'h0000000000081018004C322254081408002152240812254200083E483E093E08), // ',&,%,$
        .INIT_0A(256'h000808087F08080800492A1C7F1C2A4900040810101008040020100808081020), // +,*,),(
        .INIT_0B(256'h00010204081020400006060000000000000000007F0000000002040600000000), // /,.,-,,
        .INIT_0C(256'h001C22201820221C003E02041820221C001C080808080C080018244242422418), // 3,2,1,0
        .INIT_0D(256'h001010202040407E003C42423E02423C001E20201E02023E0020207E22242830), // 7,6,5,4
        .INIT_0E(256'h0004080C00000C000000000C00000C00003C42407C42423C003C42423C42423C), // ;,:,9,8
        .INIT_0F(256'h000800081020221C00040810201008040000003E003E00000020100804081020), // ?,>,=,<
        .INIT_10(256'h001C22010101221C003F41413F41413F0041417F2236141C005C2A155549221E), // C,B,A,@
        .INIT_11(256'h001C22710101221C000101013F01017F007F01013F01017F001F21414141211F), // G,F,E,D
        .INIT_12(256'h0022120A060A1222000E11101010103E001C08080808081C004141417F414141), // K,J,I,H
        .INIT_13(256'h001C22414141221C00416151494543410041414149556341003E020202020202),// O,N,M,L
        .INIT_14(256'h003C42403C02423C002111093F41413F005C26594141221C000101013F41413F), // S,R,Q,P
        .INIT_15(256'h00225555554949490008141422224141001C224141414141000808080808087F), // W,V,U,T
        .INIT_16(256'h0038080808080838003F02040810203F00080808081422410041221408142241), // [,Z,Y,X
        .INIT_17(256'h007F0000000000000000000000221408001C10101010101C0008083E083E1422), // _,^,],\
        .INIT_18(256'h0038040438000000001E22221E020200003C223C201C00000000000000180810), // c,b,a,`
        .INIT_19(256'h001C221C0C122C00000808081C081000001C021E221C0000003C22223C202000), // g,f,e,d
        .INIT_1A(256'h0024140C14240400000C1210100010000008080800080000002424241C040400), // k,j,i,h
        .INIT_1B(256'h0018242424180000002828282814000000545454542A00000018080808080800), // o,n,m,l
        .INIT_1C(256'h0018201804180000000404040C34000000202038243800000004041C241C0000), // s,r,q,p
        .INIT_1D(256'h00142A2A2A2200000008141422220000001824242424000000180808081C0800), // w,v,u,t
        .INIT_1E(256'h001008080C080810003E0408103E000000020408142200000022140814220000), // {,z,y,x
        .INIT_1F(256'h0000000000000000000000000000142800040808180808040008080808080808), //  ,~,},|
        .INIT_20(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_21(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_22(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_23(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_24(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_25(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_26(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_27(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_28(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_29(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_2A(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_2B(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_2C(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_2D(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_2E(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_2F(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_31(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_32(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_33(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_34(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_35(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_36(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_37(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_38(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_39(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_3A(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_3B(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_3C(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_3D(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_3E(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INIT_3F(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INITP_00(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INITP_01(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INITP_02(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INITP_03(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INITP_04(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INITP_05(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INITP_06(256'h0000000000000000000000000000000000000000000000000000000000000000),
        .INITP_07(256'h0000000000000000000000000000000000000000000000000000000000000000)
    ) CHAR_GEN_ROM_INST (
        .DO(dout),
        .DOP(),
        .ADDR(addr),
        .CLK(clk),
        .DI(8'd0),
        .DIP(1'b0),
        .EN(1'b1),
        .SSR(reset),
        .WE(1'b0)
    );
endmodule

  1. 2012年10月20日 04:55 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:2

ChipScope Analyzer でステートマシンのステートを表示する方法

ChipScope Analyzer でステートマシンのステートを表示させると通常は16進数しか表示しない。私は通常VHDLでは、ステートマシンのステートをtype で列挙するだけで値を割り当てていない。それで、XSTのレポートファイル (***.syr) を見てステートを判断していた。それだととても面倒だ。VerilogでもOne Hotに値を指定していると、XSTでグレーコードに変更されたりするので、やはり、XSTのレポートファイル (***.syr) を見る必要がある。いちいち見ながらステートを判断するのは面倒なので、ChipScope Analyzer でステートマシンのステート名を表示する方法を試してみる。なお、参考にさせて頂いたのは、Xilinxのアンサー、”ChipScope Pro : Analyzer に FSM のステート名を表示させる方法”、”ChipScope Pro Software and Cores User Guide UG029 (v14.2) July 25, 2012”の55ページToken、そしてブログでは、徒然日記さんの”ChipScope™ Proの使い方 Lab. 5”を参考にさせて頂いた。

VHDLの場合
最初にVHDLのステートマシンのステート名を表示してみよう。まずは私の様にVHDLでtypeで宣言して、ステートの値を与えていない場合(たぶんステートの値を割り当てていてもXSTで変更されることがあるので、確認しておくことをお勧めする)は、XSTのレポートファイル(今回は、char_write_axi_master.syr)でステートマシンのステート値を確認する。まずは、VHDLの記述から下に示す。

type write_transaction_state is (idle_wr, awvalid_assert, data_write, bready_assert, wr_tran_end);
signal wrt_cs : write_transaction_state;


char_write_axi_master.syr には、こう記述されたていた。

Optimizing FSM <FSM_1> on signal <wrt_cs[1:3]> with user encoding.
----------------------------
 State          | Encoding
----------------------------
 idle_wr        | 000
 awvalid_assert | 001
 data_write     | 010
 bready_assert  | 011
 wr_tran_end    | 100
----------------------------


ここからToken ファイルを作成した。”ChipScope Pro Software and Cores User Guide UG029 (v14.2) July 25, 2012”の55ページToken の項を参照した。”@DEFAULT_TOKEN=ERROR”と記述しておくとデフォルトのステート値を定義できるようだ。つまりステートのリストにないステートということでERRORと記述しているようだ。下に、ChipScpe Analyzer 用 Token ファイル、wrt_cs.tok ファイルを示す。

#Optimizing FSM on signal with user encoding.

@DEFAULT_TOKEN=ERROR

idle_wr=000\b
awvalid_assert=001\b
data_write=010\b
bready_assert=011\b
wr_tran_end=100\b


なお、\b は2進数だが、\u の符号なし10進数、\h の16進数が使えるようだ。
ChipScope Analyzer の標準の表示を見てみる。なお、上にも書いてあるが、ステートマシンのステートの割り当ては、wrt_cs[1:3]なので、通常にバスにしているのだったら、逆順にしておく必要が有る。
下に、wrt_cs のChipScope Analyzer での16進数表示を示す。
ChipScopeA_sm_disp_1_121019.png

これに wrt_cs.tok ファイルを読みこませるには、wrt_cs_FSM_FFd の右クリックメニューから Bus Radix -> Token... を選択する。
ChipScopeA_sm_disp_2_121019.png

ダイアログが表示されるので、Select New File ボタンをクリックしてwrt_cs.tok を選択して、OKボタンをクリックする。
ChipScopeA_sm_disp_3_121019.png

すると、wrt_cs_FSM_FFd にステート値が表示された。
ChipScopeA_sm_disp_4_121019.png

ちなみに、wrt_cs_FSM_FFd3, 2, 1は通常ではバスになっていないので、バスにまとめてある。上図で、wrt_cs_FSM_FFd を展開してあるが、wrt_cs_FSM_FFd3, 2, 1 という順番になっているのがわかると思う。これは、XSTの論理合成の定義が、wrt_cs[1:3]となっているためである。wrt_cs_FSM_FFd1, 2, 3をバスにした当初は、通常 wrt_cs_FSM_FFd1, 2, 3の順番に並んでいるので、バス (wrt_cs_FSM_FFd) の右クリックメニューからReverse Bus Order をクリックしてバスの順序を逆にする必要がある。


Verilogの場合
Verilog で記述したステートマシンの定義を下に示す。

    parameter [2:0]    IDLE_MAIN =            3'b000,
                    CHAR_ROM_READ =        3'b001,
                    CHAR_ROM_VALID =    3'b011,
                    CONVERT_FORMAT =    3'b010,
                    NEXT_LINE_ST =        3'b110,
                    RASTER_VALID_WAIT =    3'b111,
                    RASTER_VALID_ST =    3'b101;
                    
    reg        [2:0]    mainsm_cs;


XSTで論理合成すると、グレーコードになってしまっていたので、今回はワンホットにせずにグレーコードで記述している。こう記述すると、XSTのレポートファイル(今回は、char_write_axi_master.syr)では、そのままのステート値になっていた。

Optimizing FSM <char_gen_8raster_i/FSM_2> on signal <mainsm_cs[1:3]> with user encoding.
-------------------
 State | Encoding
-------------------
 000   | 000
 001   | 001
 011   | 011
 110   | 110
 111   | 111
 010   | 010
 101   | 101
-------------------


これを元に、mainsm_cs.tok を記述した。

#Optimizing FSM on signal with user encoding.

@DEFAULT_TOKEN=ERROR

IDLE_MAIN=000\b
CHAR_ROM_READ=001\b
CHAR_ROM_VALID=011\b
CONVERT_FORMAT=010\b
NEXT_LINE_ST=110\b
RASTER_VALID_WAIT=111\b
RASTER_VALID_ST=101\b


なお、言うまでもないかもしれないが、”#”はコメントを表す。
この Token ファイルで、main_cs_FSM_FFd のステート値を表示させたのが、下の図になる。
ChipScopeA_sm_disp_5_121019.png

これで、ChipScope Analyzer でステーマシンのステート値を表示することが出来た。ちょっとステートを見れば良い時は、Token ファイルを作るのが面倒だが、しっかりステートマシンを解析したい時は便利だと思う。
  1. 2012年10月19日 04:06 |
  2. Chipscope
  3. | トラックバック:0
  4. | コメント:4

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する13(char_wirte_axi_master IPを追加4)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する12(char_wirte_axi_master IPを追加3)”の続き。

前回は単体シミュレーションがうまく行ったDDR3 SDRAMのフレーム・バッファにキャラクタを書き込むchar_write_axi_master IPをXPSプロジェクトに組み込んだが、うまく行かなかった。今回はトラブルシュートしてみた。

最初にChipScopeで見てみたが、全く動いている感じがなかった。これは、ビットマップ・ディスプレイ・コントローラの時にも似たような状態だった。そこで、ARMのソフトウェアでGPIOの出力を1にして、それを始動のイネーブルとするとビットマップ・ディスプレイ・コントローラが動作した。それと同じ何じゃないかと思った。というか、それを忘れてしまった。(本当に懲りない困ったもんだ)
案の定、init_done信号を追加して、init_done信号が0の時はリセットしているように書き換えたら、見事動作するようになった。(とっても嬉しいというか至福の時間を過ごせた。。。)

少なくともZynqのHP やACPバスに接続するAXI Master IPのについては、ARMのブートコードが実行されてHPバスやACPバスが使用可能になるよりもAXIバスのARESETN のリセットが外れるのが速いんだと思う。それでARESTNのリセットが外れてから動作したのでは、まだAXIバスが動いていないのかもしれない?ともかく、GPIOなどを使用して、ユーザーのプログラムでAXI Master IPの動作を開始させるのが良いと思う。

画像はYouTubeにアップしたので、見てください。DDR3 SDRAMの初期値のランダムパターンにキャラクタをハードウェアで書き込んでいくのが見えると思います。なお、解像度をHDにしてご覧ください。


ハードウェアでキャラクタを書いてうまく行ったということは、ビットマップ・ディスプレイ・コントローラの描画領域をARMからアクセスする場合は、描画領域の物理アドレスをアクセス可能にして、その領域はキャッシュをOFFする必要がありそうだ。そうするとARMの構造やアセンブラまで手をだす必要があるのかも知れない。
良いARMプロセッサの本を知っていたら教えて下さい。よろしくお願いします。Xilinxの資料で勉強できるのかな?少なくとも日本語の資料で概略つかめると楽だ。。。
  1. 2012年10月18日 05:29 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する12(char_wirte_axi_master IPを追加3)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する11(char_wirte_axi_master IPを追加2)”の続き。

前回、DDR3 SDRAMのフレーム・バッファにキャラクタを書き込むchar_write_axi_master IPの単体シミュレーションが出来たので、XPSプロジェクトにAdd IPした。S_AXI_HP0に接続した。
ZedBoad_BitMap_DispCnt_95_121016.png

ACLKに100MHzのPS部からのクロックを接続し、bitmap_disp_cntrler_axi_master_v2_1_0.mpdに書いてあるすべてのパラメータをchar_write_axi_master.vhd のgeneric に書く必要があったが、そこをクリアすれば、素直に論理合成、インプリメントすることが出きた。
今まで同様にハードウェアをSDKにエクスポートして、SDKを起動し、ビットマップ・ディスプレイ・コントローラをGPIOでONするところまで、ARMプロセッサのソフトウェアを走らせたが、画面は砂嵐のままだった。
ZedBoad_BitMap_DispCnt_77_121001.jpg

単体シミュレーションでは動いていたのだが、どこがおかしいのだろうか?
ChipScope を入れて確かめてみることにした。
  1. 2012年10月16日 15:53 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

FusionPCBに基板を発注しました

アクリルサイン照明用の基板をFusionPCBに発注しました。基盤のサイズは4cm x 10cm で色は赤、基板厚は迷ったんですが、1mm にしました。0.8mm にすると薄すぎて強度が出ないかな?と思って、1mm にしました。色も白にしようと思ったんですが、そうするとArduion の基板が赤なので目立ち過ぎる感じだったので、赤にしました。

KiCADの基板図です。
acrylic_signs_10_121014.png

PCB表面のパターンです。アクリル厚みに収めるため、Arduino基板を直付けするので、その下にパターンやベタはありません。
acrylic_signs_11_121014.png

PCB裏面のパターンです。
acrylic_signs_12_121014.png

早く出来上がってくると良いと思います。抵抗は150Ωの1608を使っていますが、秋月電子でリールで購入すると安いので、それを使う予定です。75Ωの抵抗は150Ωの抵抗の2並列になっています。

あ~。もしかしたら、シルクスクリーン・テキストの最小サイズが小さすぎたかも?もうファイル送っちゃたし、だめだったら何か言ってくるでしょう?
KiCADのDRCはシルクスクリーンの項目が無いんですよね。。。

(2012/10/16:追加)
シルクが小さすぎたので、修正してもう一度FusionPCBに送りました。しかし、その前にIn Production になっていたので、ダメだと思います。残念。。。
acrylic_signs_13_121016.png
  1. 2012年10月15日 05:55 |
  2. Make出展
  3. | トラックバック:0
  4. | コメント:0

KiCADで基板の取付穴を作る

KiCADで基板の取付穴の作り方が全く分からなかった。教えてもらったので、書いておく。

1.Pcbnew のデザインルールからデザインルールを選択する。

2.デザインルール エディタ画面でカスタム ビア サイズのビア?に取付穴を設定する。その場合にドリル=直径にはできないようだ。なるべくドリルと直径を同じような値にする。但し、直径>ドリルでないとエラーになる。直径≒ドリルにすると取付穴が見えなくなるので、私はFusionPCBの最小線幅にしました。
KiCAD_4_121014.png

3.左側のアイコンか、もしくは、配置メニューから配線を選択し、配線モードにする。

4.右クリックメニューから配線幅の選択 -> 3.2mm のビアを選択する。
KiCAD_5_121014.png

5.左クリックして配線を始めてから、マウスを動かす前にVキーを押してビアを掘る。
KiCAD_6_121014.png

6.マウスを動かして配線をせずに、その場でダブルクリックすると取付穴が出来る。
KiCAD_7_121014.png
  1. 2012年10月14日 20:49 |
  2. CADツール
  3. | トラックバック:0
  4. | コメント:3

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する11(char_wirte_axi_master IPを追加2)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する10(char_wirte_axi_master IPを追加1)”の続き。

DDR3 SDRAMのフレーム・バッファにキャラクタを書き込むchar_write_axi_master IP ができた。char_write_axi_master IP は、ARMのソフトウェアからキャラクタデータを書き込むとおかしくなってしまうため、ハードウェアで書き込んだら正常に表示できるのかどうか?を確かめるために行なっている。なお、キャラクタ・ディスプレイ・コントローラでは正常に表示できていると思われるので、DDR3 SDRAMの設定に問題があるのではないか?と思う。(ARMプロセッサにすれば想定外の使い方をしているのだと思うが。。。本当はARMのブートコードを確認して適切に設定する必要があると思う。なかなかそこまで行かないのだが。。。)
単体シミュレーションでは動いているように見える。出来上がったchar_write_axi_master IP とAXI4 Slave の動作をするaxi_master_bfm を接続してシミュレーションを行った。シミュレーション波形を下の図に示す。
ZedBoad_BitMap_DispCnt_93_121014.png

シミュレーション波形で前半の何もない部分は、キャラクタROMを読みだして、そのビット配列に応じてM系列で生成したカラーコードのピクセルを1キャラクタ分FIFOに貯めている部分だ。後半部分では8ライン分を8回に分けてバーストし、AXI4バス経由でDDR3 SDRAMに書き込んでいる。

char_gen_8raster でキャラクタROMを読みだして、そのビット配列に応じてM系列で生成したカラーコードのピクセルを1キャラクタ分FIFOに貯めている。黄色のカーソルラインでその前半部分が動作しているのがわかると思う。1キャラクタの8ライン分のピクセルデータを入力データ幅32ビット、出力データ幅64ビットのFIFOに収めている。そして後半部分でそのキャラクタのピクセルを出力している。その出力イネーブルがraster_ready だ。
ZedBoad_BitMap_DispCnt_94_121014.png

これで単体シミュレーションは終了したので、これをビットマップ・ディスプレイ・コントローラに組み込んでキャラクタが表示できるかどうかをテストする。

(追加)当然ながら、この前半部分(黄色のカーソルバーから前)と後半部分(黄色のカーソルバーから後)は2回目以降は重ねあわせてレイテンシ(または、その一部)を隠蔽することができる。しかし、現在は連続的にキャラクタを描画しているが、実際には、0.2秒に1キャラクタを描画する仕様のため隠蔽する必要がない。
  1. 2012年10月14日 06:05 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

KiCADのホットキー(ショートカットキー)覚書

KiCADのホットキー(ショートカットキー)の覚書を書いておく。

ホットキーじゃないけど、マススホイールでズーム イン、ズーム アウト
CTRL+マススホイール -> 左右移動
SHIFT+マススホイール ー> 上下移動

配線の途中で打ってしまったビアを解除する -> DELキー。

2点間の距離の測定 -> SPACEキーを押すと右下のdx, dy がクリアされるので、カーソルを持って行ってdx, dy の距離を読む


キーでホットキー(ショートカットキー)のヘルプが出る

回路図のホットキー
KiCAD_1_121013.png

基板図面のホットキー
KiCAD_2_121013.png
KiCAD_3_121013.png
  1. 2012年10月13日 13:31 |
  2. CADツール
  3. | トラックバック:0
  4. | コメント:0

KiCADで使っているFreeRouter 使用時のバグ

KiCADで使っているオートルーターのFreeRouter 関連でバグがあって困ってしまった。その場合の対処方法を書いておく。バージョンが古いがFreeRouter の使い方に関しては、”KiCADからFreeRouterへデータを渡してオートルート”を参照して欲しい。現在使用しているKiCADのバージョンはBuild:(2012-jul-04-ja)-stable 。

配置も出来たので、修正も途中なのだが、試しにオートルーターに掛けてみようということで、FreeRouter のアイコンをクリックしてFreeRouter を起動するダイアログを表示した。
acrylic_signs_3_121012.png

下がFreeRouter のダイアログ。最初に”現在のボードを”Pecctra DSN"ファイルにエクスポート”のボタンをクリックして.dsn ファイルを出力する。
acrylic_signs_4_121012.png

下がそのボタンをクリックしてダイアログが開いたところだ。もうすでにacrylic_sign.dsn ファイルがあるが、保存ボタンを押すと.dsn ファイルが保存される。
acrylic_signs_5_121012.png

次にFreeRouter のダイアログで、”Java Web Start を通して FreeRouter を起動する”ボタンをクリックする。そうするとFreeRouter が起動する。
acrylic_signs_6_121012.png

Open Your Own Design ボタンをクリックして、acrylic_sign.dsn をLoad するが、Loading design のダイアログがず~と表示されていて、一向にデザインがロードされない。
acrylic_signs_7_121012.png

早速、ググってみたところ、satonohitoの日記さんの”外部基板その3”がヒットした。それによるとファイルに書いてある表面や裏面の日本語でエラーになっているということだった。
acrylic_sign.dsn の一部を下に示す。

(pcb H:\EDA\KiCad\Work\acrylic_sign\acrylic_sign.dsn
  (parser
    (string_quote ")
    (space_in_quoted_tokens on)
    (host_cad "KiCad's Pcbnew")
    (host_version "(2012-jul-04-ja)-stable")
  )
  (resolution mil 10)
  (unit mil)
  (structure
    (layer 表面
      (type signal)
      (property
        (index 0)
      )
    )
    (layer 裏面
      (type signal)
      (property
        (index 1)
      )
    )


表面をtop に、裏面をbottom に置換した。その後、もう一度FreeRouter でOpen Your Own Design ボタンをクリックして、acrylic_sign.dsn をLoad すると、Load がうまく行って、FreeRouter が立ち上がった。
acrylic_signs_8_121012.png

オートルートさせてみました。
acrylic_signs_9_121012.png
  1. 2012年10月12日 05:16 |
  2. CADツール
  3. | トラックバック:0
  4. | コメント:0

2012年10月11日の現状

アクリルサイン用の基板は部品を配置しています。色々部品も悩みましたが、基板の大きさを100mm x 40mm にすることで既存のアキシャル部品を使えるようです。ダ・ビンチ(Arduino) を載せているので、そこが大きくなりますし、端なので基板の取付穴も必要なところが基板が大きくなった原因です。
acrylic_signs_2_121011.png

”ZedBoardにビットマップ・ディスプレイ・コントローラを追加する”プロジェクトの方も、DDR3 SDRAMにキャラクタをWriteするIP (char_write_axi_master) がだいぶ出来上がってきました。
ZedBoad_BitMap_DispCnt_92_121011.png

まだ、カラーコードを指定する部分が抜けていました。最初に0から始まると最初の頃文字が見えないので、M系列を使用したランダムな色にしようと思っています。そこを記述できたら簡易BFMを使用した単体シミュレーションを行います。
  1. 2012年10月11日 05:58 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

アクリルサインを作っています

出来れば、Maker Faire Tokyo 2012に出してみたいということで照明型のアクリルサインを作っています。アクリルサインとはアクリルで来てた看板のことで、私は看板ではなくオブジェとして使いたいと思っています。アクリルの端から3原色のLEDの光を当てて、図形を浮かび上がらせます。LEDはArduinoのPWMで制御して7色の光を光量を増減しながら出して図形を彩ります。最終的には中学生のお神輿のオブジェとして使います。うちの地区では子供用のお神輿の電飾がとっても派手なんです。そこで、星の形のLED照明アクリルサインを付けて目立つ予定です。
星の形のオブジェはレーザー加工機で加工しますが、なかなか全体を光るようにするのが難しいです。色々な方式を試していますが、だいぶ傾向が分かって来ました。その中で良さそうなパターンを作ろうと思っています。
今は、Arduinoを使ったLED照明制御部を設計しています。回路図は出来たので、基板を設計しようとしています。ラジアル部品だけなので、結構簡単です。PCB CADは、KiCAD使っています。
acrylic_signs_1_121009.jpg

MFT2012展示用としては、最初の段階では、これに更にLEDを付けて、ΔΣ変調して可視光通信で音声やいろんな音を光に乗せて出すということを考えていたんですが、間に合わなそうです。

ZedBoard用のchar_wirte_axi_master IP も頑張って作成中です。平行してやっています。ただ、基板は早く作って発注したいので、基板を優先して作ります。
  1. 2012年10月09日 05:51 |
  2. Make出展
  3. | トラックバック:0
  4. | コメント:0

Segway Tour 実験に参加しました

今日の午前中はSegway Tour 実験に参加してきました。
つくば市はSegwayを公道で乗ることのできる国内唯一の場所だそうです。Segway Tour 実験が行われているというのをツィートで知ったので、申し込んでみました。1回目は抽選に外れたんですが、2回目で当たったので、今日参加してきました。
会場に行くとまずはヘルメットをかぶって、Segwayの乗り方から講習を受けました。乗り方を下に書いときます。

1.Segwayの電源をONする。(電源スイッチ長押し)
2.ハンドルを普通に握って片足を乗せる。(ここでバランサーがONになるみたいです)
3.ハンドルの棒の天辺とその下を手で握って、真っ直ぐ前を見ながら乗る。


まっすぐ乗らなないで、前に傾きながら乗ると前に走ってしまうと思います。とにかくまっすぐ乗るそうです。次は静止ですが、これが結構難しいです。最初は足に力が入ってしまいました。姿勢を正すと良いようです。猫背になると前に行っちゃいますよね?
降り方は、ハンドルの棒の天辺とその下を手で握って、真っ直ぐ前を見ながら片足ずつ降ります。Segwayを傾けると走っちゃいますから、必ずハンドルをまっすぐにしながら降りるようです。
その後は低いパイロンを置いて、静止状態から曲がる練習です。必ず曲がる方向に首を振ってからハンドルを倒して(たぶん体重も移動していると思いますが)曲がりました。更に45度に曲がる練習やスラロームを練習しました。
そうそう、Segwayを押す時は降りてから電源スイッチを1度押しして、ハンドルを持って引くのではなく、押して歩くそうです。
Segway_1_121007.jpg

だいたい練習も終了してツアーに出ました。雨も降っていたので、カッパを貸してもらって着て行きました。
ツアーはつくば市中央部のペデストリアンを北に行きました。みんなで隊列を組んで、Segwayが行くとみんな物珍しそうに見ています。子供には手を振ってたり、挨拶をしながら進みました。この辺でだいぶ慣れて来て、操作もバッチリです。正しい姿勢が重要ということがわかりました。
横断歩道では、Segwayを降りて押して渡りました。こういう規則だそうです。将来的には横断歩道も渡れるようになるかな?と言ってました。
ペデストリアンを北上して松見公園まで行って、松見公園を1回りしてきました。その後、出発地点のノバホール前まで戻りました。色々な人に注目を浴びて楽しかったです。立ちっぱなしですし、結構、疲れましたね。姿勢が良くなりました。とっても楽しかったです。もう少しスラロームとかやってみたかったです。
Segway_2_121007.jpg

最後にスタッフの皆さん、親切に教えて頂いてありがとうございました。とっても楽しかったです。お世話して頂いた方は、柏の葉セグウェイクラブの方でした。もう一度、ありがとうございました。
  1. 2012年10月07日 20:30 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する10(char_wirte_axi_master IPを追加1)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する9(MBを追加)”の続き。

ビットマップ・ディスプレイ・コントローラのXPSプロジェクトにMicroBlazeを入れると、SDKでどうしてもARM-A9のブートコードが動作しないようだ。PL部にクロックが回っていない。方法が見つからないので、MicroBlazeを全部抜いてしまって、ビットマップ・ディスプレイ・コントローラのフレームバッファのDDR3 SDRAMにキャラクタをWriteするIPを作ることにした。これが第2の方法だ。ベースとしては、”AXI4マスタIPの作製(仕様の策定)”、”AXI4マスタIPの作製2(単体シミュレーション)”、”AXI4マスタIPの作製3(インプリメント)”を参考にする。ただしこのAXI4マスタIP (char_write_axi_master) は、キャラクタ・ディスプレイ・コントローラのWrite, Readテスト用なので、ビットマップ・ディスプレイ・コントローラ用に書き換える必要がある。策定した仕様を下に示す。

・0.2秒に1回、1つのキャラクタをビットマップ・ディスプレイ・コントローラ上に描画する。

キャラクタの色は0を除いた7色とする。(2012/10/14変更)キャラクタの色は、24ビットのM系列で決定されるカラーコードを使用する。

・バースト可能な水平8ピクセル分を64ビットバス幅4バーストでフレームバッファ用DDR3 SDRAMにWriteする。これがキャラクタの1ラインとなる。

・アドレスを計算しながら、キャラクタの8ラインを描画する。

・次は色を変えて、次のキャラクタを描画する。

・キャラクタコードは0x20から0x7F までとする。

  1. 2012年10月06日 05:38 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する9(MBを追加)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する8(ACPを使用)”の続き。

前回はACPポートにビットマップ・ディスプレイ・コントローラを接続したがHP0ポートの時と変化がなかった。ACPポートからHP0ポートに戻した。次は、ビットマップ・ディスプレイ・コントローラの動作を確かめるためにPL部にMicroBlazeを入れて、そこからフレームバッファのDDR3 SDRAMにキャラクタを書き込んでみることにした。

・MicroBlazeを入れてS_AXI_HP0に接続した。

MicroBlazeを入れた時に、microblaze_0_axi_mem, microblaze_0_axi_periph が出来たが、MicroBlaze配下のIPはS_AXI_HP0 に接続したので、削除した。
ZedBoad_BitMap_DispCnt_84_121004.png

MicroBlazeの下には、割り込みコントローラのaxi_intc_mbや、タイマーのaxi_tiner_mb、キャラクタROMのchar_rom_axi_linte_mb を入れた。更に、microblaze_0_debug_module も入れてデバックもしようと思う。SDKではARM-A9のデバックしかしなかったが、MicroBlazeを入れてトリプルプロセッサになったことで、どのようにデバックできるかも気になる。下にXPSのBus Interfaces ポートの図を示す。これが完成形だ。
ZedBoad_BitMap_DispCnt_85_121005.png

Addressesタブを下に示す。processing_system7_0's Address Map とmicroblaze_0's Address Map の2つがある。microblaze_0's Address Map ではmicroblaze_0_d_bram_cntlr とmicroblaze_0_i_bram_cntlr が0番地からのアドレスを占めるので、processing_system7_0 のアドレス(DDR3 SDRAMのアドレス)が0番地から取ることができない。これについては、Xilinxのアンサー”14.1 EDK、Zynq - MicroBlaze プロセッサを接続した状態で PS DDR メモリの半分のみが使用されるのはなぜか”を参照のこと。この制限のためにprocessing_system7_0 のアドレスは0x10000000 からとなった。ちょうどフレームバッファの先頭番地なので、都合が良い。
ZedBoad_BitMap_DispCnt_86_121005.png

XPSの設定はこれで終了して、論理合成、インプリメント、ビットストリームの生成を行った。ハードウェアのエクスポートをして、SDKを立ち上げた。
ZedBoad_BitMap_DispCnt_87_121005.png

今までのXilinx Board Support Package (BSP) はARM-A9用だけだったので、SDKのFileメニューのNew から選択して、MicroBlaze用のBSPを作成した。その際に、CPUをmicroblaze_0, ps7_cortex9_0, ps7_cortex9_1 から選択できた。microblaze_0 を選択した。
ZedBoad_BitMap_DispCnt_88_121005.png

drawn_disp_mb_bsp_0 が生成された。次にCのプロジェクト(drawn_disp_mb) を作製した。
ZedBoad_BitMap_DispCnt_89_121005.png

drawn_disp.c をdrawn_disp_mb.cに改名して、drawn_disp_mbのsrc にドラック&ドロップした。エラーが出たので、キャラクタROMのIPのアドレスをXPAR_CHAR_ROM_AXI_LITE_MB_S_AXI_RNG00_BASEADDRに変更した。
ZedBoad_BitMap_DispCnt_90_121005.png

Zynqにビットストリームをダウンロードしてから、まずはARM-A9 のdrawn_dispソフトウェアをRunする。ビットマップ・ディスプレイ・コントローラが動作しない。
デバックモードにしてもmainの先頭行で停止しない。
ZedBoad_BitMap_DispCnt_91_121005.png

MicroBlazeのデバックモジュールを入れたので、それのみになってしまったのか?でも先程のアンサーもあったので、ARMとMicroBlazeが両方動作する環境もあると思うので、調査をしてみようと思う。何か情報をお持ちの方がいらっしゃったら、教えて下さい。よろしくお願いします。
  1. 2012年10月05日 14:08 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する8(ACPを使用)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する7(GPIOを使ってBDCをスタート)”の続き。

前回はディスプレイに表示しているキャラクタがおかしかった。それを直すためにARMプロセッサに接続するAXIポートを変更してみた。今までは、High Performance AXI 32b/64b Slave Portst越しにDDR3 SDRAMをビットマップ・ディスプレイ・コントローラから使用していたが、今度は64b AXI ACP Slave Portを使ってDDR3 SDRAMからピクセルデータを読むことにした。

・XPSのZynqタブで、High Performance AXI 32b/64b Slave Portstをクリックした。
ZedBoad_BitMap_DispCnt_67_120930.png

・High Performance Slave AXI Interfaces のEnable S_AXI_HP0 interface のチェックを外した。
ZedBoad_BitMap_DispCnt_78_121003.png

・Accelerator Coherency Port (ACP) Slave AXI Interface を展開して、Enable S_AXI_ACP interface にチェックを入れた。
ZedBoad_BitMap_DispCnt_79_121003.png

・Zynqタブを見ると64b AXI ACP Slave Portが使用されていた。
ZedBoad_BitMap_DispCnt_83_121003.png

・Bus Interfaces タブで、S_AXI_ACP をaxi_interconnect_1 に接続した。
ZedBoad_BitMap_DispCnt_80_121003.png

・Ports タブで、processing_system7_0 の S_AXI_ACP_ACLK がNo Connection だったので、FCLK_CLK0に接続した。
ZedBoad_BitMap_DispCnt_81_121003.png

・ProjectメニューからDesign Rule Check を選択したらエラーはなかった。

・XPSを閉じて、PlanAheadで論理合成、インプリメント・ビットストリームの生成まで行った。

・ビットストリームの生成が終了後に、ハードウェアをSDKにエクスポートして、SDKを立ち上げた。

・SDKでキャラクタを描画するソフトウェアをRunしたところ、前回同様にキャラクタが乱れていた。

残念だが、ACPを使うことが出来た。ACPのChipScopeの波形を下に示す。HP0と変わらない。
ZedBoad_BitMap_DispCnt_82_121003.png

HP0の波形を下に示す。ACPの波形を違って、S_AXI_HP0関連の信号が動作しているのがわかると思う。
ZedBoad_BitMap_DispCnt_66_120930.png

ACPポートを使用することが出来たが、キャラクタの乱れは直らなかった。キャッシュのコヒーレンシが取れているはずのACPポートでキャラクタの乱れが直らないということはキャッシュ関連では無いと思う。もしかして、キャラクタROMのReadがおかしいのか?それとも適当に決めたDDR3 SDRAMのフレームバッファ用アドレスが悪いのか?
  1. 2012年10月03日 04:49 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する7(GPIOを使ってBDCをスタート)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する6(完成とは言えない)”の続き。

前回は、ビットマップ・ディスプレイ・コントローラが動いたけど、最初にリセットが解除されてから85秒間のWaitが必要だった。それはとっても面倒なので、ARMの初期化ルーチンが終了してから、ビットマップ・ディスプレイ・コントローラが動作するように変更した。GPIOを使用して、ARMのソフトウェアでビットマップ・ディスプレイ・コントローラのスタートの合図をすることにした。
前からあるのだが、init_doneという信号をビットマップ・ディスプレイ・コントローラに追加した。DDR3 SDRAMやそのコントローラーの初期化が終了して準備が出来た段階でinit_doneを0から1にすると、ビットマップ・ディスプレイ・コントローラが動作を開始する。init_doneへ1を出力するのは、XPSに追加したGPIOだ。それでは詳しく見ていこう。

・XPSに1ビット幅のaxi_gpio を追加した (axi_gpio_0) 。

・axi_gpio_0 の外部ポートを削除した。

・bitmap_disp_cntrler_axi_master_0 のinit_done とaxi_gpio_0 のGPIO_IO_O を接続した。
ZedBoad_BitMap_DispCnt_73_120930.png

・なぜか?アドレスがマップされていなかったので、マップした。
ZedBoad_BitMap_DispCnt_74_121001.png

・XPSの生成ファイルをクリアした。

・PlanAheadで論理合成、インプリメント、ビットストリームの生成まで行った。

・SDKにハードウェアをエクスポートして、SDKを立ち上げた。

・ビットストリームをZedBoardにダウンロードしてから、SDKをデバックモードにした。
ZedBoad_BitMap_DispCnt_75_121001.png

この状態ではディスプレイに表示されていない。信号なしの状態だ。

・axi_gpio_0 から1をbitmap_disp_cntrler_axi_master_0 のinit_done に入力した。
ZedBoad_BitMap_DispCnt_76_121001.png

ディスプレイにランダムなパターンが表示された。
ZedBoad_BitMap_DispCnt_77_121001.jpg

思惑通りに行って、とっても嬉しい。デバックモードでビットマップ・ディスプレイ・コントローラをスタートさせるまでに時間があったし、当然といえば当然なのだと思う。本番でもWait時間が足りなかったら、ソフトウェアループさせるか、タイマーでビットマップ・ディスプレイ・コントローラを起動させれば良いと思う。
次は、キャラクタが化けるのを修正したい。腹案が3つあるので、まずは1つ目から確認してみよう。

(追記)
SDKからRunでビットマップ・ディスプレイ・コントローラが動作しました。問題ないようです。
  1. 2012年10月02日 05:04 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

XPSで使用するIPの制御用関数の情報

EDKのXPSを起動して、IPを追加していって回路を構成するが、それらのIPの制御用関数の情報が Xilinx\14.2\ISE_DS\EDK\sw\XilinxProcessorIPLib\drivers にある。
XPS_driver_1_121002.png

例えば、gpio_v3_00_a を見てみよう。gpio_v3_00_aフォルダに入ると、build, data, doc, examples, src フォルダがある。
XPS_driver_2_121002.png

フォルダの名前の通りのファイルが下にあるのだが、doc/html/apiフォルダの下に行くと、htmlファイルがある。index.html をブラウザで表示するとマニュアルのトップページを見ることができる。
XPS_driver_3_121002.png
  1. 2012年10月02日 03:44 |
  2. EDK
  3. | トラックバック:0
  4. | コメント:0