FRAMにアクセス (FPGA Top作成編)
Platform DesignerとFPGA Topの作成になります。
FPGAのTOP構成
FPGAのTOP構成は以下となります。(Quartusを抜粋)
ソースコードは以下の通りです。
尚、FRAM I/FブロックのPRM_???は固定値にしています。本来はブートシーケンスが制御することを想定しているため、FPGAでは簡易的に外部からの指示に応じて、メモリ内に保存するような記述にしています。
module TOP( input wire RESET_N , // @@ リセット input wire CLOCK , // @@ クロック output wire FRAM_WP_N , // @@ FRAM ライトプロテクト[0:プロテクト] output wire FRAM_CS_N , // @@ FRAM SPI チップセレクト output wire FRAM_SCLK , // @@ FRAM SPI シリアルクロック output wire FRAM_MOSI , // @@ FRAM SPI シリアル出力 input wire FRAM_MISO , // @@ FRAM SPI シリアル入力 input wire SW_SLAVE , // @@ Master/Slave切替 [0:Master] input wire SW_GO , // @@ 処理イネーブル output wire LED_LOCK // @@ LED Lock ); // @@ // @@ クロック/リセット生成 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wire cpu_rst_n; wire cpu_clk ; wire sys_rst_n; wire sys_clk ; NML_CLKRST UnCLKRST( .PAD_RST_N (RESET_N ), // input wire PAD_RST_N , // @@ 入力リセット .PAD_CLK (CLOCK ), // input wire PAD_CLK , // @@ 入力クロック .CPU_RST_N (cpu_rst_n ), // output reg CPU_RST_N , // @@ CPU用 リセット .CPU_CLK (cpu_clk ), // output wire CPU_CLK , // @@ CPU用 クロック[50MHz] .SYS_RST_N (sys_rst_n ), // output reg SYS_RST_N , // @@ System用 リセット .SYS_CLK (sys_clk ), // output wire SYS_CLK , // @@ System用 クロック[200MHz] .PLL_LOCK (LED_LOCK ) // output wire PLL_LOCK // @@ PLL LOCK ); // @@ // @@ CPU // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wire fram_ami_wen; wire [31:0] fram_ami_add; wire [ 3:0] fram_ami_ben; wire [ 7:0] fram_ami_wdt; wire fram_asi_wen; wire fram_asi_ren; wire [ 5:0] fram_asi_add; wire [31:0] fram_asi_wdt; wire fram_asi_wit; wire fram_asi_rvl; wire [31:0] fram_asi_rdt; wire fram_cpu_irq; NML_CPU UnCPU( .clk_clk (cpu_clk ), // input wire clk_clk, // clk.clk .irq_irq (fram_cpu_irq ), // input wire [0:0] irq_irq, // irq.irq .fram_write (fram_asi_wen ), // output wire fram_write, // .write .fram_read (fram_asi_ren ), // output wire fram_read, // .read .fram_address (fram_asi_add ), // output wire [5:0] fram_address, // .address .fram_writedata (fram_asi_wdt ), // output wire [31:0] fram_writedata, // .writedata .fram_waitrequest (fram_asi_wit ), // input wire fram_waitrequest, // fram.waitrequest .fram_readdatavalid (fram_asi_rvl ), // input wire fram_readdatavalid, // .readdatavalid .fram_readdata (fram_asi_rdt ), // input wire [31:0] fram_readdata, // .readdata .fram_burstcount (/** OPEN **/ ), // output wire [0:0] fram_burstcount, // .burstcount .fram_byteenable (/** OPEN **/ ), // output wire [3:0] fram_byteenable, // .byteenable .fram_debugaccess (/** OPEN **/ ), // output wire fram_debugaccess, // .debugaccess .fram_wp_export (FRAM_WP_N ), // output wire fram_wp_export, // fram_wp.export .param_clken (1'b1 ), // input wire param_clken, // .clken .param_chipselect (1'b1 ), // input wire param_chipselect, // .chipselect .param_write ( fram_ami_wen ), // input wire param_write, // .write .param_address ( fram_ami_add[11: 2] ), // input wire [9:0] param_address, // param.address .param_byteenable ( fram_ami_ben ), // input wire [3:0] param_byteenable, // .byteenable .param_writedata ({4{fram_ami_wdt}} ), // input wire [31:0] param_writedata, // .writedata .param_readdata (/** OPEN **/ ), // output wire [31:0] param_readdata, // .readdata .port_a_export ( ), // output wire [7:0] port_a_export, // port_a.export .port_b_export ( ), // output wire [7:0] port_b_export, // port_b.export .reset_reset_n (cpu_rst_n ) // input wire reset_reset_n // reset.reset_n ); // @@ // @@ FRAM I/F // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NML_FRAMIF UnFRAMIF( .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 (SW_SLAVE ), // input wire MST_SLV , // @@ Avalon-MM Master/Slave切替 [0:Master] .PRM_ENB (SW_GO ), // input wire PRM_ENB , // @@ 処理イネーブル [1→0:OFF 0→1:ON] .PRM_DIV (4'd0 ), // input wire [ 3:0] PRM_DIV , // @@ シリアルクロック周波数設定 .PRM_POL (1'b0 ), // input wire PRM_POL , // @@ シリアルクロック極性設定 .PRM_TAK (2'd0 ), // input wire [ 1:0] PRM_TAK , // @@ シリアルデータ取り込みタイミング設定 .PRM_ACS (21'd8192 ), // input wire [20:0] PRM_ACS , // @@ アクセスバイト数 (最大値:1,048,579) .PRM_OTC (2'd3 ), // input wire [ 1:0] PRM_OTC , // @@ 出力バイト数 (最大値:3) .PRM_MSK (1'b0 ), // input wire PRM_MSK , // @@ 割り込みマスク .PRM_CLR (1'b0 ), // input wire PRM_CLR , // @@ 割り込みクリア .PRM_STA (32'h0000_0000 ), // input wire [31:0] PRM_STA , // @@ Avalon-MM(Master側) ライトベースアドレス .PRM_CMD (8'h03 ), // input wire [ 7:0] PRM_CMD , // @@ RAMコマンド .PRM_ADD (24'h00_0000 ), // input wire [23:0] PRM_ADD , // @@ FRAMアドレス .SAV_WEN (fram_asi_wen ), // input wire SAV_WEN , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ライトイネーブル .SAV_REN (fram_asi_ren ), // input wire SAV_REN , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードイネーブル .SAV_ADD (fram_asi_add ), // input wire [ 5:0] SAV_ADD , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リード/ライトイネーブル .SAV_WDT (fram_asi_wdt ), // input wire [31:0] SAV_WDT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ライトデータ .SAV_WIT (fram_asi_wit ), // output wire SAV_WIT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ウェイトリクエスト .SAV_RVL (fram_asi_rvl ), // output wire SAV_RVL , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードデータイネーブル .SAV_RDT (fram_asi_rdt ), // output wire [31:0] SAV_RDT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードデータ .MAV_WEN (fram_ami_wen ), // output wire MAV_WEN , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトイネーブル .MAV_ADD (fram_ami_add ), // output wire [31:0] MAV_ADD , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトアドレス .MAV_BEN (fram_ami_ben ), // output wire [ 3:0] MAV_BEN , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) バイトイネーブル .MAV_WDT (fram_ami_wdt ), // output wire [ 7:0] MAV_WDT , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトデータ .MAV_WIT (1'b0 ), // input wire MAV_WIT , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ウェイトリクエスト .CPU_IRQ (fram_cpu_irq ), // output wire CPU_IRQ , // @@ [CPU_CLK↑同期] 割り込み信号 .MON_ENB (/** OPEN **/ ), // output wire MON_ENB , // @@ [CPU_CLK↑同期] 動作モニタ [0:停止中 1:動作中] .FRAM_CS_N (FRAM_CS_N ), // output wire FRAM_CS_N , // @@ [SYS_CLK↑同期] FRAM SPI チップセレクト .FRAM_SCLK (FRAM_SCLK ), // output wire FRAM_SCLK , // @@ [SYS_CLK↑同期] FRAM SPI シリアルクロック .FRAM_MOSI (FRAM_MOSI ), // output wire FRAM_MOSI , // @@ [SYS_CLK↑同期] FRAM SPI シリアル出力 .FRAM_MISO (FRAM_MISO ), // input wire FRAM_MISO , // @@ [SYS_CLK↑同期] FRAM SPI シリアル入力 .FRAM_OUTE (/** OPEN **/ ) // output wire FRAM_OUTE // @@ [SYS_CLK↑同期] FRAM SPI 出力イネーブル[1:出力 0:Hi-Z] ); endmodule
Platform Designer
QuartusのPlatform DesignerでNiosIIやメモリなどを配置します。
ただし、PLLやユーザーブロック(FRAM I/Fブロック)は配置せず、FPGAトップに配置します。
これはFPGAトップからRTLシミュレーションする時に、Platform Designerで生成したRTLの配線を検索するのが大変なことと、バグがあった時に修正に手間がかかるためです。
また、PLLを外に置くことにより、クロックの追加やリセット回路の変更を容易にすることが目的です。
私個人の考えとして、Platform Designerで作成する所は、変更が少ない所を配置したいと考えています。
また、Qsysファイルにはタイムスタンプがあり、このブロックを変更すると、NiosのBSPを再実行する必要があるためです。
ブロック構成図
ブロック構成を以下に示します。
※Platform Designerで作成するブロックは、モジュール「NML_CPU」になります。
※モジュールNML_FRAMIFのverilogソースは、以下をご参照ください。
nao-milk.hatenablog.com
System Contents
アドレスマップ
CPUから見たアドレスマップを以下に示します。
Quartusに読み込ませるファイル
Platform Designerの[Generate HDL]でVerilogコードが生成されます。
その時、フォルダ「モジュール名\synthesis」に"モジュール名.qip"が出来上がります。
このファイルをQuartusに読み込ませます。
"モジュール名.qsys"を読み込ませると、合成時にGenerate HDLもするため、タイムスタンプが変わり、NiosのBSPにも影響があり面倒なので、私はqipを読み込ませています。
クロック/リセット生成
PLLを配置し、リファレンスクロック(25MHz)から50MHzと200MHzクロックを生成し、クロックドメインに対応したリセット信号を生成します。
ソースコードは以下の通りです。
// ** // ** クロック/リセット生成 // ** // ***************************************************************************** module NML_CLKRST( input wire PAD_RST_N , // @@ 入力リセット input wire PAD_CLK , // @@ 入力クロック output reg CPU_RST_N , // @@ CPU用 リセット output wire CPU_CLK , // @@ CPU用 クロック[50MHz] output reg SYS_RST_N , // @@ System用 リセット output wire SYS_CLK , // @@ System用 クロック[200MHz] output wire PLL_LOCK // @@ PLL LOCK ); // @@ // @@ PLL Macro // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NML_CLKRST_PLL UnPLL( .refclk ( PAD_CLK ), // input wire refclk, // refclk.clk .rst (~PAD_RST_N ), // input wire rst, // reset.reset .outclk_0 ( CPU_CLK ), // output wire outclk_0, // outclk0.clk .outclk_1 ( SYS_CLK ), // output wire outclk_1, // outclk1.clk .locked ( PLL_LOCK ) // output wire locked // locked.export ); // @@ // @@ リセット回路 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ reg [ 1:0] cpu_rst_sft; reg [ 1:0] sys_rst_sft; always @( posedge CPU_CLK or negedge PLL_LOCK ) begin if( ~PLL_LOCK ) {CPU_RST_N,cpu_rst_sft} <= {1'b0,2'b00}; else {CPU_RST_N,cpu_rst_sft} <= {cpu_rst_sft,1'b1}; end always @( posedge SYS_CLK or negedge PLL_LOCK ) begin if( ~PLL_LOCK ) {SYS_RST_N,sys_rst_sft} <= {1'b0,2'b00}; else {SYS_RST_N,sys_rst_sft} <= {sys_rst_sft,1'b1}; end endmodule
最後に
Platform DesignerはIPマクロを配線するのに便利です。
が、ユーザーロジックを含めて配線するとデバッグ時にユーザー回路のポートを修正すると手間が掛かります。
FRAMにアクセス (シミュレーション編)
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
FRAMにアクセス (Verilogコーディング編)
FRAM I/Fのverilogコーディングとなります。
ブロック仕様は以下を参照ください。
nao-milk.hatenablog.com
FRAM I/Fブロック構成
ブロック構成は以下のようになります。
ソースコード
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]で行います。
各状態の遷移時に、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ブロックにライトします。
主な内部構成は以下の通りです。
// ** // ** 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[2]) & (end_cont[1:0]!=2'b00))); 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指定します。
- byt_cont[20:0] → byt_cont[20:0]
- byt_cont[20:0] → BUF_ADD[20:0]
これは、+1した値をラッチするもので、1クロック以内で確定する必要のないものとなります。
起点byt_cont[20:0]となる経路を以下に示します。
内部信号byt_cont[20:0]は最小32クロック後に+1加算され、BUF_ADD[20:0]も13クロック以内に+1した値が確定していれば良いようになります。
タイミング図を以下に示します。
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
シミュレーション波形
FRAM I/FブロックをTOPとしたRTLシミュレーション波形を以下に示します。
マスタモードとスレーブモードの波形となり、「デバイスID読み出し」の場合のシミュレーション波形となります。
マスタモード
スレーブモード
最後に
設計では、どこを「set_false_path」にするかも検討する必要があります。
どのタイミングで変化するのか、どれくらいまでに値が確定しておけば良いのか、把握する必要があります。
また、STA結果も、set_false_path指定した起点終点間の遅延量も確認する必要があります。
FRAMにアクセス (ブロック仕様編)
FRAM(強誘電体RAM)をアクセスするためのFPGAをテーマに、FPGAを設計するまでの一連の工程を記載して行こうと思います。
- ブロック仕様
- Verilog-HDLによるコーディング
- シミュレーション(RTLシミュレーション)
- Platform DesignerとFPGA Topの作成
- 論理合成とインプリメント
- NiosIIのコーディング
- NiosIIを含めたシミュレーション
本記事では、まずはブロックの仕様を記載します。
(後半に、FRAMとインターフェイスを行うSPIブロックのVerilogコードも記載しています。)
※FRAMのモデルは「CY15B102QN」となります。
(ちょうど、verilogのシミュレーションモデルがあったので。。。)
【背景】
昔の案件でFRAMにアクセスする機能が必要で、しかも、アクセス時間を短くしたかったため、バーストアクセスができるIPを探したのですが、見当たらなかったため、verilogで作りました。
その時は、必要な機能に限定して作成しましたが、今回は依頼されたものではないので、私の好きなように作ろうと思います。
ポイントとしては、色々なコマンド(Opcode)が発行でき、且つ ブートローダとして使えそうな機能も考慮してしみました。
- 概要
- 機能
- ブロック構成
- 入出力信号一覧
- 入出力タイミング
- 動作説明
- 内蔵RAMとFRAMアクセスの関係
- レジスタ詳細
- 設定例
- SPI I/Fのソースコード(記述:verilog-HDL)
- 使用ツール
- 最後に
概要
FRAM(CY15B102QN)デバイスに対し、リード/ライトを行います。
リード/ライト方法はSPIプロトコルで行います。
以下にFRAMデバイスの通信フォーマット例を示します。
機能
【CPU I/F (Avalon-MM)】
- MasterとSlaveに分かれ、入力信号MST_SLV(0:Master 1:Slave)により選択が可能。
- Masterを選択した場合、入力信号PRM_???に従って、FRAMから取得したデータをAvalon-MM(Master側)のブロックから出力します。
- Slaveを選択した場合、Avalon-MM(Slave側)でCPUから設定されたレジスタ値に従い、アクセスを行います。
- FRAMへの転送が完了すると、割り込みを発生します。尚、割り込みマスクが可能であり、割り込みステータスに"1"を書き込むことでクリアします。(割り込み信号はレベル信号)
- アクセスバイト数(バーストサイズ)は、レジスタREG_ASC[20:0]又は入力信号PRM_ASC[20:0]で設定が可能。尚、スレーブモード時の最大アクセスバイト数は内蔵RAMサイズに依存します。
- 出力(送信)バイト数は、レジスタREG_OTC又は入力信号PRM_OTC[1:0]で設定が可能。
- シリアルクロック(出力信号FRAM_SCLK)は内部クロックSYS_SLK[200MHz]を分周して生成し、最大50MHz(SYS_CLKの1/4分周)を出力。
- シリアルクロックの周波数は、レジスタREG_DIV[3:0]又は入力信号PRM_DIV[3:0]で設定が可能。
- 入力シリアルデータ(入力信号FRAM_MISO)は、SYS_CLK↑に同期してラッチし、レジスタREG_TAK[1:0]又は入力信号PRM_TAK[1:0]により、取り込みタイミングを調整することが可能。
ブロック構成
FRAMとI/Fを行うブロック(ブロック名:NML_FRAMIF)の構成図を以下に示します。
※CPUブロックは50MHzで動作します。
※TOPは、FPGAのTOPを示します。
入出力信号一覧
FRAM I/Fブロックの入出力信号の一覧を以下に示します。
入出力タイミング
FRAM I/Fブロックの入出力タイミングを以下に示します。
FRAM I/F信号
タイミング波形とレジスタ設定値との関係を以下に示します。
※出力信号FRAM_OUTEは、FRAMへのシリアルデータを出力しない場合はHi-Zしたいことも想定して補足信号としてあります。
FRAMデータ取り込みタイミング
シリアルクロックの最大周波数は50MHz[最小周期は20ns]となるため、FFから出力してFRAMへ届くまでの時間(シリアルクロックが届くまでの時間) と 届いてからFRAM I/Fブロックの入力FFまでの届く時間の合計が20ns以上の場合もあると考え、基準取り込みポイントから最大3クロック遅延させて取り込むことが可能です。
以下に経路遅延イメージを示します。また、取り込みタイミング図も示します。
Avalon-MM(Slave側)
リード時は、出力データを準備するためにWait信号(出力信号SAV_WIT)をアサートします。
また、レジスタと内蔵RAMをリードする場合でWait信号のアサート期間が変わります。
Avalon-MM(Master側)
wait信号(入力信号MAV_WIT)のアサート期間中は出力を保持します。
動作説明
FRAM I/Fブロックの動作に関して説明します。
マスタモード(入力信号MST_SLV=0)
マスタモードは入力信号による制御を可能とし、FRAMからの取得したデータをAvalon-MM Masterとしてライト制御します。
尚、このモードはFRAMのリード系コマンド(READ,SSRDなど)のみを想定しています。
- 入力信号PRM_DIV、PRM_POL、PRM_TAK、PRM_ACS、PRM_OTC、PRM_STA、PRM_CMD、PRM_ADDを設定します。
- PRM_ENBを"0"から"1"に設定します。これにより、動作を開始します。
- FRAMへコマンド(PRM_CMDの内容)発行を行い、PRM_OTC[1:0]に応じてアドレス発行(PRM_ADDの内容)を行います。
- FRAMから取得したシリアルデータを8bitパラレルデータに変換し、FIFOに格納します。
- FIFOがNot Emptyになると、Avalon-MM I/F(Master側)がFIFOをリードし、Avalon-MMライト動作を行います。尚、ライトアドレスは入力信号PRM_STAを加算して出力します。
- FRAMへのアクセスが完了すると、出力信号CPU_IRQをアサートします。尚、PRM_CLRを"1"にすることでクリアします。
※動作中は、出力信号MON_ENBが"1"になります。
スレーブモード(入力信号MST_SLV=1)
CPUからのレジスタ設定に応じて動作を行い、内蔵RAMに格納されたデータをFRAMへ出力し、FRAMから出力したデータを内蔵RAMに格納します。
従って、1回のFRAMへのリード又はライトはRAMサイズに依存します。
(内蔵RAMのサイズを大きくすることで、アクセスバイト数は大きくできます)
尚、内蔵RAMサイズは4096バイトとしています。
- レジスタに初期設定を行います。(REG_DIV、REG_POL、REG_TAK、REG_ACS、REG_OTC)
- FRAMへ出力したいデータをCPUから内蔵RAMへ格納します。(コマンドやアドレス、ライトデータなど)
- 処理イネーブルを"1"にします(レジスタREG_ENB)。これにより、動作を開始します。
- FRAMへの出力時は内蔵RAMからデータを取得してFRAMへ出力します。またFRAMから取得したデータは、8bitのパラレルデータに変換してRAMへ保存します。
- FRAMへのアクセスが完了すると、出力信号CPU_IRQをアサートします。尚、レジスタREG_ENBも自動的に"0"になるため、割り込みマスクを行い、レジスタREG_ENBの状態をポーリングすることにより、動作中か否かを知ることが可能です。
- CPUがFRAMから取得したデータを内蔵RAMから読み出す。(レジスタREG_BDT)
内蔵RAMとFRAMアクセスの関係
スレーブモード時の内蔵RAMとFRAM SPI通信のデータ構成の関係を以下に示します。
尚、FRAMアクセスの最初はCommand(Opcode)の出力となります。
※Command= "SSWR"と"SSRD"は特殊セクタアクセスとなり、256バイト以上のアクセスを行った場合は、特殊セクタの先頭アドレスに戻るため、グレーにしています。
マスタモード時は、内蔵RAMを介さず、取得したデータをそのままAvalon-MM Master側でFRAM I/Fブロック外部にライトします。
レジスタ詳細
FRAM I/Fのレジスタ詳細を以下に示します。
設定例
FRAM SPI通信のバイト数設定例を以下に示します。
スレーブモード
マスタモード
SPI I/Fのソースコード(記述:verilog-HDL)
FRAM I/Fの内部ブロックSPI I/F(モジュール名:NML_FRAMIF_SPI)のソースコードを以下に示します。
入出力信号の宣言と処理イネーブルがアサートされ内蔵RAMから最初のデータ(Command)をリードするための期間を生成します。
SPI通信期間を示すasc_enblを生成し、またアクセス終了後のWait期間を生成します。
シリアルクロックの生成、bitカウンタとByteカウンタを生成します。
尚、Byteカウンタは、CY16B102以上のデバイスでも対応できるよう、余分にbit幅をとっています。
SPI通信の出力と入力を示すフラグを生成し、内蔵RAMのアクセス制御を行います。
ベースとなるFRAM制御信号(base_*)を生成し、FFでラッチして出力します。
これにより、FPGAの出力FFを使用します。
FRAMからの入力シリアルデータラッチ(in_miso)し、基準取り込みタイミング信号を生成します。(入力FFを使用します)
基準取り込みタイミングを遅延します。
レジスタREG_TAKの設定値に応じて、取り込みタイミングを選択し、シリアルデータをパラレルデータに変換します。
変換したデータは、上記BUF_WDTでラッチします。
FRAMアクセス完了後、レジスタREG_ENBを"0"にするための信号REG_FINを生成します。
尚、この信号は後段ブロックでCPU_CLK(50MHz)で使用するため、High幅をSYS_CLKの8クロック分にしています。(CPU_CLKの2クロック分)
使用ツール
- 言語:verilog-HDL(ハードウェア用)、C言語(ソフトウェア用[NiosII用])
- シミュレータ:ModelSim Intel FPGA Starter Edition 10.5b
- 論理合成/インプリメント:Quartus Prime 18.1 Lite Edition
最後に
マスタモードとスレーブモードに分けたのは、試作を意図しています。
FRAMへのライトとリードも、マスタモードで行うことも可能ですが、リードだけに止めました。
機能試作や基板試作時は、スレーブモードを使ってFRAMに対して色々なコマンドを発行して、機能検討や基板変更を考えると思います。
その為、なるべく自由度が高いと思われるスレーブモードを入れました。
Pythonで画像描画
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番目に速いのか?
本文の構成(目次)は、以下のようになります。
(出力画像は、下の方に添付しています。)
ソース構成
ソース構成は、以下の通りです。
演算式
ハートマークを生成するための演算式は、以下の通りです。
私がキレイと思うハートマークを作るため、変数xとyに係数を掛けています。
動作説明
プログラムの主な動作について説明します。
※ソースコードは、後述でそのまま添付してしています。
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()
出力画像
プログラムを実行して生成した画像を以下に示します。
最後に
全てのモジュールが分かってないため、もっとスムーズなやり方があるかもしれませんが、今の知識ではここまででした。
尚、「やりたいこと」の思考からネットで「記述例」を調べ参考にしつつ、モジュールのソースも見ながら、試行錯誤しながら作成してみました。
(各モジュールの気持ち[意図]を知るため。)
※Instagramでは、緑のハート、青のハートも載せています。
https://www.instagram.com/nao_nari0504/