FPGA 版 FM 音源 (64) -- FPGA 版 EG (8)
2017 年 1 月 28 日付けの記事 (→こちら) の「周辺回路」部分の Verilog ソースを記載していきます。
まず、EG クロック・ジェネレータ・モジュール「opl3_EG_clk()」の Verilog ソースを下に示します。
"opl3_EG_clk.v"
// // opl3_EG_clk.v : OPL3 EG clock generation module // // 2017/01/18 Created by pcm1723 // module opl3_EG_clk( // Input reset, // async reset m_clk, // master clock (18.432 MHz) sob, // start of block (250 Hz) kon0, // Key ON 0 kon1, // Key ON 1 // Output clk_128fs_en, // 128fs clock enale (6.144 MHz) slot_clk_en, // slot clock enable (N_SLOT * 48 kHz) fs_clk_en, // fs clock enable (48 kHz) lrck, // LRCK (48 kHz) kon_rise, // rising edge of KON (KON0/KON1 muxed) kon_fall, // falling edge of KON (KON0/KON1 muxed) kon0_out // debounced KON0 ); // number of time slot parameter [8:0] N_SLOT = 1; parameter DEBUG = 1'b0; // master clock oversampling factor // = (m_clk / fs) = 18.432 [MHz] / 48 [kHz] = 384 localparam [8:0] M_CLK_OVS = 384; // Input port input reset; input m_clk; input sob; input kon0; input kon1; // Output port output clk_128fs_en; output slot_clk_en; output fs_clk_en; output lrck; output kon_rise; output kon_fall; output kon0_out; reg lrck; reg [2:0] kon0_z; reg [2:0] kon1_z; // wires wire lr_clk_en; // regs reg [1:0] cnt3; // 1/3 divider for 128fs reg [7:0] lr_cnt; // LR clock counter reg [1:0] deb_cnt; // debounce counter // fs clock / LR clock generator generate if (N_SLOT == 1) // slot_clk = fs_clk begin assign lr_clk_en = ( lr_cnt == (M_CLK_OVS[8:1]-1'b1) ); assign fs_clk_en = ( (~lrck) & lr_clk_en); assign slot_clk_en = fs_clk_en; // LRCK generation always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin lr_cnt <= 8'h00; lrck <= 1'b0; end else if ( lr_clk_en ) begin // reset timing lr_cnt <= 8'h00; lrck <= (~lrck); // flip LRCK end else // count up begin lr_cnt <= ( lr_cnt + 1'b1 ); end // if (reset) ... end // always @() ... end else // (N_SLOT > 1) begin reg [8:0] slot_cnt; // slot clock generator (18.432 MHz / N_SLOT) assign slot_clk_en = ( slot_cnt == ((M_CLK_OVS/N_SLOT) - 1) ); assign lr_clk_en = ( lr_cnt == (N_SLOT[8:1]-1) ); assign fs_clk_en = ( (~lrck) & lr_clk_en & slot_clk_en); always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin slot_cnt <= 9'h000; end else begin slot_cnt <= ( slot_clk_en ? 9'h000 : (slot_cnt + 1'b1) ); end // if end // always // LRCK generation always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin lr_cnt <= 1'b0; lrck <= 1'b0; end else if ( slot_clk_en ) begin if ( lr_clk_en ) begin // reset timing lr_cnt <= 1'b0; lrck <= (~lrck); end else // count up begin lr_cnt <= ( lr_cnt + 1'b1 ); end // if ( slot_clk_en ) ... end // if (reset) ... end // always @() ... end // if (N_SLOT == 1) ... endgenerate // 128fs clock (6.144 MHz) generator always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin cnt3 <= 2'b00; end else begin cnt3 <= ( clk_128fs_en ? 2'b00 : (cnt3 + 1'b1) ); end // if (reset) ... end // always @() ... // shift register for KON rising/falling edge detection always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin kon0_z[2:1] <= 2'b00; kon1_z[2:1] <= 2'b00; end else if ( fs_clk_en ) begin kon0_z[2:1] <= kon0_z[1:0]; kon1_z[2:1] <= kon1_z[1:0]; end end // always // debounce counter (4 * 4 [ms] = 16 [ms]) // and chatter-free KON sampling always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin kon0_z[0] <= 1'b0; kon1_z[0] <= 1'b0; deb_cnt <= 2'b00; end else if ( clk_128fs_en & (sob | DEBUG) ) begin if ( deb_cnt == 2'b11) begin // KON0/KON1 sampling kon0_z[0] <= kon0; kon1_z[0] <= kon1; end // if (deb_cnt == ... deb_cnt <= ( deb_cnt + 1'b1 ); end // if (reset) ... end // always @() // rising edge detection assign kon_rise = ( lrck ? ((~kon1_z[2]) & kon1_z[1]) : ((~kon0_z[2]) & kon0_z[1]) ); // falling edge detection assign kon_fall = ( lrck ? ( kon1_z[2] & (~kon1_z[1])) : ( kon0_z[2] & (~kon0_z[1])) ); assign clk_128fs_en = ( cnt3 == 2'b10 ); assign kon0_out = kon0_z[2]; endmodule
このモジュールでは、マスタークロック (m_clk) から、下のような複数のクロック・イネーブル信号と KON 関連の信号を作り出します。
- SPDIF モジュール用の 128 fs = 6.144 MHz のクロック・イネーブル信号「clk_128fs_en」の生成
- スロット・クロック・イネーブル信号「slot_clk_en」の生成
- サンプリング・クロック・イネーブル信号「fs_clk_en」の生成
- デューティー 50 % のサンプリング周波数クロックである「lrck」の生成 (イネーブル信号ではなく、クロックそのもの)
- 2 系統の KON (Key ON) 入力 (KON0 / KON1) のチャタリングを除去し、「kon_rise」, 「kon_fall」信号を作成しマルチプレクスして出力
マスタークロック (m_clk) は 18.432 MHz に選んでいるので、SPDIF 用の 128 fs クロックは m_clk を 3 分周して 18.432 [MHz] / 3 = 6.144 [MHz] を得ています。
「slot_clk_en」、「fs_clk_en」、「lrck」については、スロット数 N_SLOT = 1 の場合と、それ以外の場合とで生成方法を変えています。
N_SLOT = 1 の場合には、m_clk (18.432 MHz) を 9 ビット・アップカウンタ (相当) で 384 分周し、デューティー 50 %、クロック周波数 48 kHz の lrck を得ています。
slot_clk_en と fs_clk_en は同一の信号となり、9 ビット・アップカウンタのカウント値 (相当) 9'b111_1111 を検出して作成しています。
N_SLOT > 1 の場合には、まず slot_clk_en 信号を生成し、それを (N_SLOT / 2) 発数えて lrck 信号を反転させています。 そのため、N_SLOT は偶数でなければなりません。
KON については、SPDIF モジュールで作成される sob (Start Of Block) 信号を利用してチャタリング除去を行なっています。
sob 信号は 192 サンプルごとに発生するので、 48 [kHz] / 192 = 250 [Hz] の信号となり、その周期は 4 ms になります。 この sob 信号を 4 分周して 16 ms 周期のクロックとしたもので KON 入力をサンプリングし、持続時間が 16 ms 以下のチャタリングを除去します。
このままだと、シミュレーション実行時に時間がかかりすぎて不便なので、パラメタ「DEBUG」に「1」を渡すと sob 信号をバイパスして、128 fs クロックを 4 分周したレートで KON をサンプリングするようにしてあります。
KON0、KON1 それぞれチャタリング除去したあとに立ち上がりエッジ、立ち下がりエッジの検出を行い、マルチプレクスして kon_rise / kon_fall に出力しています。
チャタリング除去済みの KON0 信号を出力に引き出していて、波形観測の場合のトリガ信号として使えるようになっています。