FPGA 版 FM 音源 (61) -- FPGA 版 EG (5)
EG コントロール・モジュール「opl3_EG_ctrl()」を回路図で表現したものを下に示します。
図の上半分は AR / DR / RR のレート・パラメタのマルチプレクサおよび Rof (レート・オフセット) の処理回路です。
図の下半分は EG アキュムレータ値とサステイン・レベル (SL) との比較回路で、実際は EG アキュムレータ・モジュール「opl3_EG_acc()」に含まれています。
図では省略していますが、EG パラメタ AR / DR / SL / RR / EGT には入力レジスタが設けてあります。
EG ステート・マシンについては、回路図で描くと面倒なので省略してあります。
EG ステート・マシンの状態遷移図を左に示します。
遷移条件を表にしたものを下に示します。
各行が遷移前の状態、各列が遷移後の状態です。
「(なし)」と書いてある欄については、その組み合わせの遷移はないことを示しています。
表の対角要素は状態遷移しない条件を表しますが、複雑になるので空欄にしてあります。
次状態→ ↓前状態 |
Mute | Attack | Decay | Sustain | Release |
---|---|---|---|---|---|
Mute | KON↑ | (なし) | (なし) | (なし) | |
Attack | (なし) | acc=0dB | (なし) | KON↓ | |
Decay | acc=95dB | KON↑ | (egt=1) & (acc=SL) | KON↓ | ((egt=0) & (acc=SL)) |
|
Sustain | (なし) | (なし) | (なし) | KON↓ | |
Release | acc=95dB | KON↑ | (なし) | (なし) |
"opl3_EG_ctrl.v"
// // opl3_EG_ctrl.v : OPL3 EG control module // // 2017/01/12 Created by pcm1723 // module opl3_EG_ctrl( // // Input // reset, // async reset m_clk, // master clock slot_clk_en, // slot clock enable clk_en, // clock enable from prescaler eg_state_in, // EG state read data from slot mem. eq_0dB, // EG peak (0 dB) flag eq_95dB, // EG bottom (-95 dB) flag eq_slx, // EG crossing Sustain Level flag kon_rise, // rising (positive) edge of KON kon_fall, // falling (negative) edge of KON egt, // EGT(EG Type) parameter ar, // Attack Rate parameter dr, // Decay Rate parameter rr, // Release Rate parameter rof, // Rate OFfset parameter // // Output // rate, // actual Rate output rof_1, // Rof fraction b1 rof_0, // Rof fraction b0 acc_clk_en, // EG acc clock enable rate_0, // (RATE == 0 ) flag rate13, // (RATE == 13) flag rate15, // (RATE == 15) flag hi_rate, // (RATE >= 13) flag A, // Attack state indicator D, // Decay state indicator R, // Release state indicator M, // Mute state indicator S, // Sustain state indicator eg_state_out // EG state write data to slot mem. ); // Input port input reset; input m_clk; input slot_clk_en; input clk_en; // clock enable from prescaler input [2:0] eg_state_in; input kon_rise; input kon_fall; input egt; input [3:0] ar; input [3:0] dr; input [3:0] rr; input [3:0] rof; input eq_0dB; input eq_95dB; input eq_slx; // Output port output [3:0] rate; output rof_1; output rof_0; output rate_0; output rate15; output rate13; output hi_rate; output acc_clk_en; // EG acc clock enable output output A; output D; output S; output R; output M; output [2:0] eg_state_out; // // EG state definition localparam EG_MUTE = 3'b000; localparam EG_ATTACK = 3'b001; localparam EG_DECAY = 3'b010; localparam EG_SUSTAIN = 3'b110; localparam EG_RELEASE = 3'b100; // wires wire [3:0] adder4; // 4-bit adder wire carry; // carry from 4-bit adder wire [3:0] rate_mux; // AR/DR/RR mux wire [3:0] rof_masked; // Rof masked by (~RATE0) wire to_A; // transition condition wire to_D; // transition condition wire to_S; // transition condition wire to_R; // transition condition wire to_M; // transition condition wire trans; // transition condition // regs reg [3:0] ar_reg; reg [3:0] dr_reg; reg [3:0] rr_reg; reg [3:0] rof_reg; reg egt_reg; reg kon_rise_reg; reg kon_fall_reg; // EG state machine reg [2:0] eg_state; // // EG state input register always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin eg_state <= EG_MUTE; end else if (slot_clk_en) // slot clock timing begin eg_state <= eg_state_in; end else // hold begin eg_state <= eg_state; end // if (reset) ... end // always @() ... // // input registers for slot parameter // (AR / DR / RR / Rof / EGT / KON_rise / KON_fall) always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin ar_reg <= 4'h0; dr_reg <= 4'h0; rr_reg <= 4'h0; rof_reg <= 4'h0; egt_reg <= 1'b0; kon_rise_reg <= 1'b0; kon_fall_reg <= 1'b0; end else if (slot_clk_en) // slot clock timing begin ar_reg <= ar; dr_reg <= dr; rr_reg <= rr; rof_reg <= rof; egt_reg <= egt; kon_rise_reg <= kon_rise; kon_fall_reg <= kon_fall; end else // hold begin ar_reg <= ar_reg; dr_reg <= dr_reg; rr_reg <= rr_reg; rof_reg <= rof_reg; egt_reg <= egt_reg; kon_rise_reg <= kon_rise_reg; kon_fall_reg <= kon_fall_reg; end // if (reset) ... end // always @() ... // AR / DR /RR mux assign rate_mux = ( ( {4{A}} & ar_reg) | ( {4{D}} & dr_reg) | ( {4{R}} & rr_reg) ); // RATE0 detect assign rate_0 = ( rate_mux == 0 ); // Rof masking for (rate == 0) assign rof_masked = ( {4{~rate_0}} & rof_reg ); //Rof[1], Rof[0] masking for (rate == 15) assign {rof_1, rof_0} = ( {2{~rate15}} & rof_masked[1:0] ); // actual_rate = rate + rof assign {carry, adder4} = (rate_mux + rof_masked[3:2]); // saturate rate to 15 if carry assign rate = ( {4{carry}} | adder4 ); // (rate == 15) flag assign rate15 = ( rate == 15 ); // (rate == 13) flag assign rate13 = ( rate == 13 ); // hi-rate (rate >= 13 flag) assign hi_rate = ( rate >= 13 ); // EG state decode assign A = (eg_state == EG_ATTACK); assign D = (eg_state == EG_DECAY); assign S = (eg_state == EG_SUSTAIN); assign R = (eg_state == EG_RELEASE); assign M = ( ~(A | D | S | R) ); // Mute state // transition conditions assign to_A = ( (~A) & kon_rise_reg ); assign to_D = ( A & eq_0dB ); assign to_S = ( D & eq_slx ); assign to_R = ( (~(R | M)) & kon_fall_reg ); assign to_M = ( (D | R) & eq_95dB ); assign trans = ( to_A | to_D | to_S | to_R | to_M ); // disable EG acc clock when state transition occurred assign acc_clk_en = ( M | ((~trans) & clk_en) ); // next state of EG state machine assign eg_state_out = ( to_A ? EG_ATTACK : // highest priority ( to_R ? EG_RELEASE : ( to_M ? EG_MUTE : ( to_D ? EG_DECAY : ( to_S ? ( egt_reg ? EG_SUSTAIN : EG_RELEASE ) : /* else */ eg_state ))))); endmodule
EG ステートについては、スロット・メモリから読み出したデータを EG ステート・レジスタにラッチし、組み合わせロジックで状態遷移を計算した結果をスロット・メモリへ書き出しています。
状態遷移が発生した場合には、acc_clk_en 信号を強制的に下げて、EG アキュムレータ値がホールドされるようにします。
逆に、ミュート状態 (出力レベルが -95 dB 以下) では acc_clk_en 信号を上げて、最大減衰量 96 dB に相当する値 9'b1_1111_1111 が毎回 EG アキュムレータにロードされるようにしています。