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 信号を出力に引き出していて、波形観測の場合のトリガ信号として使えるようになっています。