Apple1_MiST/rtl/sid/adsr_multi.vhd

221 lines
7.3 KiB
VHDL

-------------------------------------------------------------------------------
--
-- (C) COPYRIGHT 2010 Gideon's Logic Architectures'
--
-------------------------------------------------------------------------------
--
-- Author: Gideon Zweijtzer (gideon.zweijtzer (at) gmail.com)
--
-- Note that this file is copyrighted, and is not supposed to be used in other
-- projects without written permission from the author.
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.sid_debug_pkg.all;
-- LUT: 195, FF:68
entity adsr_multi is
generic (
g_num_voices : integer := 8 );
port (
clock : in std_logic;
reset : in std_logic;
voice_i : in unsigned(3 downto 0);
enable_i : in std_logic;
voice_o : out unsigned(3 downto 0);
enable_o : out std_logic;
gate : in std_logic;
attack : in std_logic_vector(3 downto 0);
decay : in std_logic_vector(3 downto 0);
sustain : in std_logic_vector(3 downto 0);
release : in std_logic_vector(3 downto 0);
env_state: out std_logic_vector(1 downto 0); -- for testing only
env_out : out unsigned(7 downto 0) );
end adsr_multi;
-- 158 1 62 .. FF
-- 45 2 35 .. 61
-- 26 4 1C .. 34
-- 13 8 0D .. 1B
-- 6 16 07 .. 0C
-- 7 30 00 .. 06
architecture gideon of adsr_multi is
type presc_array_t is array(natural range <>) of unsigned(15 downto 0);
constant prescalers : presc_array_t(0 to 15) := (
X"0008", X"001F", X"003E", X"005E",
X"0094", X"00DB", X"010A", X"0138",
X"0187", X"03D0", X"07A1", X"0C35",
X"0F42", X"2DC7", X"4C4B", X"7A12" );
signal enveloppe : unsigned(7 downto 0) := (others => '0');
signal state : unsigned(1 downto 0) := (others => '0');
constant st_release : unsigned(1 downto 0) := "00";
constant st_attack : unsigned(1 downto 0) := "01";
constant st_decay : unsigned(1 downto 0) := "11";
type state_array_t is array(natural range <>) of unsigned(29 downto 0);
signal state_array : state_array_t(0 to g_num_voices-1) := (others => (others => '0'));
begin
env_out <= enveloppe;
env_state <= std_logic_vector(state);
-- FF-5E 01
-- 5D-37 02
-- 36-1B 04
-- 1A-0F 08
-- 0E-07 10
-- 06-01 1E
process(clock)
function logarithmic(lev: unsigned(7 downto 0)) return unsigned is
variable res : unsigned(4 downto 0);
begin
if lev = X"00" then
res := "00000"; -- prescaler off
elsif lev < X"07" then
res := "11101"; -- 1E-1
elsif lev < X"0F" then
res := "01111"; -- 10-1
elsif lev < X"1B" then
res := "00111"; -- 08-1
elsif lev < X"37" then
res := "00011"; -- 04-1
elsif lev < X"5E" then
res := "00001"; -- 02-1
else
res := "00000"; -- 01-1
end if;
return res;
end function logarithmic;
variable presc_select : integer range 0 to 15;
variable cur_state : unsigned(1 downto 0);
variable cur_env : unsigned(7 downto 0);
variable cur_pre15 : unsigned(14 downto 0);
variable cur_pre5 : unsigned(4 downto 0);
variable next_state : unsigned(1 downto 0);
variable next_env : unsigned(7 downto 0);
variable next_pre15 : unsigned(14 downto 0);
variable next_pre5 : unsigned(4 downto 0);
variable presc_val : unsigned(14 downto 0);
variable log_div : unsigned(4 downto 0);
variable do_count_15 : std_logic;
variable do_count_5 : std_logic;
begin
if rising_edge(clock) then
cur_state := state_array(0)(1 downto 0);
cur_env := state_array(0)(9 downto 2);
cur_pre15 := state_array(0)(24 downto 10);
cur_pre5 := state_array(0)(29 downto 25);
voice_o <= voice_i;
enable_o <= enable_i;
next_state := cur_state;
next_env := cur_env;
next_pre15 := cur_pre15;
next_pre5 := cur_pre5;
-- PRESCALER LOGIC, output: do_count --
-- 15 bit prescaler select --
case cur_state is
when st_attack =>
presc_select := to_integer(unsigned(attack));
when st_decay =>
presc_select := to_integer(unsigned(decay));
when others => -- includes release and idle
presc_select := to_integer(unsigned(release));
end case;
presc_val := prescalers(presc_select)(14 downto 0);
-- 15 bit prescaler counter --
do_count_15 := '0';
if cur_pre15 = presc_val then
next_pre15 := (others => '0');
do_count_15 := '1';
else
next_pre15 := cur_pre15 + 1;
end if;
-- 5 bit prescaler --
log_div := logarithmic(cur_env);
do_count_5 := '0';
if do_count_15='1' then
if (cur_state = st_attack) or cur_pre5 = log_div then
next_pre5 := "00000";
do_count_5 := '1';
else
next_pre5 := cur_pre5 + 1;
end if;
end if;
-- END PRESCALER LOGIC --
case cur_state is
when st_attack =>
if gate = '0' then
next_state := st_release;
elsif cur_env = X"FF" then
next_state := st_decay;
end if;
if do_count_15='1' then
next_env := cur_env + 1;
-- if cur_env = X"FE" or cur_env = X"FF" then -- result could be FF, but also 00!!
-- next_state := st_decay;
-- end if;
end if;
when st_decay =>
if gate = '0' then
next_state := st_release;
end if;
if do_count_15='1' and do_count_5='1' and
std_logic_vector(cur_env) /= (sustain & sustain) and
cur_env /= X"00" then
next_env := cur_env - 1;
end if;
when st_release =>
if gate = '1' then
next_state := st_attack;
end if;
if do_count_15='1' and do_count_5='1' and
cur_env /= X"00" then
next_env := cur_env - 1;
end if;
when others =>
next_state := st_release;
end case;
if enable_i='1' then
state_array(0 to g_num_voices-2) <= state_array(1 to g_num_voices-1);
state_array(g_num_voices-1) <= next_pre5 & next_pre15 & next_env & next_state;
enveloppe <= next_env;
state <= next_state;
end if;
if reset='1' then
state <= "00";
enveloppe <= (others => '0');
enable_o <= '0';
end if;
end if;
end process;
end gideon;