Project

General

Profile

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;