repo2/atari_chips/pokeyv2/SID/wavegen.vhdl @ 1470
| 1056 | 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.
  | 
      |||
---------------------------------------------------------------------------
  | 
      |||
--See: https://sourceforge.net/p/sidplay-residfp/wiki/SID%20internals/
  | 
      |||
LIBRARY ieee;
  | 
      |||
USE ieee.std_logic_1164.all;
  | 
      |||
use ieee.numeric_std.all;
  | 
      |||
| 1062 | markw | use IEEE.STD_LOGIC_MISC.all;
  | 
      |
| 1056 | markw | ||
ENTITY SID_wavegen IS
  | 
      |||
PORT
  | 
      |||
(
  | 
      |||
	CLK : IN STD_LOGIC;
  | 
      |||
	RESET_N : IN STD_LOGIC;
  | 
      |||
| 1284 | markw | 	ENABLE : IN STD_LOGIC;
  | 
      |
| 1213 | markw | ||
	CHANGING : IN STD_LOGIC;
  | 
      |||
| 1056 | markw | ||
| 1324 | markw | 	DELAYSAWTOOTH : IN STD_LOGIC;
  | 
      |
| 1056 | markw | 	RINGMOD : IN STD_LOGIC;
  | 
      |
	RINGMOD_OSC_MSB : IN STD_LOGIC;
  | 
      |||
	TEST : IN STD_LOGIC;
  | 
      |||
	LFSR_ENABLE : IN STD_LOGIC;
  | 
      |||
	OSC_IN : IN STD_LOGIC_VECTOR(11 downto 0);
  | 
      |||
	PULSE_WIDTH_IN : IN STD_LOGIC_VECTOR(11 downto 0);
  | 
      |||
	WAVESELECT_IN : IN STD_LOGIC_VECTOR(3 downto 0);
  | 
      |||
| 1213 | markw | 	WAVE_DATA_NEEDED : OUT STD_LOGIC;
  | 
      |
	WAVE_DATA_READY : IN STD_LOGIC;
  | 
      |||
	WAVE_DATA : IN STD_LOGIC_VECTOR(11 downto 0);
  | 
      |||
| 1062 | markw | 	WAVE_OUT : OUT STD_LOGIC_VECTOR(11 downto 0)
  | 
      |
);
  | 
      |||
| 1056 | markw | END SID_wavegen;
  | 
      |
ARCHITECTURE vhdl OF SID_wavegen IS
  | 
      |||
| 1062 | markw | 	signal wave_reg : std_logic_vector(11 downto 0);
  | 
      |
	signal wave_next : std_logic_vector(11 downto 0);
  | 
      |||
| 1324 | markw | 	signal wave0count1_reg : unsigned(9 downto 0);
  | 
      |
	signal wave0count1_next : unsigned(9 downto 0);
  | 
      |||
| 1062 | markw | 	signal lfsr_reg : std_logic_vector(22 downto 0);
  | 
      |
	signal lfsr_next : std_logic_vector(22 downto 0);
  | 
      |||
| 1213 | markw | 	signal wave_data_needed_reg : std_logic;
  | 
      |
	signal wave_data_needed_next : std_logic;
  | 
      |||
| 1071 | markw | 	signal pulse_comparator_reg : std_logic;
  | 
      |
	signal pulse_comparator_next : std_logic;
  | 
      |||
| 1213 | markw | 	signal multiple_wave_bits : std_logic;
  | 
      |
| 1324 | markw | 	signal no_wave_bits : std_logic;
  | 
      |
	signal osc_del_reg : std_logic_vector(11 downto 0);
  | 
      |||
	signal osc_del_next : std_logic_vector(11 downto 0);
  | 
      |||
| 1056 | markw | BEGIN
  | 
      |
	-- register
  | 
      |||
	process(clk, reset_n)
  | 
      |||
	begin
  | 
      |||
		if (reset_n = '0') then
  | 
      |||
			wave_reg <= (others=>'0');
  | 
      |||
| 1324 | markw | 			wave0count1_reg <= (others=>'0');
  | 
      |
| 1056 | markw | 			lfsr_reg <= (others=>'1');
  | 
      |
| 1071 | markw | 			pulse_comparator_reg <= '0';
  | 
      |
| 1213 | markw | 			wave_data_needed_reg <= '0';
  | 
      |
| 1324 | markw | 			osc_del_reg <= (others=>'0');
  | 
      |
| 1056 | markw | 		elsif (clk'event and clk='1') then
  | 
      |
			wave_reg <= wave_next;
  | 
      |||
| 1324 | markw | 			wave0count1_reg <= wave0count1_next;
  | 
      |
| 1056 | markw | 			lfsr_reg <= lfsr_next;
  | 
      |
| 1071 | markw | 			pulse_comparator_reg <= pulse_comparator_next;
  | 
      |
| 1213 | markw | 			wave_data_needed_reg <= wave_data_needed_next;
  | 
      |
| 1324 | markw | 			osc_del_reg <= osc_del_next;
  | 
      |
| 1056 | markw | 		end if;
  | 
      |
	end process;
  | 
      |||
	-- next state - lfsr
  | 
      |||
	--23 bit lfsr, bit0=bit22 xor bit17
  | 
      |||
	-- if noise and anything else then lfsr feedback is 0, test bit can refill it
  | 
      |||
	--outputs:  20, 18, 14, 11, 9, 5, 2 and 0
  | 
      |||
	--updated on bit 19 0->1 transition
  | 
      |||
	--ref(decap):https://sourceforge.net/p/sidplay-residfp/wiki/SID%20internals%20-%20Noise%20Generator/
  | 
      |||
	process(lfsr_reg,lfsr_enable,waveselect_in,test)
  | 
      |||
		variable noise_and_others : std_logic;
  | 
      |||
	begin
  | 
      |||
		lfsr_next <= lfsr_reg;
  | 
      |||
		if (lfsr_enable='1') then
  | 
      |||
			noise_and_others := or_reduce(waveselect_in(2 downto 0)) and waveselect_in(3);
  | 
      |||
| 1274 | markw | 			lfsr_next(0) <= (test or (lfsr_reg(22) xor lfsr_reg(17))) and not(noise_and_others);
  | 
      |
| 1056 | markw | 			lfsr_next(22 downto 1) <= lfsr_reg(21 downto 0);
  | 
      |
		end if;
  | 
      |||
	end process;
  | 
      |||
	-- next state - wave
  | 
      |||
| 1324 | markw | 	process(no_wave_bits,multiple_wave_bits,wave_reg,osc_in,waveselect_in,pulse_width_in,lfsr_reg,test,ringmod,ringmod_osc_msb,pulse_comparator_reg,wave_data,wave_data_ready,enable,osc_del_reg,delaysawtooth,wave0count1_reg)
  | 
      |
| 1056 | markw | 		variable noise : std_logic_vector(11 downto 0);
  | 
      |
		variable pulse : std_logic_vector(11 downto 0);
  | 
      |||
		variable triangle : std_logic_vector(11 downto 0);
  | 
      |||
		variable sawtooth : std_logic_vector(11 downto 0);
  | 
      |||
| 1062 | markw | 		variable pulse_comparator : std_logic;
  | 
      |
		variable triangle_xor : std_logic;
  | 
      |||
| 1065 | markw | 		variable triangle_xor_ext : std_logic_vector(10 downto 0);
  | 
      |
| 1062 | markw | 		variable osc_xored : std_logic_vector(10 downto 0);
  | 
      |
| 1324 | markw | 		variable osc_sawtooth : std_logic_vector(11 downto 0);
  | 
      |
		variable wave0tmp : unsigned(12 downto 0);
  | 
      |||
| 1056 | markw | 	begin
  | 
      |
| 1213 | markw | 		wave_next <= wave_reg;
  | 
      |
| 1324 | markw | 		wave0count1_next <= wave0count1_reg;
  | 
      |
		osc_del_next <= osc_del_reg;
  | 
      |||
| 1213 | markw | ||
| 1324 | markw | 		if (enable='1') then
  | 
      |
			osc_del_next <= osc_in;
  | 
      |||
		end if;
  | 
      |||
| 1284 | markw | 		noise:= (others=>'0');
  | 
      |
		pulse:= (others=>'0');
  | 
      |||
		triangle:= (others=>'0');
  | 
      |||
		sawtooth:= (others=>'0');
  | 
      |||
| 1056 | markw | ||
		if (waveselect_in(3)='1') then
  | 
      |||
| 1062 | markw | 			noise(11 downto 4):= lfsr_reg(20)&lfsr_reg(18)&lfsr_reg(14)&lfsr_reg(11)&lfsr_reg(9)&lfsr_reg(5)&lfsr_reg(2)&lfsr_reg(0);
  | 
      |
| 1056 | markw | 			noise(3 downto 0):= (others=>'0');
  | 
      |
		end if;
  | 
      |||
| 1284 | markw | 		-- direct...
  | 
      |
		-- rise: perfect, fall: 1 cycle ahead
  | 
      |||
		--pulse_comparator_next <= '0';
  | 
      |||
		--if (unsigned(osc_in)>unsigned(pulse_width_in)) then
  | 
      |||
		--	pulse_comparator_next <= '1';
  | 
      |||
		--end if;
  | 
      |||
		--pulse := (others=>(pulse_comparator_next or test));
  | 
      |||
		pulse_comparator_next <= '1';
  | 
      |||
		if (unsigned(osc_in)<=unsigned(pulse_width_in)) then
  | 
      |||
			pulse_comparator_next <= pulse_comparator_reg and not(enable);
  | 
      |||
| 1062 | markw | 		end if;
  | 
      |
| 1284 | markw | ||
| 1056 | markw | 		if (waveselect_in(2)='1') then
  | 
      |
| 1071 | markw | 			pulse := (others=>(pulse_comparator_reg or test));
  | 
      |
| 1056 | markw | 		end if;
  | 
      |
		--ref: https://sourceforge.net/p/sidplay-residfp/wiki/SID%20internals%20-%20Triangle%20Waveform/
  | 
      |||
| 1324 | markw | ||
		if (delaysawtooth='1') then
  | 
      |||
			osc_sawtooth:= osc_del_reg;
  | 
      |||
		else
  | 
      |||
			osc_sawtooth := osc_in;
  | 
      |||
		end if;
  | 
      |||
| 1062 | markw | 		triangle_xor := not(waveselect_in(1)) and                    -- sawtooth on->disable invert
  | 
      |
| 1324 | markw | 			((not(ringmod) and osc_sawtooth(11)) or                 -- not ringmod ->msb makes it invert
  | 
      |
			(ringmod and (osc_sawtooth(11) xnor ringmod_osc_msb))); -- ringmod -> both 0 or 1 -> invert
  | 
      |||
| 1065 | markw | 		triangle_xor_ext := (others=>triangle_xor);
  | 
      |
| 1324 | markw | 		osc_xored:= osc_sawtooth(10 downto 0) xor triangle_xor_ext;
  | 
      |
| 1056 | markw | ||
		if (waveselect_in(1)='1') then
  | 
      |||
| 1324 | markw | 			sawtooth := osc_sawtooth(11) & osc_xored(10 downto 0);
  | 
      |
| 1056 | markw | 		end if;
  | 
      |
		if (waveselect_in(0)='1') then
  | 
      |||
| 1062 | markw | 			triangle(11 downto 1) := osc_xored(10 downto 0);
  | 
      |
| 1056 | markw | 			triangle(0) := '0';
  | 
      |
		end if;
  | 
      |||
		-- AND is what the datasheet says, but it is WRONG!
  | 
      |||
		-- In fact transistors drive against each other with different resistances and
  | 
      |||
		-- a corrupt waveform is generated.
  | 
      |||
		-- TODO: Either compute or use flash storage (optionally)
  | 
      |||
| 1324 | markw | 		if (no_wave_bits='1') then
  | 
      |
			if (enable='1') then
  | 
      |||
				wave0count1_next <= unsigned("0"&wave0count1_reg(8 downto 0)) + 1;
  | 
      |||
				if (wave0count1_reg(9)='1') then -- carry
  | 
      |||
					wave0tmp := unsigned("0"&wave_reg) - 1; -- head to zero over 2 seconds!
  | 
      |||
					if (wave0tmp(12) = '1') then
  | 
      |||
						wave_next <= (others=>'0');
  | 
      |||
					else
  | 
      |||
						wave_next <= std_logic_vector(wave0tmp(11 downto 0));
  | 
      |||
					end if;
  | 
      |||
				end if;
  | 
      |||
			end if;
  | 
      |||
| 1213 | markw | 		else
  | 
      |
| 1324 | markw | 			if (multiple_wave_bits='0') then
  | 
      |
				wave_next <= noise or pulse or sawtooth or triangle;
  | 
      |||
			else
  | 
      |||
				if (wave_data_ready='1') then
  | 
      |||
					if (waveselect_in(2)='1') then
  | 
      |||
						wave_next <= wave_data and pulse;
  | 
      |||
					else
  | 
      |||
						wave_next <= wave_data;
  | 
      |||
					end if;
  | 
      |||
				end if;
  | 
      |||
| 1213 | markw | 			end if;
  | 
      |
		end if;
  | 
      |||
| 1324 | markw | ||
| 1056 | markw | 	end process;
  | 
      |
| 1324 | markw | 	multiple_wave_bits <=  --NPST   (?ST) or PS or PT
  | 
      |
| 1213 | markw | 	        not(waveselect_in(3)) and
  | 
      |
		(
  | 
      |||
			(waveselect_in(0) and waveselect_in(1))
  | 
      |||
				or
  | 
      |||
			(waveselect_in(2) and (waveselect_in(0) or waveselect_in(1)))
  | 
      |||
		)
  | 
      |||
		;
  | 
      |||
| 1324 | markw | 	no_wave_bits <= not(or_reduce(waveselect_in));
  | 
      |
| 1213 | markw | 	wave_data_needed_next <= (wave_data_needed_reg or (multiple_wave_bits and changing)) and not(wave_data_ready);
  | 
      |
| 1056 | markw | 	--output
  | 
      |
	wave_out <= wave_reg;
  | 
      |||
| 1213 | markw | 	wave_data_needed <= wave_data_needed_reg;
  | 
      |
| 1056 | markw | ||
END vhdl;
  |