Project

General

Profile

---------------------------------------------------------------------------
-- (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;

ENTITY sample_top IS
PORT
(
CLK : in std_logic;
RESET_N : in std_logic;

ENABLE : in std_logic; -- end of cycle
REQUEST : in std_logic; -- read request, provide data next cycle
WRITE_ENABLE : in std_logic;
ADDR : in std_logic_vector(4 downto 0);
DI : in std_logic_vector(7 downto 0);

DO : out std_logic_vector(7 downto 0);
AUDIO0 : out std_logic_vector(15 downto 0);
AUDIO1 : out std_logic_vector(15 downto 0);
IRQ : out std_logic;

RAM_ADDR : out std_logic_vector(15 downto 0);
RAM_WRITE_ENABLE : out std_logic;
RAM_DATA : in std_logic_vector(7 downto 0); -- next cycle: TODO, what if we use rom?

ADPCM_STEP_ADDR : out std_logic_vector(6 downto 0);
ADPCM_STEP_REQUEST : out std_logic;
ADPCM_STEP_READY : in std_logic;
ADPCM_STEP_VALUE : in std_logic_vector(14 downto 0)
);
END sample_top;
ARCHITECTURE vhdl OF sample_top IS
signal CH1_REG : std_logic_vector(12 downto 0);
signal CH0_REG : std_logic_vector(12 downto 0);
signal CH1_NEXT : std_logic_vector(12 downto 0);
signal CH0_NEXT : std_logic_vector(12 downto 0);
signal CH3_REG : std_logic_vector(12 downto 0);
signal CH2_REG : std_logic_vector(12 downto 0);
signal CH3_NEXT : std_logic_vector(12 downto 0);
signal CH2_NEXT : std_logic_vector(12 downto 0);

signal ram_cpu_addr_next : std_logic_vector(15 downto 0);
signal ram_cpu_addr_reg : std_logic_vector(15 downto 0);
signal ram_cpu_write_enable : std_logic;

signal ch0_start_addr_reg : std_logic_vector(15 downto 0);
signal ch0_start_addr_next : std_logic_vector(15 downto 0);
signal ch0_len_reg : std_logic_vector(15 downto 0);
signal ch0_len_next : std_logic_vector(15 downto 0);
signal ch0_period_reg : std_logic_vector(11 downto 0);
signal ch0_period_next : std_logic_vector(11 downto 0);
signal ch0_volume_reg : std_logic_vector(5 downto 0);
signal ch0_volume_next : std_logic_vector(5 downto 0);

signal ch1_start_addr_reg : std_logic_vector(15 downto 0);
signal ch1_start_addr_next : std_logic_vector(15 downto 0);
signal ch1_len_reg : std_logic_vector(15 downto 0);
signal ch1_len_next : std_logic_vector(15 downto 0);
signal ch1_period_reg : std_logic_vector(11 downto 0);
signal ch1_period_next : std_logic_vector(11 downto 0);
signal ch1_volume_reg : std_logic_vector(5 downto 0);
signal ch1_volume_next : std_logic_vector(5 downto 0);

signal ch2_start_addr_reg : std_logic_vector(15 downto 0);
signal ch2_start_addr_next : std_logic_vector(15 downto 0);
signal ch2_len_reg : std_logic_vector(15 downto 0);
signal ch2_len_next : std_logic_vector(15 downto 0);
signal ch2_period_reg : std_logic_vector(11 downto 0);
signal ch2_period_next : std_logic_vector(11 downto 0);
signal ch2_volume_reg : std_logic_vector(5 downto 0);
signal ch2_volume_next : std_logic_vector(5 downto 0);

signal ch3_start_addr_reg : std_logic_vector(15 downto 0);
signal ch3_start_addr_next : std_logic_vector(15 downto 0);
signal ch3_len_reg : std_logic_vector(15 downto 0);
signal ch3_len_next : std_logic_vector(15 downto 0);
signal ch3_period_reg : std_logic_vector(11 downto 0);
signal ch3_period_next : std_logic_vector(11 downto 0);
signal ch3_volume_reg : std_logic_vector(5 downto 0);
signal ch3_volume_next : std_logic_vector(5 downto 0);
signal dma_on_reg : std_logic_vector(3 downto 0);
signal dma_on_next : std_logic_vector(3 downto 0);
signal dma_on : std_logic;
signal channel_reg : std_logic_vector(2 downto 0);
signal channel_next : std_logic_vector(2 downto 0);
signal ch0_addr : std_logic_vector(16 downto 0);
signal ch1_addr : std_logic_vector(16 downto 0);
signal ch2_addr : std_logic_vector(16 downto 0);
signal ch3_addr : std_logic_vector(16 downto 0);

signal irq_en_reg : std_logic_vector(3 downto 0);
signal irq_en_next : std_logic_vector(3 downto 0);
signal irq_trigger : std_logic_vector(3 downto 0);
signal data_request : std_logic_vector(3 downto 0);
signal irq_clear_n : std_logic_vector(3 downto 0);
signal irq_active_reg : std_logic_vector(3 downto 0);
signal irq_active_next : std_logic_vector(3 downto 0);

signal adpcm_decoded : std_logic_vector(15 downto 0);
signal adpcm_reg : std_logic_vector(3 downto 0);
signal adpcm_next : std_logic_vector(3 downto 0);
signal adpcm_data_request : std_logic;
signal adpcm_data_ready_next : std_logic;
signal adpcm_data_ready_reg : std_logic;
signal adpcm_data_in : std_logic_vector(3 downto 0);
signal adpcm_on : std_logic;
signal adpcm_channel : std_logic_vector(1 downto 0);
signal adpcm_store : std_logic;
signal adpcm_step_request_raw : std_logic;
signal adpcm_step_ready_adj : std_logic;

signal bits8_reg : std_logic_vector(3 downto 0);
signal bits8_next : std_logic_vector(3 downto 0);
signal bits8 : std_logic;

signal addr_decoded5 : std_logic_vector(31 downto 0);

signal data_nibble : std_logic;

signal store_data : std_logic_vector(12 downto 0);
signal store_source : std_logic_vector(3 downto 0);
signal store_channel : std_logic_vector(1 downto 0);
signal store : std_logic;

BEGIN

decode_addr2 : entity work.complete_address_decoder
generic map(width=>5)
port map (addr_in=>ADDR(4 downto 0), addr_decoded=>addr_decoded5);

process(addr_decoded5,CH0_REG,CH1_REG,CH2_REG,CH3_REG,
ram_cpu_addr_reg, ram_data,
irq_en_reg,irq_active_reg
)
begin
DO <= (others=>'0');
if (addr_decoded5(0)='1') then
DO <= CH0_REG(12 downto 5);
end if;
if (addr_decoded5(1)='1') then
DO <= CH1_REG(12 downto 5);
end if;
if (addr_decoded5(2)='1') then
DO <= CH2_REG(12 downto 5);
end if;
if (addr_decoded5(3)='1') then
DO <= CH3_REG(12 downto 5);
end if;
if (addr_decoded5(4)='1') then
DO <= ram_cpu_addr_reg(7 downto 0);
end if;
if (addr_decoded5(5)='1') then
DO <= ram_cpu_addr_reg(15 downto 8);
end if;
if (addr_decoded5(6)='1') then --manual addr inc
DO <= ram_data;
end if;
if (addr_decoded5(17)='1') then
DO(3 downto 0) <= irq_en_reg;
end if;
if (addr_decoded5(18)='1') then
DO(3 downto 0) <= irq_active_reg;
end if;
end process;

process(adpcm_channel,adpcm_store,addr,bits8,dma_on,adpcm_on,write_enable)
begin
store <= '0';
store_channel <= (others=>'0');
store_source <= (others=>'0');

if (write_enable='0' and dma_on='1') then
store_channel <= adpcm_channel;
store <= adpcm_store;
elsif (write_enable='1') then
store_channel <= ADDR(1 downto 0);
store <= not(or_reduce(ADDR(4 downto 2)));
end if;
store_source(3) <= bits8;
store_source(2) <= dma_on;
store_source(1) <= adpcm_on;
store_source(0) <= write_enable;
end process;

process(store_source,data_nibble,
di,adpcm_decoded,ram_data)
begin
store_data <= (others=>'0');
case store_source is
when "0001"|"0011"|"0101"|"0111"|"1001"|"1011"|"1101"|"1111" =>
store_data(12 downto 5) <= di;
when "0110"|"1110" =>
store_data <= adpcm_decoded(15 downto 3);
when "1100" =>
store_data(12) <= not(ram_data(7));
store_data(11 downto 5) <= ram_data(6 downto 0);
when "0100" =>
if (data_nibble='0') then
store_data(12) <= not(ram_data(7));
store_data(11 downto 9) <= ram_data(6 downto 4);
else
store_data(12) <= not(ram_data(3));
store_data(11 downto 9) <= ram_data(2 downto 0);
end if;

when others=>
end case;
end process;

process( CH0_REG,CH1_REG,CH2_REG,CH3_REG,DI,
store,store_data,store_channel
)
begin
CH0_NEXT <= CH0_REG;
CH1_NEXT <= CH1_REG;
CH2_NEXT <= CH2_REG;
CH3_NEXT <= CH3_REG;
if (store='1') then
case store_channel is
when "00"=>
CH0_NEXT <= store_data;
when "01" =>
CH1_NEXT <= store_data;
when "10" =>
CH2_NEXT <= store_data;
when "11" =>
CH3_NEXT <= store_data;
when others =>
end case;
end if;
end process;
adpcm_decoder : entity work.sample_adpcm
port map
(
clk=>clk,
reset_n=>reset_n,
syncreset=>irq_trigger,

select_channel=>adpcm_channel,

store=>adpcm_store,

data_out=>adpcm_decoded,

dirty=>data_request,

data_request => adpcm_data_request,
data_ready => adpcm_data_ready_reg,
data_in => adpcm_data_in,

step_addr => adpcm_step_addr,
step_request => adpcm_step_request_raw,
step_ready => adpcm_step_ready_adj,
step_value => adpcm_step_value
);
--data_in=>ram_data,
--update=>data_request,
--fetch=>dma,
--data_nibble=>ch3_addr(0)&ch2_addr(0)&ch1_addr(0)&ch0_addr(0),
-- TODO -> feed in data slower and each nibble
adpcm_data_in <= ram_data(7 downto 4) when data_nibble='0' else ram_data(3 downto 0);

adpcm_step_request <= adpcm_on and dma_on and adpcm_step_request_raw;
adpcm_step_ready_adj <= (not(adpcm_on and dma_on) and adpcm_step_request_raw) or adpcm_step_ready;
process(ADDR, addr_decoded5, WRITE_ENABLE, DI,
ram_cpu_addr_reg,
ch0_start_addr_reg, ch0_len_reg, ch0_period_reg, ch0_volume_reg,
ch1_start_addr_reg, ch1_len_reg, ch1_period_reg, ch1_volume_reg,
ch2_start_addr_reg, ch2_len_reg, ch2_period_reg, ch2_volume_reg,
ch3_start_addr_reg, ch3_len_reg, ch3_period_reg, ch3_volume_reg,
dma_on_reg,dma_on,ram_data,
channel_reg,
irq_en_reg,irq_active_reg,irq_trigger,irq_clear_n,
adpcm_reg, bits8_reg
)
begin
ram_cpu_write_enable <= '0';
ram_cpu_addr_next <= ram_cpu_addr_reg;
ch0_start_addr_next <= ch0_start_addr_reg;
ch0_len_next <= ch0_len_reg;
ch0_period_next <= ch0_period_reg;
ch0_volume_next <= ch0_volume_reg;
ch1_start_addr_next <= ch1_start_addr_reg;
ch1_len_next <= ch1_len_reg;
ch1_period_next <= ch1_period_reg;
ch1_volume_next <= ch1_volume_reg;
ch2_start_addr_next <= ch2_start_addr_reg;
ch2_len_next <= ch2_len_reg;
ch2_period_next <= ch2_period_reg;
ch2_volume_next <= ch2_volume_reg;
ch3_start_addr_next <= ch3_start_addr_reg;
ch3_len_next <= ch3_len_reg;
ch3_period_next <= ch3_period_reg;
ch3_volume_next <= ch3_volume_reg;
dma_on_next <= dma_on_reg;
bits8_next <= bits8_reg;
channel_next <= channel_reg;
irq_clear_n <= (others=>'1');
irq_en_next <= irq_en_reg;
irq_active_next <= (irq_active_reg or irq_trigger) and irq_en_reg and irq_clear_n;
adpcm_next <= adpcm_reg;

if (write_enable='1') then
if (addr_decoded5(4)='1') then
ram_cpu_addr_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(5)='1') then
ram_cpu_addr_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(6)='1') then --manual addr inc
ram_cpu_write_enable <= '1';
end if;
if (addr_decoded5(7)='1') then --auto addr inc
ram_cpu_write_enable <= '1';
ram_cpu_addr_next <= ram_cpu_addr_reg + 1;
end if;

if (addr_decoded5(8)='1') then
channel_next(2 downto 0) <= DI(2 downto 0);
end if;

case channel_reg is
when "001" =>
if (addr_decoded5(9)='1') then
ch0_start_addr_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(10)='1') then
ch0_start_addr_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(11)='1') then
ch0_len_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(12)='1') then
ch0_len_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(13)='1') then
ch0_period_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(14)='1') then
ch0_period_next(11 downto 8) <= DI(3 downto 0);
end if;
if (addr_decoded5(15)='1') then
ch0_volume_next(5 downto 0) <= DI(5 downto 0);
end if;
when "010" =>
if (addr_decoded5(9)='1') then
ch1_start_addr_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(10)='1') then
ch1_start_addr_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(11)='1') then
ch1_len_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(12)='1') then
ch1_len_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(13)='1') then
ch1_period_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(14)='1') then
ch1_period_next(11 downto 8) <= DI(3 downto 0);
end if;
if (addr_decoded5(15)='1') then
ch1_volume_next(5 downto 0) <= DI(5 downto 0);
end if;
when "011" =>
if (addr_decoded5(9)='1') then
ch2_start_addr_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(10)='1') then
ch2_start_addr_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(11)='1') then
ch2_len_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(12)='1') then
ch2_len_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(13)='1') then
ch2_period_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(14)='1') then
ch2_period_next(11 downto 8) <= DI(3 downto 0);
end if;
if (addr_decoded5(15)='1') then
ch2_volume_next(5 downto 0) <= DI(5 downto 0);
end if;
when "100" =>
if (addr_decoded5(9)='1') then
ch3_start_addr_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(10)='1') then
ch3_start_addr_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(11)='1') then
ch3_len_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(12)='1') then
ch3_len_next(15 downto 8) <= DI;
end if;
if (addr_decoded5(13)='1') then
ch3_period_next(7 downto 0) <= DI;
end if;
if (addr_decoded5(14)='1') then
ch3_period_next(11 downto 8) <= DI(3 downto 0);
end if;
if (addr_decoded5(15)='1') then
ch3_volume_next(5 downto 0) <= DI(5 downto 0);
end if;
when others =>
end case;
if (addr_decoded5(16)='1') then
dma_on_next <= DI(3 downto 0);
end if;
if (addr_decoded5(17)='1') then
irq_en_next <= DI(3 downto 0);
end if;
if (addr_decoded5(18)='1') then
irq_clear_n <= DI(3 downto 0); --write 0 to disable
end if;
if (addr_decoded5(19)='1') then
adpcm_next <= DI(3 downto 0);
bits8_next <= DI(7 downto 4);
end if;
end if;
end process;
ch0_inst: entity work.sample_channel
PORT MAP
(
CLK => CLK,
RESET_N => RESET_N,
ENABLE => ENABLE,
syncreset => (dma_on_next(0) xor dma_on_reg(0)),
start_addr => ch0_start_addr_reg,
len => ch0_len_reg,
period => ch0_period_reg,
twocycles => adpcm_reg(0) or not(bits8_reg(0)),
addr => ch0_addr,
irq => irq_trigger(0),
req => data_request(0)
);
ch1_inst: entity work.sample_channel
PORT MAP
(
CLK => CLK,
RESET_N => RESET_N,
ENABLE => ENABLE,
syncreset => (dma_on_next(1) xor dma_on_reg(1)),
start_addr => ch1_start_addr_reg,
len => ch1_len_reg,
period => ch1_period_reg,
twocycles => adpcm_reg(1) or not(bits8_reg(1)),
addr => ch1_addr,
irq => irq_trigger(1),
req => data_request(1)
);
ch2_inst: entity work.sample_channel
PORT MAP
(
CLK => CLK,
RESET_N => RESET_N,
ENABLE => ENABLE,
syncreset => (dma_on_next(2) xor dma_on_reg(2)),
start_addr => ch2_start_addr_reg,
len => ch2_len_reg,
period => ch2_period_reg,
twocycles => adpcm_reg(2) or not(bits8_reg(2)),
addr => ch2_addr,
irq => irq_trigger(2),
req => data_request(2)
);
ch3_inst: entity work.sample_channel
PORT MAP
(
CLK => CLK,
RESET_N => RESET_N,
ENABLE => ENABLE,
syncreset => (dma_on_next(3) xor dma_on_reg(3)),
start_addr => ch3_start_addr_reg,
len => ch3_len_reg,
period => ch3_period_reg,
twocycles => adpcm_reg(3) or not(bits8_reg(3)),
addr => ch3_addr,
irq => irq_trigger(3),
req => data_request(3)
);
process (ch0_reg,ch1_reg,ch2_reg,ch3_reg,
ch0_volume_reg,ch1_volume_reg,ch2_volume_reg,ch3_volume_reg)
variable l : unsigned(26 downto 0);
variable r : unsigned(26 downto 0);
begin
l := resize(unsigned(CH0_REG),18)*resize(unsigned(ch0_volume_reg),9);
l := l + resize(unsigned(CH3_REG),18)*resize(unsigned(ch3_volume_reg),9);
r := resize(unsigned(CH1_REG),18)*resize(unsigned(ch1_volume_reg),9);
r := r + resize(unsigned(CH2_REG),18)*resize(unsigned(ch2_volume_reg),9);
-- TODO: probably need to register here?
AUDIO0 <= std_logic_vector(l(19 downto 4));
AUDIO1 <= std_logic_vector(r(19 downto 4));
-- TODO: modulation?
-- TODO: samples from rom and put in voice samples after core?
-- TODO: 4 bit mode?
-- options to set: per channel: modulate volume(4),modulate period(4),sample bits(4)
end process;
process(ch0_addr,ch1_addr,ch2_addr,ch3_addr,
ram_cpu_addr_reg,
adpcm_channel,
request,
dma_on_reg,
adpcm_reg,
bits8_reg,
adpcm_data_request)
begin
ram_addr <= (others=>'0');
data_nibble <= '0';
adpcm_on <= '0';
dma_on <= '0';
bits8 <= '0';

adpcm_data_ready_next <= adpcm_data_request;

case adpcm_channel is
when "00" =>
ram_addr <= ch0_addr(16 downto 1);
data_nibble <= ch0_addr(0);
adpcm_on <= adpcm_reg(0);
dma_on <= dma_on_reg(0);
bits8 <= bits8_reg(0);
when "01" =>
ram_addr <= ch1_addr(16 downto 1);
data_nibble <= ch1_addr(0);
adpcm_on <= adpcm_reg(1);
dma_on <= dma_on_reg(1);
bits8 <= bits8_reg(1);
when "10" =>
ram_addr <= ch2_addr(16 downto 1);
data_nibble <= ch2_addr(0);
adpcm_on <= adpcm_reg(2);
dma_on <= dma_on_reg(2);
bits8 <= bits8_reg(2);
when "11" =>
ram_addr <= ch3_addr(16 downto 1);
data_nibble <= ch3_addr(0);
adpcm_on <= adpcm_reg(3);
dma_on <= dma_on_reg(3);
bits8 <= bits8_reg(3);
when others =>
end case;
if (request='1') then
ram_addr <= ram_cpu_addr_reg;
adpcm_data_ready_next <= '0';
end if;
end process;
process(clk,reset_n)
begin
if (reset_n='0') then
CH0_REG <= (others=>'0');
CH1_REG <= (others=>'0');
CH2_REG <= (others=>'0');
CH3_REG <= (others=>'0');
ram_cpu_addr_reg <= (others=>'0');
ch0_start_addr_reg <= (others=>'0');
ch0_len_reg <= (others=>'0');
ch0_period_reg <= (others=>'0');
ch0_volume_reg <= (others=>'1');
ch1_start_addr_reg <= (others=>'0');
ch1_len_reg <= (others=>'0');
ch1_period_reg <= (others=>'0');
ch1_volume_reg <= (others=>'1');
ch2_start_addr_reg <= (others=>'0');
ch2_len_reg <= (others=>'0');
ch2_period_reg <= (others=>'0');
ch2_volume_reg <= (others=>'1');
ch3_start_addr_reg <= (others=>'0');
ch3_len_reg <= (others=>'0');
ch3_period_reg <= (others=>'0');
ch3_volume_reg <= (others=>'1');
dma_on_reg <= (others=>'0');
irq_en_reg <= (others=>'0');
irq_active_reg <= (others=>'0');
channel_reg <= (others=>'0');
adpcm_reg <= (others=>'0');
adpcm_data_ready_reg <= '0';

bits8_reg <= (others=>'1');
elsif (clk'event and clk='1') then
CH0_REG <= CH0_NEXT;
CH1_REG <= CH1_NEXT;
CH2_REG <= CH2_NEXT;
CH3_REG <= CH3_NEXT;
ram_cpu_addr_reg <= ram_cpu_addr_next;
ch0_start_addr_reg <= ch0_start_addr_next;
ch0_len_reg <= ch0_len_next;
ch0_period_reg <= ch0_period_next;
ch0_volume_reg <= ch0_volume_next;
ch1_start_addr_reg <= ch1_start_addr_next;
ch1_len_reg <= ch1_len_next;
ch1_period_reg <= ch1_period_next;
ch1_volume_reg <= ch1_volume_next;
ch2_start_addr_reg <= ch2_start_addr_next;
ch2_len_reg <= ch2_len_next;
ch2_period_reg <= ch2_period_next;
ch2_volume_reg <= ch2_volume_next;
ch3_start_addr_reg <= ch3_start_addr_next;
ch3_len_reg <= ch3_len_next;
ch3_period_reg <= ch3_period_next;
ch3_volume_reg <= ch3_volume_next;
dma_on_reg <= dma_on_next;
irq_en_reg <= irq_en_next;
irq_active_reg <= irq_active_next;
channel_reg <= channel_next;
adpcm_reg <= adpcm_next;
adpcm_data_ready_reg <= adpcm_data_ready_next;

bits8_reg <= bits8_next;
end if;
end process;
IRQ <= or_reduce(irq_active_reg);

RAM_WRITE_ENABLE <= RAM_CPU_WRITE_ENABLE;
END vhdl;
(3-3/3)