FPGA 版 FM 音源 (59) -- FPGA 版 EG (3)

EG クロック・プリスケーラ・モジュール「opl3_EG_psc()」を回路図で表現したものを下に示します。

Verilog で書いたものを下に示します。
"opl3_EG_psc.v"

//
// opl3_EG_psc() :  OPL3 EG prescaler module
//
// 2017/01/08 Created by pcm1723
//
module opl3_EG_psc(
//
// Input
//
    reset, // async. reset input
    m_clk, // master clock input
    fs_clk_en, // enable for sampling clock
    rate,  // EG rate input
    rof_1, // rof[1] input
    rof_0, // rof[0] input
//  
// Output
//
    clk_en,  // EG clock enable output
    sft_sel  // EG shift select output
);
// Input port						 
  input        reset;
  input        m_clk; 
  input        fs_clk_en;
  input  [3:0] rate;
  input        rof_1;
  input        rof_0;
// Output port  
  output       clk_en;
  output       sft_sel;
// wires
  wire        hi_rate;  // hi_rate flag
  wire [13:0] edge_det; // positive edge detction
//  
  wire [15:0] base; // base frequency pulses
  wire [15:0] div2; // base div 2 freq. pulses
  wire [15:0] div4; // base div 4 freq. pulses
// regs
  reg  [13:0] psc_cnt; // prescaler counter
  reg  [13:0] psc_z1;  // 1 sample delay 
//  
// hi_rate flag (rate >= 13)
  assign hi_rate = ( rate >= 13 );
// positive edge detection of prescaled clock
  assign edge_det = (psc_cnt & (~psc_z1));
// base frequency pulses  
  assign base = {1'b0,edge_det[11:0],3'b000};
// base freq. div 2 pulses  
  assign div2 = {1'b0,edge_det[12:1],3'b000};
// base freq. div 4 pulses  
  assign div4 = {1'b0,edge_det[13:2],3'b000};
// EG clock enable signal
  assign clk_en = (hi_rate | base[~rate] | (rof_1 & div2[~rate]) | (rof_0 & div4[~rate]));
// EG shift select signal
  assign sft_sel = ((hi_rate & rof_1 & (~psc_cnt[1])) | (hi_rate & rof_0 & psc_cnt[1] & psc_cnt[2]));  
// prescaler counter and 1 sample delay
  always @(posedge m_clk or posedge reset)
    begin
      if (reset) // async reset
        begin
          psc_z1  <= 14'h0000;
          psc_cnt <= 14'h0000;
        end
      else if (fs_clk_en) // fs clock 
        begin
          psc_z1  <= psc_cnt;
          psc_cnt <= (psc_cnt + 1'b1);
        end
      else // hold
        begin
          psc_z1  <= psc_z1;
          psc_cnt <= psc_cnt;
        end // if ...
    end // always begin ...
//  
endmodule // opl3_EG_psc()

このモジュールに限らず、すべてのモジュールで非同期リセットの「reset」入力を設けていますが、これは主にシミュレーション時に unknown だらけになるのを解消するためで、実際の回路では特に必要ありません。
各スロットの EG アキュムレータ / EG ステート・マシンの状態はメモリ (シフトレジスタ) として別に確保されているので、その内容を初期化する必要があり、「mem_init」という信号を用意しています。
EG クロック・プリスケーラ部は 1 回路だけ存在して、各スロットで共通に使用し、サンプリング周波数 fs のレートでのみ状態が更新されます。
周波数は数十 kHz と低くスピードは要求されないので、メタルゲート標準 CMOS ロジックで言えば CD4040B などの「リプル・カウンタ」でも十分ですが、実際の FPGA 回路としてはリプル・カウンタは好ましくなく、同期型カウンタにする必要があります。
また、カウンタの各ビットの立ち上がりを「微分」するのに FF を使わず「組み合わせゲート」だけで実現することも可能ですが、FPGA 回路としては FF を使った方が簡単になります。
EG クロック・プリスケーラ単体でのシミュレーション結果を下に示します。 (図をクリックすると拡大します)

opl3_EG_psc_msim1.png

図の下半分のシアン色のトレースがプリスケーラ・カウンタの各ビットの立ち上がりを「微分」した「edge_det[13:0]」の各ビットで、インデクスが 1 増えると周波数は 1/2 になり、なおかつ、お互いに重なり合わない位置にパルスが配置されています。