diff --git a/README.md b/README.md index a36c2d4..4d2786a 100755 --- a/README.md +++ b/README.md @@ -95,13 +95,13 @@ Close up of the attack phase: +----------------------------+------+-------+------------+-----------+-------+ | Site Type | Used | Fixed | Prohibited | Available | Util% | +----------------------------+------+-------+------------+-----------+-------+ - | Slice LUTs | 1256 | 0 | 0 | 17600 | 7.14 | - | LUT as Logic | 1014 | 0 | 0 | 17600 | 5.76 | - | LUT as Memory | 242 | 0 | 0 | 6000 | 4.03 | - | LUT as Distributed RAM | 192 | 0 | | | | - | LUT as Shift Register | 50 | 0 | | | | - | Slice Registers | 1106 | 0 | 0 | 35200 | 3.14 | - | Register as Flip Flop | 1106 | 0 | 0 | 35200 | 3.14 | + | Slice LUTs | 1164 | 0 | 0 | 17600 | 6.61 | + | LUT as Logic | 967 | 0 | 0 | 17600 | 5.49 | + | LUT as Memory | 197 | 0 | 0 | 6000 | 3.28 | + | LUT as Distributed RAM | 144 | 0 | | | | + | LUT as Shift Register | 53 | 0 | | | | + | Slice Registers | 1102 | 0 | 0 | 35200 | 3.13 | + | Register as Flip Flop | 1102 | 0 | 0 | 35200 | 3.13 | | Register as Latch | 0 | 0 | 0 | 35200 | 0.00 | | F7 Muxes | 9 | 0 | 0 | 8800 | 0.10 | | F8 Muxes | 1 | 0 | 0 | 4400 | 0.02 | diff --git a/fpga/Makefile b/fpga/Makefile index d212ca4..bb8d34f 100755 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -63,7 +63,7 @@ RTL_SRC = \ modules/operator/src/vibrato.sv \ modules/operator/src/envelope_generator.sv \ modules/operator/src/ksl_add_rom.sv \ - modules/operator/src/env_rate_counter.sv \ + modules/operator/src/calc_envelope_shift.sv \ modules/operator/src/tremolo.sv \ modules/operator/src/opl3_log_sine_lut.sv \ modules/operator/src/opl3_exp_lut.sv \ diff --git a/fpga/modules/operator/Makefile b/fpga/modules/operator/Makefile index 009e819..5443642 100755 --- a/fpga/modules/operator/Makefile +++ b/fpga/modules/operator/Makefile @@ -7,7 +7,7 @@ RTL_SRC = \ src/opl3_exp_lut.sv \ src/vibrato.sv \ src/envelope_generator.sv \ - src/env_rate_counter.sv \ + src/calc_envelope_shift.sv \ src/ksl_add_rom.sv \ src/tremolo.sv \ ../clks/src/clk_div.sv \ diff --git a/fpga/modules/operator/sim/operator_tb.sv b/fpga/modules/operator/sim/operator_tb.sv index 65c4c51..93d4ead 100755 --- a/fpga/modules/operator/sim/operator_tb.sv +++ b/fpga/modules/operator/sim/operator_tb.sv @@ -57,7 +57,7 @@ module operator_tb bit [REG_MULT_WIDTH-1:0] mult = 5; bit [REG_BLOCK_WIDTH-1:0] block = 4; - bit [REG_WS_WIDTH-1:0] ws = 7; + bit [REG_WS_WIDTH-1:0] ws = 0; bit vib = 0; bit dvb = 0; bit kon = 0; @@ -86,8 +86,8 @@ module operator_tb bit [REG_FB_WIDTH-1:0] fb_p1 = 0; bit signed [OP_OUT_WIDTH-1:0] modulation_p1 = 0; - bit [BANK_NUM_WIDTH-1:0] bank_num = 12; - bit [OP_NUM_WIDTH-1:0] op_num = 12; + bit [BANK_NUM_WIDTH-1:0] bank_num = 1; + bit [OP_NUM_WIDTH-1:0] op_num = 17; always begin #CLK_HALF_PERIOD clk = 0; diff --git a/fpga/modules/operator/src/calc_envelope_shift.sv b/fpga/modules/operator/src/calc_envelope_shift.sv new file mode 100755 index 0000000..f05383a --- /dev/null +++ b/fpga/modules/operator/src/calc_envelope_shift.sv @@ -0,0 +1,199 @@ +/******************************************************************************* +# +html+
+#
+#   FILENAME: calc_envelope_shift.sv
+#   AUTHOR: Greg Taylor     CREATION DATE: 2 Nov 2014
+#
+#   DESCRIPTION:
+#
+#   CHANGE HISTORY:
+#   3 June 2024     Greg Taylor
+#       Renamed from env_rate_counter.sv and refactored to match implementation in
+#       https://github.com/nukeykt/Nuked-OPL3
+#
+#   2 Nov 2014    Greg Taylor
+#       Initial version
+#
+#   Copyright (C) 2014 Greg Taylor 
+#
+#   This file is part of OPL3 FPGA.
+#
+#   OPL3 FPGA is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU Lesser General Public License as published by
+#   the Free Software Foundation, either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   OPL3 FPGA is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU Lesser General Public License for more details.
+#
+#   You should have received a copy of the GNU Lesser General Public License
+#   along with OPL3 FPGA.  If not, see .
+#
+#   Original Java Code:
+#   Copyright (C) 2008 Robson Cozendey 
+#
+#   Original C++ Code:
+#   Copyright (C) 2013-2020 Nuke.YKT
+#
+#   Some code based on forum posts in:
+#   http://forums.submarine.org.uk/phpBB/viewforum.php?f=9,
+#   Copyright (C) 2010-2013 by carbon14 and opl3
+#
+#******************************************************************************/
+`timescale 1ns / 1ps
+`default_nettype none
+
+module calc_envelope_shift
+    import opl3_pkg::*;
+(
+    input wire clk,
+    input wire sample_clk_en,
+    input wire [BANK_NUM_WIDTH-1:0] bank_num,
+    input wire [OP_NUM_WIDTH-1:0] op_num,
+    input wire ksr, // key scale rate
+    input wire nts, // keyboard split selection
+    input wire [REG_FNUM_WIDTH-1:0] fnum,
+    input wire [REG_BLOCK_WIDTH-1:0] block,
+    input wire [REG_ENV_WIDTH-1:0] requested_rate_p0,
+    output logic [REG_ENV_WIDTH+1-1:0] rate_hi_p2 = 0,
+    output logic [ENV_SHIFT_WIDTH-1:0] env_shift_p2 = 0
+);
+    localparam EG_TIMER_WIDTH = 13;
+    localparam PIPELINE_DELAY = 3;
+    localparam EG_ADD_WIDTH = $clog2(13);
+    localparam logic [3:0] EG_INC_STEP [4] = {
+        4'b0000,
+        4'b1000,
+        4'b1010,
+        4'b1110
+    };
+
+    logic [EG_TIMER_WIDTH-1:0] eg_timer = 0;
+    logic [1:0] timer = 0;
+    logic eg_timerrem = 0;
+    logic eg_state = 0;
+    logic [EG_ADD_WIDTH-1:0] eg_add = 0;
+    logic [REG_BLOCK_WIDTH:0] block_shifted;
+    logic [REG_BLOCK_WIDTH:0] ksv_p0;
+    logic [REG_BLOCK_WIDTH:0] ks_p0;
+    logic [REG_ENV_WIDTH+2-1:0] requested_rate_shifted_p0;
+    logic [REG_ENV_WIDTH+3-1:0] rate_p1 = 0;
+    logic [REG_ENV_WIDTH+1-1:0] rate_hi_pre_p1;
+    logic [REG_ENV_WIDTH+1-1:0] rate_hi_p1;
+    logic [1:0] rate_lo_p1;
+    logic [REG_ENV_WIDTH+2-1:0] eg_shift_p1;
+    logic requested_rate_not_zero_p1;
+    logic [ENV_SHIFT_WIDTH:0] env_shift_pre_p2;
+    logic [PIPELINE_DELAY:1] sample_clk_en_p;
+    logic [PIPELINE_DELAY:1] [BANK_NUM_WIDTH-1:0] bank_num_p;
+    logic [PIPELINE_DELAY:1] [OP_NUM_WIDTH-1:0] op_num_p;
+
+    pipeline_sr #(
+        .ENDING_CYCLE(PIPELINE_DELAY)
+    ) sample_clk_en_sr (
+        .clk,
+        .in(sample_clk_en),
+        .out(sample_clk_en_p)
+    );
+
+    pipeline_sr #(
+        .DATA_WIDTH(BANK_NUM_WIDTH),
+        .ENDING_CYCLE(PIPELINE_DELAY)
+    ) bank_num_sr (
+        .clk,
+        .in(bank_num),
+        .out(bank_num_p)
+    );
+
+    pipeline_sr #(
+        .DATA_WIDTH(OP_NUM_WIDTH),
+        .ENDING_CYCLE(PIPELINE_DELAY)
+    ) op_num_sr (
+        .clk,
+        .in(op_num),
+        .out(op_num_p)
+    );
+
+    always_comb begin
+        block_shifted = block << 1;
+        ksv_p0 = block_shifted | (nts ? fnum[8] : fnum[9]);
+        ks_p0 = ksr ? ksv_p0 : ksv_p0 >> 2;
+        requested_rate_shifted_p0 = requested_rate_p0 << 2;
+    end
+
+    always_ff @(posedge clk)
+        rate_p1 <= ks_p0 + requested_rate_shifted_p0;
+
+    always_comb begin
+        rate_hi_pre_p1 = rate_p1 >> 2;
+        rate_hi_p1 = rate_hi_pre_p1[4] ? 'h0f : rate_hi_pre_p1;
+        rate_lo_p1 = rate_p1[1:0];
+        eg_shift_p1 = rate_hi_p1 + eg_add;
+
+        env_shift_pre_p2 = rate_hi_p1[1:0] + EG_INC_STEP[rate_lo_p1][timer];
+    end
+
+    always_ff @(posedge clk) begin
+        requested_rate_not_zero_p1 <= requested_rate_p0 != 0;
+        env_shift_p2 <= 0;
+        rate_hi_p2 <= rate_hi_p1;
+
+        if (requested_rate_not_zero_p1) begin
+            if (rate_hi_p1 < 12) begin
+                if (eg_state)
+                    unique case (eg_shift_p1)
+                    12: env_shift_p2 <= 1;
+                    13: env_shift_p2 <= rate_lo_p1[1];
+                    14: env_shift_p2 <= rate_lo_p1[0];
+                    default:;
+                    endcase
+            end
+            else begin
+                env_shift_p2 <= env_shift_pre_p2;
+                if (env_shift_pre_p2[ENV_SHIFT_WIDTH])
+                    env_shift_p2 <= 'h3;
+                if (env_shift_pre_p2 == 0)
+                    env_shift_p2 <= eg_state;
+            end
+        end
+    end
+
+    always_ff @(posedge clk)
+        // once per sample, after operators are done
+        if (sample_clk_en_p[3] && bank_num_p[3] == 1 && op_num_p[3] == 17) begin
+            priority casex (eg_timer)
+            'b0_0000_0000_0000: eg_add <= 0;
+            'b1_0000_0000_0000: eg_add <= 13;
+            'bx_1000_0000_0000: eg_add <= 12;
+            'bx_x100_0000_0000: eg_add <= 11;
+            'bx_xx10_0000_0000: eg_add <= 10;
+            'bx_xxx1_0000_0000: eg_add <= 9;
+            'bx_xxxx_1000_0000: eg_add <= 8;
+            'bx_xxxx_x100_0000: eg_add <= 7;
+            'bx_xxxx_xx10_0000: eg_add <= 6;
+            'bx_xxxx_xxx1_0000: eg_add <= 5;
+            'bx_xxxx_xxxx_1000: eg_add <= 4;
+            'bx_xxxx_xxxx_x100: eg_add <= 3;
+            'bx_xxxx_xxxx_xx10: eg_add <= 2;
+            'bx_xxxx_xxxx_xxx1: eg_add <= 1;
+            'bx_xxxx_xxxx_xxxx:;
+            endcase
+
+            if (eg_timerrem || eg_state) begin
+                if (eg_timer == '1) begin
+                    eg_timer <= 0;
+                    eg_timerrem <= 1;
+                end
+                else begin
+                    eg_timer <= eg_timer + 1;
+                    eg_timerrem <= 0;
+                end
+            end
+
+            eg_state <= !eg_state;
+            timer <= timer + 1;
+        end
+endmodule
+`default_nettype wire
diff --git a/fpga/modules/operator/src/env_rate_counter.sv b/fpga/modules/operator/src/env_rate_counter.sv
deleted file mode 100755
index ec5079d..0000000
--- a/fpga/modules/operator/src/env_rate_counter.sv
+++ /dev/null
@@ -1,151 +0,0 @@
-/*******************************************************************************
-#   +html+
-#
-#   FILENAME: env_rate_counter.sv
-#   AUTHOR: Greg Taylor     CREATION DATE: 2 Nov 2014
-#
-#   DESCRIPTION:
-#
-#   CHANGE HISTORY:
-#   2 Nov 2014    Greg Taylor
-#       Initial version
-#
-#   Copyright (C) 2014 Greg Taylor 
-#
-#   This file is part of OPL3 FPGA.
-#
-#   OPL3 FPGA is free software: you can redistribute it and/or modify
-#   it under the terms of the GNU Lesser General Public License as published by
-#   the Free Software Foundation, either version 3 of the License, or
-#   (at your option) any later version.
-#
-#   OPL3 FPGA is distributed in the hope that it will be useful,
-#   but WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#   GNU Lesser General Public License for more details.
-#
-#   You should have received a copy of the GNU Lesser General Public License
-#   along with OPL3 FPGA.  If not, see .
-#
-#   Original Java Code:
-#   Copyright (C) 2008 Robson Cozendey 
-#
-#   Original C++ Code:
-#   Copyright (C) 2012  Steffen Ohrendorf 
-#
-#   Some code based on forum posts in:
-#   http://forums.submarine.org.uk/phpBB/viewforum.php?f=9,
-#   Copyright (C) 2010-2013 by carbon14 and opl3
-#
-#******************************************************************************/
-`timescale 1ns / 1ps
-`default_nettype none
-
-module env_rate_counter
-    import opl3_pkg::*;
-(
-    input wire clk,
-    input wire sample_clk_en,
-    input wire [BANK_NUM_WIDTH-1:0] bank_num,
-    input wire [OP_NUM_WIDTH-1:0] op_num,
-    input wire ksr, // key scale rate
-    input wire nts, // keyboard split selection
-    input wire [REG_FNUM_WIDTH-1:0] fnum,
-    input wire [REG_BLOCK_WIDTH-1:0] block,
-    input wire [REG_ENV_WIDTH-1:0] requested_rate_p0,
-    output logic [ENV_RATE_COUNTER_OVERFLOW_WIDTH-1:0] rate_counter_overflow_p1 = 0
-);
-    localparam COUNTER_WIDTH = 15;
-    localparam OVERFLOW_TMP_MAX_VALUE = 7<<15;
-    localparam PIPELINE_DELAY = 2;
-
-    logic rate_tmp0_p0;
-    logic [REG_BLOCK_WIDTH+1-1:0] rate_tmp1_p0;
-    logic [REG_BLOCK_WIDTH+1-1:0] rate_tmp2_p0;
-    logic [$clog2(60)-1:0] effective_rate_p1 = 0;
-    logic [$clog2(60)-2-1:0] rate_value_p1;
-    logic [REG_ENV_WIDTH+2-1:0] requested_rate_shifted_p0;
-    logic [1:0] rof_p1;
-    logic [COUNTER_WIDTH-1:0] counter_p1;
-    logic [COUNTER_WIDTH-1:0] counter_new_p2;
-    logic [$clog2(OVERFLOW_TMP_MAX_VALUE)-1:0] overflow_tmp_p1;
-    logic [PIPELINE_DELAY:1] requested_rate_not_zero_p;
-    logic [PIPELINE_DELAY:1] sample_clk_en_p;
-    logic [PIPELINE_DELAY:1] [BANK_NUM_WIDTH-1:0] bank_num_p;
-    logic [PIPELINE_DELAY:1] [OP_NUM_WIDTH-1:0] op_num_p;
-
-    pipeline_sr #(
-        .ENDING_CYCLE(PIPELINE_DELAY)
-    ) sample_clk_en_sr (
-        .clk,
-        .in(sample_clk_en),
-        .out(sample_clk_en_p)
-    );
-
-    pipeline_sr #(
-        .DATA_WIDTH(BANK_NUM_WIDTH),
-        .ENDING_CYCLE(PIPELINE_DELAY)
-    ) bank_num_sr (
-        .clk,
-        .in(bank_num),
-        .out(bank_num_p)
-    );
-
-    pipeline_sr #(
-        .DATA_WIDTH(OP_NUM_WIDTH),
-        .ENDING_CYCLE(PIPELINE_DELAY)
-    ) op_num_sr (
-        .clk,
-        .in(op_num),
-        .out(op_num_p)
-    );
-
-    pipeline_sr #(
-        .ENDING_CYCLE(PIPELINE_DELAY)
-    ) requested_rate_not_zero_sr (
-        .clk,
-        .in(requested_rate_p0 != 0),
-        .out(requested_rate_not_zero_p)
-    );
-
-    always_comb rate_tmp0_p0 = nts ? fnum[8] : fnum[9];
-    always_comb rate_tmp1_p0 = rate_tmp0_p0 | (block << 1);
-    always_comb rate_tmp2_p0 = ksr ? rate_tmp1_p0 : rate_tmp1_p0 >> 2;
-    always_comb requested_rate_shifted_p0 = requested_rate_p0 << 2;
-
-    always_ff @(posedge clk)
-        if (rate_tmp2_p0 + requested_rate_shifted_p0 > 60)
-            effective_rate_p1 <= 60;
-        else
-            effective_rate_p1 <= rate_tmp2_p0 + requested_rate_shifted_p0;
-
-    always_comb rate_value_p1 = effective_rate_p1 >> 2;
-    always_comb rof_p1 = effective_rate_p1[1:0];
-
-    mem_multi_bank #(
-        .DATA_WIDTH(COUNTER_WIDTH),
-        .DEPTH(NUM_OPERATORS_PER_BANK),
-        .OUTPUT_DELAY(1),
-        .DEFAULT_VALUE(0),
-        .NUM_BANKS(NUM_BANKS)
-    ) counter_mem (
-        .clk,
-        .wea(sample_clk_en_p[2] && requested_rate_not_zero_p[2]),
-        .reb(sample_clk_en),
-        .banka(bank_num_p[2]),
-        .addra(op_num_p[2]),
-        .bankb(bank_num),
-        .addrb(op_num),
-        .dia(counter_new_p2),
-        .dob(counter_p1)
-    );
-
-    always_comb begin
-        overflow_tmp_p1 = counter_p1 + ((4 | rof_p1) << rate_value_p1);
-        rate_counter_overflow_p1 = overflow_tmp_p1 >> 15;
-    end
-
-    always_ff @(posedge clk)
-        counter_new_p2 <= overflow_tmp_p1;
-endmodule
-`default_nettype wire
diff --git a/fpga/modules/operator/src/envelope_generator.sv b/fpga/modules/operator/src/envelope_generator.sv
index 07b8243..1006fcd 100755
--- a/fpga/modules/operator/src/envelope_generator.sv
+++ b/fpga/modules/operator/src/envelope_generator.sv
@@ -7,6 +7,9 @@
 #   DESCRIPTION:
 #
 #   CHANGE HISTORY:
+#   19 June 2024     Greg Taylor
+#       Refactored to match implementation in https://github.com/nukeykt/Nuked-OPL3
+#
 #   30 Oct 2014    Greg Taylor
 #       Initial version
 #
@@ -31,7 +34,7 @@
 #   Copyright (C) 2008 Robson Cozendey 
 #
 #   Original C++ Code:
-#   Copyright (C) 2012  Steffen Ohrendorf 
+#   Copyright (C) 2013-2020 Nuke.YKT
 #
 #   Some code based on forum posts in:
 #   http://forums.submarine.org.uk/phpBB/viewforum.php?f=9,
@@ -65,11 +68,12 @@ module envelope_generator
     input wire [REG_FNUM_WIDTH-1:0] fnum,
     input wire [REG_MULT_WIDTH-1:0] mult,
     input wire [REG_BLOCK_WIDTH-1:0] block,
-    input wire key_on_pulse_p0,
-    input wire key_off_pulse_p0,
-    output logic [ENV_WIDTH-1:0] env_p3 = SILENCE
+    input wire key_on_p0,
+    output logic [FINAL_ENV_WIDTH-1:0] env_p3 = SILENCE,
+    output logic pg_reset_p2
 );
     localparam PIPELINE_DELAY = 3;
+    localparam ENV_WIDTH = 9;
 
     // state_t goes on a memory--explicitly define width/values
     typedef enum logic [3:0] {
@@ -79,21 +83,30 @@ module envelope_generator
         RELEASE   = 4'b1000
     } state_t;
 
-    state_t state_p0, next_state_p0, state_p1 = RELEASE;
+    state_t state_p0, state_p1 = RELEASE, state_p2 = RELEASE, next_state_p2, state_p3 = RELEASE;
 
     logic [KSL_ADD_WIDTH-1:0] ksl_add_p2;
     logic [ENV_WIDTH-1:0] env_int_p0;
-    logic [ENV_WIDTH-1:0] env_int_p1 = 0;
-    logic [ENV_WIDTH-1:0] env_int_p2 = 0;
-    logic [ENV_WIDTH:0] env_add_p1; // one more bit for overflow check
+    logic [ENV_WIDTH-1:0] env_int_pre_p2;
+    logic [ENV_WIDTH-1:0] eg_inc_p2;
+    logic eg_off_p2;
+    logic [ENV_WIDTH+4-1:0] env_int_extra_bits_p2;
+    logic [ENV_WIDTH-1:0] env_int_new_p3 = 0;
     logic [AM_VAL_WIDTH-1:0] am_val_p2;
     logic [REG_ENV_WIDTH-1:0] requested_rate_p0;
-    logic [ENV_RATE_COUNTER_OVERFLOW_WIDTH-1:0] rate_counter_overflow_p1;
-    logic [ENV_WIDTH+1:0] env_tmp_p2; // two more bits wide than env for overflow check
+    logic [ENV_SHIFT_WIDTH-1:0] env_shift_p2;
+    logic [REG_ENV_WIDTH+1-1:0] rate_hi_p2;
+    logic [ENV_WIDTH+1:0] env_pre_p2;
+    logic eg_reset_p0;
+    logic [REG_TL_WIDTH+2-1:0] tl_shifted_p2;
     logic [PIPELINE_DELAY:1] sample_clk_en_p;
     logic [PIPELINE_DELAY:1] [BANK_NUM_WIDTH-1:0] bank_num_p;
     logic [PIPELINE_DELAY:1] [OP_NUM_WIDTH-1:0] op_num_p;
     logic [PIPELINE_DELAY:1] [REG_TL_WIDTH-1:0] tl_p;
+    logic [PIPELINE_DELAY:1] [REG_ENV_WIDTH-1:0] sl_p;
+    logic [PIPELINE_DELAY:1] [ENV_WIDTH-1:0] env_int_p;
+    logic [PIPELINE_DELAY:1] key_on_p;
+    logic [PIPELINE_DELAY:1] eg_reset_p;
 
     pipeline_sr #(
         .ENDING_CYCLE(PIPELINE_DELAY)
@@ -130,6 +143,42 @@ module envelope_generator
         .out(tl_p)
     );
 
+    pipeline_sr #(
+        .DATA_WIDTH(REG_ENV_WIDTH),
+        .ENDING_CYCLE(PIPELINE_DELAY)
+    ) sl_sr (
+        .clk,
+        .in(sl),
+        .out(sl_p)
+    );
+
+    pipeline_sr #(
+        .DATA_WIDTH(ENV_WIDTH),
+        .ENDING_CYCLE(PIPELINE_DELAY)
+    ) env_int_sr (
+        .clk,
+        .in(env_int_p0),
+        .out(env_int_p)
+    );
+
+    pipeline_sr #(
+        .DATA_WIDTH(1),
+        .ENDING_CYCLE(PIPELINE_DELAY)
+    ) key_on_sr (
+        .clk,
+        .in(key_on_p0),
+        .out(key_on_p)
+    );
+
+    pipeline_sr #(
+        .DATA_WIDTH(1),
+        .ENDING_CYCLE(PIPELINE_DELAY)
+    ) eg_reset_sr (
+        .clk,
+        .in(eg_reset_p0),
+        .out(eg_reset_p)
+    );
+
     ksl_add_rom ksl_add_rom (
         .*
     );
@@ -145,47 +194,37 @@ module envelope_generator
         .clk,
         .reset('0),
         .reset_mem(reset),
-        .wea(sample_clk_en_p[1]),
+        .wea(sample_clk_en_p[3]),
         .reb(sample_clk_en),
-        .banka(bank_num_p[1]),
-        .addra(op_num_p[1]),
+        .banka(bank_num_p[3]),
+        .addra(op_num_p[3]),
         .bankb(bank_num),
         .addrb(op_num),
-        .dia({state_p1}),
+        .dia({state_p3}),
         .dob({state_p0}),
         .reset_mem_done_pulse()
     );
 
-    always_ff @(posedge clk)
-        if (sample_clk_en)
-            state_p1 <= next_state_p0;
-
     always_comb begin
-        unique case (state_p0)
-        ATTACK: next_state_p0 = env_int_p0 == 0 ? DECAY : ATTACK;
-        DECAY: next_state_p0 = (env_int_p0 >> 4) >= sl ? SUSTAIN : DECAY;
-        SUSTAIN: next_state_p0 = !egt ? RELEASE : SUSTAIN;
-        RELEASE: next_state_p0 = RELEASE;
-        endcase
+        eg_reset_p0 = 0;
 
-        if (key_on_pulse_p0)
-            next_state_p0 = env_int_p0 == 0 ? DECAY : ATTACK;
-        else if (key_off_pulse_p0)
-            next_state_p0 = RELEASE;
+        if (key_on_p0 && state_p0 == RELEASE) begin
+            eg_reset_p0 = 1;
+            requested_rate_p0 = ar;
+        end
+        else
+            unique case (state_p0)
+            ATTACK: requested_rate_p0 = ar;
+            DECAY: requested_rate_p0 = dr;
+            SUSTAIN: requested_rate_p0 = !egt ? rr : 0;
+            RELEASE: requested_rate_p0 = rr;
+            endcase
     end
 
-    always_comb
-        unique case (next_state_p0)
-        ATTACK: requested_rate_p0 = ar;
-        DECAY: requested_rate_p0 = dr;
-        SUSTAIN: requested_rate_p0 = 0;
-        RELEASE: requested_rate_p0 = rr;
-        endcase
-
     /*
-     * Calculate rate_counter_overflow
+     * Calculate envelope shift
      */
-    env_rate_counter env_rate_counter (
+    calc_envelope_shift calc_envelope_shift (
         .*
     );
 
@@ -197,43 +236,16 @@ module envelope_generator
         .NUM_BANKS(NUM_BANKS)
     ) env_int_mem (
         .clk,
-        .wea(sample_clk_en_p[2]),
+        .wea(sample_clk_en_p[3]),
         .reb(sample_clk_en),
-        .banka(bank_num_p[2]),
-        .addra(op_num_p[2]),
+        .banka(bank_num_p[3]),
+        .addra(op_num_p[3]),
         .bankb(bank_num),
         .addrb(op_num),
-        .dia(env_int_p2),
+        .dia(env_int_new_p3),
         .dob(env_int_p0)
     );
 
-    always_comb env_add_p1 = env_int_p1 + rate_counter_overflow_p1;
-
-    always_ff @(posedge clk) begin
-        env_int_p1 <= env_int_p0;
-        env_int_p2 <= env_int_p1;
-
-        if (sample_clk_en_p[1]) begin
-            if (state_p1 == ATTACK && rate_counter_overflow_p1 != 0)
-                // The maximum value of overflow is 7. An overflow can only occur
-                // if m_env < floor(m_env/8)*7 + 1. Let's substitute m_env by 8*x:
-                // 8*x < 1 + x*7
-                // <=> 8*x - 7*x < 1
-                // <=> x < 1
-                // But the attack only occurs if m_env>0, so an overflow cannot occur
-                // here.
-                // +1 for one's complement.
-                env_int_p2 <= env_int_p1 - (((env_int_p1*rate_counter_overflow_p1) >> 3) + 1);
-            else if (state_p1 == DECAY || state_p1 == RELEASE) begin
-                if (env_add_p1[ENV_WIDTH])
-                    // env_int would overflow
-                    env_int_p2 <= SILENCE;
-                else
-                    env_int_p2 <= env_add_p1;
-            end
-        end
-    end
-
     /*
      * Calculate am_val
      */
@@ -241,17 +253,59 @@ module envelope_generator
         .*
     );
 
-    always_comb
-        if (am)
-            env_tmp_p2 = env_int_p2 + (tl_p[2] << 2) + ksl_add_p2 + am_val_p2; // max val 1044
-        else
-            env_tmp_p2 = env_int_p2 + (tl_p[2] << 2) + ksl_add_p2;
+    always_comb begin
+        pg_reset_p2 = eg_reset_p[2];
+        env_int_pre_p2 = env_int_p[2];
+        eg_inc_p2 = 0;
+        eg_off_p2 = 0;
+        next_state_p2 = state_p2;
+        env_int_extra_bits_p2 = env_int_p[2];
+
+        // instant attack
+        if (eg_reset_p[2] && rate_hi_p2 == 'hf)
+            env_int_pre_p2 = 0;
+
+        // envelope off
+        if ((env_int_p[2] & 'h1f8) == 'h1f8)
+            eg_off_p2 = 1;
+        if (state_p2 != ATTACK && !eg_reset_p[2] && eg_off_p2)
+            env_int_pre_p2 = SILENCE;
+
+        unique case (state_p2)
+        ATTACK: begin
+            if (env_int_p[2] == 0)
+                next_state_p2 = DECAY;
+            else if (key_on_p[2] && env_shift_p2 > 0 && rate_hi_p2 != 'hf)
+                eg_inc_p2 = ~env_int_extra_bits_p2 >> (4 - env_shift_p2);
+        end
+        DECAY: begin
+            if ((env_int_p[2] >> 4) == sl_p[2])
+                next_state_p2 = SUSTAIN;
+            else if (!eg_off_p2 && !eg_reset_p[2] && env_shift_p2 > 0)
+                eg_inc_p2 = 1 << (env_shift_p2 - 1);
+        end
+        SUSTAIN, RELEASE: begin
+            if (!eg_off_p2 && !eg_reset_p[2] && env_shift_p2 > 0)
+                eg_inc_p2 = 1 << (env_shift_p2 - 1);
+        end
+        endcase
+
+        if (eg_reset_p[2])
+            next_state_p2 = ATTACK;
+        if (!key_on_p[2])
+            next_state_p2 = RELEASE;
+    end
+
+    always_ff @(posedge clk) begin
+        state_p1 <= state_p0;
+        state_p2 <= state_p1;
+        state_p3 <= next_state_p2;
+        env_int_new_p3 <= env_int_pre_p2 + eg_inc_p2;
+    end
+
+    always_comb tl_shifted_p2 = tl_p[2] << 2;
 
-    // clamp envelope
     always_ff @(posedge clk)
-        if (env_tmp_p2[ENV_WIDTH+1:ENV_WIDTH] != 0) // overflow
-            env_p3 <= SILENCE;
-        else
-            env_p3 <= env_tmp_p2;
+        env_p3 <= env_int_p[2] + tl_shifted_p2 + ksl_add_p2 + (am ? am_val_p2 : 0); // max val 1044
 endmodule
 `default_nettype wire
\ No newline at end of file
diff --git a/fpga/modules/operator/src/operator.sv b/fpga/modules/operator/src/operator.sv
index 533dc7f..32fd58a 100755
--- a/fpga/modules/operator/src/operator.sv
+++ b/fpga/modules/operator/src/operator.sv
@@ -85,31 +85,17 @@ module operator
     logic [PIPELINE_DELAY:1] [BANK_NUM_WIDTH-1:0] bank_num_p;
     logic [PIPELINE_DELAY:1] [OP_NUM_WIDTH-1:0] op_num_p;
     logic [PHASE_ACC_WIDTH-1:0] phase_inc_p2;
-    logic key_on_pulse_p0;
+    logic key_on_p0_pulse_p0;
     logic key_off_pulse_p0;
-    logic [ENV_WIDTH-1:0] env_p3;
+    logic [FINAL_ENV_WIDTH-1:0] env_p3;
     logic signed [OP_OUT_WIDTH-1:0] feedback_result_p1;
     logic signed [OP_OUT_WIDTH+1+2**REG_FB_WIDTH-1:0] feedback_result_tmp_p1;
-    logic bd0_on_pulse_p0;
-    logic bd1_on_pulse_p0;
-    logic sd_on_pulse_p0;
-    logic tom_on_pulse_p0;
-    logic tc_on_pulse_p0;
-    logic hh_on_pulse_p0;
-    logic rhythm_kon_pulse_p0;
-    logic bd0_off_pulse_p0;
-    logic bd1_off_pulse_p0;
-    logic sd_off_pulse_p0;
-    logic tom_off_pulse_p0;
-    logic tc_off_pulse_p0;
-    logic hh_off_pulse_p0;
-    logic rhythm_koff_pulse_p0;
-    logic prev_kon_p0;
-    logic kon_p1;
     logic [1:0] [OP_OUT_WIDTH-1:0] feedback_p1;
     logic [1:0] [OP_OUT_WIDTH-1:0] feedback_p6;
     logic [PIPELINE_DELAY:2] [1:0] [OP_OUT_WIDTH-1:0] feedback_p;
     operator_t op_type_p0;
+    logic key_on_p0;
+    logic pg_reset_p2;
 
     pipeline_sr #(
         .ENDING_CYCLE(PIPELINE_DELAY)
@@ -137,171 +123,36 @@ module operator
         .out(op_num_p)
     );
 
-    always_ff @(posedge clk)
-        kon_p1 <= kon;
-
-    mem_multi_bank #(
-        .DATA_WIDTH(1),
-        .DEPTH(NUM_OPERATORS_PER_BANK),
-        .OUTPUT_DELAY(0),
-        .DEFAULT_VALUE(0),
-        .NUM_BANKS(NUM_BANKS)
-    ) kon_mem (
-        .clk,
-        .wea(sample_clk_en_p[1]),
-        .reb(sample_clk_en),
-        .banka(bank_num_p[1]),
-        .addra(op_num_p[1]),
-        .bankb(bank_num),
-        .addrb(op_num),
-        .dia(kon_p1),
-        .dob(prev_kon_p0)
-    );
-
     always_comb begin
         op_type_p0 = OP_NORMAL;
+        key_on_p0 = kon;
+
         if (bank_num == 0 && ryt)
             unique case (op_num)
-            12, 15:  op_type_p0 = OP_BASS_DRUM;
-            13:      op_type_p0 = OP_HI_HAT;
-            14:      op_type_p0 = OP_TOM_TOM;
-            16:      op_type_p0 = OP_SNARE_DRUM;
-            17:      op_type_p0 = OP_TOP_CYMBAL;
-            default: op_type_p0 = OP_NORMAL;
+            12, 15:  begin
+                op_type_p0 = OP_BASS_DRUM;
+                key_on_p0 = bd;
+            end
+            13: begin
+                op_type_p0 = OP_HI_HAT;
+                key_on_p0 = hh;
+            end
+            14: begin
+                op_type_p0 = OP_TOM_TOM;
+                key_on_p0 = tom;
+            end
+            16: begin
+                op_type_p0 = OP_SNARE_DRUM;
+                key_on_p0 = sd;
+            end
+            17: begin
+                op_type_p0 = OP_TOP_CYMBAL;
+                key_on_p0 = tc;
+            end
+            default:;
             endcase
     end
 
-    edge_detector #(
-        .EDGE_LEVEL(1),
-        .CLK_DLY(0)
-    ) bd0_on_edge_detect (
-        .clk_en(ryt && sample_clk_en && bank_num == 0 && op_num == 12),
-        .in(bd),
-        .edge_detected(bd0_on_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(1),
-        .CLK_DLY(0)
-    ) bd1_on_edge_detect (
-        .clk_en(ryt && sample_clk_en && bank_num == 0 && op_num == 15),
-        .in(bd),
-        .edge_detected(bd1_on_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(1),
-        .CLK_DLY(0)
-    ) sd_on_edge_detect (
-        .clk_en(op_type_p0 == OP_SNARE_DRUM && sample_clk_en),
-        .in(sd),
-        .edge_detected(sd_on_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(1),
-        .CLK_DLY(0)
-    ) tom_on_edge_detect (
-        .clk_en(op_type_p0 == OP_TOM_TOM && sample_clk_en),
-        .in(tom),
-        .edge_detected(tom_on_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(1),
-        .CLK_DLY(0)
-    ) tc_on_edge_detect (
-        .clk_en(op_type_p0 == OP_TOP_CYMBAL && sample_clk_en),
-        .in(tc),
-        .edge_detected(tc_on_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(1),
-        .CLK_DLY(0)
-    ) hh_on_edge_detect (
-        .clk_en(op_type_p0 == OP_HI_HAT && sample_clk_en),
-        .in(hh),
-        .edge_detected(hh_on_pulse_p0),
-        .*
-    );
-
-    edge_detector #(
-        .EDGE_LEVEL(0),
-        .CLK_DLY(0)
-    ) bd0_off_edge_detect (
-        .clk_en(ryt && sample_clk_en && bank_num == 0 && op_num == 12),
-        .in(bd),
-        .edge_detected(bd0_off_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(0),
-        .CLK_DLY(0)
-    ) bd1_off_edge_detect (
-        .clk_en(ryt && sample_clk_en && bank_num == 0 && op_num == 15),
-        .in(bd),
-        .edge_detected(bd1_off_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(0),
-        .CLK_DLY(0)
-    ) sd_off_edge_detect (
-        .clk_en(op_type_p0 == OP_SNARE_DRUM && sample_clk_en),
-        .in(sd),
-        .edge_detected(sd_off_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(0),
-        .CLK_DLY(0)
-    ) tom_off_edge_detect (
-        .clk_en(op_type_p0 == OP_TOM_TOM && sample_clk_en),
-        .in(tom),
-        .edge_detected(tom_off_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(0),
-        .CLK_DLY(0)
-    ) tc_off_edge_detect (
-        .clk_en(op_type_p0 == OP_TOP_CYMBAL && sample_clk_en),
-        .in(tc),
-        .edge_detected(tc_off_pulse_p0),
-        .*
-    );
-    edge_detector #(
-        .EDGE_LEVEL(0),
-        .CLK_DLY(0)
-    ) hh_off_edge_detect (
-        .clk_en(op_type_p0 == OP_HI_HAT && sample_clk_en),
-        .in(hh),
-        .edge_detected(hh_off_pulse_p0),
-        .*
-    );
-
-    always_comb begin
-        rhythm_kon_pulse_p0 =
-            bd0_on_pulse_p0 ||
-            bd1_on_pulse_p0 ||
-            sd_on_pulse_p0 ||
-            tom_on_pulse_p0 ||
-            tc_on_pulse_p0 ||
-            hh_on_pulse_p0;
-
-        rhythm_koff_pulse_p0 =
-            bd0_off_pulse_p0 ||
-            bd1_off_pulse_p0 ||
-            sd_off_pulse_p0 ||
-            tom_off_pulse_p0 ||
-            tc_off_pulse_p0 ||
-            hh_off_pulse_p0;
-
-        key_on_pulse_p0 = (!prev_kon_p0 && kon) || rhythm_kon_pulse_p0;
-        key_off_pulse_p0 = (prev_kon_p0 && !kon) || rhythm_koff_pulse_p0;
-    end
-
     calc_phase_inc calc_phase_inc (
         .*
     );
diff --git a/fpga/modules/operator/src/phase_generator.sv b/fpga/modules/operator/src/phase_generator.sv
index dd6b302..0a53c20 100755
--- a/fpga/modules/operator/src/phase_generator.sv
+++ b/fpga/modules/operator/src/phase_generator.sv
@@ -54,8 +54,8 @@ module phase_generator
     input wire [OP_NUM_WIDTH-1:0] op_num,
     input wire [PHASE_ACC_WIDTH-1:0] phase_inc_p2,
     input wire [REG_WS_WIDTH-1:0] ws,
-    input wire [ENV_WIDTH-1:0] env_p3,
-    input wire key_on_pulse_p0,
+    input wire [FINAL_ENV_WIDTH-1:0] env_p3,
+    input wire pg_reset_p2,
     input wire [OP_OUT_WIDTH-1:0] modulation_p1,
     input var operator_t op_type_p0,
     output logic signed [OP_OUT_WIDTH-1:0] out_p6 = 0
@@ -65,7 +65,6 @@ module phase_generator
     localparam PIPELINE_DELAY = 6;
 
     logic [PIPELINE_DELAY:1] sample_clk_en_p;
-    logic [PIPELINE_DELAY:1] key_on_pulse_p;
     logic [PHASE_ACC_WIDTH-1:0] phase_acc_p2;
     logic [PHASE_ACC_WIDTH-1:0] phase_acc_p3 = 0;
     logic [PHASE_FINAL_WIDTH-1:0] final_phase_p3;
@@ -73,7 +72,7 @@ module phase_generator
     logic [PHASE_FINAL_WIDTH-1:0] rhythm_phase_p3;
     logic [LOG_SIN_OUT_WIDTH-1:0] log_sin_out_p4;
     logic [OP_OUT_WIDTH-1:0] pre_gain_p4;
-    logic [OP_OUT_WIDTH:0] post_gain_p4; // need extra bit to detect overflow
+    logic [FINAL_ENV_WIDTH+4-1:0] post_gain_p4; // need extra bits to detect overflow
     logic [REG_WS_WIDTH-1:0] ws_post_opl_p0;
     logic [PIPELINE_DELAY:1] [REG_WS_WIDTH-1:0] ws_post_opl_p;
     logic [PIPELINE_DELAY:1] [BANK_NUM_WIDTH-1:0] bank_num_p;
@@ -81,7 +80,7 @@ module phase_generator
     logic [PIPELINE_DELAY:1] [$bits(operator_t)-1:0] op_type_p;
     logic [PIPELINE_DELAY:2] [OP_OUT_WIDTH-1:0] modulation_p;
     logic [PIPELINE_DELAY:4] [PHASE_FINAL_WIDTH-1:0] final_phase_p;
-    logic [ENV_WIDTH-1:0] env_p4 = 0;
+    logic [FINAL_ENV_WIDTH+3-1:0] env_shifted_p4 = 0;
     logic [OP_OUT_WIDTH-1:0] level_p4;
     logic [OP_OUT_WIDTH-1:0] level_p5 = 0;
     logic [EXP_OUT_WIDTH-1:0] exp_out_p5;
@@ -91,14 +90,6 @@ module phase_generator
     pipeline_sr #(
         .ENDING_CYCLE(PIPELINE_DELAY)
     ) sample_clk_en_sr (
-        .clk,
-        .in(key_on_pulse_p0),
-        .out(key_on_pulse_p)
-    );
-
-    pipeline_sr #(
-        .ENDING_CYCLE(PIPELINE_DELAY)
-    ) key_on_pulse_sr (
         .clk,
         .in(sample_clk_en),
         .out(sample_clk_en_p)
@@ -191,11 +182,10 @@ module phase_generator
      * back into the accumulator.
      */
     always_ff @(posedge clk)
-        if (sample_clk_en_p[2])
-            if (key_on_pulse_p[2])
-                phase_acc_p3 <= 0;
-            else
-                phase_acc_p3 <= phase_acc_p2 + phase_inc_p2;
+        if (pg_reset_p2)
+            phase_acc_p3 <= 0;
+        else
+            phase_acc_p3 <= phase_acc_p2 + phase_inc_p2;
 
     /*
      * Some rhythm instruments modify the phase, otherwise pass-through normally.
@@ -224,7 +214,7 @@ module phase_generator
     );
 
     always_ff @(posedge clk)
-        env_p4 <= env_p3;
+        env_shifted_p4 <= env_p3 << 3;
 
     always_comb begin
         unique case (ws_post_opl_p[4])
@@ -235,8 +225,8 @@ module phase_generator
         7:          pre_gain_p4 = (final_phase_p[4][9] ? (final_phase_p[4][8:0] ^ 'h1ff) : final_phase_p[4][9:0]) << 3;
         endcase
 
-        post_gain_p4 = pre_gain_p4 + (env_p4 << 3);
-        level_p4 = post_gain_p4 > 'h1fff ? 'h1fff : post_gain_p4;
+        post_gain_p4 = pre_gain_p4 + env_shifted_p4;
+        level_p4 = post_gain_p4 > 'h1fff ? 'h1fff : post_gain_p4; // clamp level
     end
 
     opl3_exp_lut exp_lut_inst (
diff --git a/fpga/modules/top_level/pkg/opl3_pkg.sv b/fpga/modules/top_level/pkg/opl3_pkg.sv
index 62a5462..4b540da 100755
--- a/fpga/modules/top_level/pkg/opl3_pkg.sv
+++ b/fpga/modules/top_level/pkg/opl3_pkg.sv
@@ -74,12 +74,12 @@ package opl3_pkg;
 
     localparam SAMPLE_WIDTH = 16;
     localparam DAC_LEFT_SHIFT = signed'(DAC_OUTPUT_WIDTH - SAMPLE_WIDTH - 2) < 0 ? 0 : DAC_OUTPUT_WIDTH - SAMPLE_WIDTH - 3;
-    localparam ENV_WIDTH = 9;
+    localparam FINAL_ENV_WIDTH = 11;
     localparam OP_OUT_WIDTH = 13;
     localparam PHASE_ACC_WIDTH = 20;
     localparam PHASE_FINAL_WIDTH = 10;
     localparam VIB_VAL_WIDTH = REG_FNUM_WIDTH - 7;
-    localparam ENV_RATE_COUNTER_OVERFLOW_WIDTH = $clog2(7);
+    localparam ENV_SHIFT_WIDTH = 3;
     localparam TREMOLO_MAX_COUNT = 13*1024;
     localparam TREMOLO_INDEX_WIDTH = $clog2(TREMOLO_MAX_COUNT);
     localparam AM_VAL_WIDTH = TREMOLO_INDEX_WIDTH - 8;