nao-milkの経験ブログ

25年間の半導体エンジニア経験で知り得た内容を記載したブログです。

FRAMにアクセス (シミュレーション編)

f:id:nao-milk:20210419163451p:plain
FRAM I/FブロックをトップとしたRTLシミュレーションになります。
ここでは、テストベンチ記述を記載します。
尚、言語はSystemVerilogとなります。

FRAM I/Fブロックの仕様及びソースコードは以下を参照ください。
nao-milk.hatenablog.com
nao-milk.hatenablog.com

対向モデル

FRAM I/Fブロックの対向モデル(FRAMデバイス)は、シミュレーションモデルがベンダーで提供されていますので、それを入手してください。

テストベンチ

テストベンチは、FRAM I/FブロックとFRAMモデルを配置し、クロック生成及びタスクを準備し、テストシナリオを記述します。
尚、レジスタなどは構造体にした方が便利なため、記述はSystemVerilogにしています。

ヘッダファイル

レジスタの構造体とアドレス 及び よく使う処理をタスク化しています。

// --
// --   レジスタパック
// -------------------------------------------------------------------
typedef struct  packed  {   // ** 処理イネーブル
    reg  [31: 1]    Non ;       // 未使用
    reg             ENB ;       // 処理イネーブル
} Register_ENB;
typedef struct  packed  {   // ** 制御パラメータ
    reg  [31: 7]    Non ;       // 未使用
    reg  [ 6: 5]    TOK ;       // 取り込みタイミング
    reg             POL ;       // シリアルクロック極性
    reg  [ 3: 0]    DIV ;       // シリアルクロック分周
} Register_CNT;
typedef struct  packed  {   // ** アクセスバイト数
    reg  [31:21]    Non ;       // 未使用
    reg  [20: 0]    ACS ;       // アクセスバイト数
} Register_ACS;
typedef struct  packed  {   // ** 出力バイト数
    reg  [31:21]    Non ;       // 未使用
    reg  [20: 0]    OTC ;       // 出力バイト数
} Register_OTC;
typedef struct  packed  {   // ** 割り込みステータス
    reg  [31: 1]    Non ;       // 未使用
    reg             IRS ;       // 割り込みステータス
} Register_IRS;
typedef struct  packed  {   // ** 割り込みマスク
    reg  [31: 1]    Non ;       // 未使用
    reg             IRM ;       // 割り込みマスク
} Register_IRM;
typedef struct  packed  {   // ** バッファリード/ライトアドレス
    reg  [31:21]    Non ;       // 未使用
    reg  [20: 0]    BAD ;       // バッファリード/ライトアドレス
} Register_BAD;
typedef struct  packed  {   // ** バッファリード/ライトデータ
    reg  [31: 8]    Non ;       // 未使用
    reg  [ 7: 0]    BDT ;       // バッファリード/ライトデータ
} Register_BDT;

struct  {
    reg  [31:0]     ENB = 'h00;
    reg  [31:0]     CNT = 'h04;
    reg  [31:0]     ACS = 'h08;
    reg  [31:0]     OTC = 'h0C;
    reg  [31:0]     IRS = 'h10;
    reg  [31:0]     IRM = 'h14;
    reg  [31:0]     BAD = 'h18;
    reg  [31:0]     BDT = 'h1C;
} REG_ADDR;
struct  {
    Register_ENB    ENB = 32'h0000_0000;
    Register_CNT    CNT = 32'h0000_0000;
    Register_ACS    ACS = 32'h0000_0000;
    Register_OTC    OTC = 32'h0000_0000;
    Register_IRS    IRS = 32'h0000_0000;
    Register_IRM    IRM = 32'h0000_0001;
    Register_BAD    BAD = 32'h0000_0000;
    Register_BDT    BDT = 32'h0000_0000;
} REG_DATA;

// --
// --   処理タスク
// -------------------------------------------------------------------

// ++
// ++   書き込みイネーブル ラッチ設定
// +++++++++++++++++++++++++++++++++++++++++++
task SetWREN();
    begin
        $display("\n===== %m (Enable Write) =====");
        REG_DATA.ACS.ACS    = 0;    WrCPU(REG_ADDR.ACS,REG_DATA.ACS);   // アクセスバイト数
        REG_DATA.OTC.OTC    = 0;    WrCPU(REG_ADDR.OTC,REG_DATA.OTC);   // 出力バイト数
        REG_DATA.IRM.IRM    = 1;    WrCPU(REG_ADDR.IRM,REG_DATA.IRM);   // 割り込みマスク
        REG_DATA.IRS.IRS    = 1;    WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス クリア
                                    RdCPU(REG_ADDR.IRS,REG_DATA.IRS);
                                    $display(" >>> Read : %p",REG_DATA.IRS);
        REG_DATA.BAD.BAD    = 0;    WrCPU(REG_ADDR.BAD,REG_DATA.BAD);   // バッファアドレス
        REG_DATA.BDT.BDT    ='h06;  WrCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
        REG_DATA.ENB.ENB    = 1;    WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                    RdCPU(REG_ADDR.ENB,REG_DATA.ENB);
        while(REG_DATA.ENB.ENB===1) begin
            repeat(20)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);
        end
        REG_DATA.IRS.IRS    = 1;    WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス クリア
        REG_DATA.IRM.IRM    = 0;    WrCPU(REG_ADDR.IRM,REG_DATA.IRM);   // 割り込みマスク 解除
    end
endtask

テストベンチトップ

テストシナリオでは、リセット解除後からマスタモード → スレーブモードのリード系 → スレーブモードのライト系の動作を行い、各コマンドの発行を行っています。
また、FRAMのサイズも 256K×8bit分アクセスするテストを行っています。
最終的にすべて実行し、変数errorが0であれば、問題無しとしています。
尚、レジスタの初期値確認や割り込み動作などは、別のシナリオで確認しています。

データ比較は、
リード(FRAMデバイス → FRAM I/Fブロック)時、FRAMデバイス内の配列とAvalon-MM Master/Slave I/Fで取得したデータが等しいか確認します。
ライト(FRAM I/Fブロック → FRAMデバイス)時、Avalon-MM Slave I/Fブロックにライトしたデータが、FRAMデバイス内の配列と等しいか確認します。

`timescale  1ns/1ps

module  TB_TOP;
integer     PERIOD      = 0;
reg         CPU_RST_N   = 0;    // @@ CPU I/F用 リセット
reg         CPU_CLK     = 0;    // @@ CPU I/F用 クロック
reg         SYS_RST_N   = 0;    // @@ システム動作用 リセット
reg         SYS_CLK     = 0;    // @@ システム動作用 クロック
reg         MST_SLV     = 0;    // @@ Avalon-MM Master/Slave切替 [0:Master]
reg         PRM_ENB     = 0;    // @@ 処理イネーブル [1→0:OFF 0→1:ON]
reg  [ 3:0] PRM_DIV     = 0;    // @@ シリアルクロック周波数設定
reg         PRM_POL     = 0;    // @@ シリアルクロック極性設定
reg  [ 1:0] PRM_TAK     = 0;    // @@ シリアルデータ取り込みタイミング設定
reg  [20:0] PRM_ACS     = 0;    // @@ アクセスバイト数 (最大値:1,048,579)
reg  [ 1:0] PRM_OTC     = 0;    // @@ 出力バイト数 (最大値:3)
reg         PRM_MSK     = 1;    // @@ 割り込みマスク
reg         PRM_CLR     = 0;    // @@ 割り込みクリア
reg  [31:0] PRM_STA     = 0;    // @@ Avalon-MM(Master側) ライトベースアドレス
reg  [ 7:0] PRM_CMD     = 0;    // @@ RAMコマンド
reg  [23:0] PRM_ADD     = 0;    // @@ FRAMアドレス
reg         SAV_WEN     = 0;    // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  ライトイネーブル
reg         SAV_REN     = 0;    // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  リードイネーブル
reg  [ 5:0] SAV_ADD     = 0;    // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  リード/ライトイネーブル
reg  [31:0] SAV_WDT     = 0;    // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  ライトデータ
reg         MAV_WIT     = 0;    // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ウェイトリクエスト
wire        SAV_WIT     ;       // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  ウェイトリクエスト
wire        SAV_RVL     ;       // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  リードデータイネーブル
wire [31:0] SAV_RDT     ;       // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  リードデータ
wire        MAV_WEN     ;       // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトイネーブル
wire [31:0] MAV_ADD     ;       // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトアドレス
wire [ 3:0] MAV_BEN     ;       // @@ [CPU_CLK↑同期] Avalon-MM(Master側) バイトイネーブル
wire [ 7:0] MAV_WDT     ;       // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトデータ
wire        CPU_IRQ     ;       // @@ [CPU_CLK↑同期] 割り込み信号
wire        MON_ENB     ;       // @@ [CPU_CLK↑同期] 動作モニタ [0:停止中  1:動作中]
wire        FRAM_CS_N   ;       // @@ [SYS_CLK↑同期] FRAM SPI チップセレクト
wire        FRAM_SCLK   ;       // @@ [SYS_CLK↑同期] FRAM SPI シリアルクロック
wire        FRAM_MOSI   ;       // @@ [SYS_CLK↑同期] FRAM SPI シリアル出力
wire        FRAM_MISO   ;       // @@ [SYS_CLK↑同期] FRAM SPI シリアル入力
wire        FRAM_OUTE   ;       // @@ [SYS_CLK↑同期] FRAM SPI 出力イネーブル[1:出力 0:Hi-Z]

// ++ タイミング設定
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Clock波形
real        tcCPU_CLK   = 20.000;
real        tpCPU_CLK   =  5.000;
real        tnCPU_CLK   = 15.000;
real        tcSYS_CLK   =  5.000;
real        tpSYS_CLK   =  1.000;
real        tnSYS_CLK   =  3.500;

// FRAM モデル間遅延
defparam    UnDlyFRAM_CS_N.NsStep= 0 ,UnDlyFRAM_CS_N.PsStep=  0;
defparam    UnDlyFRAM_SCLK.NsStep= 0 ,UnDlyFRAM_SCLK.PsStep=  0;
defparam    UnDlyFRAM_MOSI.NsStep= 0 ,UnDlyFRAM_MOSI.PsStep=  0;
defparam    UnDlyFRAM_MISO.NsStep= 0 ,UnDlyFRAM_MISO.PsStep=  0;

// --
// -- テストシナリオ
// -------------------------------------------------------------------
`define MASTER_MODE
`define SLAVE_MODE_READ
`define SLAVE_MODE_WRITE

reg  [ 7:0] PMEM[0:262144];
integer     error = 0;
`include    "TB_HEAD.sv"
always @( posedge CPU_CLK ) begin
    if( (MAV_WEN===1'b1) & (MAV_WIT==0) ) begin
        if( MAV_BEN[0]===1'b1 ) PMEM[MAV_ADD-PRM_STA] <= MAV_WDT;
        if( MAV_BEN[1]===1'b1 ) PMEM[MAV_ADD-PRM_STA] <= MAV_WDT;
        if( MAV_BEN[2]===1'b1 ) PMEM[MAV_ADD-PRM_STA] <= MAV_WDT;
        if( MAV_BEN[3]===1'b1 ) PMEM[MAV_ADD-PRM_STA] <= MAV_WDT;
    end
    MAV_WIT <= $random;
end

defparam    FRAM.hasSN = 1;
integer     ei;
initial begin
    #0;
    repeat(10)  @(PERIOD);
                @(posedge CPU_CLK);     #1  CPU_RST_N   = 1;
                @(posedge SYS_CLK);     #1  SYS_RST_N   = 1;

    // @@
    // @@   Master Mode
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
`ifdef  MASTER_MODE
    repeat(20)  @(PERIOD);
    MST_SLV = 0;                // @@ Avalon-MM Master/Slave切替 [0:Master]

    // ++ [ 初期設定 ] ++
    PRM_STA = 32'h8000_0000;    // @@ Avalon-MM(Master側) ライトベースアドレス
    PRM_DIV = 0;                // @@ シリアルクロック周波数設定
    PRM_POL = 0;                // @@ シリアルクロック極性設定
    PRM_TAK = 0;                // @@ シリアルデータ取り込みタイミング設定
    // ++ [ 割り込み設定 ] ++
    repeat( 5)  @(PERIOD)   PRM_CLR = 1;    // @@ 割り込みクリア
    PRM_CLR = 0;
    PRM_MSK = 0;                // @@ 割り込みマスク

    // ++ デバイスID読み出し
    // +++++++++++++++++++++++++++++++++++++++++++
    FRAM.Device_ID[0] = 'h80;
    FRAM.Device_ID[1] = 'h40;
    FRAM.Device_ID[2] = 'h20;
    FRAM.Device_ID[3] = 'h10;
    FRAM.Device_ID[4] = 'h08;
    FRAM.Device_ID[5] = 'h04;
    FRAM.Device_ID[6] = 'h02;
    FRAM.Device_ID[7] = 'h01;
    FRAM.Device_ID[8] = 'hA5;
    repeat( 5)  @(PERIOD);  $display("\n===== Read Device ID =====");
                            PRM_ACS = 9;    // @@ アクセスバイト数 (最大値:1,048,579)
                            PRM_OTC = 0;    // @@ 出力バイト数 (最大値:3)
                            PRM_CMD = 'h9F; // @@ RAMコマンド
                            PRM_ADD = 0;    // @@ FRAMアドレス
    repeat( 5)  @(PERIOD);  PRM_ENB = 0;    // @@ 処理イネーブル [1→0:OFF 0→1:ON]
                            PRM_ENB = 1;
    wait(CPU_IRQ===1'b1);   $display(" >>> CPU_IRQ=1");
    repeat( 5)  @(PERIOD);
    repeat( 5)  @(PERIOD)   PRM_CLR = 1;    // @@ 割り込みクリア
                            PRM_CLR = 0;
    repeat( 5)  @(PERIOD);  PRM_ENB = 0;    // @@ 処理イネーブル [1→0:OFF 0→1:ON]
    repeat(10)  @(PERIOD);
    for(ei=0;ei<9;ei=ei+1)begin
        $display(" >>> Check : [%h] => %h , %h",ei[7:0],PMEM[ei],FRAM.Device_ID[ei]);
        if(PMEM[ei]!==FRAM.Device_ID[ei])   error = error + 1;
    end
    $display(" %m >> Error : %d",error);

    // ++ シリアル番号読み出し
    // +++++++++++++++++++++++++++++++++++++++++++
    FRAM.Serial_No[0] = 'hFF^'h80;
    FRAM.Serial_No[1] = 'hFF^'h40;
    FRAM.Serial_No[2] = 'hFF^'h20;
    FRAM.Serial_No[3] = 'hFF^'h10;
    FRAM.Serial_No[4] = 'hFF^'h08;
    FRAM.Serial_No[5] = 'hFF^'h04;
    FRAM.Serial_No[6] = 'hFF^'h02;
    FRAM.Serial_No[7] = 'hFF^'h01;
    repeat( 5)  @(PERIOD);  $display("\n===== Read Serial No =====");
                            PRM_ACS = 8;    // @@ アクセスバイト数 (最大値:1,048,579)
                            PRM_OTC = 0;    // @@ 出力バイト数 (最大値:3)
                            PRM_CMD = 'hC3; // @@ RAMコマンド
                            PRM_ADD = 0;    // @@ FRAMアドレス
    repeat( 5)  @(PERIOD);  PRM_ENB = 0;    // @@ 処理イネーブル [1→0:OFF 0→1:ON]
                            PRM_ENB = 1;
    wait(CPU_IRQ===1'b1);   $display(" >>> CPU_IRQ=1");
    repeat( 5)  @(PERIOD);
    repeat( 5)  @(PERIOD)   PRM_CLR = 1;    // @@ 割り込みクリア
                            PRM_CLR = 0;
    repeat( 5)  @(PERIOD);  PRM_ENB = 0;    // @@ 処理イネーブル [1→0:OFF 0→1:ON]
    repeat(10)  @(PERIOD);
    for(ei=0;ei<8;ei=ei+1)begin
        $display(" >>> Check : [%h] => %h , %h",ei[7:0],PMEM[ei],FRAM.Serial_No[ei]);
        if(PMEM[ei]!==FRAM.Serial_No[ei])   error = error + 1;
    end
    $display(" %m >> Error : %d",error);

    // ++ 特殊セクタ読み出し
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<256;ei=ei+1)    FRAM.Smem[ei] = $random;
    repeat( 5)  @(PERIOD);  $display("\n===== Special Sector Read =====");
                            PRM_ACS = 256+4-1;  // @@ アクセスバイト数 (最大値:1,048,579)
                            PRM_OTC =     4-1;  // @@ 出力バイト数 (最大値:3)
                            PRM_CMD = 'h4B;     // @@ RAMコマンド
                            PRM_ADD = 'h000000; // @@ FRAMアドレス
    repeat( 5)  @(PERIOD);  PRM_ENB = 0;        // @@ 処理イネーブル [1→0:OFF 0→1:ON]
                            PRM_ENB = 1;
    wait(CPU_IRQ===1'b1);   $display(" >>> CPU_IRQ=1");
    repeat( 5)  @(PERIOD);
    repeat( 5)  @(PERIOD)   PRM_CLR = 1;    // @@ 割り込みクリア
                            PRM_CLR = 0;
    repeat( 5)  @(PERIOD);  PRM_ENB = 0;    // @@ 処理イネーブル [1→0:OFF 0→1:ON]
    repeat(10)  @(PERIOD);
    for(ei=0;ei<256;ei=ei+1)begin
        $display(" >>> Check : [%h] => %h , %h",ei[7:0],PMEM[ei],FRAM.Smem[ei]);
        if(PMEM[ei]!==FRAM.Smem[ei])    error = error + 1;
    end
    $display(" %m >> Error : %d",error);

    // ++ 読み出し動作
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<262144;ei=ei+1) FRAM.mem[ei] = $random;
    repeat( 5)  @(PERIOD);  $display("\n===== Read Data from Memory =====");
                            PRM_ACS = 262144+4-1;   // @@ アクセスバイト数 (最大値:1,048,579)
                            PRM_OTC =        4-1;   // @@ 出力バイト数 (最大値:3)
                            PRM_CMD = 'h03;         // @@ RAMコマンド
                            PRM_ADD = 'h000000;     // @@ FRAMアドレス
    repeat( 5)  @(PERIOD);  PRM_ENB = 0;            // @@ 処理イネーブル [1→0:OFF 0→1:ON]
                            PRM_ENB = 1;
    wait(CPU_IRQ===1'b1);   $display(" >>> CPU_IRQ=1");
    repeat( 5)  @(PERIOD);
    repeat( 5)  @(PERIOD)   PRM_CLR = 1;    // @@ 割り込みクリア
                            PRM_CLR = 0;
    repeat( 5)  @(PERIOD);  PRM_ENB = 0;    // @@ 処理イネーブル [1→0:OFF 0→1:ON]
    repeat(10)  @(PERIOD);
    for(ei=0;ei<262144;ei=ei+1)begin
        $display(" >>> Check : [%h] => %h , %h",ei[7:0],PMEM[ei],FRAM.mem[ei]);
        if(PMEM[ei]!==FRAM.mem[ei])    error = error + 1;
    end
    $display(" %m >> Error : %d",error);
`endif

    // @@
    // @@   Slave Mode (Read)
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
`ifdef  SLAVE_MODE_READ
    repeat(20)  @(PERIOD);
    MST_SLV = 1;                // @@ Avalon-MM Master/Slave切替 [0:Master]

    // ++ [ 初期設定 ] ++
    PRM_STA = 32'h8000_0000;    // @@ Avalon-MM(Master側) ライトベースアドレス
    PRM_DIV = 15;               // @@ シリアルクロック周波数設定
    PRM_POL = 1;                // @@ シリアルクロック極性設定
    PRM_TAK = 3;                // @@ シリアルデータ取り込みタイミング設定
    // ++ [ 割り込み設定 ] ++
    repeat( 5)  @(PERIOD)   PRM_CLR = 1;    // @@ 割り込みクリア
    PRM_CLR = 1;
    PRM_MSK = 1;                // @@ 割り込みマスク

    // ++
    // ++   制御レジスタの設定
    // +++++++++++++++++++++++++++++++++++++++++++
    REG_DATA.CNT.TOK    = 0;    // 取り込みタイミング
    REG_DATA.CNT.POL    = 1;    // シリアルクロック極性
    REG_DATA.CNT.DIV    = 0;    // シリアルクロック分周
    REG_DATA.ACS.ACS    = 0;    // アクセスバイト数
    REG_DATA.OTC.OTC    = 0;    // 出力バイト数
    REG_DATA.IRS.IRS    = 1;    // 割り込みステータス
    REG_DATA.IRM.IRM    = 0;    // 割り込みマスク
    REG_DATA.BAD.BAD    = 0;    // バッファリード/ライトアドレス
    REG_DATA.BDT.BDT    = 0;    // バッファリード/ライトデータ
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.CNT,REG_DATA.CNT);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.ACS,REG_DATA.ACS);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.OTC,REG_DATA.OTC);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.IRS,REG_DATA.IRS);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.IRM,REG_DATA.IRM);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,REG_DATA.BAD);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,REG_DATA.BDT);

    // ++ デバイスID読み出し
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<9;ei=ei+1)  FRAM.Device_ID[ei] = $random;
    repeat( 5)  @(PERIOD);  $display("\n===== Read Device ID =====");
    REG_DATA.ACS.ACS    = 9;    // アクセスバイト数
    REG_DATA.OTC.OTC    = 0;    // 出力バイト数
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.ACS,REG_DATA.ACS);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.OTC,REG_DATA.OTC);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,'h00000000);     // バッファアドレス
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,'h9F);           // バッファデータ
    for(ei=0;ei<9;ei=ei+1)  WrCPU(REG_ADDR.BDT,'hFF);           // バッファデータ[リード領域の初期化]
    REG_DATA.ENB.ENB = 1;   WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                            $display(" >>> Read : %p",REG_DATA.ENB);
    wait(CPU_IRQ===1'b1);   $display(" >>> CPU_IRQ=1");         // 割り込み待ち
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                            $display(" >>> Read : %p",REG_DATA.ENB);
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                            $display(" >>> Read : %p",REG_DATA.IRS); 
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータスクリア
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                            $display(" >>> Read : %p - Clear",REG_DATA.IRS); 
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,'h00000001);     // バッファアドレス
    for(ei=0;ei<9;ei=ei+1) begin
        repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.BDT,REG_DATA.BDT);
        $display(" >>> Check : [%h] => %h , %h",ei[23:0],REG_DATA.BDT.BDT,FRAM.Device_ID[ei]);
        if(REG_DATA.BDT.BDT!==FRAM.Device_ID[ei])   error = error + 1;
    end
    $display(" %m >> Error : %d",error);

    // ++ シリアル番号読み出し
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<8;ei=ei+1)  FRAM.Serial_No[ei] = $random;
    repeat( 5)  @(PERIOD);  $display("\n===== Read Serial No =====");
    REG_DATA.ACS.ACS    = 8;    // アクセスバイト数
    REG_DATA.OTC.OTC    = 0;    // 出力バイト数
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.ACS,REG_DATA.ACS);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.OTC,REG_DATA.OTC);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,'h00000000);     // バッファアドレス
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,'hC3);           // バッファデータ
    for(ei=0;ei<8;ei=ei+1)  WrCPU(REG_ADDR.BDT,'hFF);           // バッファデータ[リード領域の初期化]
    REG_DATA.ENB.ENB = 1;   WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                            $display(" >>> Read : %p",REG_DATA.ENB);
    wait(CPU_IRQ===1'b1);   $display(" >>> CPU_IRQ=1");         // 割り込み待ち
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                            $display(" >>> Read : %p",REG_DATA.ENB);
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                            $display(" >>> Read : %p",REG_DATA.IRS); 
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータスクリア
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                            $display(" >>> Read : %p - Clear",REG_DATA.IRS); 
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,'h00000001);     // バッファアドレス
    for(ei=0;ei<8;ei=ei+1) begin
        repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.BDT,REG_DATA.BDT);
        $display(" >>> Check : [%h] => %h , %h",ei[23:0],REG_DATA.BDT.BDT,FRAM.Serial_No[ei]);
        if(REG_DATA.BDT.BDT!==FRAM.Serial_No[ei])   error = error + 1;
    end
    $display(" %m >> Error : %d",error);

    // ++ 特殊セクタ読み出し
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<256;ei=ei+1)    FRAM.Smem[ei] = $random;
    repeat( 5)  @(PERIOD);  $display("\n===== Special Sector Read =====");
    REG_DATA.ACS.ACS    = 256+4-1;  // アクセスバイト数
    REG_DATA.OTC.OTC    = 4-1;      // 出力バイト数
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.ACS,REG_DATA.ACS);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.OTC,REG_DATA.OTC);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,'h00000000);     // バッファアドレス
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,'h4B);           // バッファデータ
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,'h00);           // バッファデータ
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,'h00);           // バッファデータ
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,'h00);           // バッファデータ
    REG_DATA.ENB.ENB = 1;   WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                            $display(" >>> Read : %p",REG_DATA.ENB);
    wait(CPU_IRQ===1'b1);   $display(" >>> CPU_IRQ=1");         // 割り込み待ち
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                            $display(" >>> Read : %p",REG_DATA.ENB);
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                            $display(" >>> Read : %p",REG_DATA.IRS); 
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータスクリア
    repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                            $display(" >>> Read : %p - Clear",REG_DATA.IRS); 
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,'h00000004);     // バッファアドレス
    for(ei=0;ei<256;ei=ei+1) begin
        repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.BDT,REG_DATA.BDT);
        $display(" >>> Check : [%h] => %h , %h",ei[23:0],REG_DATA.BDT.BDT,FRAM.Smem[ei]);
        if(REG_DATA.BDT.BDT!==FRAM.Smem[ei])    error = error + 1;
    end
    $display(" %m >> Error : %d",error);

    // ++ 読み出し動作
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<262144;ei=ei+1)    FRAM.mem[ei] = $random;
    repeat( 5)  @(PERIOD);  $display("\n===== Read Data from Memory =====");
    begin : MemRead
        ei = 0;
        while( ei < 262144 ) begin
            REG_DATA.ACS.ACS    = 2048+4-1; // アクセスバイト数
            REG_DATA.OTC.OTC    =      4-1; // 出力バイト数
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.ACS,REG_DATA.ACS);
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.OTC,REG_DATA.OTC);
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,'h00000000);     // バッファアドレス
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,'h03);           // バッファデータ
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,ei[23:16]);      // バッファデータ
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,ei[15: 8]);      // バッファデータ
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,ei[ 7: 0]);      // バッファデータ
            REG_DATA.ENB.ENB = 1;   WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
            repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                    $display(" >>> Read : %p",REG_DATA.ENB);
            wait(CPU_IRQ===1'b1);   $display(" >>> CPU_IRQ=1");         // 割り込み待ち
            repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                    $display(" >>> Read : %p",REG_DATA.ENB);
            repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                                    $display(" >>> Read : %p",REG_DATA.IRS); 
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータスクリア
            repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                                    $display(" >>> Read : %p - Clear",REG_DATA.IRS); 
            repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,'h00000004);     // バッファアドレス
            repeat(2048) begin
                repeat( 5)  @(PERIOD);  RdCPU(REG_ADDR.BDT,REG_DATA.BDT);
                $display(" >>> Check : [%h] => %h , %h",ei[23:0],REG_DATA.BDT.BDT,FRAM.mem[ei]);
                if(REG_DATA.BDT.BDT!==FRAM.mem[ei]) error = error + 1;
                ei = ei + 1;
            end
            $display(" %m >> Error : %d",error);
        end
    end
`endif

    // @@
    // @@   Slave Mode (Write)
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
`ifdef  SLAVE_MODE_WRITE
    repeat(20)  @(PERIOD);
    MST_SLV = 1;                // @@ Avalon-MM Master/Slave切替 [0:Master]

    // ++   制御レジスタの設定
    // +++++++++++++++++++++++++++++++++++++++++++
    REG_DATA.CNT.TOK    = 0;    // 取り込みタイミング
    REG_DATA.CNT.POL    = 1;    // シリアルクロック極性
    REG_DATA.CNT.DIV    = 0;    // シリアルクロック分周
    REG_DATA.ACS.ACS    = 0;    // アクセスバイト数
    REG_DATA.OTC.OTC    = 0;    // 出力バイト数
    REG_DATA.IRS.IRS    = 1;    // 割り込みステータス
    REG_DATA.IRM.IRM    = 0;    // 割り込みマスク
    REG_DATA.BAD.BAD    = 0;    // バッファリード/ライトアドレス
    REG_DATA.BDT.BDT    = 0;    // バッファリード/ライトデータ
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.CNT,REG_DATA.CNT);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.ACS,REG_DATA.ACS);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.OTC,REG_DATA.OTC);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.IRS,REG_DATA.IRS);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.IRM,REG_DATA.IRM);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BAD,REG_DATA.BAD);
    repeat( 5)  @(PERIOD);  WrCPU(REG_ADDR.BDT,REG_DATA.BDT);

    // ++ ステータス レジスタの読み出し
    // +++++++++++++++++++++++++++++++++++++++++++
    ei =0;
    repeat( 5)  @(PERIOD);  $display("\n===== Read Status Register =====");
    REG_DATA.ACS.ACS    = 1;    WrCPU(REG_ADDR.ACS,REG_DATA.ACS);   // アクセスバイト数
    REG_DATA.OTC.OTC    = 0;    WrCPU(REG_ADDR.OTC,REG_DATA.OTC);   // 出力バイト数
    REG_DATA.IRM.IRM    = 1;    WrCPU(REG_ADDR.IRM,REG_DATA.IRM);   // 割り込みマスク
    REG_DATA.IRS.IRS    = 1;    WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス クリア
                                RdCPU(REG_ADDR.IRS,REG_DATA.IRS);
                                $display(" >>> Read : %p",REG_DATA.IRS);
    REG_DATA.BAD.BAD    = 0;    WrCPU(REG_ADDR.BAD,REG_DATA.BAD);   // バッファアドレス
    REG_DATA.BDT.BDT    ='h05;  WrCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
    REG_DATA.ENB.ENB    = 1;    WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                RdCPU(REG_ADDR.ENB,REG_DATA.ENB);
    while(REG_DATA.ENB.ENB===1) begin
        repeat(20)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);
    end
    REG_DATA.IRM.IRM    = 0;    WrCPU(REG_ADDR.IRM,REG_DATA.IRM);   // 割り込みマスク 解除
    REG_DATA.BAD.BAD    = 1;    WrCPU(REG_ADDR.BAD,REG_DATA.BAD);   // バッファアドレス
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
    $display(" >>> Check : [%h] => %h , %h",ei[23:0],REG_DATA.BDT.BDT,FRAM.realStatusRegister);
    if(REG_DATA.BDT.BDT!==FRAM.realStatusRegister)  error = error + 1;
    $display(" %m >> Error : %d",error);

    // ++ シリアル番号の書き込み
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<8;ei=ei+1)      PMEM[ei] = $random;
    repeat( 5)  @(PERIOD);      $display("\n===== Write Serial Number =====");
    SetWREN();                  repeat( 5)  @(PERIOD);
    REG_DATA.ACS.ACS    = 8;    WrCPU(REG_ADDR.ACS,REG_DATA.ACS);   // アクセスバイト数
    REG_DATA.OTC.OTC    = 8;    WrCPU(REG_ADDR.OTC,REG_DATA.OTC);   // 出力バイト数
    REG_DATA.BAD.BAD    = 0;    WrCPU(REG_ADDR.BAD,REG_DATA.BAD);   // バッファアドレス
    repeat( 5)  @(PERIOD);      WrCPU(REG_ADDR.BDT,'hC2);           // バッファデータ
    for(ei=0;ei<8;ei=ei+1)      WrCPU(REG_ADDR.BDT,PMEM[ei]);       // バッファデータ
    REG_DATA.ENB.ENB = 1;       WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                $display(" >>> Read : %p",REG_DATA.ENB);
    wait(CPU_IRQ===1'b1);       $display(" >>> CPU_IRQ=1");         // 割り込み待ち
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                $display(" >>> Read : %p",REG_DATA.ENB);
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                                $display(" >>> Read : %p",REG_DATA.IRS); 
    repeat( 5)  @(PERIOD);      WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータスクリア
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                                $display(" >>> Read : %p - Clear",REG_DATA.IRS); 
    for(ei=0;ei<8;ei=ei+1) begin
        $display(" >>> Check : [%h] => %h , %h",ei[23:0],PMEM[ei],FRAM.Serial_No[ei]);
        if(PMEM[ei]!==FRAM.Serial_No[ei])   error = error + 1;
    end
    $display(" %m >> Error : %d",error);

    // ++ 特殊セクタの書き込み
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<256;ei=ei+1)    PMEM[ei] = $random;
    repeat( 5)  @(PERIOD);      $display("\n===== Special Sector Write =====");
    SetWREN();                  repeat( 5)  @(PERIOD);
    REG_DATA.ACS.ACS = 256+4-1; WrCPU(REG_ADDR.ACS,REG_DATA.ACS);   // アクセスバイト数
    REG_DATA.OTC.OTC = 256+4-1; WrCPU(REG_ADDR.OTC,REG_DATA.OTC);   // 出力バイト数
    REG_DATA.BAD.BAD = 0;       WrCPU(REG_ADDR.BAD,REG_DATA.BAD);   // バッファアドレス
    REG_DATA.BDT.BDT = 'h42;    WrCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
    REG_DATA.BDT.BDT = 'h00;    WrCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
    REG_DATA.BDT.BDT = 'h00;    WrCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
    REG_DATA.BDT.BDT = 'h00;    WrCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
    for(ei=0;ei<256;ei=ei+1)    WrCPU(REG_ADDR.BDT,PMEM[ei]);       // バッファデータ
    REG_DATA.ENB.ENB = 1;       WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                $display(" >>> Read : %p",REG_DATA.ENB);
    wait(CPU_IRQ===1'b1);       $display(" >>> CPU_IRQ=1");         // 割り込み待ち
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                $display(" >>> Read : %p",REG_DATA.ENB);
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                                $display(" >>> Read : %p",REG_DATA.IRS); 
    repeat( 5)  @(PERIOD);      WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータスクリア
    repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                                $display(" >>> Read : %p - Clear",REG_DATA.IRS); 
    for(ei=0;ei<256;ei=ei+1) begin
        $display(" >>> Check : [%h] => %h , %h",ei[23:0],PMEM[ei],FRAM.Smem[ei]);
        if(PMEM[ei]!==FRAM.Smem[ei])    error = error + 1;
    end
    $display(" %m >> Error : %d",error);

    // ++ メモリ データの書き込み
    // +++++++++++++++++++++++++++++++++++++++++++
    for(ei=0;ei<262144;ei=ei+1) PMEM[ei] = $random;
    repeat( 5)  @(PERIOD);      $display("\n===== Write Data to Memory =====");
    begin : Write
        integer     wi;
        ei = 0;
        wi = 0;
        while(ei < 262144 ) begin
            SetWREN();                  repeat( 5)  @(PERIOD);
            REG_DATA.ACS.ACS = 2048+4-1;WrCPU(REG_ADDR.ACS,REG_DATA.ACS);   // アクセスバイト数
            REG_DATA.OTC.OTC = 2048+4-1;WrCPU(REG_ADDR.OTC,REG_DATA.OTC);   // 出力バイト数
            REG_DATA.BAD.BAD = 0;       WrCPU(REG_ADDR.BAD,REG_DATA.BAD);   // バッファアドレス
            REG_DATA.BDT.BDT = 'h02;    WrCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
                                        WrCPU(REG_ADDR.BDT,ei[23:16]);      // バッファデータ
                                        WrCPU(REG_ADDR.BDT,ei[15: 8]);      // バッファデータ
                                        WrCPU(REG_ADDR.BDT,ei[ 7: 0]);      // バッファデータ
            repeat(2048) begin
                                        WrCPU(REG_ADDR.BDT,PMEM[wi]);       // バッファデータ
                                        wi = wi + 1;
            end
            REG_DATA.ENB.ENB = 1;       WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
            repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                        $display(" >>> Read : %p",REG_DATA.ENB);
            wait(CPU_IRQ===1'b1);       $display(" >>> CPU_IRQ=1");         // 割り込み待ち
            repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                        $display(" >>> Read : %p",REG_DATA.ENB);
            repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                                        $display(" >>> Read : %p",REG_DATA.IRS); 
            repeat( 5)  @(PERIOD);      WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータスクリア
            repeat( 5)  @(PERIOD);      RdCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス
                                        $display(" >>> Read : %p - Clear",REG_DATA.IRS); 
            repeat(2048) begin
                $display(" >>> Check : [%h] => %h , %h",ei[23:0],PMEM[ei],FRAM.mem[ei]);
                if(PMEM[ei]!==FRAM.mem[ei]) error = error + 1;
                ei = ei + 1;
            end
        end
    end
    $display(" %m >> Error : %d",error);

    // ++ ステータス レジスタ書き込み
    // +++++++++++++++++++++++++++++++++++++++++++
    ei  = 0;                    PMEM[ei] = 'hFF;
    repeat( 5)  @(PERIOD);  $display("\n===== Write Status Register =====");
    SetWREN();                  repeat( 5)  @(PERIOD);
    REG_DATA.ACS.ACS    = 1;    WrCPU(REG_ADDR.ACS,REG_DATA.ACS);   // アクセスバイト数
    REG_DATA.OTC.OTC    = 1;    WrCPU(REG_ADDR.OTC,REG_DATA.OTC);   // 出力バイト数
    REG_DATA.IRM.IRM    = 1;    WrCPU(REG_ADDR.IRM,REG_DATA.IRM);   // 割り込みマスク
    REG_DATA.IRS.IRS    = 1;    WrCPU(REG_ADDR.IRS,REG_DATA.IRS);   // 割り込みステータス クリア
                                RdCPU(REG_ADDR.IRS,REG_DATA.IRS);
                                $display(" >>> Read : %p",REG_DATA.IRS);
    REG_DATA.BAD.BAD    = 0;    WrCPU(REG_ADDR.BAD,REG_DATA.BAD);   // バッファアドレス
    REG_DATA.BDT.BDT    ='h01;  WrCPU(REG_ADDR.BDT,REG_DATA.BDT);   // バッファデータ
    repeat( 5)  @(PERIOD);      WrCPU(REG_ADDR.BDT,PMEM[0]);        // バッファデータ
    REG_DATA.ENB.ENB    = 1;    WrCPU(REG_ADDR.ENB,REG_DATA.ENB);   // 処理イネーブル
                                RdCPU(REG_ADDR.ENB,REG_DATA.ENB);
    while(REG_DATA.ENB.ENB===1) begin
        repeat(20)  @(PERIOD);  RdCPU(REG_ADDR.ENB,REG_DATA.ENB);
    end
    REG_DATA.IRM.IRM    = 0;    WrCPU(REG_ADDR.IRM,REG_DATA.IRM);   // 割り込みマスク 解除
    PMEM[ei] = PMEM[ei] & 'hCC;
    $display(" >>> Check : [%h] => %h , %h",ei[23:0],PMEM[0],FRAM.realStatusRegister);
    if(PMEM[0]!==FRAM.realStatusRegister)  error = error + 1;
    $display(" %m >> Error : %d",error);

`endif
    repeat(20)  @(PERIOD);
    $finish();
end

// --
// -- Clock生成
// -------------------------------------------------------------------
    initial begin
        forever begin
            fork
                #(tpCPU_CLK)    CPU_CLK = 1;
                #(tnCPU_CLK)    CPU_CLK = 0;
                #(tcCPU_CLK)    PERIOD  = PERIOD+1;
            join
        end
    end
    initial begin
        forever begin
            fork
                #(tpSYS_CLK)    SYS_CLK = 1;
                #(tnSYS_CLK)    SYS_CLK = 0;
                #(tcSYS_CLK);
            join
        end
    end

// --
// --   ターゲットのインスタンス
// -------------------------------------------------------------------
    wire        dlyFRAM_CS_N;   DlyBuf  UnDlyFRAM_CS_N(.A(FRAM_CS_N),.Y(dlyFRAM_CS_N) );
    wire        dlyFRAM_SCLK;   DlyBuf  UnDlyFRAM_SCLK(.A(FRAM_SCLK),.Y(dlyFRAM_SCLK) );
    wire        dlyFRAM_MOSI;   DlyBuf  UnDlyFRAM_MOSI(.A(FRAM_MOSI),.Y(dlyFRAM_MOSI) );
    wire        dlyFRAM_MISO;   DlyBuf  UnDlyFRAM_MISO(.A(FRAM_MISO),.Y(dlyFRAM_MISO) );

    NML_FRAMIF TARGET(
        .CPU_RST_N  (CPU_RST_N      ),  // input   wire        CPU_RST_N   ,   // @@ CPU I/F用 リセット
        .CPU_CLK    (CPU_CLK        ),  // input   wire        CPU_CLK     ,   // @@ CPU I/F用 クロック
        .SYS_RST_N  (SYS_RST_N      ),  // input   wire        SYS_RST_N   ,   // @@ システム動作用 リセット
        .SYS_CLK    (SYS_CLK        ),  // input   wire        SYS_CLK     ,   // @@ システム動作用 クロック
        .MST_SLV    (MST_SLV        ),  // input   wire        MST_SLV     ,   // @@ Avalon-MM Master/Slave切替 [0:Master]
        .PRM_ENB    (PRM_ENB        ),  // input   wire        PRM_ENB     ,   // @@ 処理イネーブル [1→0:OFF 0→1:ON]
        .PRM_DIV    (PRM_DIV        ),  // input   wire [ 3:0] PRM_DIV     ,   // @@ シリアルクロック周波数設定
        .PRM_POL    (PRM_POL        ),  // input   wire        PRM_POL     ,   // @@ シリアルクロック極性設定
        .PRM_TAK    (PRM_TAK        ),  // input   wire [ 1:0] PRM_TAK     ,   // @@ シリアルデータ取り込みタイミング設定
        .PRM_ACS    (PRM_ACS        ),  // input   wire [20:0] PRM_ACS     ,   // @@ アクセスバイト数 (最大値:1,048,579)
        .PRM_OTC    (PRM_OTC        ),  // input   wire [ 1:0] PRM_OTC     ,   // @@ 出力バイト数 (最大値:3)
        .PRM_MSK    (PRM_MSK        ),  // input   wire        PRM_MSK     ,   // @@ 割り込みマスク
        .PRM_CLR    (PRM_CLR        ),  // input   wire        PRM_CLR     ,   // @@ 割り込みクリア
        .PRM_STA    (PRM_STA        ),  // input   wire [31:0] PRM_STA     ,   // @@ Avalon-MM(Master側) ライトベースアドレス
        .PRM_CMD    (PRM_CMD        ),  // input   wire [ 7:0] PRM_CMD     ,   // @@ RAMコマンド
        .PRM_ADD    (PRM_ADD        ),  // input   wire [23:0] PRM_ADD     ,   // @@ FRAMアドレス
        .SAV_WEN    (SAV_WEN        ),  // input   wire        SAV_WEN     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  ライトイネーブル
        .SAV_REN    (SAV_REN        ),  // input   wire        SAV_REN     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  リードイネーブル
        .SAV_ADD    (SAV_ADD        ),  // input   wire [ 5:0] SAV_ADD     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  リード/ライトイネーブル
        .SAV_WDT    (SAV_WDT        ),  // input   wire [31:0] SAV_WDT     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  ライトデータ
        .SAV_WIT    (SAV_WIT        ),  // output  wire        SAV_WIT     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  ウェイトリクエスト
        .SAV_RVL    (SAV_RVL        ),  // output  wire        SAV_RVL     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  リードデータイネーブル
        .SAV_RDT    (SAV_RDT        ),  // output  wire [31:0] SAV_RDT     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Slave側)  リードデータ
        .MAV_WEN    (MAV_WEN        ),  // output  wire        MAV_WEN     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトイネーブル
        .MAV_ADD    (MAV_ADD        ),  // output  wire [31:0] MAV_ADD     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトアドレス
        .MAV_BEN    (MAV_BEN        ),  // output  wire [ 3:0] MAV_BEN     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Master側) バイトイネーブル
        .MAV_WDT    (MAV_WDT        ),  // output  wire [ 7:0] MAV_WDT     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトデータ
        .MAV_WIT    (MAV_WIT        ),  // input   wire        MAV_WIT     ,   // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ウェイトリクエスト
        .CPU_IRQ    (CPU_IRQ        ),  // output  wire        CPU_IRQ     ,   // @@ [CPU_CLK↑同期] 割り込み信号
        .MON_ENB    (MON_ENB        ),  // output  wire        MON_ENB     ,   // @@ [CPU_CLK↑同期] 動作モニタ [0:停止中  1:動作中]
        .FRAM_CS_N  (   FRAM_CS_N   ),  // output  reg         FRAM_CS_N   ,   // @@ [SYS_CLK↑同期]   SPI チップセレクト
        .FRAM_SCLK  (   FRAM_SCLK   ),  // output  reg         FRAM_SCLK   ,   // @@ [SYS_CLK↑同期]   SPI シリアルクロック
        .FRAM_MOSI  (   FRAM_MOSI   ),  // output  reg         FRAM_MOSI   ,   // @@ [SYS_CLK↑同期]   SPI シリアル出力
        .FRAM_MISO  (dlyFRAM_MISO   ),  // input   wire        FRAM_MISO   ,   // @@ [SYS_CLK↑同期]   SPI シリアル入力
        .FRAM_OUTE  (   FRAM_OUTE   )   // output  reg         FRAM_OUTE       // @@ [SYS_CLK↑同期]   SPI 出力イネーブル[1:出力 0:Hi-Z]
    );

    reg         FRAM_VDD = 0;
    initial begin   #1; FRAM_VDD=1; end
    FRAM_SPI FRAM(
        .CSB    (dlyFRAM_CS_N   ),
        .SCK    (dlyFRAM_SCLK   ),
        .SI     (dlyFRAM_MOSI   ),
        .SO     (   FRAM_MISO   ),
        .WPB    (1'b1           ),
        .VDD    (   FRAM_VDD    )
    );

// --
// --   Task
// -------------------------------------------------------------------
    task WrCPU(
        input   [31:0]  Addr,
        input   [31:0]  Data
    );
        reg             Comp;
        begin
            Comp = 0;
            @(posedge CPU_CLK) begin    SAV_WEN<=1; SAV_ADD<=Addr; SAV_WDT<=Data;   end
            while( Comp == 0 ) begin
                @(posedge CPU_CLK) begin
                    if( SAV_WIT===1'b0 ) begin
                        SAV_WEN<=0; SAV_ADD<=0; SAV_WDT<=0;
                        Comp<=1;
                    end
                end
                #1;
            end
        end
    endtask
    task RdCPU(
        input   [31:0]  Addr,
        output  [31:0]  Data
    );
        reg             Comp;
        begin
            Comp = 0;
            @(posedge CPU_CLK) begin    SAV_REN<=1; SAV_ADD<=Addr;  end
            while( Comp == 0 ) begin
                @(posedge CPU_CLK) begin
                    if( SAV_WIT===1'b0 ) begin
                        SAV_REN<=0; SAV_ADD<=0;
                    end
                    if( SAV_RVL===1'b1 ) begin
                        Data <= SAV_RDT;
                        Comp <= 1;
                    end
                end
                #1;
            end
        end
    endtask

endmodule

module DlyBuf
#(
    parameter   NsStep = 1,
    parameter   PsStep = 1
)
(
    input   wire    A ,
    output  wire    Y
);

    wire    [NsStep:0] SigNs;
    wire    [PsStep:0] SigPs;

    assign  SigNs[0] = A;
    generate
        genvar  ins;

        if( NsStep != 0 ) begin
            for(ins=1;ins<=NsStep;ins=ins+1) begin
                DlyBufCoreNs UnDlyNs(.A(SigNs[ins-1]),.Y(SigNs[ins]) );
            end
        end
    endgenerate

    assign  SigPs[0] = SigNs[NsStep];
    generate
        genvar  ips;

        if( PsStep != 0 ) begin
            for(ips=1;ips<=PsStep;ips=ips+1) begin
                DlyBufCorePs UnDlyPs(.A(SigPs[ips-1]),.Y(SigPs[ips]) );
            end
        end
    endgenerate

    assign  Y = SigPs[PsStep];

endmodule

module DlyBufCoreNs(input wire A, output wire Y);   assign #(1.000) Y = A;  endmodule
module DlyBufCorePs(input wire A, output wire Y);   assign #(0.001) Y = A;  endmodule

シミュレーション

ModelSimを用いて、シミュレーションを行います。
実行時のトップモジュールは、"TB_TOP"となり、RAM及びFIFOを配置しているため、Libraryをaltera_mf_verにして実行する必要があります。

実行結果は、error=0となり、データ比較に関してはエラーが0になりました。
また、アサーションとかもありますが、今回は割愛します。

シミュレーション波形

シミュレーションを実行すると以下のような波形が確認できます。
※入力信号MAV_WITは、テストベンチから常にランダムな波形を入力しています。

マスタモード デバイスID読み出し

f:id:nao-milk:20210420174952p:plain

マスタモード 特殊セクタ読み出し

f:id:nao-milk:20210420184703p:plain

マスタモードの全体

f:id:nao-milk:20210420184917p:plain
256KBアクセスで、約41msで終了します。

全ての波形を貼り付けると量が大きくなるため、割愛いたします。

ご興味のある方は、前回のソースコードと今回のテストベンチをコピペしてご利用ください。