nao-milkの経験ブログ

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

FRAMにアクセス (Verilogコーディング編)

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


FRAM I/Fのverilogコーディングとなります。
ブロック仕様は以下を参照ください。
nao-milk.hatenablog.com

FRAM I/Fブロック構成

ブロック構成は以下のようになります。

f:id:nao-milk:20210416124855p:plain
ブロック図

ソースコード

verilog-HDLのソースコードを以下より示します。

FRAM I/Fブロックトップ

CPUとのI/F用クロック(入力信号CPU_CLK)は50MHz、内部動作用クロック(入力信号SYS_CLK)は200MHzとなります。
クロック乗せ換えは、処理イネーブル(???_reg_enb)と処理イネーブル クリアパルス(???_reg_fin)のみ行い、その他の信号は動作前(処理イネーブルが"1"になる前)に確定しているため、set_false_path指定します。

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

    parameter   P_BUF_ADBIT = 12    ;   // @@ バッファのアドレスbit幅
    parameter   P_BUF_SIZE  = 4095  ;   // @@ バッファサイズ (サイス-1)
    wire        asi_reg_enb,spi_reg_enb;
    wire [ 3:0] asi_reg_div,spi_reg_div;
    wire        asi_reg_pol,spi_reg_pol;
    wire [ 1:0] asi_reg_tak,spi_reg_tak;
    wire [20:0] asi_reg_acs,spi_reg_acs;
    wire [20:0] asi_reg_otc,spi_reg_otc;
    wire        asi_reg_fin,spi_reg_fin;
    wire        asi_buf_wen,spi_buf_wen;
    wire        asi_buf_ren,spi_buf_ren;
    wire [20:0] asi_buf_add,spi_buf_add;
    wire [ 7:0] asi_buf_wdt,spi_buf_wdt;
    wire        asi_buf_rvl,spi_buf_rvl;
    wire [ 7:0] asi_buf_rdt,spi_buf_rdt;
    wire        asi_buf_fcr;
    wire        asi_ami_str;
    wire        ami_buf_emp;
    wire        ami_buf_rvl;
    wire [ 7:0] ami_buf_rdt;
    wire        ami_buf_ren;

    // @@
    // @@ Avalon-MM Slave I/F
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    NML_FRAMIF_ASI
    #(
        .P_BUF_ADBIT    (P_BUF_ADBIT    ),  // parameter   P_BUF_ADBIT = 12    ,   // @@ バッファのアドレスbit幅
        .P_BUF_SIZE     (P_BUF_SIZE     )   // parameter   P_BUF_SIZE  = 4095      // @@ バッファサイズ (サイス-1)
    )
    UnASI(
        .RESET_N        (CPU_RST_N      ),  // input   wire        RESET_N     ,   // @@ システムリセット
        .CLOCK          (CPU_CLK        ),  // input   wire        CLOCK       ,   // @@ システムクロック
        .MST_SLV        (MST_SLV        ),  // input   wire        MST_SLV     ,   // @@ FRAMIF周辺 Avalon-MM Master/Slave切替 [0:Master]
        .PRM_ENB        (PRM_ENB        ),  // input   wire        PRM_ENB     ,   // @@ FRAMIF周辺 処理イネーブル [1→0:OFF 0→1:ON]
        .PRM_DIV        (PRM_DIV        ),  // input   wire [ 3:0] PRM_DIV     ,   // @@ FRAMIF周辺 シリアルクロック周波数設定
        .PRM_POL        (PRM_POL        ),  // input   wire        PRM_POL     ,   // @@ FRAMIF周辺 シリアルクロック極性設定
        .PRM_TAK        (PRM_TAK        ),  // input   wire [ 1:0] PRM_TAK     ,   // @@ FRAMIF周辺 シリアルデータ取り込みタイミング設定
        .PRM_ACS        (PRM_ACS        ),  // input   wire [20:0] PRM_ACS     ,   // @@ FRAMIF周辺 アクセスバイト数 (最大値:1,048,579)
        .PRM_OTC        (PRM_OTC        ),  // input   wire [ 1:0] PRM_OTC     ,   // @@ FRAMIF周辺 出力バイト数 (最大値:3)
        .PRM_MSK        (PRM_MSK        ),  // input   wire        PRM_MSK     ,   // @@ FRAMIF周辺 割り込みマスク
        .PRM_CLR        (PRM_CLR        ),  // input   wire        PRM_CLR     ,   // @@ FRAMIF周辺 割り込みクリア
        .AVA_WEN        (SAV_WEN        ),  // input   wire        AVA_WEN     ,   // @@ Avalon-MM ライトイネーブル
        .AVA_REN        (SAV_REN        ),  // input   wire        AVA_REN     ,   // @@ Avalon-MM リードイネーブル
        .AVA_ADD        (SAV_ADD        ),  // input   wire [ 5:0] AVA_ADD     ,   // @@ Avalon-MM リード/ライトイネーブル
        .AVA_WDT        (SAV_WDT        ),  // input   wire [31:0] AVA_WDT     ,   // @@ Avalon-MM ライトデータ
        .AVA_WIT        (SAV_WIT        ),  // output  wire        AVA_WIT     ,   // @@ Avalon-MM ウェイトリクエスト
        .AVA_RVL        (SAV_RVL        ),  // output  reg         AVA_RVL     ,   // @@ Avalon-MM リードデータイネーブル
        .AVA_RDT        (SAV_RDT        ),  // output  reg  [31:0] AVA_RDT     ,   // @@ Avalon-MM リードデータ
        .REG_ENB        (asi_reg_enb    ),  // output  reg         REG_ENB     ,   // @@ レジスタ  処理イネーブル
        .REG_DIV        (asi_reg_div    ),  // output  reg  [ 3:0] REG_DIV     ,   // @@ レジスタ  シリアルクロック分周
        .REG_POL        (asi_reg_pol    ),  // output  reg  [ 1:0] REG_POL     ,   // @@ レジスタ  SPI Mode (0 又は 3のみ受付可能)
        .REG_TAK        (asi_reg_tak    ),  // output  reg  [ 1:0] REG_TAK     ,   // @@ レジスタ  シリアルデータ取り込みタイミング
        .REG_ACS        (asi_reg_acs    ),  // output  reg  [20:0] REG_ACS     ,   // @@ レジスタ  アクセスバイト数(設定値+1)
        .REG_OTC        (asi_reg_otc    ),  // output  reg  [20:0] REG_OTC     ,   // @@ レジスタ  出力バイト数(設定値+1)
        .REG_FIN        (asi_reg_fin    ),  // input   wire        REG_FIN     ,   // @@ レジスタ  処理イネーブル クリアパルス
        .BUF_WEN        (asi_buf_wen    ),  // output  wire        BUF_WEN     ,   // @@ バッファ  ライトイネーブル
        .BUF_REN        (asi_buf_ren    ),  // output  wire        BUF_REN     ,   // @@ バッファ  リードイネーブル
        .BUF_ADD        (asi_buf_add    ),  // output  reg  [20:0] BUF_ADD     ,   // @@ バッファ  リード/ライトアドレス
        .BUF_WDT        (asi_buf_wdt    ),  // output  wire [ 7:0] BUF_WDT     ,   // @@ バッファ  ライトデータ
        .BUF_RVL        (asi_buf_rvl    ),  // input   wire        BUF_RVL     ,   // @@ バッファ  リードデータ有効パルス
        .BUF_RDT        (asi_buf_rdt    ),  // input   wire [ 7:0] BUF_RDT         // @@ バッファ  リードデータ
        .BUF_FCR        (asi_buf_fcr    ),  // output  reg         BUF_FCR     ,   // @@ バッファ   FIFOクリア(レベル信号)
        .AMI_STR        (asi_ami_str    ),  // output  reg         AMI_STR     ,   // @@ Master     開始パルス
        .CPU_IRQ        (CPU_IRQ        ),  // output  reg         CPU_IRQ     ,   // @@ 転送完了割り込み信号(レベル割り込み)
        .MON_ENB        (MON_ENB        )   // output  reg         MON_ENB         // @@ 動作モニタ [0:停止中  1:動作中]
    );

    // @@
    // @@ Avalon-MM Master I/F
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    NML_FRAMIF_AMI UnAMI(
        .RESET_N        (CPU_RST_N      ),  // input   wire        RESET_N     ,   // @@ システムリセット
        .CLOCK          (CPU_CLK        ),  // input   wire        CLOCK       ,   // @@ システムクロック
        .PRM_STA        (PRM_STA        ),  // input   wire [31:0] PRM_STA     ,   // @@ Avalon-MM(Master側) ライトベースアドレス
        .AMI_STR        (asi_ami_str    ),  // input   wire        AMI_STR     ,   // @@ Master     開始パルス
        .EMP_AMI        (ami_buf_emp    ),  // input   wire        EMP_AMI     ,   // @@ FRAMIF_AMI用 FIFOエンプティ
        .RVL_AMI        (ami_buf_rvl    ),  // input   wire        RVL_AMI     ,   // @@ FRAMIF_AMI用 リードデータ有効パルス
        .RDT_AMI        (ami_buf_rdt    ),  // input   wire [ 7:0] RDT_AMI     ,   // @@ FRAMIF_AMI用 リードデータ
        .REN_AMI        (ami_buf_ren    ),  // output  reg         REN_AMI     ,   // @@ FRAMIF_AMI用 リードリクエスト
        .AVA_WEN        (MAV_WEN        ),  // output  reg         AVA_WEN     ,   // @@ Avalon-MM(Master側) ライトイネーブル
        .AVA_ADD        (MAV_ADD        ),  // output  reg  [31:0] AVA_ADD     ,   // @@ Avalon-MM(Master側) ライトアドレス
        .AVA_BEN        (MAV_BEN        ),  // output  reg  [ 3:0] AVA_BEN     ,   // @@ Avalon-MM(Master側) バイトイネーブル
        .AVA_WDT        (MAV_WDT        ),  // output  reg  [ 7:0] AVA_WDT     ,   // @@ Avalon-MM(Master側) ライトデータ
        .AVA_WIT        (MAV_WIT        )   // input   wire        AVA_WIT         // @@ Avalon-MM(Master側) ウェイトリクエスト
    );

    // @@
    // @@ クロック乗せ換え
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    // ++ Buffer
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    NML_FRAMIF_BUF
    #(
        .P_BUF_ADBIT    (P_BUF_ADBIT    )   // parameter   P_BUF_ADBIT = 12                    // @@ バッファのアドレスbit幅
    )
    UnBUF(
        .CPU_RST_N      (CPU_RST_N      ),  // input   wire        RST_REG_N   ,   // @@ CPU I/F用 リセット
        .CPU_CLK        (CPU_CLK        ),  // input   wire        CLK_REG     ,   // @@ CPU I/F用 クロック
        .SYS_RST_N      (SYS_RST_N      ),  // input   wire        RST_SPI_N   ,   // @@ システム動作用 リセット
        .SYS_CLK        (SYS_CLK        ),  // input   wire        CLK_SPI     ,   // @@ システム動作用 クロック
        .MST_SLV        (MST_SLV        ),  // input   wire        MST_SLV     ,   // @@ FRAMIF周辺   Avalon-MM Master/Slave切替 [0:Master]
        .PRM_CMD        (PRM_CMD        ),  // input   wire [ 7:0] PRM_CMD     ,   // @@ FRAMIF周辺   FRAMコマンド
        .PRM_ADD        (PRM_ADD        ),  // input   wire [23:0] PRM_ADD     ,   // @@ FRAMIF周辺   FRAMアドレス
        .BUF_FCR        (asi_buf_fcr    ),  // input   wire        BUF_FCR     ,   // @@ バッファ     FIFOクリア(レベル信号)
        .REN_AMI        (ami_buf_ren    ),  // input   wire        REN_AMI     ,   // @@ FRAMIF_AMI用 リードリクエスト
        .EMP_AMI        (ami_buf_emp    ),  // output  wire        EMP_AMI     ,   // @@ FRAMIF_AMI用 FIFOエンプティ
        .RVL_AMI        (ami_buf_rvl    ),  // output  reg         RVL_AMI     ,   // @@ FRAMIF_AMI用 リードデータ有効パルス
        .RDT_AMI        (ami_buf_rdt    ),  // output  wire [ 7:0] RDT_AMI     ,   // @@ FRAMIF_AMI用 リードデータ
        .WEN_ASI        (asi_buf_wen    ),  // input   wire        WEN_ASI     ,   // @@ FRAMIF_ASI用 ライトイネーブル
        .REN_ASI        (asi_buf_ren    ),  // input   wire        REN_ASI     ,   // @@ FRAMIF_ASI用 リードイネーブル
        .ADD_ASI        (asi_buf_add    ),  // input   wire [20:0] ADD_ASI     ,   // @@ FRAMIF_ASI用 リード/ライトアドレス
        .WDT_ASI        (asi_buf_wdt    ),  // input   wire [ 7:0] WDT_ASI     ,   // @@ FRAMIF_ASI用 ライトデータ
        .RVL_ASI        (asi_buf_rvl    ),  // output  reg         RVL_ASI     ,   // @@ FRAMIF_ASI用 リードデータ有効パルス
        .RDT_ASI        (asi_buf_rdt    ),  // output  wire [ 7:0] RDT_ASI     ,   // @@ FRAMIF_ASI用 リードデータ
        .WEN_SPI        (spi_buf_wen    ),  // input   wire        WEN_SPI     ,   // @@ FRAMIF_SPI用 ライトイネーブル
        .REN_SPI        (spi_buf_ren    ),  // input   wire        REN_SPI     ,   // @@ FRAMIF_SPI用 リードイネーブル
        .ADD_SPI        (spi_buf_add    ),  // input   wire [20:0] ADD_SPI     ,   // @@ FRAMIF_SPI用 リード/ライトアドレス
        .WDT_SPI        (spi_buf_wdt    ),  // input   wire [ 7:0] WDT_SPI     ,   // @@ FRAMIF_SPI用 ライトデータ
        .RVL_SPI        (spi_buf_rvl    ),  // output  reg         RVL_SPI     ,   // @@ FRAMIF_SPI用 リードデータ有効パルス
        .RDT_SPI        (spi_buf_rdt    )   // output  reg  [ 7:0] RDT_SPI         // @@ FRAMIF_SPI用 リードデータ
    );

    // ++ Register
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    NML_FRAMIF_EXC #(.P_WIDTH( 1),.P_INIT( 1'b0)) UnEXC_ENB(.RI(CPU_RST_N),.CI(CPU_CLK),.DI(asi_reg_enb),
                                                            .RO(SYS_RST_N),.CO(SYS_CLK),.DO(spi_reg_enb)  );
    assign  spi_reg_div   = asi_reg_div;
    assign  spi_reg_pol   = asi_reg_pol;
    assign  spi_reg_tak   = asi_reg_tak;
    assign  spi_reg_acs   = asi_reg_acs;
    assign  spi_reg_otc   = asi_reg_otc;
    NML_FRAMIF_EXC #(.P_WIDTH( 1),.P_INIT( 1'b0)) UnEXC_FIN(.RO(CPU_RST_N),.CO(CPU_CLK),.DO(asi_reg_fin),
                                                            .RI(SYS_RST_N),.CI(SYS_CLK),.DI(spi_reg_fin)  );


    // @@
    // @@ SPI I/F
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    NML_FRAMIF_SPI
    #(
        .P_WAITC        (3'd7           ),  // parameter   P_WAITC     = 3'd7  ,   // @@ REG_ENB↑からアクセス開始までの時間
        .P_FINWT        (4'd8           )   // parameter   P_FINWT     = 4'd8      // @@ REG_FINのHighパルス幅
    )
    UnSPI(
        .RESET_N        (SYS_RST_N      ),  // input   wire        RESET_N     ,   // @@ システムリセット
        .CLOCK          (SYS_CLK        ),  // input   wire        CLOCK       ,   // @@ システムクロック              [最大周波数=200MHz]
        .FRAM_CS_N      (FRAM_CS_N      ),  // output  reg         FRAM_CS_N   ,   // @@ FRAM SPI  チップセレクト
        .FRAM_SCLK      (FRAM_SCLK      ),  // output  reg         FRAM_SCLK   ,   // @@ FRAM SPI  シリアルクロック    [最大周波数= 50MHz]
        .FRAM_MOSI      (FRAM_MOSI      ),  // output  reg         FRAM_MOSI   ,   // @@ FRAM SPI  シリアル出力
        .FRAM_MISO      (FRAM_MISO      ),  // input   wire        FRAM_MISO   ,   // @@ FRAM SPI  シリアル入力
        .FRAM_OUTE      (FRAM_OUTE      ),  // output  reg         FRAM_OUTE   ,   // @@ FRAM SPI  出力イネーブル      [1:出力 0:Hi-Z]
        .REG_ENB        (spi_reg_enb    ),  // input   wire        REG_ENB     ,   // @@ レジスタ  処理イネーブル
        .REG_DIV        (spi_reg_div    ),  // input   wire [ 3:0] REG_DIV     ,   // @@ レジスタ  シリアルクロック分周
        .REG_POL        (spi_reg_pol    ),  // input   wire        REG_POL     ,   // @@ レジスタ  シリアルクロック極性
        .REG_TAK        (spi_reg_tak    ),  // input   wire [ 1:0] REG_TAK     ,   // @@ レジスタ  シリアルデータ取り込みタイミング
        .REG_ACS        (spi_reg_acs    ),  // input   wire [20:0] REG_ACS     ,   // @@ レジスタ  アクセスバイト数(設定値+1)
        .REG_OTC        (spi_reg_otc    ),  // input   wire [20:0] REG_OTC     ,   // @@ レジスタ  出力バイト数(設定値+1)
        .REG_FIN        (spi_reg_fin    ),  // output  reg         REG_FIN     ,   // @@ レジスタ  処理イネーブル クリアパルス
        .BUF_WEN        (spi_buf_wen    ),  // output  reg         BUF_WEN     ,   // @@ バッファ  ライトイネーブル
        .BUF_REN        (spi_buf_ren    ),  // output  reg         BUF_REN     ,   // @@ バッファ  リードイネーブル
        .BUF_ADD        (spi_buf_add    ),  // output  reg  [20:0] BUF_ADD     ,   // @@ バッファ  リード/ライトアドレス
        .BUF_WDT        (spi_buf_wdt    ),  // output  reg  [ 7:0] BUF_WDT     ,   // @@ バッファ  ライトデータ
        .BUF_RVL        (spi_buf_rvl    ),  // input   wire        BUF_RVL     ,   // @@ バッファ  リードデータ有効パルス
        .BUF_RDT        (spi_buf_rdt    )   // input   wire [ 7:0] BUF_RDT         // @@ バッファ  リードデータ
    );

endmodule

Avalon-MM Slave I/F

レジスタの配置、Bufferブロックの内蔵RAMのリード/ライト制御を行います。

レジスタは、入力信号MST_SLV[0:Master 1:Salve]に応じて、入力信号PRM_???の入力値 又はCPUからライトされた値を保存します。

// **
// **   Avalon-MM Slave I/F for FRAM
// **
// *****************************************************************************
module NML_FRAMIF_ASI
#(
    parameter   P_BUF_ADBIT = 12    ,   // @@ バッファのアドレスbit幅
    parameter   P_BUF_SIZE  = 4095      // @@ バッファサイズ (サイス-1)
)(
    input   wire        RESET_N     ,   // @@ システムリセット
    input   wire        CLOCK       ,   // @@ システムクロック
    input   wire        MST_SLV     ,   // @@ FRAMIF周辺 Avalon-MM Master/Slave切替 [0:Master]
    input   wire        PRM_ENB     ,   // @@ FRAMIF周辺 処理イネーブル [1→0:OFF 0→1:ON]
    input   wire [ 3:0] PRM_DIV     ,   // @@ FRAMIF周辺 シリアルクロック周波数設定
    input   wire        PRM_POL     ,   // @@ FRAMIF周辺 シリアルクロック極性設定
    input   wire [ 1:0] PRM_TAK     ,   // @@ FRAMIF周辺 シリアルデータ取り込みタイミング設定
    input   wire [20:0] PRM_ACS     ,   // @@ FRAMIF周辺 アクセスバイト数 (最大値:1,048,579)
    input   wire [ 1:0] PRM_OTC     ,   // @@ FRAMIF周辺 出力バイト数 (最大値:3)
    input   wire        PRM_MSK     ,   // @@ FRAMIF周辺 割り込みマスク
    input   wire        PRM_CLR     ,   // @@ FRAMIF周辺 割り込みクリア
    input   wire        AVA_WEN     ,   // @@ Avalon-MM  ライトイネーブル
    input   wire        AVA_REN     ,   // @@ Avalon-MM  リードイネーブル
    input   wire [ 5:0] AVA_ADD     ,   // @@ Avalon-MM  リード/ライトイネーブル
    input   wire [31:0] AVA_WDT     ,   // @@ Avalon-MM  ライトデータ
    output  wire        AVA_WIT     ,   // @@ Avalon-MM  ウェイトリクエスト
    output  reg         AVA_RVL     ,   // @@ Avalon-MM  リードデータイネーブル
    output  reg  [31:0] AVA_RDT     ,   // @@ Avalon-MM  リードデータ
    output  reg         REG_ENB     ,   // @@ レジスタ   処理イネーブル
    output  reg  [ 3:0] REG_DIV     ,   // @@ レジスタ   シリアルクロック分周
    output  reg         REG_POL     ,   // @@ レジスタ   SPI Mode (0 又は 3のみ受付可能)
    output  reg  [ 1:0] REG_TAK     ,   // @@ レジスタ   シリアルデータ取り込みタイミング
    output  reg  [20:0] REG_ACS     ,   // @@ レジスタ   アクセスバイト数(設定値+1)
    output  reg  [20:0] REG_OTC     ,   // @@ レジスタ   出力バイト数(設定値+1)
    input   wire        REG_FIN     ,   // @@ レジスタ   処理イネーブル クリアパルス
    output  wire        BUF_WEN     ,   // @@ バッファ   ライトイネーブル
    output  wire        BUF_REN     ,   // @@ バッファ   リードイネーブル
    output  reg  [20:0] BUF_ADD     ,   // @@ バッファ   リード/ライトアドレス
    output  wire [ 7:0] BUF_WDT     ,   // @@ バッファ   ライトデータ
    input   wire        BUF_RVL     ,   // @@ バッファ   リードデータ有効パルス
    input   wire [ 7:0] BUF_RDT     ,   // @@ バッファ   リードデータ
    output  reg         BUF_FCR     ,   // @@ バッファ   FIFOクリア(レベル信号)
    output  reg         AMI_STR     ,   // @@ Master     開始パルス
    output  reg         CPU_IRQ     ,   // @@ 転送完了割り込み信号(レベル割り込み)
    output  reg         MON_ENB         // @@ 動作モニタ [0:停止中  1:動作中]
);

    // @@
    // @@ 外部パラメータの取り込み  (動作中に変更しないため、False Path)
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg         in_slv;
    reg         in_enb;
    reg  [ 3:0] in_div;
    reg         in_pol;
    reg  [ 1:0] in_tak;
    reg  [20:0] in_acs;
    reg  [ 1:0] in_otc;
    reg         in_msk;
    reg         in_clr;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            in_slv <=  1'd0;
            in_enb <=  1'd0;
            in_div <=  4'd0;
            in_pol <=  1'd0;
            in_tak <=  2'd0;
            in_acs <= 21'd0;
            in_otc <=  2'd0;
            in_msk <=  1'd0;
            in_clr <=  1'd0;
        end
        else begin
            in_slv <= MST_SLV;
            in_enb <= PRM_ENB;
            in_div <= PRM_DIV;
            in_pol <= PRM_POL;
            in_tak <= PRM_TAK;
            in_acs <= PRM_ACS;
            in_otc <= PRM_OTC;
            in_msk <= PRM_MSK;
            in_clr <= PRM_CLR;
        end
    end

    reg         mst_enbl;
    reg         mst_enbl_dly1;
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            mst_enbl        <= 1'b0;
            mst_enbl_dly1   <= 1'b0;
        end
        else begin
            mst_enbl        <= ( ~in_slv ) ?    in_enb : 1'b0;
            mst_enbl_dly1   <= mst_enbl;
        end
    end
    wire        mst_enbl_set1   = ( ( mst_enbl) & (~mst_enbl_dly1) );
    wire        mst_enbl_set0   = ( (~mst_enbl) & ( mst_enbl_dly1) );

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  AMI_STR <= 1'b0;
        else                            AMI_STR <= mst_enbl_set1;
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  BUF_FCR <= 1'b1;
        else                            BUF_FCR <= ~mst_enbl;
    end

    // @@
    // @@ Avalon-MM I/F
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg         in_ava_wen;
    reg         in_ava_ren;
    reg  [ 5:0] in_ava_add;
    reg  [31:0] in_ava_wdt;
    wire        ot_ava_wit;
    wire        ot_ava_rvl;
    wire [31:0] ot_ava_rdt;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            in_ava_wen <= 1'b0;
            in_ava_ren <= 1'b0;
            in_ava_add <= 6'h00;
            in_ava_wdt <= 32'd0;
        end
        else begin
            in_ava_wen <= AVA_WEN;
            in_ava_ren <= AVA_REN;
            in_ava_add <= AVA_ADD;
            in_ava_wdt <= AVA_WDT;
        end
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            AVA_RVL <= 1'b0;
            AVA_RDT <= 32'd0;
        end
        else begin
            AVA_RVL <= ot_ava_rvl;
            AVA_RDT <= ot_ava_rdt;
        end
    end
    assign  AVA_WIT = ( AVA_REN & ot_ava_wit );

    // ++ アクセス制御
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg         acc_wen;
    reg         acc_ren;
    wire [ 3:0] acc_add = in_ava_add[5:2];
    wire [31:0] acc_wdt = in_ava_wdt;
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            acc_wen <= 1'b0;
            acc_ren <= 1'b0;
        end
        else begin
            acc_wen <= ( AVA_WEN & (~in_ava_wen) ) ?   1'b1 : 1'b0;
            acc_ren <= ( AVA_REN & (~in_ava_ren) ) ?   1'b1 : 1'b0;
        end
    end

    // @@
    // @@ レジスタ/バッファ制御
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    // ++ レジスタマップ
    // ++
    // ++   0x00    : 処理イネーブル                    [   0]=                 REG_ENB
    // ++   0x04    : 制御パラメータ                    [ 6:0]={REG_TAK,REG_POL,REG_DIV}
    // ++   0x08    : アクセスバイト数                  [20:0]=                 REG_ACS
    // ++   0x0C    : 出力バイト数                      [20:0]=                 REG_OTC
    // ++   0x10    : 割り込みステータス                [   0]=転送完了
    // ++   0x14    : 割り込みマスク                    [   0]=転送完了マスク   [1:マスク]
    // ++   0x18    : バッファリード/ライトアドレス
    // ++   0x1C    : バッファリード/ライトデータ
    // ++   0x20-3F : 予約(アクセス禁止)

    // ++ リード/ライト制御 (デコード)
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    wire        set_enb = ( in_slv & acc_wen & (acc_add==4'h0) );
    wire        set_cnt = ( in_slv & acc_wen & (acc_add==4'h1) );
    wire        set_acs = ( in_slv & acc_wen & (acc_add==4'h2) );
    wire        set_otc = ( in_slv & acc_wen & (acc_add==4'h3) );
    wire        set_irs = ( in_slv & acc_wen & (acc_add==4'h4) & acc_wdt[0] );
    wire        set_irm = ( in_slv & acc_wen & (acc_add==4'h5) );
    wire        set_bad = ( in_slv & acc_wen & (acc_add==4'h6) );
    wire        set_bdt = ( in_slv & acc_wen & (acc_add==4'h7) );

    wire        get_enb = ( acc_ren & (acc_add==4'h0) );
    wire        get_cnt = ( acc_ren & (acc_add==4'h1) );
    wire        get_acs = ( acc_ren & (acc_add==4'h2) );
    wire        get_otc = ( acc_ren & (acc_add==4'h3) );
    wire        get_irs = ( acc_ren & (acc_add==4'h4) );
    wire        get_irm = ( acc_ren & (acc_add==4'h5) );
    wire        get_bad = ( acc_ren & (acc_add==4'h6) );
    wire        get_bdt = ( acc_ren & (acc_add==4'h7) );

    // ++ バッファ制御
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    assign  BUF_WEN = set_bdt;
    assign  BUF_REN = get_bdt;
    assign  BUF_WDT = acc_wdt[7:0];
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  BUF_ADD <= 21'd0;
        else if( set_bad )              BUF_ADD <= in_ava_wdt[20:0];
        else if( set_bdt | get_bdt )    BUF_ADD <= BUF_ADD + 21'd1;
    end

    // ++ レジスタライト制御
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg         reg_irs;    // 割り込みステータス
    reg         reg_irm;    // 割り込みマスク
    wire [31:0] reg_clp = ( acc_wdt > P_BUF_SIZE ) ?    P_BUF_SIZE : acc_wdt;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  REG_ENB <= 1'b0;
        else if( REG_FIN )              REG_ENB <= 1'b0;
        else if( mst_enbl_set0 )        REG_ENB <= 1'b0;
        else if( mst_enbl_set1 )        REG_ENB <= 1'b1;
        else if( set_enb )              REG_ENB <= acc_wdt[0];
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  reg_irs <= 1'b0;
        else if( ~in_slv & in_clr )     reg_irs <= 1'b0;
        else if( set_irs )              reg_irs <= 1'b0;
        else if( REG_FIN )              reg_irs <= 1'b1;
    end

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  MON_ENB <= 1'b0;
        else                            MON_ENB <= REG_ENB;
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  CPU_IRQ <= 1'b0;
        else                            CPU_IRQ <= ( reg_irm ) ?    1'b0 : reg_irs;
    end

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            REG_TAK <=  2'd0;
            REG_POL <=  1'd0;
            REG_DIV <=  4'd0;
            REG_ACS <= 21'd0;
            REG_OTC <= 21'd0;
            reg_irm <=  1'b1;
        end
        else begin
            REG_TAK <= (~in_slv) ?         in_tak   : (set_cnt) ?   acc_wdt[ 6: 5] : REG_TAK;
            REG_POL <= (~in_slv) ?         in_pol   : (set_cnt) ?   acc_wdt[    4] : REG_POL;
            REG_DIV <= (~in_slv) ?         in_div   : (set_cnt) ?   acc_wdt[ 3: 0] : REG_DIV;
            REG_ACS <= (~in_slv) ?         in_acs   : (set_acs) ?   reg_clp[20: 0] : REG_ACS;
            REG_OTC <= (~in_slv) ?  {19'd0,in_otc}  : (set_otc) ?   acc_wdt[20: 0] : REG_OTC;
            reg_irm <= (~in_slv) ?         in_msk   : (set_irm) ?   acc_wdt[    0] : reg_irm;
       end
    end

    // ++ レジスタリード制御
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    assign  ot_ava_rvl  = ( acc_ren & (~get_bdt) ) | BUF_RVL;
    assign  ot_ava_rdt  = ( ( get_enb ) ?   { {31{1'b0}},                 REG_ENB  } : 32'd0 )
                        | ( ( get_cnt ) ?   { {25{1'b0}},{REG_TAK,REG_POL,REG_DIV} } : 32'd0 )
                        | ( ( get_acs ) ?   { {11{1'b0}},                 REG_ACS  } : 32'd0 )
                        | ( ( get_otc ) ?   { {11{1'b0}},                 REG_OTC  } : 32'd0 )
                        | ( ( get_irs ) ?   { {31{1'b0}},                 reg_irs  } : 32'd0 )
                        | ( ( get_irm ) ?   { {31{1'b0}},                 reg_irm  } : 32'd0 )
                        | ( ( get_bad ) ?   { {11{1'b0}},                 BUF_ADD  } : 32'd0 )
                        | ( ( BUF_RVL ) ?   { {24{1'b0}},                 BUF_RDT  } : 32'd0 )
                        ;
    assign  ot_ava_wit  = ~ot_ava_rvl;

endmodule

Avalon-MM Master I/F

Bufferブロック内のFIFOからのエンプティ信号(入力信号EMP_AMI)がNot Empty(EMP_AMI="0")になると動作を開始します。
シーケンス管理は、内部信号seq[1:0]で行います。

  1. 内部信号seq=0:FIFO Not Empty待ち
  2. 内部信号seq=1:FIFO リードデータ取得待ち
  3. 内部信号seq=2:Avalon-MM Wait信号解除待ち

各状態の遷移時に、FIFOにリードイネーブルを生成し、Avaon-MMの制御信号をアサートします。

// **
// **   Avalon-MM Master I/F for FRAM
// **
// *****************************************************************************
module NML_FRAMIF_AMI(
    input   wire        RESET_N     ,   // @@ システムリセット
    input   wire        CLOCK       ,   // @@ システムクロック
    input   wire [31:0] PRM_STA     ,   // @@ Avalon-MM(Master側) ライトベースアドレス
    input   wire        AMI_STR     ,   // @@ Master     開始パルス
    input   wire        EMP_AMI     ,   // @@ FRAMIF_AMI用 FIFOエンプティ
    input   wire        RVL_AMI     ,   // @@ FRAMIF_AMI用 リードデータ有効パルス
    input   wire [ 7:0] RDT_AMI     ,   // @@ FRAMIF_AMI用 リードデータ
    output  reg         REN_AMI     ,   // @@ FRAMIF_AMI用 リードリクエスト
    output  reg         AVA_WEN     ,   // @@ Avalon-MM(Master側) ライトイネーブル
    output  reg  [31:0] AVA_ADD     ,   // @@ Avalon-MM(Master側) ライトアドレス
    output  reg  [ 3:0] AVA_BEN     ,   // @@ Avalon-MM(Master側) バイトイネーブル
    output  reg  [ 7:0] AVA_WDT     ,   // @@ Avalon-MM(Master側) ライトデータ
    input   wire        AVA_WIT         // @@ Avalon-MM(Master側) ウェイトリクエスト
);

    // @@
    // @@ シーケンス
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg  [ 1:0] seq;
    wire        seq_str = ( (seq==2'd0) & (~EMP_AMI) );     // FIFO にデータ格納
    wire        seq_get = ( (seq==2'd1) & ( RVL_AMI) );     // FIFO のデータ取得
    wire        seq_end = ( (seq==2'd2) & (~AVA_WIT) );     // Avalon-MM 完了

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  seq <= 2'd0;
        else if( AMI_STR | seq_end )    seq <= 2'd0;
        else if( seq_str | seq_get )    seq <= seq + 2'd1;
        else                            seq <= seq;
    end

    // @@
    // @@ バッファ制御
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  REN_AMI <= 1'd0;
        else                            REN_AMI <= seq_str;
    end

    // @@
    // @@ Avalon-MM制御
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg  [31:0] add;
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                  add <= 32'd0;
        else if( AMI_STR )              add <= PRM_STA;
        else if( seq_get )              add <= add + 32'd1;
        else                            add <= add;
    end

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            AVA_WEN <=  1'd0;
            AVA_ADD <= 32'd0;
            AVA_BEN <=  4'd0;
            AVA_WDT <=  8'd0;
        end
        else if( seq_end ) begin
            AVA_WEN <=  1'd0;
            AVA_ADD <= 32'd0;
            AVA_BEN <=  4'd0;
            AVA_WDT <=  8'd0;
        end
        else if( seq_get ) begin
            AVA_WEN <=  1'd1;
            AVA_ADD <=  add;
            AVA_BEN <=  (add[1:0]==2'd0) ?  4'b0001 :
                        (add[1:0]==2'd1) ?  4'b0010 :
                        (add[1:0]==2'd2) ?  4'b0100 : 4'b1000;
            AVA_WDT <=  RDT_AMI;
        end
        else begin
            AVA_WEN <= AVA_WEN;
        end
    end

endmodule

Buffer

Quartusで生成したRAMマクロとFIFOマクロを配置します。
このマクロでCPU_CLK(50MHz)⇔SYS_CLK(200MHz)の乗せ換えを行います。

RAMは、Avalon-MM Slave I/FブロックとSPI I/Fブロックの送受信データを保存するためのメモリであり、FIFOはSPI I/Fブロックで取得したデータをAvalon-MM Master I/Fブロックへ送るためのメモリとなります。

入力信号MST_SLVの入力値に応じて、SPI I/Fブロックからのライト動作をRAMにするか、FIFOにするか切り替えます。

リードデータ有効パルス(出力信号RVL_AMI,RVL_ASI,RVL_SPI)は、リードデータラッチ側でリードデータ遅延を意識しなくても良いように、Bufferブロックでリードデータ取り込み用パルスを生成します。

// **
// **   Buffer for FRAM
// **
// *****************************************************************************
module NML_FRAMIF_BUF
#(
    parameter   P_BUF_ADBIT = 12        // @@ バッファのアドレスbit幅
)(
    input   wire        CPU_RST_N   ,   // @@ CPU I/F用 リセット
    input   wire        CPU_CLK     ,   // @@ CPU I/F用 クロック
    input   wire        SYS_RST_N   ,   // @@ システム動作用 リセット
    input   wire        SYS_CLK     ,   // @@ システム動作用 クロック
    input   wire        MST_SLV     ,   // @@ FRAMIF周辺   Avalon-MM Master/Slave切替 [0:Master]
    input   wire [ 7:0] PRM_CMD     ,   // @@ FRAMIF周辺   FRAMコマンド
    input   wire [23:0] PRM_ADD     ,   // @@ FRAMIF周辺   FRAMアドレス
    input   wire        BUF_FCR     ,   // @@ バッファ     FIFOクリア(レベル信号)
    input   wire        REN_AMI     ,   // @@ FRAMIF_AMI用 リードリクエスト
    output  wire        EMP_AMI     ,   // @@ FRAMIF_AMI用 FIFOエンプティ
    output  reg         RVL_AMI     ,   // @@ FRAMIF_AMI用 リードデータ有効パルス
    output  wire [ 7:0] RDT_AMI     ,   // @@ FRAMIF_AMI用 リードデータ
    input   wire        WEN_ASI     ,   // @@ FRAMIF_ASI用 ライトイネーブル
    input   wire        REN_ASI     ,   // @@ FRAMIF_ASI用 リードイネーブル
    input   wire [20:0] ADD_ASI     ,   // @@ FRAMIF_ASI用 リード/ライトアドレス
    input   wire [ 7:0] WDT_ASI     ,   // @@ FRAMIF_ASI用 ライトデータ
    output  reg         RVL_ASI     ,   // @@ FRAMIF_ASI用 リードデータ有効パルス
    output  wire [ 7:0] RDT_ASI     ,   // @@ FRAMIF_ASI用 リードデータ
    input   wire        WEN_SPI     ,   // @@ FRAMIF_SPI用 ライトイネーブル
    input   wire        REN_SPI     ,   // @@ FRAMIF_SPI用 リードイネーブル
    input   wire [20:0] ADD_SPI     ,   // @@ FRAMIF_SPI用 リード/ライトアドレス
    input   wire [ 7:0] WDT_SPI     ,   // @@ FRAMIF_SPI用 ライトデータ
    output  reg         RVL_SPI     ,   // @@ FRAMIF_SPI用 リードデータ有効パルス
    output  reg  [ 7:0] RDT_SPI         // @@ FRAMIF_SPI用 リードデータ
);

    // @@
    // @@ 外部パラメータの取り込み  (動作中に変更しないため、False Path)
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg         in_mode_sys;
    reg  [ 7:0] in_comm_sys;
    reg  [23:0] in_addr_sys;

    always @( posedge SYS_CLK or negedge SYS_RST_N ) begin
        if( ~SYS_RST_N ) begin
            in_mode_sys <= 1'b0;
            in_comm_sys <= 8'd0;
            in_addr_sys <= 24'd0;
        end
        else begin
            in_mode_sys <= MST_SLV;
            in_comm_sys <= PRM_CMD;
            in_addr_sys <= PRM_ADD;
        end
    end

    // @@
    // @@ マスタモード用 (FIFOを配置)
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    // ++ SYS_CLK同期 ライト側
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg         fifo_wreq_sys;
    reg  [ 7:0] fifo_wdat_sys;

    always @( posedge SYS_CLK or negedge SYS_RST_N ) begin
        if( ~SYS_RST_N ) begin
            fifo_wreq_sys <= 1'b0;
            fifo_wdat_sys <= 8'd0;
        end
        else begin
            fifo_wreq_sys <= ( ~in_mode_sys ) ?     WEN_SPI : 1'b0;
            fifo_wdat_sys <= ( ~in_mode_sys ) ?     WDT_SPI : 8'd0;
        end
    end

    // ++ FIFO
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    NML_FRAMIF_FIFO UnFIFO(
        .wrclk      (SYS_CLK        ),  // input           wrclk;
        .wrreq      (fifo_wreq_sys  ),  // input           wrreq;
        .data       (fifo_wdat_sys  ),  // input   [ 7:0]  data;
        .wrempty    (/** OPEN **/   ),  // output          wrempty;
        .wrfull     (/** OPEN **/   ),  // output          wrfull;
        .wrusedw    (/** OPEN **/   ),  // output  [ 9:0]  wrusedw;
        .aclr       (BUF_FCR        ),  // input           aclr;
        .rdclk      (CPU_CLK        ),  // input           rdclk;
        .rdreq      (REN_AMI        ),  // input           rdreq;
        .q          (RDT_AMI        ),  // output  [ 7:0]  q;
        .rdempty    (EMP_AMI        ),  // output          rdempty;
        .rdfull     (/** OPEN **/   ),  // output          rdfull;
        .rdusedw    (/** OPEN **/   )   // output  [ 9:0]  rdusedw;
    );

    // ++ CPU_CLK同期 リード側
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg  [ 1:0] tmp_ami;
    always @( posedge CPU_CLK or negedge CPU_RST_N ) begin
        if( ~CPU_RST_N )                {RVL_AMI,tmp_ami} <= {1'b0,2'b0};
        else                            {RVL_AMI,tmp_ami} <= {tmp_ami,REN_AMI};
    end

    // @@
    // @@ スレーブモード用 (RAMを配置)
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    // ++ SYS_CLK同期 A側
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg         ram_wen_sys;
    reg         ram_ren_sys;
    reg  [20:0] ram_add_sys;
    reg  [ 7:0] ram_wdt_sys;
    wire [ 7:0] ram_rdt_sys;
    reg  [ 1:0] ram_tmp_sys;

    always @( posedge SYS_CLK or negedge SYS_RST_N ) begin
        if( ~SYS_RST_N ) begin
            ram_wen_sys <= 1'b0;
            ram_ren_sys <= 1'b0;
            ram_add_sys <= 21'd0;
            ram_wdt_sys <= 8'd0;
        end
        else begin
            ram_wen_sys <= ( in_mode_sys ) ?    WEN_SPI : 1'b0;
            ram_ren_sys <=                      REN_SPI;
            ram_add_sys <=                      ADD_SPI;
            ram_wdt_sys <=                      WDT_SPI;
        end
    end
    always @( posedge SYS_CLK or negedge SYS_RST_N ) begin
        if( ~SYS_RST_N )                RDT_SPI <= 8'd0;
        else if( in_mode_sys )          RDT_SPI <= ram_rdt_sys;
        else if( ram_ren_sys ) begin
            RDT_SPI <= ((ram_add_sys==21'd0) ?  in_comm_sys[ 7: 0] : 8'd0)
                    |  ((ram_add_sys==21'd1) ?  in_addr_sys[23:16] : 8'd0)
                    |  ((ram_add_sys==21'd2) ?  in_addr_sys[15: 8] : 8'd0)
                    |  ((ram_add_sys==21'd3) ?  in_addr_sys[ 7: 0] : 8'd0);
        end
        else                            RDT_SPI <= RDT_SPI;
    end
    always @( posedge SYS_CLK or negedge SYS_RST_N ) begin
        if( ~SYS_RST_N )            {RVL_SPI,ram_tmp_sys} <= {1'b0,2'b0};
        else                        {RVL_SPI,ram_tmp_sys} <= {ram_tmp_sys,ram_ren_sys};
    end

    // ++ RAM
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    NML_FRAMIF_RAM UnRAM01(
        .clock_a    (SYS_CLK                        ),  // input           clock_a;
        .wren_a     (ram_wen_sys                    ),  // input           wren_a;
        .rden_a     (ram_ren_sys                    ),  // input           rden_a;
        .address_a  (ram_add_sys[P_BUF_ADBIT-1:0]   ),  // input   [11:0]  address_a;
        .data_a     (ram_wdt_sys                    ),  // input   [ 7:0]  data_a;
        .q_a        (ram_rdt_sys                    ),  // output  [ 7:0]  q_a;
        .clock_b    (CPU_CLK                        ),  // input           clock_b;
        .wren_b     (WEN_ASI                        ),  // input           wren_b;
        .rden_b     (REN_ASI                        ),  // input           rden_b;
        .address_b  (ADD_ASI[P_BUF_ADBIT-1:0]       ),  // input   [11:0]  address_b;
        .data_b     (WDT_ASI                        ),  // input   [ 7:0]  data_b;
        .q_b        (RDT_ASI                        )   // output  [ 7:0]  q_b;
    );

    // ++ CPU_CLK同期 B側
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg  [ 1:0] ram_tmp_asi;

    always @( posedge CPU_CLK or negedge CPU_RST_N ) begin
        if( ~CPU_RST_N )            {RVL_ASI,ram_tmp_asi} <= {1'b0,2'b0};
        else                        {RVL_ASI,ram_tmp_asi} <= {ram_tmp_asi,REN_ASI};
    end

endmodule

SPI I/F

設定値に応じて、FRAMのSPI制御信号を生成します。
SPI出力(送信時)は、Bufferブロックからリードデータを取得し、SPI出力します。
SPI入力(受信時)は、シリアルデータを8bitパラレルに変換し、Bufferブロックにライトします。

主な内部構成は以下の通りです。

f:id:nao-milk:20210419132755p:plain
SPI I/Fブロック 内部構成
// **
// **   SPI I/F for FRAM
// **
// *****************************************************************************
module NML_FRAMIF_SPI
#(
    parameter   P_WAITC     = 3'd7  ,   // @@ REG_ENB↑からアクセス開始までの時間
    parameter   P_FINWT     = 4'd8      // @@ REG_FINのHighパルス幅
)
(
    input   wire        RESET_N     ,   // @@ システムリセット
    input   wire        CLOCK       ,   // @@ システムクロック              [最大周波数=200MHz]
    output  reg         FRAM_CS_N   ,   // @@ FRAM SPI  チップセレクト
    output  reg         FRAM_SCLK   ,   // @@ FRAM SPI  シリアルクロック    [最大周波数= 50MHz]
    output  reg         FRAM_MOSI   ,   // @@ FRAM SPI  シリアル出力
    input   wire        FRAM_MISO   ,   // @@ FRAM SPI  シリアル入力
    output  reg         FRAM_OUTE   ,   // @@ FRAM SPI  出力イネーブル      [1:出力 0:Hi-Z]
    input   wire        REG_ENB     ,   // @@ レジスタ  処理イネーブル
    input   wire [ 3:0] REG_DIV     ,   // @@ レジスタ  シリアルクロック分周
    input   wire        REG_POL     ,   // @@ レジスタ  シリアルクロック極性
    input   wire [ 1:0] REG_TAK     ,   // @@ レジスタ  シリアルデータ取り込みタイミング
    input   wire [20:0] REG_ACS     ,   // @@ レジスタ  アクセスバイト数(設定値+1)
    input   wire [20:0] REG_OTC     ,   // @@ レジスタ  出力バイト数(設定値+1)
    output  reg         REG_FIN     ,   // @@ レジスタ  処理イネーブル クリアパルス
    output  reg         BUF_WEN     ,   // @@ バッファ  ライトイネーブル
    output  reg         BUF_REN     ,   // @@ バッファ  リードイネーブル
    output  reg  [20:0] BUF_ADD     ,   // @@ バッファ  リード/ライトアドレス
    output  reg  [ 7:0] BUF_WDT     ,   // @@ バッファ  ライトデータ
    input   wire        BUF_RVL     ,   // @@ バッファ  リードデータ有効パルス
    input   wire [ 7:0] BUF_RDT         // @@ バッファ  リードデータ
);

    // @@
    // @@ 処理イネーブル
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg         enb_d1;
    wire        init;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              enb_d1  <= 1'b0;
        else                                        enb_d1  <= REG_ENB;
    end
    assign  init    = ( REG_ENB & (~enb_d1) ) ?     1'b1 : 1'b0;

    // @@
    // @@ 開始ウェイト (バッファの最初のデータ読み期間)
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg         wit_enbl;
    reg  [ 2:0] wit_cont;
    wire        wit_term;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              wit_enbl <= 1'b0;
        else if( ~REG_ENB )                         wit_enbl <= 1'b0;
        else if( wit_term )                         wit_enbl <= 1'b0;
        else if( init )                             wit_enbl <= 1'b1;
        else                                        wit_enbl <= wit_enbl;
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              wit_cont <= 3'h0;
        else if( ~REG_ENB )                         wit_cont <= 3'h0;
        else if( ~wit_enbl )                        wit_cont <= 3'h0;
        else                                        wit_cont <= wit_cont + 3'h1;
    end
    assign  wit_term= ( wit_enbl & (wit_cont==P_WAITC) ) ?  1'b1 : 1'b0;

    // @@
    // @@ アクセス制御
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg         acc_enbl;
    wire        acc_term;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              acc_enbl <= 1'b0;
        else if( ~REG_ENB )                         acc_enbl <= 1'b0;
        else if( acc_term )                         acc_enbl <= 1'b0;
        else if( wit_term )                         acc_enbl <= 1'b1;
        else                                        acc_enbl <= acc_enbl;
    end

    reg  [ 2:0] end_cont;
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              end_cont <= 3'd0;
        else if( ~REG_ENB )                         end_cont <= 3'd0;
        else if( acc_term )                         end_cont <= 3'd1;
        else if( end_cont!=3'd0 )                   end_cont <= end_cont + 3'd1;
        else                                        end_cont <= end_cont;
    end

    // @@
    // @@ シリアルクロック周波数
    // @@
    // @@   SPI_SCLK周波数 = CLOCK周波数 / ((REG_DIV+1)*4)
    // @@   例)
    // @@       CLOCK周波数 = 200MHz
    // @@       REG_DIV     = 0
    // @@       SPI_SCLK周波数 => 50MHz
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg  [ 6:0] sck_cont;
    wire [ 6:0] sck_cycl    = {1'b0,REG_DIV,2'b11};
    wire        sck_pose;
    wire        sck_nege;

    // ++ Division
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              sck_cont <= 7'h00;
        else if( ~REG_ENB )                         sck_cont <= 7'h00;
        else if( ~acc_enbl | acc_term )             sck_cont <= 7'h00;
        else if(  sck_nege )                        sck_cont <= 7'h00;
        else                                        sck_cont <= sck_cont + 7'h01;
    end
    assign  sck_pose= ( sck_cont=={1'b0,sck_cycl[6:1]} ) ?  1'b1 : 1'b0;
    assign  sck_nege= ( sck_cont==      sck_cycl       ) ?  1'b1 : 1'b0;

    // @@
    // @@ 管理カウンタ
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    // ++ Bit Count
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg  [ 2:0] bit_cont;
    wire        bit_term;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              bit_cont <= 3'd0;
        else if( ~REG_ENB )                         bit_cont <= 3'd0;
        else if( ~acc_enbl | acc_term )             bit_cont <= 3'd0;
        else if(  sck_nege )                        bit_cont <= bit_cont + 3'd1;
        else                                        bit_cont <= bit_cont;
    end
    assign  bit_term= ( bit_cont==3'd7 ) ?          sck_nege : 1'b0;

    // ++ Byte Count
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg  [20:0] byt_cont;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              byt_cont <= 21'd0;
        else if( ~REG_ENB )                         byt_cont <= 21'd0;
        else if( ~acc_enbl | acc_term )             byt_cont <= 21'd0;
        else if(  bit_term )                        byt_cont <= byt_cont + 21'd1;
        else                                        byt_cont <= byt_cont;
    end
    assign  acc_term= ( byt_cont==REG_ACS ) ?       bit_term : 1'b0;

    // @@
    // @@ SPI 入出力切替
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg         spi_flag;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              spi_flag <= 1'b0;
        else if( ~REG_ENB )                         spi_flag <= 1'b0;
        else if( wit_term )                         spi_flag <= 1'b1;
        else if( ~acc_enbl | acc_term )             spi_flag <= 1'b0;
        else if( bit_term & (byt_cont==REG_OTC) )   spi_flag <= 1'b0;
        else                                        spi_flag <= spi_flag;
    end

    // @@
    // @@ バッファ制御
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg  [ 7:0] rbf_temp;
    reg         rbf_flag;
    wire        rbf_enbl;
    wire [20:0] rbf_addr;
    reg         wbf_flag;
    wire        wbf_enbl;
    wire [20:0] wbf_addr;
    wire [ 7:0] wbf_data;

    // ++ Enable generation
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    wire [20:0] prm_oct = REG_OTC - 21'd1;
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              rbf_flag <= 1'b0;
        else if( ~REG_ENB )                         rbf_flag <= 1'b0;
        else if( wit_term ) begin
            if( REG_OTC==21'd0 )                    rbf_flag <= 1'b0;
            else                                    rbf_flag <= 1'b1;
        end
        else if( ~acc_enbl | acc_term )             rbf_flag <= 1'b0;
        else if( bit_term & (byt_cont==prm_oct) )   rbf_flag <= 1'b0;
        else                                        rbf_flag <= rbf_flag;
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              wbf_flag <= 1'b0;
        else if( ~REG_ENB )                         wbf_flag <= 1'b0;
        else if( ~acc_enbl | acc_term )             wbf_flag <= 1'b0;
        else if( bit_term & (byt_cont==REG_OTC) )   wbf_flag <= 1'b1;
        else                                        wbf_flag <= wbf_flag;
    end
    assign  rbf_enbl= ( rbf_flag & acc_enbl & sck_pose & (bit_cont==3'd3) );

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              BUF_WEN <= 1'b0;
        else if( ~REG_ENB )                         BUF_WEN <= 1'b0;
        else                                        BUF_WEN <= wbf_enbl;
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              BUF_REN <= 1'b0;
        else if( ~REG_ENB )                         BUF_REN <= 1'b0;
        else                                        BUF_REN <= init | rbf_enbl;
    end

    // ++ Address generation
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    assign  rbf_addr= byt_cont + 21'd1;
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              BUF_ADD <= 21'd0;
        else if( ~REG_ENB )                         BUF_ADD <= 21'd0;
        else                                        BUF_ADD <= ( rbf_enbl ) ?   rbf_addr :
                                                               ( wbf_enbl ) ?   wbf_addr : 21'd0;
    end

    // ++ S/P conversion
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              rbf_temp <= 8'h00;
        else if( ~REG_ENB )                         rbf_temp <= 8'h00;
        else if( wbf_flag )                         rbf_temp <= 8'h00;
        else if( BUF_RVL )                          rbf_temp <= BUF_RDT;
        else                                        rbf_temp <= rbf_temp;
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              BUF_WDT <= 8'h00;
        else if( ~REG_ENB )                         BUF_WDT <= 8'h00;
        else                                        BUF_WDT <= ( wbf_enbl ) ?   wbf_data : 8'h00;
    end

    // @@
    // @@ SPI I/F 信号
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    // ++ SPI signal generation
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    wire        bas_cs_n;
    wire        bas_sclk;
    reg         bas_mosi;
    reg  [ 6:0] bas_temp;

    assign  bas_cs_n= ~( REG_ENB & acc_enbl );
    assign  bas_sclk=  ( bas_cs_n ) ?                           REG_POL :
                       ( sck_cont>{1'b0,sck_cycl[6:1]} ) ?      1'b1 : 1'b0;
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              {bas_mosi,bas_temp} <= 8'h00;
        else if( ~REG_ENB )                         {bas_mosi,bas_temp} <= rbf_temp;
        else if( (~acc_enbl) | bit_term )           {bas_mosi,bas_temp} <= rbf_temp;
        else if(  sck_nege )                        {bas_mosi,bas_temp} <= {bas_temp,1'b0};
        else                                        {bas_mosi,bas_temp} <= {bas_mosi,bas_temp};
    end

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            FRAM_CS_N <= 1'b1;
            FRAM_SCLK <= 1'b0;
            FRAM_MOSI <= 1'b0;
            FRAM_OUTE <= 1'b0;
        end
        else begin
            FRAM_CS_N <= (~wit_term) & bas_cs_n & (end_cont==3'd0);
            FRAM_SCLK <= bas_sclk;
            FRAM_MOSI <= bas_mosi & acc_enbl & (~wbf_flag);
            FRAM_OUTE <= spi_flag;
        end
    end

    // ++ SPI 入力信号
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg         in_miso;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              in_miso <= 1'b0;
        else                                        in_miso <= FRAM_MISO;
    end

    // ++ Capture timing
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg         cap_jdge,cap_jdtm;
    reg  [20:0] cap_jdad;
    reg         cap_enbl,cap_entm;
    reg  [20:0] cap_enad;
    reg  [ 6:0] cap_temp;
    reg         cap_vlid;
    reg  [20:0] cap_addr;
    reg  [ 7:0] cap_data;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            cap_jdtm <= 1'b0;
            cap_jdge <= 1'b0;
            cap_jdad <= 21'd0;
        end
        else if( ~REG_ENB ) begin
            cap_jdtm <= 1'b0;
            cap_jdge <= 1'b0;
            cap_jdad <= 21'd0;
        end
        else begin
            cap_jdtm <= wbf_flag & bit_term;
            cap_jdge <= wbf_flag & sck_nege;
            cap_jdad <= byt_cont;
        end
    end

    // -- 取り込み遅延
    reg         cap_jdtm_dly1,cap_jdtm_dly2,cap_jdtm_dly3;
    reg         cap_jdge_dly1,cap_jdge_dly2,cap_jdge_dly3;
    reg  [20:0] cap_jdad_dly1,cap_jdad_dly2,cap_jdad_dly3;
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            cap_jdtm_dly1 <= 1'b0;
            cap_jdge_dly1 <= 1'b0;
            cap_jdad_dly1 <= 21'd0;
            cap_jdtm_dly2 <= 1'b0;
            cap_jdge_dly2 <= 1'b0;
            cap_jdad_dly2 <= 21'd0;
            cap_jdtm_dly3 <= 1'b0;
            cap_jdge_dly3 <= 1'b0;
            cap_jdad_dly3 <= 21'd0;
        end
        else if( ~REG_ENB ) begin
            cap_jdtm_dly1 <= 1'b0;
            cap_jdge_dly1 <= 1'b0;
            cap_jdad_dly1 <= 21'd0;
            cap_jdtm_dly2 <= 1'b0;
            cap_jdge_dly2 <= 1'b0;
            cap_jdad_dly2 <= 21'd0;
            cap_jdtm_dly3 <= 1'b0;
            cap_jdge_dly3 <= 1'b0;
            cap_jdad_dly3 <= 21'd0;
        end
        else begin
            cap_jdtm_dly1 <= cap_jdtm;
            cap_jdge_dly1 <= cap_jdge;
            cap_jdad_dly1 <= cap_jdad;
            cap_jdtm_dly2 <= cap_jdtm_dly1;
            cap_jdge_dly2 <= cap_jdge_dly1;
            cap_jdad_dly2 <= cap_jdad_dly1;
            cap_jdtm_dly3 <= cap_jdtm_dly2;
            cap_jdge_dly3 <= cap_jdge_dly2;
            cap_jdad_dly3 <= cap_jdad_dly2;
        end
    end
    // -------------------------------------------

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            cap_entm <= 1'b0;
            cap_enbl <= 1'b0;
            cap_enad <= 21'd0;
        end
        else if( ~REG_ENB ) begin
            cap_entm <= 1'b0;
            cap_enbl <= 1'b0;
            cap_enad <= 21'd0;
        end
        else begin
            case( REG_TAK )
                2'd1    : begin
                    cap_entm <= cap_jdtm_dly1;
                    cap_enbl <= cap_jdge_dly1;
                    cap_enad <= cap_jdad_dly1;
                end
                2'd2    : begin
                    cap_entm <= cap_jdtm_dly2;
                    cap_enbl <= cap_jdge_dly2;
                    cap_enad <= cap_jdad_dly2;
                end
                2'd3    : begin
                    cap_entm <= cap_jdtm_dly3;
                    cap_enbl <= cap_jdge_dly3;
                    cap_enad <= cap_jdad_dly3;
                end
                default : begin
                    cap_entm <= cap_jdtm;
                    cap_enbl <= cap_jdge;
                    cap_enad <= cap_jdad;
                end
            endcase
        end
    end

    // ++ P/S conversion
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              cap_temp <= 7'h00;
        else if( ~REG_ENB )                         cap_temp <= 7'h00;
        else if( cap_enbl )                         cap_temp <= {cap_temp[5:0],in_miso};
        else                                        cap_temp <= cap_temp;
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N ) begin
            cap_vlid <= 1'b0;
            cap_addr <= 21'd0;
            cap_data <= 8'h00;
        end
        else begin
            cap_vlid <= cap_entm;
            cap_addr <= cap_enad;
            cap_data <= {cap_temp,in_miso};
        end
    end

    assign  wbf_enbl= cap_vlid;
    assign  wbf_addr= cap_addr;
    assign  wbf_data= cap_data;

    // @@
    // @@ レジスタ I/F
    // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    reg  [ 3:0] fin_cont;

    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              fin_cont <= 4'd0;
        else if( fin_cont==P_FINWT )                fin_cont <= 4'd0;
        else if( end_cont==3'd7 )                   fin_cont <= 4'd1;
        else if( fin_cont!=4'd0 )                   fin_cont <= fin_cont + 4'd1;
        else                                        fin_cont <= fin_cont;
    end
    always @( posedge CLOCK or negedge RESET_N ) begin
        if( ~RESET_N )                              REG_FIN <= 1'b0;
        else if( fin_cont==P_FINWT )                REG_FIN <= 1'b0;
        else if( end_cont==3'd7 )                   REG_FIN <= 1'b1;
        else                                        REG_FIN <= REG_FIN;
    end

endmodule

このブロックは200MHzで動作します。
21bitのbyteカウンタは、以下の経路をset_false_path指定します。

  1. byt_cont[20:0] → byt_cont[20:0]
  2. byt_cont[20:0] → BUF_ADD[20:0]

これは、+1した値をラッチするもので、1クロック以内で確定する必要のないものとなります。
起点byt_cont[20:0]となる経路を以下に示します。

f:id:nao-milk:20210419140934p:plain
経路 (起点:byt_cont[20:0])

内部信号byt_cont[20:0]は最小32クロック後に+1加算され、BUF_ADD[20:0]も13クロック以内に+1した値が確定していれば良いようになります。
タイミング図を以下に示します。

f:id:nao-milk:20210419141125p:plain
タイミング図(起点:byt_cont[20:0])

Clock transfer

2段FFによるクロック乗せ換えとなります。
パラメータでデータ幅と初期値が設定可能になっていますが、これは流用性を考慮しています。
尚、レジスタ値用の乗せ換えとなります。

// **
// **   Clock transfer for FRAM
// **
// *****************************************************************************
module NML_FRAMIF_EXC
#(
    parameter   P_WIDTH = 32        ,   // データ幅
    parameter   P_INIT  = 32'd0         // 初期値
)
(
    input   wire                RI  ,   // @@ 入力データ用 リセット
    input   wire                CI  ,   // @@ 入力データ用 クロック
    input   wire [P_WIDTH-1:0]  DI  ,   // @@ 入力データ
    input   wire                RO  ,   // @@ 出力データ用 リセット
    input   wire                CO  ,   // @@ 出力データ用 クロック
    output  reg  [P_WIDTH-1:0]  DO      // @@ 出力データ
);

    reg  [P_WIDTH-1:0]  in_d;
    reg  [P_WIDTH-1:0]  as_d;

    always @( posedge CI or negedge RI ) begin
        if( ~RI )                       in_d <= P_INIT;
        else                            in_d <= DI;
    end

    always @( posedge CO or negedge RO ) begin
        if( ~RO )                       as_d <= P_INIT;
        else                            as_d <= in_d;
    end

    always @( posedge CO or negedge RO ) begin
        if( ~RO )                       DO <= P_INIT;
        else                            DO <= as_d;
    end

endmodule

マクロ生成

RAMマクロ

Bufferブロック内のRAMマクロの生成内容は、以下の通りとなります。

f:id:nao-milk:20210419143520p:plain
RAMマクロ

FIFOマクロ

f:id:nao-milk:20210419143937p:plain
FIFOマクロ

メモリサイズ

FRAM I/Fブロックで使用するブロックメモリサイズは、以下となります。

  • FIFO:M10K Block = 1
  • RAM:M10K Block = 4

FIFOは、BlockRAM=1となるギリギリまでのサイズ、512Word×8bit分取っています。
実際は、そこまでのサイズは必要としませんが、余裕をもっています。

RAMは、4096Word×8bitとしています。

シミュレーション波形

FRAM I/FブロックをTOPとしたRTLシミュレーション波形を以下に示します。
マスタモードとスレーブモードの波形となり、「デバイスID読み出し」の場合のシミュレーション波形となります。

マスタモード

f:id:nao-milk:20210419145925p:plain
シミュレーション波形 (マスタモード)

スレーブモード

f:id:nao-milk:20210419150758p:plain
シミュレーション波形 (スレーブモード)

最後に

設計では、どこを「set_false_path」にするかも検討する必要があります。
どのタイミングで変化するのか、どれくらいまでに値が確定しておけば良いのか、把握する必要があります。
また、STA結果も、set_false_path指定した起点終点間の遅延量も確認する必要があります。

FRAMにアクセス (ブロック仕様編)

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

FRAM(強誘電体RAM)をアクセスするためのFPGAをテーマに、FPGAを設計するまでの一連の工程を記載して行こうと思います。

  1. ブロック仕様
  2. Verilog-HDLによるコーディング
  3. シミュレーション(RTLシミュレーション)
  4. Platform DesignerとFPGA Topの作成
  5. 論理合成とインプリメント
  6. NiosIIのコーディング
  7. NiosIIを含めたシミュレーション

 

本記事では、まずはブロックの仕様を記載します。

(後半に、FRAMとインターフェイスを行うSPIブロックのVerilogコードも記載しています。)

※FRAMのモデルは「CY15B102QN」となります。

 (ちょうど、verilogのシミュレーションモデルがあったので。。。)

 

【背景】

昔の案件でFRAMにアクセスする機能が必要で、しかも、アクセス時間を短くしたかったため、バーストアクセスができるIPを探したのですが、見当たらなかったため、verilogで作りました。

その時は、必要な機能に限定して作成しましたが、今回は依頼されたものではないので、私の好きなように作ろうと思います。

ポイントとしては、色々なコマンド(Opcode)が発行でき、且つ ブートローダとして使えそうな機能も考慮してしみました。

 

 

 

概要

FRAM(CY15B102QN)デバイスに対し、リード/ライトを行います。

リード/ライト方法はSPIプロトコルで行います。

 

以下にFRAMデバイスの通信フォーマット例を示します。

 

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

FRAMデバイス リード/ライト例


機能

【CPU I/F (Avalon-MM)】

  1. MasterとSlaveに分かれ、入力信号MST_SLV(0:Master 1:Slave)により選択が可能。
  2. Masterを選択した場合、入力信号PRM_???に従って、FRAMから取得したデータをAvalon-MM(Master側)のブロックから出力します。
  3. Slaveを選択した場合、Avalon-MM(Slave側)でCPUから設定されたレジスタ値に従い、アクセスを行います。
  4. FRAMへの転送が完了すると、割り込みを発生します。尚、割り込みマスクが可能であり、割り込みステータスに"1"を書き込むことでクリアします。(割り込み信号はレベル信号)

 

【FRAMデバイスI/F (SPIプロトコル)】

  1. アクセスバイト数(バーストサイズ)は、レジスタREG_ASC[20:0]又は入力信号PRM_ASC[20:0]で設定が可能。尚、スレーブモード時の最大アクセスバイト数は内蔵RAMサイズに依存します。
  2. 出力(送信)バイト数は、レジスタREG_OTC又は入力信号PRM_OTC[1:0]で設定が可能。
  3. シリアルクロック(出力信号FRAM_SCLK)は内部クロックSYS_SLK[200MHz]を分周して生成し、最大50MHz(SYS_CLKの1/4分周)を出力。
  4. シリアルクロックの周波数は、レジスタREG_DIV[3:0]又は入力信号PRM_DIV[3:0]で設定が可能。
  5. 入力シリアルデータ(入力信号FRAM_MISO)は、SYS_CLK↑に同期してラッチし、レジスタREG_TAK[1:0]又は入力信号PRM_TAK[1:0]により、取り込みタイミングを調整することが可能。

 

ブロック構成

FRAMとI/Fを行うブロック(ブロック名:NML_FRAMIF)の構成図を以下に示します。

※CPUブロックは50MHzで動作します。

※TOPは、FPGAのTOPを示します。

 

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

FRAM I/F ブロック構成

入出力信号一覧

FRAM I/Fブロックの入出力信号の一覧を以下に示します。

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

入出力信号一覧

入出力タイミング

FRAM I/Fブロックの入出力タイミングを以下に示します。

 

FRAM I/F信号

タイミング波形とレジスタ設定値との関係を以下に示します。

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

入出力タイミング (FRAMデバイス用 SPI I/F信号タイミング)

※出力信号FRAM_OUTEは、FRAMへのシリアルデータを出力しない場合はHi-Zしたいことも想定して補足信号としてあります。

 

FRAMデータ取り込みタイミング

シリアルクロックの最大周波数は50MHz[最小周期は20ns]となるため、FFから出力してFRAMへ届くまでの時間(シリアルクロックが届くまでの時間) と 届いてからFRAM I/Fブロックの入力FFまでの届く時間の合計が20ns以上の場合もあると考え、基準取り込みポイントから最大3クロック遅延させて取り込むことが可能です。

以下に経路遅延イメージを示します。また、取り込みタイミング図も示します。

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

FRAMデータ取得までの経路遅延イメージ

 

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

入出力タイミング (FRAMデバイス用 SPI取り込みタイミング)

 

Avalon-MM(Slave側)

リード時は、出力データを準備するためにWait信号(出力信号SAV_WIT)をアサートします。

また、レジスタと内蔵RAMをリードする場合でWait信号のアサート期間が変わります。

 

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

入出力タイミング(Avalon-MM Slave)

 

Avalon-MM(Master側)

wait信号(入力信号MAV_WIT)のアサート期間中は出力を保持します。

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

入出力タイミング(Avalon-MM Master)

 

動作説明

FRAM I/Fブロックの動作に関して説明します。

 

マスタモード(入力信号MST_SLV=0)

マスタモードは入力信号による制御を可能とし、FRAMからの取得したデータをAvalon-MM Masterとしてライト制御します。

尚、このモードはFRAMのリード系コマンド(READ,SSRDなど)のみを想定しています。

  1. 入力信号PRM_DIV、PRM_POL、PRM_TAK、PRM_ACS、PRM_OTC、PRM_STA、PRM_CMD、PRM_ADDを設定します。
  2. PRM_ENBを"0"から"1"に設定します。これにより、動作を開始します。
  3. FRAMへコマンド(PRM_CMDの内容)発行を行い、PRM_OTC[1:0]に応じてアドレス発行(PRM_ADDの内容)を行います。
  4. FRAMから取得したシリアルデータを8bitパラレルデータに変換し、FIFOに格納します。
  5. FIFOがNot Emptyになると、Avalon-MM I/F(Master側)がFIFOをリードし、Avalon-MMライト動作を行います。尚、ライトアドレスは入力信号PRM_STAを加算して出力します。
  6. FRAMへのアクセスが完了すると、出力信号CPU_IRQをアサートします。尚、PRM_CLRを"1"にすることでクリアします。

 ※動作中は、出力信号MON_ENBが"1"になります。

 

スレーブモード(入力信号MST_SLV=1)

CPUからのレジスタ設定に応じて動作を行い、内蔵RAMに格納されたデータをFRAMへ出力し、FRAMから出力したデータを内蔵RAMに格納します。

従って、1回のFRAMへのリード又はライトはRAMサイズに依存します。

(内蔵RAMのサイズを大きくすることで、アクセスバイト数は大きくできます)

尚、内蔵RAMサイズは4096バイトとしています。

  1. レジスタに初期設定を行います。(REG_DIV、REG_POL、REG_TAK、REG_ACS、REG_OTC)
  2. FRAMへ出力したいデータをCPUから内蔵RAMへ格納します。(コマンドやアドレス、ライトデータなど)
  3. 処理イネーブルを"1"にします(レジスタREG_ENB)。これにより、動作を開始します。
  4. FRAMへの出力時は内蔵RAMからデータを取得してFRAMへ出力します。またFRAMから取得したデータは、8bitのパラレルデータに変換してRAMへ保存します。
  5. FRAMへのアクセスが完了すると、出力信号CPU_IRQをアサートします。尚、レジスタREG_ENBも自動的に"0"になるため、割り込みマスクを行い、レジスタREG_ENBの状態をポーリングすることにより、動作中か否かを知ることが可能です。
  6. CPUがFRAMから取得したデータを内蔵RAMから読み出す。(レジスタREG_BDT)

 

内蔵RAMとFRAMアクセスの関係

スレーブモード時の内蔵RAMとFRAM SPI通信のデータ構成の関係を以下に示します。

尚、FRAMアクセスの最初はCommand(Opcode)の出力となります。

 

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

内蔵RAMとFRAM SPI通信との関係

 ※Command= "SSWR"と"SSRD"は特殊セクタアクセスとなり、256バイト以上のアクセスを行った場合は、特殊セクタの先頭アドレスに戻るため、グレーにしています。

 

マスタモード時は、内蔵RAMを介さず、取得したデータをそのままAvalon-MM Master側でFRAM I/Fブロック外部にライトします。

 

レジスタ詳細

FRAM I/Fのレジスタ詳細を以下に示します。

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


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

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

 

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

 設定例

FRAM SPI通信のバイト数設定例を以下に示します。

 スレーブモード

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

設定例 (スレーブモード)

 マスタモード

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

設定例 (マスタモード)

 

SPI I/Fのソースコード(記述:verilog-HDL)

FRAM I/Fの内部ブロックSPI I/F(モジュール名:NML_FRAMIF_SPI)のソースコードを以下に示します。

 

入出力信号の宣言と処理イネーブルがアサートされ内蔵RAMから最初のデータ(Command)をリードするための期間を生成します。

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

 

SPI通信期間を示すasc_enblを生成し、またアクセス終了後のWait期間を生成します。

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

 

シリアルクロックの生成、bitカウンタとByteカウンタを生成します。

尚、Byteカウンタは、CY16B102以上のデバイスでも対応できるよう、余分にbit幅をとっています。

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

 

SPI通信の出力と入力を示すフラグを生成し、内蔵RAMのアクセス制御を行います。

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

 

ベースとなるFRAM制御信号(base_*)を生成し、FFでラッチして出力します。

これにより、FPGAの出力FFを使用します。

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

 

FRAMからの入力シリアルデータラッチ(in_miso)し、基準取り込みタイミング信号を生成します。(入力FFを使用します)

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

 

基準取り込みタイミングを遅延します。

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

 

レジスタREG_TAKの設定値に応じて、取り込みタイミングを選択し、シリアルデータをパラレルデータに変換します。

変換したデータは、上記BUF_WDTでラッチします。

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

 

FRAMアクセス完了後、レジスタREG_ENBを"0"にするための信号REG_FINを生成します。

尚、この信号は後段ブロックでCPU_CLK(50MHz)で使用するため、High幅をSYS_CLKの8クロック分にしています。(CPU_CLKの2クロック分)

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

 

使用ツール

  • 言語:verilog-HDL(ハードウェア用)、C言語(ソフトウェア用[NiosII用])
  • シミュレータ:ModelSim Intel FPGA Starter Edition 10.5b
  • 論理合成/インプリメント:Quartus Prime 18.1 Lite Edition

 

最後に

マスタモードとスレーブモードに分けたのは、試作を意図しています。

FRAMへのライトとリードも、マスタモードで行うことも可能ですが、リードだけに止めました。

機能試作や基板試作時は、スレーブモードを使ってFRAMに対して色々なコマンドを発行して、機能検討や基板変更を考えると思います。

その為、なるべく自由度が高いと思われるスレーブモードを入れました。

 

Pythonで画像描画

f:id:nao-milk:20210408013747p:plain
Pythonを独学して3日目ですが、数式からハートマークを作成する画像描画プログラムを作成してみました。

修正履歴

facebookである方より、アドバイス頂いたので修正しました。
※アドバイスありがとうございました。

[アドバイス内容]
 pow()なのか**なのか統一しません?
 つーか、なんで混在しているんですか?
 違いはわかっていますか?

[修正内容]
 「ソースコード」の章に記述した「HeartSymbol()」内のべき乗を「**」に統一しました。
 尚、
 混在した理由は、べき乗の演算を「**」にすべきか、
 「pow」にするべきかを迷っており、そのまま投稿してしまいました。

[新たな発見]
今回のアドバイスで「べき乗」は、「**」と「pow」と「math.pow」があることが分かりました。
この3つの演算の演算速度を測りました。(time.perf_counter_ns()で計測)
 「**」 :2,103,908,740 ns (2.103秒)
 「pow」:2,153,195,800 ns (2.153秒)
 「math.pow」:2,123,512,380 ns (2.123秒)
 ※各5回実行して、平均した秒数となります。
今回の修正は、実行速度の速い「**」に統一しました。

[新たな疑問]
dis()を使ってコードを見たのですが、「math.pow」が一番多いのに、なぜ速度が2番目に速いのか?

本文の構成(目次)は、以下のようになります。
(出力画像は、下の方に添付しています。)

Window構成

勉強を始めて3日目なので、簡単になりますがWindowは以下のような構成になります。

f:id:nao-milk:20210408101706p:plain
Window構成

メインフレームの初期サイズは、640x640になります。

ソース構成

ソース構成は、以下の通りです。

f:id:nao-milk:20210408103516p:plain
ソース構成

演算式

ハートマークを生成するための演算式は、以下の通りです。
私がキレイと思うハートマークを作るため、変数xとyに係数を掛けています。

f:id:nao-milk:20210408103841p:plain
演算式

動作説明

プログラムの主な動作について説明します。
ソースコードは、後述でそのまま添付してしています。

1. 下地を生成

変数xとyの値(座標)からとして色成分(範囲:0~255)を置いていくため、オブジェクトPhotoImage()を使って512×512の下地を作っています。
これにより、メソッドput()を使用してxとy座標に色を設定していきます。

オブジェクトPhotoImageは、PNGなどのファイルを読み込み使用するみたいですが、幅と高さを指定するだけだと、画像を置くための下地を作ってくれるみたいでした。
tkinterの__init__.plを見ながら施行錯誤しました。

2. 画像生成

出力画像サイズを512×512としたため、X方向とY方向の位置を管理する変数pxとpyを準備しています。
また、
演算式ではX及びY座標を-32~32の範囲にしたかったため、変数txとtyで実際の座標位置を管理しています。
従って、座標精度(座標点の間隔)は0.125となります。(精度=64÷512)

変数txとtyから上記演算式の計算を行い(関数:HeartSymbol)、算出値の整数部を画素値(色値)としています。
ただし、
算出値が255を超える場合は255とし(関数:ClipValue)、"255-算出値"の値をR成分に設定しています。
※中心(座標x,y=0,0)に行くほど算出値が0になる性質の式のため、255から算出値を引いています。
 (ハートマークなので、中心に近づくほど「赤色」が良いかな?と思った次第です)
 尚、G成分及び及びB成分は0にしています。背景色が黒、ハートマークは赤となり、
 中心に近づくほど、赤色が濃くなっていきます。

3. Windowの表示

メインフレームCanvas領域(512×512)を作成し、上記「2.画像生成」で作成した画像を乗せています。
また、
ユーザーがメインフレームサイズを小さくした場合でも、視覚範囲で画像が見れるようスクロールバーを追加しました。

ファイル保存

画像表示だけでなくファイルにも保存したかったため、メソッドwriteを使ってPNG形式で保存しています。

補足)

オブジェクトPhotoImage()に「幅」と「高さ」のみを設定すると、データタイプは「photo」になりました。(おそらく、初期値だと思っています。)
RGBの設定がしたかったため、そのままにしていますが、「bitmap」(2値画像)にする方法までは確認していません。

ソースコード

ソースコードを以下に示します。
モジュール「csv」をインポートしていますが、演算結果などをExcelに保存して確認したかったために使用しました。下記ソース内では、モジュールcsvを使用したコードは削除しています。

import math
import tkinter as tk
import csv

#--
#-- 関数
#-----------------------------------------------------------

# @@ 座標からハートマークを生成する。
# @@
# @@    f(x,y) = (x*a)^2 + ((y*b) - ((x*c)^2)^(1/3))^2
# @@        a,b,c : 係数
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
def HeartSymbol(
        PointX      ,   # X座標
        PointY          # Y座標
    ):
    # べき乗を「**」に統一
    #########################################
    ResVal = (PointX*0.60)**2 + (PointY*0.75 - (((PointX*1.5)**2)**(1/3)))**2

#    ResVal = pow( PointX*0.60                                ,2) +    \
#             pow((PointY*0.75 - pow(pow((PointX*1.5),2),1/3)),2)

#    ResVal = math.pow( PointX*0.60                                          ,2) +    \
#             math.pow((PointY*0.75 - math.pow(math.pow((PointX*1.5),2),1/3)),2)
    return  ResVal

# @@ クリップ処理
# @@  最大値から最小値内のValueを返す
# @@  変数Valueが最大値以上、最大値を返す。
# @@  変数Valueが最小値未満、最小値を返す。
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
def ClipValue(
        Value       ,   # 入力値
        MaxVal      ,   # 最大値
        MinVal          # 最小値
    ):
    if(     Value > MaxVal ):   ResVal = MaxVal
    elif(   Value < MinVal ):   ResVal = MinVal
    else:                       ResVal = Value

    return  ResVal

#--
#-- Window 生成
#-----------------------------------------------------------
class WindowApp(tk.Frame):
    def __init__(self,
            master      = None          ,   # メインWindow
            title       = "Nao-Milk"    ,   # Windowタイトル
            width       = 64            ,   # Canvasの幅
            height      = 32            ,   # Canvasの高さ
            filename    = "play.png"        # 出力画像ファイル名 
        ):
        super().__init__(master)
        self.pack()
        self.master.title(title)
        self.master.geometry("640x640")

        # [ Create Screen ]
        self.image = tk.PhotoImage(width=width,height=height)   #++ 下地作成
        self.sizex = self.image.width()                         #++ 下地の幅(確認)
        self.sizey = self.image.height()                        #++ 下地の高さ(確認)
        self.type  = self.image.type()                          #++ 下地のType(確認)
                                                                #++   [e.g. "photo" or "bitmap".]
        print(" --> W x H = ",self.sizex, self.sizey , "type[",self.type ,"]")

        # [ Generate Image ]  R成分のみ演算
        #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        HalfTX = 32.0;                                          #++ X方向 座標領域の半分
        HalfTY = 32.0;                                          #++ Y方向 座標領域の半分

        HalfPX = int(self.sizex/2);     HalfPY = int(self.sizey/2);     #++ 出力領域の半分
        StartX = HalfTX*-1;             StartY = HalfTY* 1;             #++ 座標の開始位置
        ResolX = (1/HalfPX*HalfTX)* 1;  ResolY = (1/HalfPY*HalfTY)*-1   #++ 座標間隔

        ty = StartY
        for py in range(0,self.sizey):
            tx = StartX
            for px in range(0,self.sizex):
                # ----- 演算 -----
                fr = HeartSymbol(tx,ty)
                fg = 0
                fb = 0
                r = 255-ClipValue(math.floor(fr),255,0)         #++ R成分
                g =     ClipValue(math.floor(fg),255,0)         #++ G成分
                b =     ClipValue(math.floor(fb),255,0)         #++ B成分

                # ----- 出力位置に色を設定 -----
                color = "#" + format(r, '02x') + format(g, '02x') + format(b, '02x')
                self.image.put(color,to=(px,py))

                tx = tx + ResolX

            ty = ty + ResolY

        #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        # [ display graphical elements ]
        self.canvas = tk.Canvas(self.master,width=self.sizex,height=self.sizey,bg="black")
        self.canvas.create_image(0,0,image=self.image,anchor=tk.NW)

        # [ Scrollbar ]
        bar_y = tk.Scrollbar(self.master, orient=tk.VERTICAL)
        bar_x = tk.Scrollbar(self.master, orient=tk.HORIZONTAL)
        bar_y.pack(side=tk.RIGHT , fill=tk.Y)
        bar_x.pack(side=tk.BOTTOM, fill=tk.X)
        bar_y.config(command=self.canvas.yview)
        bar_x.config(command=self.canvas.xview)
        self.canvas.config(yscrollcommand=bar_y.set, xscrollcommand=bar_x.set)
        self.canvas.config(scrollregion=(0, 0, self.sizex, self.sizey))
        self.canvas.pack(anchor=tk.NW, side=tk.LEFT)
        
        # [ File Save ]
        self.image.write(filename=filename)

#--
#-- Main
#-----------------------------------------------------------
if __name__ == "__main__":
    Frame1 = tk.Tk()                                # ++ Toplevel widget
    WinAp1 = WindowApp(
                master      = Frame1            ,   # ++ メインWindow
                title       = "Nao-Milk Play2"  ,   # ++ Windowタイトル
                width       = 512               ,   # ++ Canvasの幅
                height      = 512               ,   # ++ Canvasの高さ
                filename    = "play2.png"           # ++ 出力画像ファイル名
            )
    WinAp1.mainloop()

出力画像

プログラムを実行して生成した画像を以下に示します。

f:id:nao-milk:20210408012848p:plain
出力画像

実行環境

最後に

全てのモジュールが分かってないため、もっとスムーズなやり方があるかもしれませんが、今の知識ではここまででした。
尚、「やりたいこと」の思考からネットで「記述例」を調べ参考にしつつ、モジュールのソースも見ながら、試行錯誤しながら作成してみました。
(各モジュールの気持ち[意図]を知るため。)

Instagramでは、緑のハート、青のハートも載せています。
https://www.instagram.com/nao_nari0504/

Excel(VBA)で拡大/縮小処理の確認(バイリニア補間追加)

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

拡大/縮小処理に「バイリニア補間処理」を追加した場合の画像をご紹介します。

前回の「Excel(VBA)で拡大/縮小処理の確認」を改造し、少し画質が良くなったものとなります。

(バイリニア補間の詳細は、ネットで検索すると演算式が出てきますので、割愛します。)

nao-milk.hatenablog.com

 

バイリニア補間画像

以下に画像を示します。

※前回の「単純に参照画素を出力」する方法を「近傍法」と言います。

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

拡大縮小画像(バイリニア補間)


近傍法より、バイリニア補間法がキレイに見えると思います。

 

補足)

 バイリニア補間法より「バイキュービック補間法」という方法がキレイになります。

 

VBAの追加変更箇所

バイリニア補間を追加/変更箇所を以下に示します。

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

VBA記述(追加変更箇所)

配列Pix()を追加し、GetPix()とBilinear()を追加しました。

GetPix()は、参照画素の座標を指定し、入力画像から画素値を取得します。

ただし、

入力画像範囲外の座標を指定した場合は端の画素を取得できるようになっています。

 

GetPix()記述

GetPixのVBA記述を以下に示します。

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

VBA記述(GetPix)

Bilinear()記述

BilinearのVBA記述を以下に示します。

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

VBA記述(Bilinear)

バイリニアは、4画素に対して重み付けを行い、その合計を結果として出力します。

Pix(0)が注目画素となり、その周辺画素がPix(1)~(3)になります。

 Pix(0) = 座標(x,y)

 Pix(1) = 座標(x+1,y)

 Pix(2) = 座標(x,Y+1)

 Pix(3) = 座標(x+1,y+1)

 

引数DtXがX方向の小数部、DtYがY方向の小数部となります。

変倍カウンタ CtXの下位16bit、CtYの下位16bitが小数部です。

 

各重み付けは、

X方向の重み付けしてからY方向の重み付けを行います。

これはビットオーバーフロー(演算結果が32bit以上)を防ぐため整数値に戻してから次のY方向の重み付けを行います。

演算式をbit幅で表すと「9bit×17bit×17bit」となり、演算結果の必要bit数は43bitとなり、Long型の32bit超えます。

 

そこで、最初に

 X方向:9bit×17bit → 26bit → (2^16=65536[17bit])で割り、9bitにして

 Y方向:9bit×17bit → 26bit → (2^16=65536[17bit])で割り、9bitにしています。

 

最後に

今回は、補間処理に関して紹介しました。

拡大/縮小では、参照画素を出力するだけではカクカクした画像になり、補間処理を行うことで滑らかになります。

 

Excel(VBA)で拡大/縮小処理の確認

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

拡大/縮小処理のプログラムをExcel VBAで作成しました。

尚、生成中は浮動小数演算を使用せず固定小数を用いています。

 

 

 参考記事

拡大/縮小処理は、以下の記事を参照ください。

nao-milk.hatenablog.com

 

浮動小数を使用しない演算については、以下の2つの記事を参照ください。 

nao-milk.hatenablog.com

 

nao-milk.hatenablog.com

 

どのような画像になるのか

横×縦=32×32画素の入力画像をVBAで0.75倍、1.25倍した画像を以下に示します。

尚、バイリニアなどの補間処理は行っていません。

※画素値は0~511までの範囲をとり、0が白、511に近づくほど青になっていきます。

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

拡大縮小画像(±0.25)

 

プログラム構成

Subプロシージャのみとなり、以下がプログラムです。

このプログラムの特徴は以下の通りです。

  1. 横と縦の変倍率は独立しています。(ParaRateXとParaRateY)
  2. 入力画像の横と縦のサイズは指定が可能。(ParaISizeXとParaISizeY)
  3. 出力画像の横と縦のサイズは指定が可能。(ParaOSizeXとParaOSizeY)
  4. 変倍カウンタの小数部のbit幅は指定が可能。(ParaBtSize)
  5. 拡大/縮小時、入力画像が無い場合は、出力画素値を「-1」にします。

 

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

VBA記述(BigSmall)

パラメータ取得(11~17行目)

シート「パラメータ」で設定された値を取得します。(以下、シート「パラメータ」の抜粋)

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

B列の「設定値」を変更し、[拡大/縮小 開始]ボタンをクリックすると、シート「Image」にある入力画像を拡大又は縮小して結果をシート「出力」に出力します。

冒頭の画像(「拡大縮小画像(±0.25)」)が抜粋したものになります。

 

カウンタ加算値(25~28行目)

変倍率から変倍カウンタの加算値を算出します。(25,27行目)

また、浮動小数から固定小数に変換します。(26,28行目)

小数部のbit幅は、シート「パラメータ」の"変倍カウンタ小数部bit幅"で指定した値を使用します。(このプログラムでは、16bitに設定しています。)

 

カウンタ動作(43~68行目)

横と縦方向にカウンタを動作させて拡大又は縮小処理を行います。

 

X方向 及び Y方向の出力画素数は、変数OtX及びOtYで管理し、出力画像サイズまで動作します。

また、変倍カウンタは変数CtX及びCtYとなり、カウンタ加算値で算出した値を毎回加算します。

 

拡大/縮小時に入力画像から参照する画素は変数CtXとCtYの整数部になり、その整数部(入力画像の参照画素位置)は、変数ItX及びItYとなります。

尚、変数DtXとDtYは小数部となります。(補間時に使用する目的で残しています。)

 

54行目で変数ItXとItYからシート「Image」にある入力画像から画素を参照し、変数OutValueに代入します。

その後、

61行目で出力画素位置(変数OtXとOtY)で示す位置にOutValueの値を出力します。

(シート「出力」に出力します。)

 

 

変倍カウンタ動作 

拡大及び縮小時のカウンタ動作を以下に示します。

「カウンタ値」欄は、VBAからExcelに表示させた値となり、

「整数部」欄は、「カウンタ値 / 65536」の整数値を示し、

「小数部」欄は、「カウンタ値 % 65536」の値を示します。

また、「(小数値)」欄は、「小数部 / 65536」を示し、「浮動小数値」は変倍率から浮動小数で計算したカウンタ値になります。

 

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

変倍カウンタ値

  

最後に

画質は参照画素をそのまま出力しているため、悪いですが、変倍カウンタは浮動小数で計算した場合と誤差1画素となります。

画質評価で小数部のbit幅を増やすなどで設計検討したりします。