--------------------------------------------------------------------------- -- (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_MISC.all; ENTITY PSG_volume_profile IS PORT ( CLK : IN STD_LOGIC; RESET_N : IN STD_LOGIC; CHANNEL_1A : IN STD_LOGIC_VECTOR(4 downto 0); CHANNEL_1B : IN STD_LOGIC_VECTOR(4 downto 0); CHANNEL_1C : IN STD_LOGIC_VECTOR(4 downto 0); CHANNEL_1_CHANGED : IN STD_LOGIC; CHANNEL_2A : IN STD_LOGIC_VECTOR(4 downto 0); CHANNEL_2B : IN STD_LOGIC_VECTOR(4 downto 0); CHANNEL_2C : IN STD_LOGIC_VECTOR(4 downto 0); CHANNEL_2_CHANGED : IN STD_LOGIC; CHANNEL_MASK_1 : IN STD_LOGIC_VECTOR(5 downto 0); --1ABC/2ABC CHANNEL_MASK_2 : IN STD_LOGIC_VECTOR(5 downto 0); AUDIO_OUT_1 : OUT STD_LOGIC_VECTOR(15 downto 0); AUDIO_OUT_2 : OUT STD_LOGIC_VECTOR(15 downto 0); PROFILE_ADDR : OUT std_logic_vector(4 downto 0); PROFILE_REQUEST : OUT std_logic; PROFILE_READY : IN std_logic; PROFILE_DATA : IN std_logic_vector(15 downto 0) ); END PSG_volume_profile; ARCHITECTURE vhdl OF PSG_volume_profile IS signal acc_reg: unsigned(17 downto 0); signal acc_next: unsigned(17 downto 0); signal vol_1_reg: unsigned(15 downto 0); signal vol_1_next: unsigned(15 downto 0); signal vol_2_reg: unsigned(15 downto 0); signal vol_2_next: unsigned(15 downto 0); signal state_reg : std_logic_vector(2 downto 0); signal state_next : std_logic_vector(2 downto 0); constant state_idle : std_logic_vector(2 downto 0) := "000"; constant state_update1_request : std_logic_vector(2 downto 0) := "001"; constant state_update1_wait : std_logic_vector(2 downto 0) := "010"; constant state_update1_done : std_logic_vector(2 downto 0) := "011"; constant state_update2_request : std_logic_vector(2 downto 0) := "100"; constant state_update2_wait : std_logic_vector(2 downto 0) := "101"; constant state_update2_done : std_logic_vector(2 downto 0) := "110"; signal channelsel_reg: std_logic_vector(5 downto 0); signal channelsel_next: std_logic_vector(5 downto 0); signal mask: std_logic_vector(5 downto 0); signal ready : std_logic; signal store : std_logic; signal request : std_logic; signal volume : unsigned(15 downto 0); signal channel_mux : std_logic_vector(4 downto 0); signal outputdev_mux : std_logic; -- function logvolume(x: std_logic_vector(4 downto 0)) return unsigned is -- begin -- case x is -- when "00000" => return "0000000000000000"; -- when "00001" => return "0000000000011100"; -- when "00010" => return "0000000000111101"; -- when "00011" => return "0000000001100110"; -- when "00100" => return "0000000010011001"; -- when "00101" => return "0000000011010111"; -- when "00110" => return "0000000100100011"; -- when "00111" => return "0000000110000000"; -- when "01000" => return "0000000111110001"; -- when "01001" => return "0000001001111101"; -- when "01010" => return "0000001100100111"; -- when "01011" => return "0000001111111000"; -- when "01100" => return "0000010011111000"; -- when "01101" => return "0000011000110001"; -- when "01110" => return "0000011110110001"; -- when "01111" => return "0000100110000111"; -- when "10000" => return "0000101111000111"; -- when "10001" => return "0000111010001000"; -- when "10010" => return "0001000111101000"; -- when "10011" => return "0001011000001010"; -- when "10100" => return "0001101100011001"; -- when "10101" => return "0010000101001100"; -- when "10110" => return "0010100011100011"; -- when "10111" => return "0011001000101111"; -- when "11000" => return "0011110110010010"; -- when "11001" => return "0100101110000100"; -- when "11010" => return "0101110010011000"; -- when "11011" => return "0111000110000011"; -- when "11100" => return "1000101100100001"; -- when "11101" => return "1010101010000001"; -- when "11110" => return "1101000011101111"; -- when "11111" => return "1111111111111111"; -- end case; -- end logvolume; BEGIN -- register process(clk, reset_n) begin if (reset_n = '0') then vol_1_reg <= (others=>'0'); vol_2_reg <= (others=>'0'); channelsel_reg <= "100000"; acc_reg <= (others=>'0'); state_reg <= state_idle; elsif (clk'event and clk='1') then vol_1_reg <= vol_1_next; vol_2_reg <= vol_2_next; channelsel_reg <= channelsel_next; acc_reg <= acc_next; state_reg <= state_next; end if; end process; -- next state -- channel in->compute->result->store->add process(state_reg, acc_reg, channelsel_reg, ready, volume,channel_mask_1,channel_mask_2, mask, channel_1_changed,channel_2_changed) variable start : std_logic; variable inc : std_logic; variable active : std_logic; variable last : std_logic; begin state_next <= state_reg; acc_next <= acc_reg; channelsel_next <= channelsel_reg; outputdev_mux <= '0'; start := '0'; inc := '0'; active := or_reduce(mask and channelsel_reg); last := channelsel_reg(0); request <= '0'; store <= '0'; case state_reg is when state_idle => if ((channel_1_changed or channel_2_changed)='1') then state_next <= state_update1_request; start := '1'; end if; when state_update1_request => request <= active; inc := not(active); if (active='1') then state_next <= state_update1_wait; else if (last='1') then state_next <= state_update1_done; end if; end if; when state_update1_wait => inc := ready; request <= not(ready); if (ready='1') then if (last='1') then state_next <= state_update1_done; else state_next <= state_update1_request; end if; end if; when state_update1_done => state_next <= state_update2_request; store <= '1'; start := '1'; when state_update2_request => outputdev_mux <= '1'; request <= active; inc := not(active); if (active='1') then state_next <= state_update2_wait; else if (last='1') then state_next <= state_update2_done; end if; end if; when state_update2_wait => outputdev_mux <= '1'; inc := ready; request <= not(ready); if (ready='1') then if (last='1') then state_next <= state_update2_done; else state_next <= state_update2_request; end if; end if; when state_update2_done => outputdev_mux <= '1'; state_next <= state_idle; store <= '1'; when others=> state_next <= state_idle; end case; if (start='1') then acc_next <= (others=>'0'); channelsel_next <= "100000"; end if; if (inc='1') then channelsel_next <= "0"&channelsel_reg(5 downto 1); end if; if (ready='1') then acc_next <= acc_reg + resize(volume,17); end if; end process; process(outputdev_mux,vol_1_reg,vol_2_reg,store,acc_reg,channel_mask_1,channel_mask_2) begin mask <= (others=>'0'); vol_1_next <= vol_1_reg; vol_2_next <= vol_2_reg; if (outputdev_mux='1') then mask <= channel_mask_1; if (store='1') then vol_1_next <= acc_reg(17 downto 2); end if; else mask <= channel_mask_2; if (store='1') then vol_2_next <= acc_reg(17 downto 2); end if; end if; end process; process(channelsel_reg,channel_1a,channel_1b,channel_1c,channel_2a,channel_2b,channel_2c) begin channel_mux <= (others=>'0'); case channelsel_reg is when "100000" => channel_mux <= channel_1a; when "010000" => channel_mux <= channel_1b; when "001000" => channel_mux <= channel_1c; when "000100" => channel_mux <= channel_2a; when "000010" => channel_mux <= channel_2b; when "000001" => channel_mux <= channel_2c; when others=> end case; end process; -- there is this vol table array recorded from the device, best to make use of that I think once I have flash support working -- for now, lets use log of each channel then sum -- + I'd like to review that table in octave to understand what is going on... -- from octave based on datasheet only: sqrt(2)^i/sqrt(2)^15; volume <= unsigned(profile_data); ready <= profile_ready; -- output AUDIO_OUT_1 <= STD_LOGIC_VECTOR(vol_1_reg); AUDIO_OUT_2 <= STD_LOGIC_VECTOR(vol_2_reg); PROFILE_ADDR <= channel_mux; PROFILE_REQUEST <= request; END vhdl;