repo2/atari_chips/pokeyv2/PSG/top.vhdl
1009 | markw | ---------------------------------------------------------------------------
|
|
-- (c) 2020 mark watson
|
|||
-- I am happy for anyone to use this for non-commercial use.
|
|||
-- If my vhdl files are used commercially or otherwise sold,
|
|||
-- please contact me for explicit permission at scrameta (gmail).
|
|||
-- This applies for source and binary form and derived works.
|
|||
---------------------------------------------------------------------------
|
|||
LIBRARY ieee;
|
|||
USE ieee.std_logic_1164.all;
|
|||
use ieee.numeric_std.all;
|
|||
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
|
|||
use IEEE.STD_LOGIC_MISC.all;
|
|||
LIBRARY work;
|
|||
-- audio only, no need for io part
|
|||
1036 | markw | ENTITY PSG_top IS
|
|
1009 | markw | PORT
|
|
(
|
|||
CLK : in std_logic;
|
|||
RESET_N : in std_logic;
|
|||
ENABLE : in std_logic;
|
|||
1038 | markw | ||
ENVELOPE32 : in std_logic := '1'; -- 0=16 step,1=32 step
|
|||
1009 | markw | ||
ADDR : in std_logic_vector(3 downto 0); --TODO: Handy to address this way, but could use the original crappy way if people prefer!
|
|||
WRITE_ENABLE : in std_logic;
|
|||
DI : in std_logic_vector(7 downto 0);
|
|||
DO : out std_logic_vector(7 downto 0);
|
|||
IOA_IN : in std_logic_vector(7 downto 0) := (others=>'0');
|
|||
IOB_IN : in std_logic_vector(7 downto 0) := (others=>'0');
|
|||
IOA_OUT : out std_logic_vector(7 downto 0);
|
|||
IOB_OUT : out std_logic_vector(7 downto 0);
|
|||
IOA_OE : out std_logic;
|
|||
IOB_OE : out std_logic;
|
|||
1139 | markw | channel_a_vol : out std_logic_vector(4 downto 0);
|
|
channel_b_vol : out std_logic_vector(4 downto 0);
|
|||
1220 | markw | channel_c_vol : out std_logic_vector(4 downto 0);
|
|
channel_changed : out std_logic
|
|||
1009 | markw | );
|
|
1036 | markw | END PSG_top;
|
|
1009 | markw | ||
1036 | markw | ARCHITECTURE vhdl OF PSG_top IS
|
|
1031 | markw | signal period_channel_a_reg : std_logic_vector(11 downto 0);
|
|
signal period_channel_a_next : std_logic_vector(11 downto 0);
|
|||
signal period_channel_b_reg : std_logic_vector(11 downto 0);
|
|||
signal period_channel_b_next : std_logic_vector(11 downto 0);
|
|||
signal period_channel_c_reg : std_logic_vector(11 downto 0);
|
|||
signal period_channel_c_next : std_logic_vector(11 downto 0);
|
|||
1009 | markw | ||
1031 | markw | signal period_noise_reg : std_logic_vector(4 downto 0);
|
|
signal period_noise_next : std_logic_vector(4 downto 0);
|
|||
1009 | markw | ||
signal vol_channel_a_reg : std_logic_vector(4 downto 0);
|
|||
signal vol_channel_a_next : std_logic_vector(4 downto 0);
|
|||
signal vol_channel_b_reg : std_logic_vector(4 downto 0);
|
|||
signal vol_channel_b_next : std_logic_vector(4 downto 0);
|
|||
signal vol_channel_c_reg : std_logic_vector(4 downto 0);
|
|||
signal vol_channel_c_next : std_logic_vector(4 downto 0);
|
|||
1031 | markw | signal period_envelope_reg : std_logic_vector(15 downto 0);
|
|
signal period_envelope_next : std_logic_vector(15 downto 0);
|
|||
1009 | markw | ||
signal shape_envelope_reg : std_logic_vector(3 downto 0);
|
|||
signal shape_envelope_next : std_logic_vector(3 downto 0);
|
|||
signal mixer_noise_reg : std_logic_vector(2 downto 0);
|
|||
signal mixer_noise_next : std_logic_vector(2 downto 0);
|
|||
signal mixer_tone_reg : std_logic_vector(2 downto 0);
|
|||
signal mixer_tone_next : std_logic_vector(2 downto 0);
|
|||
signal io_output_reg : std_logic_vector(1 downto 0);
|
|||
signal io_output_next : std_logic_vector(1 downto 0);
|
|||
signal ioa_reg : std_logic_vector(7 downto 0);
|
|||
signal ioa_next : std_logic_vector(7 downto 0);
|
|||
signal iob_reg : std_logic_vector(7 downto 0);
|
|||
signal iob_next : std_logic_vector(7 downto 0);
|
|||
signal addr_decoded : std_logic_vector(15 downto 0);
|
|||
1038 | markw | signal core_tick : std_logic;
|
|
1054 | markw | signal core_tick_half : std_logic;
|
|
1009 | markw | signal channel_a_tick : std_logic;
|
|
signal channel_b_tick : std_logic;
|
|||
signal channel_c_tick : std_logic;
|
|||
signal noise_tick : std_logic;
|
|||
1053 | markw | signal noise_val : std_logic;
|
|
1009 | markw | ||
signal channel_a_val : std_logic;
|
|||
signal channel_b_val : std_logic;
|
|||
signal channel_c_val : std_logic;
|
|||
1220 | markw | ||
signal channel_a_changed : std_logic;
|
|||
signal channel_b_changed : std_logic;
|
|||
signal channel_c_changed : std_logic;
|
|||
1009 | markw | ||
1038 | markw | signal envelope_reg : std_logic_vector(4 downto 0);
|
|
1031 | markw | signal envelope_count_reset : std_logic;
|
|
1009 | markw | ||
BEGIN
|
|||
process(clk,reset_n)
|
|||
begin
|
|||
if (reset_n='0') then
|
|||
1031 | markw | period_channel_a_reg <= (others=>'0');
|
|
period_channel_b_reg <= (others=>'0');
|
|||
period_channel_c_reg <= (others=>'0');
|
|||
period_noise_reg <= (others=>'0');
|
|||
1009 | markw | vol_channel_a_reg <= (others=>'0');
|
|
vol_channel_b_reg <= (others=>'0');
|
|||
vol_channel_c_reg <= (others=>'0');
|
|||
1031 | markw | period_envelope_reg <= (others=>'0');
|
|
1009 | markw | shape_envelope_reg <= (others=>'0');
|
|
mixer_noise_reg <= (others=>'0');
|
|||
mixer_tone_reg <= (others=>'0');
|
|||
io_output_reg <= (others=>'0');
|
|||
ioa_reg <= (others=>'0');
|
|||
iob_reg <= (others=>'0');
|
|||
elsif (clk'event and clk='1') then
|
|||
1031 | markw | period_channel_a_reg <= period_channel_a_next;
|
|
period_channel_b_reg <= period_channel_b_next;
|
|||
period_channel_c_reg <= period_channel_c_next;
|
|||
period_noise_reg <= period_noise_next;
|
|||
1009 | markw | vol_channel_a_reg <= vol_channel_a_next;
|
|
vol_channel_b_reg <= vol_channel_b_next;
|
|||
vol_channel_c_reg <= vol_channel_c_next;
|
|||
1031 | markw | period_envelope_reg <= period_envelope_next;
|
|
1009 | markw | shape_envelope_reg <= shape_envelope_next;
|
|
mixer_noise_reg <= mixer_noise_next;
|
|||
mixer_tone_reg <= mixer_tone_next;
|
|||
io_output_reg <= io_output_next;
|
|||
ioa_reg <= ioa_next;
|
|||
iob_reg <= iob_next;
|
|||
end if;
|
|||
end process;
|
|||
decode_addr1 : entity work.complete_address_decoder
|
|||
generic map(width=>4)
|
|||
port map (addr_in=>ADDR(3 downto 0), addr_decoded=>addr_decoded);
|
|||
process(addr_decoded,write_enable,di,
|
|||
1031 | markw | period_channel_a_reg,period_channel_b_reg,period_channel_c_reg,
|
|
period_noise_reg,
|
|||
1009 | markw | vol_channel_a_reg,vol_channel_b_reg,vol_channel_c_reg,
|
|
1031 | markw | period_envelope_reg,
|
|
1009 | markw | shape_envelope_reg,
|
|
mixer_noise_reg,
|
|||
mixer_tone_reg,
|
|||
ioa_reg,
|
|||
iob_reg,
|
|||
io_output_reg
|
|||
)
|
|||
begin
|
|||
1031 | markw | period_channel_a_next <= period_channel_a_reg;
|
|
period_channel_b_next <= period_channel_b_reg;
|
|||
period_channel_c_next <= period_channel_c_reg;
|
|||
period_noise_next <= period_noise_reg;
|
|||
1009 | markw | vol_channel_a_next <= vol_channel_a_reg;
|
|
vol_channel_b_next <= vol_channel_b_reg;
|
|||
vol_channel_c_next <= vol_channel_c_reg;
|
|||
1031 | markw | period_envelope_next <= period_envelope_reg;
|
|
1009 | markw | shape_envelope_next <= shape_envelope_reg;
|
|
mixer_noise_next <= mixer_noise_reg;
|
|||
mixer_tone_next <= mixer_tone_reg;
|
|||
io_output_next <= io_output_reg;
|
|||
ioa_next <= ioa_reg;
|
|||
iob_next <= iob_reg;
|
|||
1031 | markw | envelope_count_reset <= '0';
|
|
1009 | markw | ||
if (write_enable='1') then
|
|||
if (addr_decoded(0)='1') then
|
|||
1031 | markw | period_channel_a_next(7 downto 0) <= di;
|
|
1009 | markw | end if;
|
|
if (addr_decoded(1)='1') then
|
|||
1031 | markw | period_channel_a_next(11 downto 8) <= di(3 downto 0);
|
|
1009 | markw | end if;
|
|
if (addr_decoded(2)='1') then
|
|||
1031 | markw | period_channel_b_next(7 downto 0) <= di;
|
|
1009 | markw | end if;
|
|
if (addr_decoded(3)='1') then
|
|||
1031 | markw | period_channel_b_next(11 downto 8) <= di(3 downto 0);
|
|
1009 | markw | end if;
|
|
if (addr_decoded(4)='1') then
|
|||
1031 | markw | period_channel_c_next(7 downto 0) <= di;
|
|
1009 | markw | end if;
|
|
if (addr_decoded(5)='1') then
|
|||
1031 | markw | period_channel_c_next(11 downto 8) <= di(3 downto 0);
|
|
1009 | markw | end if;
|
|
if (addr_decoded(6)='1') then
|
|||
1031 | markw | period_noise_next <= di(4 downto 0);
|
|
1009 | markw | end if;
|
|
if (addr_decoded(7)='1') then
|
|||
io_output_next <= di(7 downto 6);
|
|||
mixer_noise_next <= di(5 downto 3);
|
|||
mixer_tone_next <= di(2 downto 0);
|
|||
end if;
|
|||
if (addr_decoded(8)='1') then
|
|||
vol_channel_a_next <= di(4 downto 0);
|
|||
end if;
|
|||
if (addr_decoded(9)='1') then
|
|||
vol_channel_b_next <= di(4 downto 0);
|
|||
end if;
|
|||
if (addr_decoded(10)='1') then
|
|||
vol_channel_c_next <= di(4 downto 0);
|
|||
end if;
|
|||
if (addr_decoded(11)='1') then
|
|||
1031 | markw | period_envelope_next(7 downto 0) <= di;
|
|
1009 | markw | end if;
|
|
if (addr_decoded(12)='1') then
|
|||
1031 | markw | period_envelope_next(15 downto 8) <= di;
|
|
1009 | markw | end if;
|
|
if (addr_decoded(13)='1') then
|
|||
shape_envelope_next <= di(3 downto 0);
|
|||
1031 | markw | envelope_count_reset <= '1';
|
|
1009 | markw | end if;
|
|
if (addr_decoded(14)='1') then
|
|||
ioa_next <= di;
|
|||
end if;
|
|||
if (addr_decoded(15)='1') then
|
|||
iob_next <= di;
|
|||
end if;
|
|||
end if;
|
|||
end process;
|
|||
process(addr_decoded,
|
|||
1031 | markw | period_channel_a_reg,period_channel_b_reg,period_channel_c_reg,
|
|
period_noise_reg,
|
|||
1009 | markw | vol_channel_a_reg,vol_channel_b_reg,vol_channel_c_reg,
|
|
1031 | markw | period_envelope_reg,
|
|
1009 | markw | shape_envelope_reg,
|
|
mixer_noise_reg,
|
|||
mixer_tone_reg,
|
|||
ioa_in,
|
|||
iob_in,
|
|||
io_output_reg
|
|||
)
|
|||
begin
|
|||
do <= (others=>'0');
|
|||
1042 | markw | if (addr_decoded(0)='1') then
|
|
do <= period_channel_a_reg(7 downto 0);
|
|||
end if;
|
|||
if (addr_decoded(1)='1') then
|
|||
do(3 downto 0) <= period_channel_a_reg(11 downto 8);
|
|||
end if;
|
|||
if (addr_decoded(2)='1') then
|
|||
do <= period_channel_b_reg(7 downto 0);
|
|||
end if;
|
|||
if (addr_decoded(3)='1') then
|
|||
do(3 downto 0) <= period_channel_b_reg(11 downto 8);
|
|||
end if;
|
|||
1009 | markw | ||
1042 | markw | if (addr_decoded(4)='1') then
|
|
do <= period_channel_c_reg(7 downto 0);
|
|||
end if;
|
|||
if (addr_decoded(5)='1') then
|
|||
do(3 downto 0) <= period_channel_c_reg(11 downto 8);
|
|||
end if;
|
|||
if (addr_decoded(6)='1') then
|
|||
do(4 downto 0) <= period_noise_reg;
|
|||
end if;
|
|||
if (addr_decoded(7)='1') then
|
|||
do(7 downto 6) <= io_output_reg;
|
|||
do(5 downto 3) <= mixer_noise_reg;
|
|||
do(2 downto 0) <= mixer_tone_reg;
|
|||
end if;
|
|||
if (addr_decoded(8)='1') then
|
|||
do(4 downto 0) <= vol_channel_a_reg;
|
|||
end if;
|
|||
if (addr_decoded(9)='1') then
|
|||
do(4 downto 0) <= vol_channel_b_reg;
|
|||
end if;
|
|||
if (addr_decoded(10)='1') then
|
|||
do(4 downto 0) <= vol_channel_c_reg;
|
|||
end if;
|
|||
if (addr_decoded(11)='1') then
|
|||
do <= period_envelope_reg(7 downto 0);
|
|||
end if;
|
|||
if (addr_decoded(12)='1') then
|
|||
do <= period_envelope_reg(15 downto 8);
|
|||
end if;
|
|||
1009 | markw | ||
1042 | markw | if (addr_decoded(13)='1') then
|
|
do(3 downto 0) <= shape_envelope_reg;
|
|||
end if;
|
|||
if (addr_decoded(14)='1') then
|
|||
do <= ioa_in;
|
|||
end if;
|
|||
1009 | markw | ||
1042 | markw | if (addr_decoded(15)='1') then
|
|
do <= iob_in;
|
|||
end if;
|
|||
1009 | markw | end process;
|
|
1038 | markw | ||
-- initial divide by 8
|
|||
core_ticker : entity work.PSG_freqdiv
|
|||
GENERIC MAP
|
|||
(
|
|||
bits => 4
|
|||
)
|
|||
PORT MAP
|
|||
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
ENABLE => enable,
|
|||
BIT_OUT => core_tick,
|
|||
THRESHOLD => "1000"
|
|||
);
|
|||
1009 | markw | ||
-- channels A-C, frequency divider
|
|||
1036 | markw | channel_a_ticker : entity work.PSG_freqdiv
|
|
1009 | markw | GENERIC MAP
|
|
(
|
|||
1038 | markw | bits => 12
|
|
1009 | markw | )
|
|
PORT MAP
|
|||
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
1038 | markw | ENABLE => core_tick,
|
|
1009 | markw | ||
BIT_OUT => channel_a_tick,
|
|||
1038 | markw | THRESHOLD => unsigned(period_channel_a_reg)
|
|
1009 | markw | );
|
|
1036 | markw | channel_b_ticker : entity work.PSG_freqdiv
|
|
1009 | markw | GENERIC MAP
|
|
(
|
|||
1038 | markw | bits => 12
|
|
1009 | markw | )
|
|
PORT MAP
|
|||
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
1038 | markw | ENABLE => core_tick,
|
|
1009 | markw | ||
BIT_OUT => channel_b_tick,
|
|||
1038 | markw | THRESHOLD => unsigned(period_channel_b_reg)
|
|
1009 | markw | );
|
|
1036 | markw | channel_c_ticker : entity work.PSG_freqdiv
|
|
1009 | markw | GENERIC MAP
|
|
(
|
|||
1038 | markw | bits => 12
|
|
1009 | markw | )
|
|
PORT MAP
|
|||
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
1038 | markw | ENABLE => core_tick,
|
|
1009 | markw | ||
BIT_OUT => channel_c_tick,
|
|||
1038 | markw | THRESHOLD => unsigned(period_channel_c_reg)
|
|
1009 | markw | );
|
|
-- noise
|
|||
--17-bit LFSR with taps at bits 17 and 14
|
|||
--ref:https://listengine.tuxfamily.org/lists.tuxfamily.org/hatari-devel/2012/09/msg00045.html
|
|||
-- noise freq->noise_tick->noise_val
|
|||
1054 | markw | noise_preticker : entity work.PSG_freqdiv
|
|
GENERIC MAP
|
|||
(
|
|||
1055 | markw | bits => 2
|
|
1054 | markw | )
|
|
PORT MAP
|
|||
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
ENABLE => core_tick,
|
|||
BIT_OUT => core_tick_half,
|
|||
1055 | markw | THRESHOLD => "10"
|
|
1054 | markw | );
|
|
1036 | markw | noise_ticker : entity work.PSG_freqdiv
|
|
1009 | markw | GENERIC MAP
|
|
(
|
|||
1054 | markw | bits => 5
|
|
1009 | markw | )
|
|
PORT MAP
|
|||
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
1054 | markw | ENABLE => core_tick_half,
|
|
1009 | markw | ||
BIT_OUT => noise_tick,
|
|||
1054 | markw | THRESHOLD => unsigned(period_noise_reg)
|
|
1009 | markw | );
|
|
1036 | markw | noise : entity work.PSG_noise
|
|
1009 | markw | PORT MAP
|
|
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
1054 | markw | ENABLE => noise_tick,
|
|
1053 | markw | TICK => noise_tick,
|
|
1009 | markw | ||
1053 | markw | BIT_OUT => noise_val
|
|
1009 | markw | );
|
|
1049 | markw | ||
1009 | markw | -- mix noise and channel
|
|
1036 | markw | mix_a : entity work.PSG_mixer
|
|
1009 | markw | PORT MAP
|
|
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
ENABLE => enable,
|
|||
1053 | markw | NOISE => noise_val,
|
|
1009 | markw | CHANNEL => channel_a_tick,
|
|
NOISE_OFF => mixer_noise_reg(0),
|
|||
TONE_OFF => mixer_tone_reg(0),
|
|||
BIT_OUT => channel_a_val
|
|||
);
|
|||
1036 | markw | mix_b : entity work.PSG_mixer
|
|
1009 | markw | PORT MAP
|
|
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
ENABLE => enable,
|
|||
1053 | markw | NOISE => noise_val,
|
|
1009 | markw | CHANNEL => channel_b_tick,
|
|
NOISE_OFF => mixer_noise_reg(1),
|
|||
TONE_OFF => mixer_tone_reg(1),
|
|||
BIT_OUT => channel_b_val
|
|||
);
|
|||
1036 | markw | mix_c : entity work.PSG_mixer
|
|
1009 | markw | PORT MAP
|
|
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
ENABLE => enable,
|
|||
1053 | markw | NOISE => noise_val,
|
|
1009 | markw | CHANNEL => channel_c_tick,
|
|
NOISE_OFF => mixer_noise_reg(2),
|
|||
TONE_OFF => mixer_tone_reg(2),
|
|||
BIT_OUT => channel_c_val
|
|||
);
|
|||
1031 | markw | ||
-- envelope
|
|||
1036 | markw | envelope : entity work.PSG_envelope
|
|
1031 | markw | PORT MAP
|
|
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
1038 | markw | ENABLE => core_tick,
|
|
1031 | markw | ||
1038 | markw | STEP32 => envelope32,
|
|
1031 | markw | COUNT_RESET => envelope_count_reset,
|
|
1032 | markw | SHAPE => shape_envelope_reg,
|
|
PERIOD => period_envelope_reg,
|
|||
1031 | markw | ||
ENVELOPE => envelope_reg
|
|||
);
|
|||
1009 | markw | ||
-- volume
|
|||
1036 | markw | vol_a : entity work.PSG_volume
|
|
1009 | markw | PORT MAP
|
|
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
ENABLE => enable,
|
|||
CHANNEL => channel_a_val,
|
|||
FIXED => vol_channel_a_reg,
|
|||
ENVELOPE => envelope_reg,
|
|||
1220 | markw | VOL_OUT => channel_a_vol,
|
|
CHANGED => channel_a_changed
|
|||
1009 | markw | );
|
|
1036 | markw | vol_b : entity work.PSG_volume
|
|
1009 | markw | PORT MAP
|
|
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
ENABLE => enable,
|
|||
CHANNEL => channel_b_val,
|
|||
FIXED => vol_channel_b_reg,
|
|||
ENVELOPE => envelope_reg,
|
|||
1220 | markw | VOL_OUT => channel_b_vol,
|
|
CHANGED => channel_b_changed
|
|||
1009 | markw | );
|
|
1036 | markw | vol_c : entity work.PSG_volume
|
|
1009 | markw | PORT MAP
|
|
(
|
|||
CLK => clk,
|
|||
RESET_N => reset_n,
|
|||
ENABLE => enable,
|
|||
CHANNEL => channel_c_val,
|
|||
FIXED => vol_channel_c_reg,
|
|||
ENVELOPE => envelope_reg,
|
|||
1220 | markw | VOL_OUT => channel_c_vol,
|
|
CHANGED => channel_c_changed
|
|||
1009 | markw | );
|
|
-- outputs
|
|||
IOA_OUT <= ioa_reg;
|
|||
IOB_OUT <= iob_reg;
|
|||
IOA_OE <= io_output_reg(0);
|
|||
IOB_OE <= io_output_reg(1);
|
|||
1220 | markw | channel_changed <= channel_a_changed or channel_b_changed or channel_c_changed;
|
|
1009 | markw | ||
end vhdl;
|
|||