`timescale 1ns / 1ps
|
`timescale 1ns / 1ps
|
// ============================================================================
|
// ============================================================================
|
// __
|
// __
|
// \\__/ o\ (C) 2007-2017 Robert Finch, Waterloo
|
// \\__/ o\ (C) 2007-2017 Robert Finch, Waterloo
|
// \ __ / All rights reserved.
|
// \ __ / All rights reserved.
|
// \/_// robfinch<remove>@finitron.ca
|
// \/_// robfinch<remove>@finitron.ca
|
// ||
|
// ||
|
//
|
//
|
// PSGEnvelopeGenerator.v
|
// PSGEnvelopeGenerator.v
|
//
|
//
|
// This source file is free software: you can redistribute it and/or modify
|
// This source file is free software: you can redistribute it and/or modify
|
// it under the terms of the GNU Lesser General Public License as published
|
// 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
|
// by the Free Software Foundation, either version 3 of the License, or
|
// (at your option) any later version.
|
// (at your option) any later version.
|
//
|
//
|
// This source file is distributed in the hope that it will be useful,
|
// This source file is distributed in the hope that it will be useful,
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
//
|
//
|
//=============================================================================
|
//=============================================================================
|
/*
|
/*
|
sample attack values / rates
|
sample attack values / rates
|
----------------------------
|
----------------------------
|
8 2ms
|
8 2ms
|
32 8ms
|
32 8ms
|
64 16ms
|
64 16ms
|
96 24ms
|
96 24ms
|
152 38ms
|
152 38ms
|
224 56ms
|
224 56ms
|
272 68ms
|
272 68ms
|
320 80ms
|
320 80ms
|
400 100ms
|
400 100ms
|
955 239ms
|
955 239ms
|
1998 500ms
|
1998 500ms
|
3196 800ms
|
3196 800ms
|
3995 1s
|
3995 1s
|
12784 3.2s
|
12784 3.2s
|
21174 5.3s
|
21174 5.3s
|
31960 8s
|
31960 8s
|
|
|
rate = 990.00ns x 256 x value
|
rate = 990.00ns x 256 x value
|
*/
|
*/
|
|
|
module PSGEnvelopeGenerator(rst, clk, gate, attack, decay, sustain, relese, o, envState);
|
module PSGEnvelopeGenerator(rst, clk, gate, attack, decay, sustain, relese, o, envState);
|
input rst; // reset
|
input rst; // reset
|
input clk; // core clock
|
input clk; // core clock
|
input gate;
|
input gate;
|
input [15:0] attack;
|
input [23:0] attack;
|
input [15:0] decay;
|
input [23:0] decay;
|
input [7:0] sustain; // sustain level
|
input [7:0] sustain; // sustain level
|
input [15:0] relese;
|
input [23:0] relese;
|
output [7:0] o;
|
output [7:0] o;
|
output [2:0] envState;
|
output [2:0] envState;
|
|
|
// envelope generator states
|
// envelope generator states
|
parameter ENV_IDLE = 3'd0;
|
parameter ENV_IDLE = 3'd0;
|
parameter ENV_ATTACK = 3'd1;
|
parameter ENV_ATTACK = 3'd1;
|
parameter ENV_DECAY = 3'd2;
|
parameter ENV_DECAY = 3'd2;
|
parameter ENV_SUSTAIN = 3'd3;
|
parameter ENV_SUSTAIN = 3'd3;
|
parameter ENV_RELEASE = 3'd4;
|
parameter ENV_RELEASE = 3'd4;
|
|
|
// Per channel count storage
|
// Per channel count storage
|
reg [7:0] envCtr;
|
reg [7:0] envCtr;
|
reg [7:0] envCtr2;
|
reg [7:0] envCtr2;
|
reg [7:0] iv; // interval value for decay/release
|
reg [7:0] iv; // interval value for decay/release
|
reg [2:0] icnt; // interval count
|
reg [2:0] icnt; // interval count
|
reg [19:0] envDvn;
|
reg [27:0] envDvn;
|
reg [2:0] envState;
|
reg [2:0] envState;
|
|
|
reg [2:0] envStateNxt;
|
reg [2:0] envStateNxt;
|
reg [15:0] envStepPeriod; // determines the length of one step of the envelope generator
|
reg [23:0] envStepPeriod; // determines the length of one step of the envelope generator
|
|
|
integer n;
|
integer n;
|
|
|
wire attack_next = envState==ENV_IDLE && gate;
|
wire attack_next = envState==ENV_IDLE && gate;
|
wire decay_next = envState==ENV_ATTACK && envCtr==8'hFE && sustain != 8'hFF;
|
wire decay_next = envState==ENV_ATTACK && envCtr==8'hFE && sustain != 8'hFF;
|
wire release_next = envState==ENV_SUSTAIN && !gate;
|
wire release_next = envState==ENV_SUSTAIN && !gate;
|
|
|
// Envelope generate state machine
|
// Envelope generate state machine
|
// Determine the next envelope state
|
// Determine the next envelope state
|
always @*
|
always @*
|
begin
|
begin
|
envStateNxt <= envState; // default to hold state
|
envStateNxt <= envState; // default to hold state
|
case (envState)
|
case (envState)
|
ENV_IDLE:
|
ENV_IDLE:
|
if (gate)
|
if (gate)
|
envStateNxt <= ENV_ATTACK;
|
envStateNxt <= ENV_ATTACK;
|
ENV_ATTACK:
|
ENV_ATTACK:
|
if (envCtr==8'hFE) begin
|
if (envCtr==8'hFE) begin
|
if (sustain==8'hFF)
|
if (sustain==8'hFF)
|
envStateNxt <= ENV_SUSTAIN;
|
envStateNxt <= ENV_SUSTAIN;
|
else
|
else
|
envStateNxt <= ENV_DECAY;
|
envStateNxt <= ENV_DECAY;
|
end
|
end
|
ENV_DECAY:
|
ENV_DECAY:
|
if (envCtr==sustain)
|
if (envCtr==sustain)
|
envStateNxt <= ENV_SUSTAIN;
|
envStateNxt <= ENV_SUSTAIN;
|
ENV_SUSTAIN:
|
ENV_SUSTAIN:
|
if (~gate)
|
if (~gate)
|
envStateNxt <= ENV_RELEASE;
|
envStateNxt <= ENV_RELEASE;
|
ENV_RELEASE:
|
ENV_RELEASE:
|
if (envCtr==8'h00)
|
if (envCtr==8'h00)
|
envStateNxt <= ENV_IDLE;
|
envStateNxt <= ENV_IDLE;
|
else if (gate)
|
else if (gate)
|
envStateNxt <= ENV_SUSTAIN;
|
envStateNxt <= ENV_SUSTAIN;
|
// In case of hardware problem
|
// In case of hardware problem
|
default:
|
default:
|
envStateNxt <= ENV_IDLE;
|
envStateNxt <= ENV_IDLE;
|
endcase
|
endcase
|
end
|
end
|
|
|
always @(posedge clk)
|
always @(posedge clk)
|
if (rst)
|
if (rst)
|
envState <= ENV_IDLE;
|
envState <= ENV_IDLE;
|
else
|
else
|
envState <= envStateNxt;
|
envState <= envStateNxt;
|
|
|
|
|
// Handle envelope counter
|
// Handle envelope counter
|
always @(posedge clk)
|
always @(posedge clk)
|
if (rst) begin
|
if (rst) begin
|
envCtr <= 0;
|
envCtr <= 0;
|
envCtr2 <= 0;
|
envCtr2 <= 0;
|
icnt <= 1;
|
icnt <= 1;
|
iv <= 0;
|
iv <= 0;
|
end
|
end
|
else begin
|
else begin
|
case (envState)
|
case (envState)
|
ENV_IDLE:
|
ENV_IDLE:
|
begin
|
begin
|
envCtr <= 0;
|
envCtr <= 0;
|
envCtr2 <= 0;
|
envCtr2 <= 0;
|
icnt <= 1;
|
icnt <= 1;
|
iv <= 0;
|
iv <= 0;
|
end
|
end
|
ENV_SUSTAIN:
|
ENV_SUSTAIN:
|
begin
|
begin
|
envCtr2 <= 0;
|
envCtr2 <= 0;
|
icnt <= 1;
|
icnt <= 1;
|
iv <= sustain >> 3;
|
iv <= sustain >> 3;
|
end
|
end
|
ENV_ATTACK:
|
ENV_ATTACK:
|
begin
|
begin
|
icnt <= 1;
|
icnt <= 1;
|
iv <= (8'hff - sustain) >> 3;
|
iv <= (8'hff - sustain) >> 3;
|
if (envDvn==20'h0) begin
|
if (envDvn==28'h0) begin
|
envCtr2 <= 0;
|
envCtr2 <= 0;
|
envCtr <= envCtr + 1;
|
envCtr <= envCtr + 1;
|
end
|
end
|
end
|
end
|
ENV_DECAY,
|
ENV_DECAY,
|
ENV_RELEASE:
|
ENV_RELEASE:
|
if (envDvn==20'h0) begin
|
if (envDvn==28'h0) begin
|
envCtr <= envCtr - 1;
|
envCtr <= envCtr - 1;
|
if (envCtr2==iv) begin
|
if (envCtr2==iv) begin
|
envCtr2 <= 0;
|
envCtr2 <= 0;
|
if (icnt < 3'd7)
|
if (icnt < 3'd7)
|
icnt <= icnt + 1;
|
icnt <= icnt + 1;
|
end
|
end
|
else
|
else
|
envCtr2 <= envCtr2 + 1;
|
envCtr2 <= envCtr2 + 1;
|
end
|
end
|
endcase
|
endcase
|
end
|
end
|
|
|
// Determine envelope divider adjustment source
|
// Determine envelope divider adjustment source
|
always @*
|
always @*
|
begin
|
begin
|
case(envState)
|
case(envState)
|
ENV_ATTACK: envStepPeriod <= attack;
|
ENV_ATTACK: envStepPeriod <= attack;
|
ENV_DECAY: envStepPeriod <= decay;
|
ENV_DECAY: envStepPeriod <= decay;
|
ENV_RELEASE: envStepPeriod <= relese;
|
ENV_RELEASE: envStepPeriod <= relese;
|
default: envStepPeriod <= 16'h0;
|
default: envStepPeriod <= 24'h0;
|
endcase
|
endcase
|
end
|
end
|
|
|
|
|
// double the delay at appropriate points
|
// double the delay at appropriate points
|
// for exponential modelling
|
// for exponential modelling
|
wire [19:0] envStepPeriod1 = {4'b0,envStepPeriod} << icnt;
|
wire [27:0] envStepPeriod1 = {4'b0,envStepPeriod} << icnt;
|
|
|
|
|
// handle the clock divider
|
// handle the clock divider
|
// loadable down counter
|
// loadable down counter
|
// This sets the period of each step of the envelope
|
// This sets the period of each step of the envelope
|
always @(posedge clk)
|
always @(posedge clk)
|
if (rst)
|
if (rst)
|
envDvn <= 0;
|
envDvn <= 0;
|
else begin
|
else begin
|
casex({attack_next,decay_next,release_next})
|
casex({attack_next,decay_next,release_next})
|
3'b1xx: envDvn <= {4'h0,attack};
|
3'b1xx: envDvn <= {4'h0,attack};
|
3'b01x: envDvn <= {4'h0,decay};
|
3'b01x: envDvn <= {4'h0,decay};
|
3'b001: envDvn <= {4'h0,relese};
|
3'b001: envDvn <= {4'h0,relese};
|
default:
|
default:
|
if (envDvn==20'h0)
|
if (envDvn==28'h0)
|
envDvn <= envStepPeriod1;
|
envDvn <= envStepPeriod1;
|
else
|
else
|
envDvn <= envDvn - 1;
|
envDvn <= envDvn - 1;
|
endcase
|
endcase
|
end
|
end
|
|
|
assign o = envCtr;
|
assign o = envCtr;
|
|
|
endmodule
|
endmodule
|
|
|
|
|