Add SAA1099.

This commit is contained in:
sorgelig
2018-08-17 15:23:12 +08:00
parent 174e824d7e
commit c2dc884ac3
5 changed files with 455 additions and 5 deletions

View File

@ -375,6 +375,7 @@ set_global_assignment -name VHDL_FILE src/sound/soundrive.vhd
set_global_assignment -name VHDL_FILE src/sound/turbosound.vhd
set_global_assignment -name VHDL_FILE src/sound/ay8910.vhd
set_global_assignment -name VHDL_FILE src/sound/gs.vhd
set_global_assignment -name SYSTEMVERILOG_FILE src/sound/saa1099.sv
set_global_assignment -name VERILOG_FILE src/memory/dma.v
set_global_assignment -name VERILOG_FILE src/memory/arbiter.v
set_global_assignment -name VERILOG_FILE src/video/video_ts_render.v

View File

@ -381,6 +381,7 @@ set_global_assignment -name VHDL_FILE src/sound/soundrive.vhd
set_global_assignment -name VHDL_FILE src/sound/turbosound.vhd
set_global_assignment -name VHDL_FILE src/sound/ay8910.vhd
set_global_assignment -name VHDL_FILE src/sound/gs.vhd
set_global_assignment -name SYSTEMVERILOG_FILE src/sound/saa1099.sv
set_global_assignment -name VERILOG_FILE src/memory/dma.v
set_global_assignment -name VERILOG_FILE src/memory/arbiter.v
set_global_assignment -name VERILOG_FILE src/video/video_ts_render.v

View File

@ -20,7 +20,8 @@ module clock (
output reg f0, f1,
output reg h0, h1,
output reg c0, c1, c2, c3,
output wire ay_clk
output wire ay_clk,
output reg ce_saa
);
@ -47,5 +48,14 @@ module clock (
ay_cnt <= ay_cnt + (skip_cnt[7] & ay_mod[0] ? 4'd2 : 4'd1);
end
always @(posedge clk) begin
reg [2:0] div;
div <= div + 1'd1;
if(div == 6) div <= 0;
ce_saa <= (div == 0 || div == 3);
end
endmodule

403
src/sound/saa1099.sv Normal file
View File

@ -0,0 +1,403 @@
//============================================================================
//
// 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

View File

@ -353,6 +353,12 @@ signal gs_c : std_logic_vector(13 downto 0);
signal gs_d : std_logic_vector(13 downto 0);
signal gs_do_bus : std_logic_vector(7 downto 0);
-- SAA1099
signal saa_wr_n : std_logic;
signal saa_out_l : std_logic_vector(7 downto 0);
signal saa_out_r : std_logic_vector(7 downto 0);
signal ce_saa : std_logic;
-------------------------------------------------------------------------------
-- COMPONENTS TS Lab
-------------------------------------------------------------------------------
@ -368,7 +374,8 @@ port (
c1 : out std_logic;
c2 : out std_logic;
c3 : out std_logic;
ay_clk : out std_logic);
ay_clk : out std_logic;
ce_saa : out std_logic);
end component;
component zclock is
@ -801,6 +808,19 @@ port (
dout : out std_logic_vector(7 downto 0));
end component;
component saa1099
port (
clk_sys : in std_logic;
ce : in std_logic;
rst_n : in std_logic;
cs_n : in std_logic;
a0 : in std_logic;
wr_n : in std_logic;
din : in std_logic_vector(7 downto 0);
out_l : out std_logic_vector(7 downto 0);
out_r : out std_logic_vector(7 downto 0));
end component;
-------------------------------------------------------------------------------
begin
@ -817,7 +837,7 @@ port map (
c1 => c1,
c2 => c2,
c3 => c3,
ay_clk => open);
ce_saa => ce_saa);
TS02: zclock
port map (
@ -1388,6 +1408,18 @@ port map (
OUTC => gs_c,
OUTD => gs_d);
U16: saa1099
port map(
clk_sys => clk_28mhz,
ce => ce_saa,
rst_n => not reset,
cs_n => '0',
a0 => cpu_a_bus(8), -- 0=data, 1=address
wr_n => saa_wr_n,
din => cpu_do_bus,
out_l => saa_out_l,
out_r => saa_out_r);
-------------------------------------------------------------------------------
-- Global
-------------------------------------------------------------------------------
@ -1456,7 +1488,10 @@ port_bff7 <= '1' when (cpu_iorq_n = '0' and cpu_a_bus = X"BFF7" and cpu_m1_n = '
-- Z-Controller
SD_CS_N <= sdcs_n_TS;
SOUND_L <= ("0000" & port_xxfe_reg(4) & "0000000000") + ("0000" & ssg_cn0_a & "000") + ("0000" & ssg_cn0_b & "000") + ("0000" & ssg_cn1_a & "000") + ("0000" & ssg_cn1_b & "000") + ("0000" & covox_a & "000") + ("0000" & covox_b & "000") + ("00" & gs_a) + ("00" & gs_b); -- + ("0000" & saa_out_l & "000");
SOUND_R <= ("0000" & port_xxfe_reg(4) & "0000000000") + ("0000" & ssg_cn0_c & "000") + ("0000" & ssg_cn0_b & "000") + ("0000" & ssg_cn1_c & "000") + ("0000" & ssg_cn1_b & "000") + ("0000" & covox_c & "000") + ("0000" & covox_d & "000") + ("00" & gs_c) + ("00" & gs_d); -- + ("0000" & saa_out_r & "000");
-- SAA1099
saa_wr_n <= '0' when (cpu_iorq_n = '0' and cpu_wr_n = '0' and cpu_a_bus(7 downto 0) = "11111111" and dos = '0') else '1';
SOUND_L <= ("0000" & port_xxfe_reg(4) & "0000000000") + ("0000" & ssg_cn0_a & "000") + ("0000" & ssg_cn0_b & "000") + ("0000" & ssg_cn1_a & "000") + ("0000" & ssg_cn1_b & "000") + ("0000" & covox_a & "000") + ("0000" & covox_b & "000") + ("00" & gs_a) + ("00" & gs_b) + ("0000" & saa_out_l & "000");
SOUND_R <= ("0000" & port_xxfe_reg(4) & "0000000000") + ("0000" & ssg_cn0_c & "000") + ("0000" & ssg_cn0_b & "000") + ("0000" & ssg_cn1_c & "000") + ("0000" & ssg_cn1_b & "000") + ("0000" & covox_c & "000") + ("0000" & covox_d & "000") + ("00" & gs_c) + ("00" & gs_d) + ("0000" & saa_out_r & "000");
end rtl;