FPGA 版 FM 音源 (60) -- FPGA 版 EG (4)
EG アキュムレータ・モジュール「opl3_EG_acc()」を回路図で表現したものを下に示します。
「EG Accumulator」と書いてある部分は単なるレジスタで、外部のアダーなどと合わせてアキュムレータの機能を持たせています。
ミュート状態や、 AR = 15 の場合にレジスタに定数値をロードする必要があるので、バラした状態で書いてあります。
また、実際には「EG Accumulator」の入力部分で回路を切断して、スロット・メモリ・モジュールと接続しています。
スロット・メモリ (シフトレジスタ) から読み出した当該スロットのデータを EG アキュムレータでラッチし、レジスタ出力から組み合わせ回路で計算した次サンプル・データの結果をスロット・メモリに書き込みます。
スロット数 N_SLOT = 1 では、スロット・メモリ・モジュールの内部は「素通し」になり、結局は上の回路図と同等になります。
EG ステート・マシンの状態遷移の判断に使う EG アキュムレータ値の「コンパレータ」 3 種のうち、0 dB との比較と、95 dB との比較の 2 つだけを回路図に描いてあります。
回路図が手狭になったので、残りひとつのサステイン・レベルとの比較回路はコントロール・モジュールの回路に描いてあります。
Verilog で書いたものを下に示します。 サステイン・レベルとの比較回路も含まれています。
"opl3_EG_acc.v"
// // opl3_EG_acc.v : OPL3 EG accumulator module // // 2017/01/09 Created by pcm1723 // module opl3_EG_acc( // // Input // reset, // async reset input m_clk, // master clock input slot_clk_en, // slot clock timing acc_clk_en, // EG accum. clock timing sft_sel, // EG fb shift select rate13, // (rate == 13) flag rate15, // (rate == 15) flag hi_rate, // hi-rate flag A, // Attack state D, // Decay state R, // Release state M, // Mute state sl, // Sustain Level eg_acc_in, // EG accum. input (read from mem) // // Output // eq_0dB, // EG_acc == 0dB eq_95dB, // EG_acc == 95dB eq_slx, // EG_acc == SL eg_acc_out // EG accum. output (write to mem) ); // Input port input reset; input m_clk; input slot_clk_en; input acc_clk_en; input sft_sel; input hi_rate; input rate15; input rate13; input A; input D; input R; input M; input [3:0] sl; input [8:0] eg_acc_in; // Output port output eq_0dB; output eq_slx; output eq_95dB; output [8:0] eg_acc_out; // constant localparam EG_0dB = 9'b0_0000_0000; localparam EG_96dB = 9'b1_1111_1111; // wires wire [8:0] adder9; // 9-bit adder wire [6:0] shift1_a; // 1-bit shifter A wire [6:0] shift1_b; // 1-bit shifter B wire [8:0] acc_fb; // acc feedback value wire ar15; // Attack_state & (rate == 15) wire [4:0] slx; // decoded Sustain Level // res reg [3:0] sl_reg; // Sustain Level input latch reg [8:0] eg_acc; // EG accumulator // always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin sl_reg <= 4'b0000; end else if (slot_clk_en) // slot update timing begin sl_reg <= sl; end else // hold begin sl_reg <= sl_reg; end // if (reset) ... end // always @() ... // always @(posedge m_clk or posedge reset) begin if (reset) // async reset begin eg_acc <= EG_0dB; // 0 dB end else if (slot_clk_en) // acc update timing begin eg_acc <= eg_acc_in; end else // hold begin eg_acc <= eg_acc; end // if (reset) ... end // always @() ... // // decoded Sustain Level // 0..42 dB for SL=0..14, 93 dB for SL=15) assign slx = { (&sl_reg), sl_reg }; // (EG_state = Attack) and (rate == 15) assign ar15 = (A & rate15); // compare EG_acc against 0dB (9'b0_0000_0000) assign eq_0dB = (~(|eg_acc)); // compare EG_acc against 95dB (9'b1_1111_11xx) assign eq_95dB = (&eg_acc[8:2]); // compare EG_acc against Sustain Level assign eq_slx = (eg_acc[8:4] == slx); // EG_acc output assign eg_acc_out = ( {9{M}} | ( {9{~ar15}} & adder9) ); // 9-bit adder assign adder9 = ( { {2{acc_fb[8]}}, shift1_b} + eg_acc ); // 1-bit shifter B assign shift1_b = ( (rate13 | (~hi_rate)) ? { acc_fb[8], shift1_a[6:1] } : shift1_a ); // 1-bit shifter A assign shift1_a = ( (~(sft_sel | rate15)) ? acc_fb[7:1] : acc_fb[6:0] ); // acc feedback value (force 9'h000 for (acc_clk_en == 0)) assign acc_fb = ( { 6'h00, (acc_clk_en & (D | R)), 2'h0 } | ( {9{A & acc_clk_en}} & { 1'b1, (~eg_acc[8:1]) } ) ); endmodule
N_SLOT = 1 の設定に相当する EG アキュムレータ・モジュール単体でのシミュレーション結果を下に示します。 (図をクリックすると拡大します。)
本来はプリスケーラ・モジュールからの「CLK_EN」信号や「SFT_SEL」信号で細かく制御されるのですが、アキュムレータ・モジュール単体のテストではバレル・シフタの 0 / 1 / 2 ビット・シフトに対応して 3 種のスロープが発生できるだけです。
また、コントロール回路もないので、シミュレーションのテストベンチでアタックとディケイだけの簡単な制御回路を作成してコントロールしています。