mirror of
https://github.com/UzixLS/TSConf_MiST.git
synced 2025-07-19 07:11:22 +03:00
404 lines
10 KiB
Systemverilog
404 lines
10 KiB
Systemverilog
//============================================================================
|
|
//
|
|
// SAA1099 sound generator
|
|
// Copyright (C) 2016 Sorgelig
|
|
//
|
|
// Based on SAA1099.v code from Miguel Angel Rodriguez Jodar
|
|
// Based on SAASound code from Dave Hooper
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU General Public License as published by the Free
|
|
// Software Foundation; either version 2 of the License, or (at your option)
|
|
// any later version.
|
|
//
|
|
// This program 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 General Public License for
|
|
// more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
//
|
|
//============================================================================
|
|
`timescale 1ns / 1ps
|
|
`default_nettype none
|
|
|
|
|
|
module saa1099
|
|
(
|
|
input clk_sys,
|
|
input ce, // 8 MHz
|
|
input rst_n,
|
|
input cs_n,
|
|
input a0, // 0=data, 1=address
|
|
input wr_n,
|
|
input [7:0] din,
|
|
output [7:0] out_l,
|
|
output [7:0] out_r
|
|
);
|
|
|
|
reg [7:0] amplit0, amplit1, amplit2, amplit3, amplit4, amplit5;
|
|
reg [7:0] freq0, freq1, freq2, freq3, freq4, freq5;
|
|
reg [7:0] oct10, oct32, oct54;
|
|
reg [7:0] freqenable;
|
|
reg [7:0] noiseenable;
|
|
reg [7:0] noisegen;
|
|
reg [7:0] envelope0, envelope1;
|
|
reg [7:0] ctrl;
|
|
|
|
reg [4:0] addr;
|
|
wire rst = ~rst_n | ctrl[1];
|
|
reg wr;
|
|
|
|
always @(posedge clk_sys) begin
|
|
reg old_wr;
|
|
old_wr <= wr_n;
|
|
|
|
wr <= 0;
|
|
if(~rst_n) begin
|
|
addr <= 0;
|
|
{amplit0, amplit1, amplit2, amplit3, amplit4, amplit5} <= 0;
|
|
{freq0, freq1, freq2, freq3, freq4, freq5} <= 0;
|
|
{oct10, oct32, oct54} <= 0;
|
|
{freqenable, noiseenable, noisegen} <= 0;
|
|
{envelope0, envelope1} <= 0;
|
|
ctrl <= 0;
|
|
end
|
|
else begin
|
|
if(!cs_n & old_wr & !wr_n) begin
|
|
wr <= 1;
|
|
if(a0) addr <= din[4:0];
|
|
else begin
|
|
case (addr)
|
|
'h00: amplit0 <= din;
|
|
'h01: amplit1 <= din;
|
|
'h02: amplit2 <= din;
|
|
'h03: amplit3 <= din;
|
|
'h04: amplit4 <= din;
|
|
'h05: amplit5 <= din;
|
|
|
|
'h08: freq0 <= din;
|
|
'h09: freq1 <= din;
|
|
'h0A: freq2 <= din;
|
|
'h0B: freq3 <= din;
|
|
'h0C: freq4 <= din;
|
|
'h0D: freq5 <= din;
|
|
|
|
'h10: oct10 <= din;
|
|
'h11: oct32 <= din;
|
|
'h12: oct54 <= din;
|
|
|
|
'h14: freqenable <= din;
|
|
'h15: noiseenable<= din;
|
|
'h16: noisegen <= din;
|
|
|
|
'h18: envelope0 <= din;
|
|
'h19: envelope1 <= din;
|
|
|
|
'h1C: ctrl <= din;
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
wire [21:0] out0;
|
|
saa1099_triplet top
|
|
(
|
|
.*,
|
|
.vol('{amplit0, amplit1, amplit2}),
|
|
.env(envelope0),
|
|
|
|
.freq('{freq0, freq1, freq2}),
|
|
.octave('{oct10[2:0], oct10[6:4], oct32[2:0]}),
|
|
.freq_en(freqenable[2:0]),
|
|
|
|
.noise_en(noiseenable[2:0]),
|
|
.noise_freq(noisegen[1:0]),
|
|
|
|
.wr_addr(wr & a0 & (din[4:0] == 'h18)),
|
|
.wr_data(wr & !a0 & (addr == 'h18)),
|
|
|
|
.out(out0)
|
|
);
|
|
|
|
wire [21:0] out1;
|
|
saa1099_triplet bottom
|
|
(
|
|
.*,
|
|
.vol('{amplit3, amplit4, amplit5}),
|
|
.env(envelope1),
|
|
|
|
.freq('{freq3, freq4, freq5}),
|
|
.octave('{oct32[6:4], oct54[2:0], oct54[6:4]}),
|
|
.freq_en(freqenable[5:3]),
|
|
|
|
.noise_en(noiseenable[5:3]),
|
|
.noise_freq(noisegen[5:4]),
|
|
|
|
.wr_addr(wr & a0 & (din[4:0] == 'h19)),
|
|
.wr_data(wr & !a0 & (addr == 'h19)),
|
|
|
|
.out(out1)
|
|
);
|
|
|
|
saa1099_output_mixer outmix_l(.*, .en(ctrl[0]), .in0(out0[10:0]), .in1(out1[10:0]), .out(out_l));
|
|
saa1099_output_mixer outmix_r(.*, .en(ctrl[0]), .in0(out0[21:11]), .in1(out1[21:11]), .out(out_r));
|
|
|
|
endmodule
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
module saa1099_triplet
|
|
(
|
|
input rst,
|
|
input clk_sys,
|
|
input ce,
|
|
|
|
input [7:0] vol[3],
|
|
input [7:0] env,
|
|
|
|
input [7:0] freq[3],
|
|
input [2:0] octave[3],
|
|
input [2:0] freq_en,
|
|
|
|
input [2:0] noise_en,
|
|
input [1:0] noise_freq,
|
|
|
|
input wr_addr,
|
|
input wr_data,
|
|
|
|
output[21:0] out
|
|
);
|
|
|
|
wire tone0, tone1, tone2, noise;
|
|
wire pulse_noise, pulse_envelope;
|
|
wire[21:0] out0, out1, out2;
|
|
|
|
saa1099_tone freq_gen0(.*, .out(tone0), .octave(octave[0]), .freq(freq[0]), .pulse(pulse_noise));
|
|
saa1099_tone freq_gen1(.*, .out(tone1), .octave(octave[1]), .freq(freq[1]), .pulse(pulse_envelope));
|
|
saa1099_tone freq_gen2(.*, .out(tone2), .octave(octave[2]), .freq(freq[2]), .pulse());
|
|
saa1099_noise noise_gen(.*, .out(noise));
|
|
|
|
saa1099_amp amp0(.*, .mixmode({noise_en[0], freq_en[0]}), .tone(tone0), .envreg(0), .vol(vol[0]), .out(out0));
|
|
saa1099_amp amp1(.*, .mixmode({noise_en[1], freq_en[1]}), .tone(tone1), .envreg(0), .vol(vol[1]), .out(out1));
|
|
saa1099_amp amp2(.*, .mixmode({noise_en[2], freq_en[2]}), .tone(tone2), .envreg(env), .vol(vol[2]), .out(out2));
|
|
|
|
assign out[10:0] = out0[8:0] + out1[8:0] + out2[8:0];
|
|
assign out[21:11] = out0[17:9] + out1[17:9] + out2[17:9];
|
|
|
|
endmodule
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module saa1099_tone
|
|
(
|
|
input rst,
|
|
input clk_sys,
|
|
input ce,
|
|
input [2:0] octave,
|
|
input [7:0] freq,
|
|
output reg out,
|
|
output reg pulse
|
|
);
|
|
|
|
wire [16:0] fcount = ((17'd511 - freq) << (4'd8 - octave)) - 1'd1;
|
|
always @(posedge clk_sys) begin
|
|
reg [16:0] count;
|
|
|
|
pulse <= 0;
|
|
if(rst) begin
|
|
count <= fcount;
|
|
out <= 0;
|
|
end else if(ce) begin
|
|
if(!count) begin
|
|
count <= fcount;
|
|
pulse <= 1;
|
|
out <= ~out;
|
|
end else begin
|
|
count <= count - 1'd1;
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module saa1099_noise
|
|
(
|
|
input rst,
|
|
input clk_sys,
|
|
input ce,
|
|
input pulse_noise,
|
|
input [1:0] noise_freq,
|
|
output out
|
|
);
|
|
|
|
reg [16:0] lfsr = 0;
|
|
wire [16:0] new_lfsr = {(lfsr[0] ^ lfsr[2] ^ !lfsr), lfsr[16:1]};
|
|
wire [10:0] fcount = (11'd256 << noise_freq) - 1'b1;
|
|
|
|
always @(posedge clk_sys) begin
|
|
reg [10:0] count;
|
|
|
|
if(rst) begin
|
|
count <= fcount;
|
|
end else
|
|
if(noise_freq != 3) begin
|
|
if(ce) begin
|
|
if(!count) begin
|
|
count <= fcount;
|
|
lfsr <= new_lfsr;
|
|
end else begin
|
|
count <= count - 1'd1;
|
|
end
|
|
end
|
|
end else if(pulse_noise) begin
|
|
lfsr <= new_lfsr;
|
|
end
|
|
end
|
|
|
|
assign out = lfsr[0];
|
|
|
|
endmodule
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module saa1099_amp
|
|
(
|
|
input rst,
|
|
input clk_sys,
|
|
input [7:0] envreg,
|
|
input [1:0] mixmode,
|
|
input tone,
|
|
input noise,
|
|
input wr_addr,
|
|
input wr_data,
|
|
input pulse_envelope,
|
|
input [7:0] vol,
|
|
output reg [17:0] out
|
|
);
|
|
|
|
wire phases[8] = '{0,0,0,0,1,1,0,0};
|
|
wire [1:0] env[8][2] = '{'{0,0}, '{1,1}, '{2,0}, '{2,0}, '{3,2}, '{3,2}, '{3,0}, '{3,0}};
|
|
wire [3:0] levels[4][16] =
|
|
'{
|
|
'{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
'{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15},
|
|
'{15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
|
|
'{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}
|
|
};
|
|
|
|
reg [2:0] shape;
|
|
reg stereo;
|
|
wire resolution = envreg[4];
|
|
wire enable = envreg[7];
|
|
reg [3:0] counter;
|
|
reg phase;
|
|
wire[3:0] mask = {3'b000, resolution};
|
|
|
|
always @(posedge clk_sys) begin
|
|
reg clock;
|
|
reg new_data;
|
|
|
|
if(rst | ~enable) begin
|
|
new_data <= 0;
|
|
stereo <= envreg[0];
|
|
shape <= envreg[3:1];
|
|
clock <= envreg[5];
|
|
phase <= 0;
|
|
counter <= 0;
|
|
end
|
|
else begin
|
|
if(wr_data) new_data <= 1;
|
|
if(clock ? wr_addr : pulse_envelope) begin // pulse from internal or external clock?
|
|
counter <= counter + resolution + 1'd1;
|
|
if((counter | mask) == 15) begin
|
|
if(phase >= phases[shape]) begin
|
|
if(~shape[0]) counter <= 15;
|
|
if(new_data | shape[0]) begin // if we reached one of the designated points (3) or (4) and there is pending data, load it
|
|
new_data <= 0;
|
|
stereo <= envreg[0];
|
|
shape <= envreg[3:1];
|
|
clock <= envreg[5];
|
|
phase <= 0;
|
|
if(new_data) counter <= 0;
|
|
end
|
|
end else begin
|
|
phase <= 1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
wire [3:0] env_l = levels[env[shape][phase]][counter] & ~mask;
|
|
wire [3:0] env_r = stereo ? (4'd15 & ~mask) - env_l : env_l; // bit 0 of envreg inverts envelope shape
|
|
|
|
reg [1:0] outmix;
|
|
always_comb begin
|
|
case(mixmode)
|
|
0: outmix <= 0;
|
|
1: outmix <= {tone, 1'b0};
|
|
2: outmix <= {noise, 1'b0};
|
|
3: outmix <= {tone & ~noise, tone & noise};
|
|
endcase
|
|
end
|
|
|
|
wire [8:0] vol_mix_l = {vol[3:1], vol[0] & ~enable, 5'b00000} >> outmix[0];
|
|
wire [8:0] vol_mix_r = {vol[7:5], vol[4] & ~enable, 5'b00000} >> outmix[0];
|
|
wire [8:0] env_out_l;
|
|
wire [8:0] env_out_r;
|
|
saa1099_mul_env mod_l(.vol(vol_mix_l[8:4]), .env(env_l), .out(env_out_l));
|
|
saa1099_mul_env mod_r(.vol(vol_mix_r[8:4]), .env(env_r), .out(env_out_r));
|
|
|
|
always_comb begin
|
|
case({enable, outmix})
|
|
'b100, 'b101: out = {env_out_r, env_out_l};
|
|
'b001, 'b010: out = {vol_mix_r, vol_mix_l};
|
|
default: out = 0;
|
|
endcase
|
|
end
|
|
|
|
endmodule
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module saa1099_mul_env
|
|
(
|
|
input [4:0] vol,
|
|
input [3:0] env,
|
|
output [8:0] out
|
|
);
|
|
|
|
assign out = (env[0] ? vol : 9'd0)+
|
|
(env[1] ? { vol,1'b0} : 9'd0)+
|
|
(env[2] ? { vol,2'b00} : 9'd0)+
|
|
(env[3] ? {vol,3'b000} : 9'd0);
|
|
|
|
endmodule
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module saa1099_output_mixer
|
|
(
|
|
input clk_sys,
|
|
input ce,
|
|
input en,
|
|
input [10:0] in0,
|
|
input [10:0] in1,
|
|
output reg [7:0] out
|
|
);
|
|
|
|
wire [17:0] o = 18'd91 * ({1'b0,in0} + {1'b0,in1});
|
|
|
|
// Clean the audio.
|
|
always @(posedge clk_sys) begin
|
|
reg ced;
|
|
ced <= ce;
|
|
if(ced) out <= ~en ? 8'h00 : o[17:10];
|
|
end
|
|
|
|
endmodule
|