From b8eed4413b7d093d8df73dc2c5ffc83462a4a2ed Mon Sep 17 00:00:00 2001 From: Mike Stirling Date: Sun, 31 Jul 2011 13:01:46 +0100 Subject: Added sound generator support and interface to WM8731L audio codec. Uses SN76489 implementation from FPGA arcade. --- README | 6 +- bbc_micro_de1.qsf | 25 ++- bbc_micro_de1.vhd | 123 +++++++++++++ i2c_loader.vhd | 300 +++++++++++++++++++++++++++++++ i2s_intf.vhd | 191 ++++++++++++++++++++ sn76489-1.0/COPYING | 340 +++++++++++++++++++++++++++++++++++ sn76489-1.0/README | 143 +++++++++++++++ sn76489-1.0/sn76489_attenuator-c.vhd | 14 ++ sn76489-1.0/sn76489_attenuator.vhd | 114 ++++++++++++ sn76489-1.0/sn76489_clock_div-c.vhd | 14 ++ sn76489-1.0/sn76489_clock_div.vhd | 134 ++++++++++++++ sn76489-1.0/sn76489_comp_pack-p.vhd | 96 ++++++++++ sn76489-1.0/sn76489_latch_ctrl-c.vhd | 14 ++ sn76489-1.0/sn76489_latch_ctrl.vhd | 138 ++++++++++++++ sn76489-1.0/sn76489_noise-c.vhd | 19 ++ sn76489-1.0/sn76489_noise.vhd | 281 +++++++++++++++++++++++++++++ sn76489-1.0/sn76489_tone-c.vhd | 19 ++ sn76489-1.0/sn76489_tone.vhd | 191 ++++++++++++++++++++ sn76489-1.0/sn76489_top-c.vhd | 31 ++++ sn76489-1.0/sn76489_top.vhd | 202 +++++++++++++++++++++ 20 files changed, 2386 insertions(+), 9 deletions(-) create mode 100644 i2c_loader.vhd create mode 100644 i2s_intf.vhd create mode 100644 sn76489-1.0/COPYING create mode 100644 sn76489-1.0/README create mode 100644 sn76489-1.0/sn76489_attenuator-c.vhd create mode 100644 sn76489-1.0/sn76489_attenuator.vhd create mode 100644 sn76489-1.0/sn76489_clock_div-c.vhd create mode 100644 sn76489-1.0/sn76489_clock_div.vhd create mode 100644 sn76489-1.0/sn76489_comp_pack-p.vhd create mode 100644 sn76489-1.0/sn76489_latch_ctrl-c.vhd create mode 100644 sn76489-1.0/sn76489_latch_ctrl.vhd create mode 100644 sn76489-1.0/sn76489_noise-c.vhd create mode 100644 sn76489-1.0/sn76489_noise.vhd create mode 100644 sn76489-1.0/sn76489_tone-c.vhd create mode 100644 sn76489-1.0/sn76489_tone.vhd create mode 100644 sn76489-1.0/sn76489_top-c.vhd create mode 100644 sn76489-1.0/sn76489_top.vhd diff --git a/README b/README index 6da2db8..47daf92 100644 --- a/README +++ b/README @@ -1,2 +1,6 @@ -T65 implementation from http://www.fpgaarcade.com/resources/T65_v302.zip +T65 implementation from http://www.fpgaarcade.com/resources/T65_v302.zip (bug fixes from PACE dev) +M6522 implementation from FPGA arcade VIC20 design. Various bug fixes added locally. +SN76489 implementation by Arnim Laeuger and obtained from FPGA arcade + + diff --git a/bbc_micro_de1.qsf b/bbc_micro_de1.qsf index 1ecc8b9..fd68838 100644 --- a/bbc_micro_de1.qsf +++ b/bbc_micro_de1.qsf @@ -501,6 +501,23 @@ set_global_assignment -name LL_MEMBER_STATE LOCKED -section_id "Root Region" set_global_assignment -name MISC_FILE "U:/git_repos/fpga/bbc/bbc_micro_de1.dpf" set_location_assignment PIN_AB15 -to FL_CE_N set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "3.3-V LVTTL" +set_global_assignment -name USE_CONFIGURATION_DEVICE ON +set_global_assignment -name STRATIX_CONFIGURATION_DEVICE EPCS4 +set_global_assignment -name RESERVE_ASDO_AFTER_CONFIGURATION "AS INPUT TRI-STATED" +set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO" +set_location_assignment PIN_U20 -to SD_nCS +set_location_assignment PIN_V20 -to SD_SCLK +set_location_assignment PIN_Y20 -to SD_MOSI +set_location_assignment PIN_W20 -to SD_MISO +set_global_assignment -name VHDL_FILE i2s_intf.vhd +set_global_assignment -name VHDL_FILE i2c_loader.vhd +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_comp_pack-p.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_noise.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_tone.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_top.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_attenuator.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_clock_div.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_latch_ctrl.vhd" set_global_assignment -name VHDL_FILE ps2_intf.vhd set_global_assignment -name VHDL_FILE simple_uart.vhd set_global_assignment -name VHDL_FILE m6522.vhd @@ -519,12 +536,4 @@ set_global_assignment -name QIP_FILE ehbasic.qip set_global_assignment -name VHDL_FILE m6522_tb.vhd set_global_assignment -name VHDL_FILE keyboard.vhd set_global_assignment -name VHDL_FILE debugger.vhd -set_global_assignment -name USE_CONFIGURATION_DEVICE ON -set_global_assignment -name STRATIX_CONFIGURATION_DEVICE EPCS4 -set_global_assignment -name RESERVE_ASDO_AFTER_CONFIGURATION "AS INPUT TRI-STATED" -set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO" -set_location_assignment PIN_U20 -to SD_nCS -set_location_assignment PIN_V20 -to SD_SCLK -set_location_assignment PIN_Y20 -to SD_MOSI -set_location_assignment PIN_W20 -to SD_MISO set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/bbc_micro_de1.vhd b/bbc_micro_de1.vhd index 77922e2..b313620 100644 --- a/bbc_micro_de1.vhd +++ b/bbc_micro_de1.vhd @@ -353,6 +353,94 @@ port ( ); end component; +----------------------------- +-- SN76489 sound generator +----------------------------- + +component sn76489_top is + + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + ready_o : out std_logic; + d_i : in std_logic_vector(0 to 7); + aout_o : out signed(0 to 7) + ); + +end component; + +component i2s_intf is +generic( + mclk_rate : positive := 12000000; + sample_rate : positive := 8000; + preamble : positive := 1; -- I2S + word_length : positive := 16 + ); + +port ( + -- 2x MCLK in (e.g. 24 MHz for WM8731 USB mode) + CLK : in std_logic; + nRESET : in std_logic; + + -- Parallel IO + PCM_INL : out std_logic_vector(word_length - 1 downto 0); + PCM_INR : out std_logic_vector(word_length - 1 downto 0); + PCM_OUTL : in std_logic_vector(word_length - 1 downto 0); + PCM_OUTR : in std_logic_vector(word_length - 1 downto 0); + + -- Codec interface (right justified mode) + -- MCLK is generated at half of the CLK input + I2S_MCLK : out std_logic; + -- LRCLK is equal to the sample rate and is synchronous to + -- MCLK. It must be related to MCLK by the oversampling ratio + -- given in the codec datasheet. + I2S_LRCLK : out std_logic; + + -- Data is shifted out on the falling edge of BCLK, sampled + -- on the rising edge. The bit rate is determined such that + -- it is fast enough to fit preamble + word_length bits into + -- each LRCLK half cycle. The last cycle of each word may be + -- stretched to fit to LRCLK. This is OK at least for the + -- WM8731 codec. + -- The first falling edge of each timeslot is always synchronised + -- with the LRCLK edge. + I2S_BCLK : out std_logic; + -- Output bitstream + I2S_DOUT : out std_logic; + -- Input bitstream + I2S_DIN : in std_logic + ); +end component; + +component i2c_loader is +generic ( + -- Address of slave to be loaded + device_address : integer := 16#1a#; + -- Number of retries to allow before stopping + num_retries : integer := 0; + -- Length of clock divider in bits. Resulting bus frequency is + -- CLK/2^(log2_divider + 2) + log2_divider : integer := 6 +); + +port ( + CLK : in std_logic; + nRESET : in std_logic; + + I2C_SCL : inout std_logic; + I2C_SDA : inout std_logic; + + IS_DONE : out std_logic; + IS_ERROR : out std_logic + ); +end component; + component debugger is generic ( -- Set this for a reasonable half flash duration relative to the @@ -540,6 +628,13 @@ signal keyb_out : std_logic; signal keyb_int : std_logic; signal keyb_break : std_logic; +-- Sound generator +signal sound_ready : std_logic; +signal sound_di : std_logic_vector(7 downto 0); +signal sound_ao : signed(7 downto 0); +signal pcm_inl : std_logic_vector(15 downto 0); +signal pcm_inr : std_logic_vector(15 downto 0); + -- Memory enables signal ram_enable : std_logic; -- 0x0000 signal rom_enable : std_logic; -- 0x8000 (BASIC/sideways ROMs) @@ -753,6 +848,32 @@ begin keyb_break, SW(7 downto 0) ); + + -- Sound generator (and drive logic for I2S codec) + sound : sn76489_top port map ( + clock, mhz4_clken, + reset_n, '0', sound_enable_n, + sound_ready, sound_di, + sound_ao + ); + i2s : i2s_intf port map ( + CLOCK_24(0), reset_n, + pcm_inl, pcm_inr, + std_logic_vector(sound_ao) & "00000000", + std_logic_vector(sound_ao) & "00000000", + AUD_XCK, AUD_DACLRCK, + AUD_BCLK, AUD_DACDAT, AUD_ADCDAT + ); + i2c : i2c_loader + generic map ( + log2_divider => 7 + ) + port map ( + clock, reset_n, + I2C_SCLK, I2C_SDAT, + LEDR(5), -- IS_DONE + LEDR(4) -- IS_ERROR + ); -- Asynchronous reset -- PLL is reset by external reset switch @@ -1018,6 +1139,8 @@ begin sys_via_pa_in(6 downto 0) <= sys_via_pa_out(6 downto 0); -- Must loop back output pins or keyboard won't work keyb_column <= sys_via_pa_out(3 downto 0); keyb_row <= sys_via_pa_out(6 downto 4); + -- Sound + sound_di <= sys_via_pa_out; -- Others (idle until missing bits implemented) sys_via_pb_in(7 downto 4) <= (others => '1'); diff --git a/i2c_loader.vhd b/i2c_loader.vhd new file mode 100644 index 0000000..385d9c5 --- /dev/null +++ b/i2c_loader.vhd @@ -0,0 +1,300 @@ +-- ZX Spectrum for Altera DE1 +-- +-- Copyright (c) 2009-2010 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.STD_LOGIC_ARITH.ALL; +use IEEE.STD_LOGIC_UNSIGNED.ALL; +use IEEE.STD_LOGIC_MISC.ALL; -- for AND_REDUCE +use IEEE.NUMERIC_STD.ALL; + +entity i2c_loader is +generic ( + -- Address of slave to be loaded + device_address : integer := 16#1a#; + -- Number of retries to allow before stopping + num_retries : integer := 0; + -- Length of clock divider in bits. Resulting bus frequency is + -- CLK/2^(log2_divider + 2) + log2_divider : integer := 6 +); + +port ( + CLK : in std_logic; + nRESET : in std_logic; + + I2C_SCL : inout std_logic; + I2C_SDA : inout std_logic; + + IS_DONE : out std_logic; + IS_ERROR : out std_logic + ); +end i2c_loader; + +architecture i2c_loader_arch of i2c_loader is +type regs is array(0 to 19) of std_logic_vector(7 downto 0); +constant init_regs : regs := ( + -- Left line in, 0dB, unmute + X"00", X"17", + -- Right line in, 0dB, unmute + X"02", X"17", + -- Left headphone out, 0dB + X"04", X"79", + -- Right headphone out, 0dB + X"06", X"79", + -- Audio path, DAC enabled, Line in, Bypass off, mic unmuted + X"08", X"10", + -- Digital path, Unmute, HP filter enabled + X"0A", X"00", + -- Power down mic, clkout and xtal osc + X"0C", X"62", + -- Format 16-bit I2S, no bit inversion or phase changes + X"0E", X"02", + -- Sampling control, 8 kHz USB mode (MCLK = 250fs * 6) + X"10", X"0D", + -- Activate + X"12", X"01" + ); +-- Number of bursts (i.e. total number of registers) +constant burst_length : positive := 2; +-- Number of bytes to transfer per burst +constant num_bursts : positive := (init_regs'length / burst_length); + +type state_t is (Idle, Start, Data, Ack, Stop, Pause, Done); +signal state : state_t; +signal phase : std_logic_vector(1 downto 0); +subtype nbit_t is integer range 0 to 7; +signal nbit : nbit_t; +subtype nbyte_t is integer range 0 to burst_length; -- +1 for address byte +signal nbyte : nbyte_t; +subtype thisbyte_t is integer range 0 to init_regs'length; -- +1 for "done" +signal thisbyte : thisbyte_t; +subtype retries_t is integer range 0 to num_retries; +signal retries : retries_t; + +signal clken : std_logic; +signal divider : std_logic_vector(log2_divider-1 downto 0); +signal shiftreg : std_logic_vector(7 downto 0); +signal scl_out : std_logic; +signal sda_out : std_logic; +signal nak : std_logic; +begin + -- Create open-drain outputs for I2C bus + I2C_SCL <= '0' when scl_out = '0' else 'Z'; + I2C_SDA <= '0' when sda_out = '0' else 'Z'; + -- Status outputs are driven both ways + IS_DONE <= '1' when state = Done else '0'; + IS_ERROR <= nak; + + -- Generate clock enable for desired bus speed + clken <= AND_REDUCE(divider); + process(nRESET,CLK) + begin + if nRESET = '0' then + divider <= (others => '0'); + elsif falling_edge(CLK) then + divider <= divider + '1'; + end if; + end process; + + -- The I2C loader process + process(nRESET,CLK) + begin + if nRESET = '0' then + scl_out <= '1'; + sda_out <= '1'; + state <= Idle; + phase <= "00"; + nbit <= 0; + nbyte <= 0; + thisbyte <= 0; + shiftreg <= (others => '0'); + nak <= '0'; -- No error + retries <= num_retries; + elsif rising_edge(CLK) and clken = '1' then + -- Next phase by default + phase <= phase + 1; + + -- STATE: IDLE + if state = Idle then + -- Start loading the device registers straight away + -- A 'GO' bit could be polled here if required + state <= Start; + phase <= "00"; + scl_out <= '1'; + sda_out <= '1'; + + -- STATE: START + elsif state = Start then + -- Generate START condition + case phase is + when "00" => + -- Drop SDA first + sda_out <= '0'; + when "10" => + -- Then drop SCL + scl_out <= '0'; + when "11" => + -- Advance to next state + -- Shift register loaded with device slave address + state <= Data; + nbit <= 7; + shiftreg <= std_logic_vector(to_unsigned(device_address,7)) & '0'; -- writing + nbyte <= burst_length; + when others => + null; + end case; + + -- STATE: DATA + elsif state = Data then + -- Generate data + case phase is + when "00" => + -- Drop SCL + scl_out <= '0'; + when "01" => + -- Output data and shift (MSb first) + sda_out <= shiftreg(7); + shiftreg <= shiftreg(6 downto 0) & '0'; + when "10" => + -- Raise SCL + scl_out <= '1'; + when "11" => + -- Next bit or advance to next state when done + if nbit = 0 then + state <= Ack; + else + nbit <= nbit - 1; + end if; + when others => + null; + end case; + + -- STATE: ACK + elsif state = Ack then + -- Generate ACK clock and check for error condition + case phase is + when "00" => + -- Drop SCL + scl_out <= '0'; + when "01" => + -- Float data + sda_out <= '1'; + when "10" => + -- Sample ack bit + nak <= I2C_SDA; + if I2C_SDA = '1' then + -- Error + nbyte <= 0; -- Close this burst and skip remaining registers + thisbyte <= init_regs'length; + else + -- Hold ACK to avoid spurious stops - this seems to fix a + -- problem with the Wolfson codec which releases the ACK + -- right on the falling edge of the clock pulse. It looks like + -- the device interprets this is a STOP condition and then fails + -- to acknowledge the next byte. We can avoid this by holding the + -- ACK condition for a little longer. + sda_out <= '0'; + end if; + -- Raise SCL + scl_out <= '1'; + when "11" => + -- Advance to next state + if nbyte = 0 then + -- No more bytes in this burst - generate a STOP + state <= Stop; + else + -- Generate next byte + state <= Data; + nbit <= 7; + shiftreg <= init_regs(thisbyte); + nbyte <= nbyte - 1; + thisbyte <= thisbyte + 1; + end if; + when others => + null; + end case; + + -- STATE: STOP + elsif state = Stop then + -- Generate STOP condition + case phase is + when "00" => + -- Drop SCL first + scl_out <= '0'; + when "01" => + -- Drop SDA + sda_out <= '0'; + when "10" => + -- Raise SCL + scl_out <= '1'; + when "11" => + if thisbyte = init_regs'length then + -- All registers done, advance to finished state. This will + -- bring SDA high while SCL is still high, completing the STOP + -- condition + state <= Done; + else + -- Load the next register after a short delay + state <= Pause; + end if; + when others => + null; + end case; + + -- STATE: PAUSE + elsif state = Pause then + -- Delay for one cycle of 'phase' then start the next burst + scl_out <= '1'; + sda_out <= '1'; + if phase = "11" then + state <= Start; + end if; + + -- STATE: DONE + else + -- Finished + scl_out <= '1'; + sda_out <= '1'; + + if nak = '1' and retries > 0 then + -- We can retry in the event of a NAK in case the + -- slave got out of sync for some reason + retries <= retries - 1; + state <= Idle; + end if; + end if; + end if; + end process; +end i2c_loader_arch; + diff --git a/i2s_intf.vhd b/i2s_intf.vhd new file mode 100644 index 0000000..43247a5 --- /dev/null +++ b/i2s_intf.vhd @@ -0,0 +1,191 @@ +-- ZX Spectrum for Altera DE1 +-- +-- Copyright (c) 2009-2010 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.STD_LOGIC_ARITH.ALL; +use IEEE.STD_LOGIC_UNSIGNED.ALL; + +entity i2s_intf is +generic( + mclk_rate : positive := 12000000; + sample_rate : positive := 8000; + preamble : positive := 1; -- I2S + word_length : positive := 16 + ); + +port ( + -- 2x MCLK in (e.g. 24 MHz for WM8731 USB mode) + CLK : in std_logic; + nRESET : in std_logic; + + -- Parallel IO + PCM_INL : out std_logic_vector(word_length - 1 downto 0); + PCM_INR : out std_logic_vector(word_length - 1 downto 0); + PCM_OUTL : in std_logic_vector(word_length - 1 downto 0); + PCM_OUTR : in std_logic_vector(word_length - 1 downto 0); + + -- Codec interface (right justified mode) + -- MCLK is generated at half of the CLK input + I2S_MCLK : out std_logic; + -- LRCLK is equal to the sample rate and is synchronous to + -- MCLK. It must be related to MCLK by the oversampling ratio + -- given in the codec datasheet. + I2S_LRCLK : out std_logic; + + -- Data is shifted out on the falling edge of BCLK, sampled + -- on the rising edge. The bit rate is determined such that + -- it is fast enough to fit preamble + word_length bits into + -- each LRCLK half cycle. The last cycle of each word may be + -- stretched to fit to LRCLK. This is OK at least for the + -- WM8731 codec. + -- The first falling edge of each timeslot is always synchronised + -- with the LRCLK edge. + I2S_BCLK : out std_logic; + -- Output bitstream + I2S_DOUT : out std_logic; + -- Input bitstream + I2S_DIN : in std_logic + ); +end i2s_intf; + +architecture i2s_intf_arch of i2s_intf is +constant ratio_mclk_fs : positive := (mclk_rate / sample_rate); +constant lrdivider_top : positive := (ratio_mclk_fs / 2) - 1; +constant bdivider_top : positive := (ratio_mclk_fs / 8 / (preamble + word_length) * 2) - 1; +constant nbits : positive := preamble + word_length; + +subtype lrdivider_t is integer range 0 to lrdivider_top; +subtype bdivider_t is integer range 0 to bdivider_top; +subtype bitcount_t is integer range 0 to nbits; + +signal lrdivider : lrdivider_t; +signal bdivider : bdivider_t; +signal bitcount : bitcount_t; + +signal mclk_r : std_logic; +signal lrclk_r : std_logic; +signal bclk_r : std_logic; + +-- Shift register is long enough for the number of data bits +-- plus the preamble, plus an extra bit on the right to register +-- the incoming data +signal shiftreg : std_logic_vector(nbits downto 0); +begin + I2S_MCLK <= mclk_r; + I2S_LRCLK <= lrclk_r; + I2S_BCLK <= bclk_r; + I2S_DOUT <= shiftreg(nbits); -- data goes out MSb first + + process(nRESET,CLK) + begin + if nRESET = '0' then + PCM_INL <= (others => '0'); + PCM_INR <= (others => '0'); + + -- Preload down-counters for clock generation + lrdivider <= lrdivider_top; + bdivider <= bdivider_top; + bitcount <= nbits; + + mclk_r <= '0'; + lrclk_r <= '0'; + bclk_r <= '0'; + shiftreg <= (others => '0'); + elsif rising_edge(CLK) then + -- Generate MCLK at half input clock rate + mclk_r <= not mclk_r; + + -- Generate LRCLK at rate specified by codec configuration + if lrdivider = 0 then + -- LRCLK divider has reached 0 - start again from the top + lrdivider <= lrdivider_top; + + -- Generate LRCLK edge and sync the BCLK counter + lrclk_r <= not lrclk_r; + bclk_r <= '0'; + bitcount <= nbits; -- 1 extra required for setup + bdivider <= bdivider_top; + + -- Load shift register with output data padding preamble + -- with 0s. Load output buses with input word from the + -- previous timeslot. + shiftreg(nbits downto nbits - preamble + 1) <= (others => '0'); + if lrclk_r = '0' then + -- Previous channel input is LEFT. This is available in the + -- shift register at the end of a cycle, right justified + PCM_INL <= shiftreg(word_length - 1 downto 0); + -- Next channel to output is RIGHT. Load this into the + -- shift register at the start of a cycle, left justified + shiftreg(word_length downto 1) <= PCM_OUTR; + else + -- Previous channel input is RIGHT + PCM_INR <= shiftreg(word_length - 1 downto 0); + -- Next channel is LEFT + shiftreg(word_length downto 1) <= PCM_OUTL; + end if; + else + -- Decrement the LRCLK counter + lrdivider <= lrdivider - 1; + + -- Generate BCLK at a suitable rate to fit the required number + -- of bits into each timeslot. Data is changed on the falling edge, + -- sampled on the rising edge + if bdivider = 0 then + -- If all bits have been output for this phase then + -- stop and wait to sync back up with LRCLK + if bitcount > 0 then + -- Reset + bdivider <= bdivider_top; + + -- Toggle BCLK + bclk_r <= not bclk_r; + if bclk_r = '0' then + -- Rising edge - shift in current bit and decrement bit counter + bitcount <= bitcount - 1; + shiftreg(0) <= I2S_DIN; + else + -- Falling edge - shift out next bit + shiftreg(nbits downto 1) <= shiftreg(nbits - 1 downto 0); + end if; + end if; + else + -- Decrement the BCLK counter + bdivider <= bdivider - 1; + end if; + end if; + + end if; + end process; +end i2s_intf_arch; + diff --git a/sn76489-1.0/COPYING b/sn76489-1.0/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/sn76489-1.0/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/sn76489-1.0/README b/sn76489-1.0/README new file mode 100644 index 0000000..3363014 --- /dev/null +++ b/sn76489-1.0/README @@ -0,0 +1,143 @@ + +An SN76489AN Compatible Implementation in VHDL +============================================== +Version: $Date: 2006/06/18 19:28:40 $ + +Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +See the file COPYING. + + +Integration +----------- + +The sn76489 design exhibits all interface signals as the original chip. It +only differs in the audio data output which is provided as an 8 bit signed +vector instead of an analog output pin. + + generic ( + clock_div_16_g : integer := 1 + -- Set to '1' when operating the design in SN76489 mode. The primary clock + -- input is divided by 16 in this variant. The data sheet mentions the + -- SN76494 which contains a divide-by-2 clock input stage. Set the generic + -- to '0' to enable this mode. + ); + port ( + clock_i : in std_logic; + -- Primary clock input + -- Drive with the target frequency or any integer multiple of it. + + clock_en_i : in std_logic; + -- Clock enable + -- A '1' on this input qualifies a valid rising edge on clock_i. A '0' + -- disables the next rising clock edge, effectivley halting the design + -- until the next enabled rising clock edge. + -- Can be used to run the core at lower frequencies than applied on + -- clock_i. + + res_n_i : in std_logic; + -- Asynchronous low active reset input. + -- Sets all sequential elements to a known state. + + ce_n_i : in std_logic; + -- Chip enable, low active. + + we_n_i : in std_logic; + -- Write enable, low active. + + ready_o : out std_logic; + -- Ready indication to microprocessor. + + d_i : in std_logic_vector(0 to 7); + -- Data input + -- MSB 0 ... 7 LSB + + aout_o : out signed(0 to 7) + -- Audio output, signed vector + -- MSB/SIGN 0 ... 7 LSB + ); + + +Both 8 bit vector ports are defined (0 to 7) which declares bit 0 to be the +MSB and bit 7 to be the LSB. This has been implemented according to TI's data +sheet, thus all register/data format figures apply 1:1 for this design. +Many systems will flip the system data bus bit wise before it is connected to +this PSG. This is simply achieved with the following VHDL construct: + + signal data_s : std_logic_vector(7 downto 0); + + ... + d_i => data_s, + ... + +d_i and data_s will be assigned from left to right, resulting in the expected +bit assignment: + + d_i data_s + 0 7 + 1 6 + ... + 6 1 + 7 0 + + +As this design is fully synchronous, care has to be taken when the design +replaces an SN76489 in asynchronous mode. No problems are expected when +interfacing the code to other synchronous components. + + +Design Hierarchy +---------------- + + sn76489_top + | + +-- sn76489_latch_ctrl + | + +-- sn76489_clock_div + | + +-- sn76489_tone + | | + | \-- sn76489_attentuator + | + +-- sn76489_tone + | | + | \-- sn76489_attentuator + | + +-- sn76489_tone + | | + | \-- sn76489_attentuator + | + \-- sn76489_noise + | + \-- sn76489_attentuator + +Resulting compilation sequence: + + sn76489_comp_pack-p.vhd + sn76489_top.vhd + sn76489_latch_ctrl.vhd + sn76489_latch_ctrl-c.vhd + sn76489_clock_div.vhd + sn76489_clock_div-c.vhd + sn76489_attenuator.vhd + sn76489_attenuator-c.vhd + sn76489_tone.vhd + sn76489_tone-c.vhd + sn76489_noise.vhd + sn76489_noise-c.vhd + sn76489_top-c.vhd + +Skip the files containing VHDL configurations when analyzing the code for +synthesis. + + +References +---------- + +* TI Data sheet SN76489.pdf + ftp://ftp.whtech.com/datasheets%20&%20manuals/SN76489.pdf + +* John Kortink's article on the SN76489: + http://web.inter.nl.net/users/J.Kortink/home/articles/sn76489/ + +* Maxim's "SN76489 notes" in + http://www.smspower.org/maxim/docs/SN76489.txt diff --git a/sn76489-1.0/sn76489_attenuator-c.vhd b/sn76489-1.0/sn76489_attenuator-c.vhd new file mode 100644 index 0000000..b97da9d --- /dev/null +++ b/sn76489-1.0/sn76489_attenuator-c.vhd @@ -0,0 +1,14 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_attenuator-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_attenuator_rtl_c0 of sn76489_attenuator is + + for rtl + end for; + +end sn76489_attenuator_rtl_c0; diff --git a/sn76489-1.0/sn76489_attenuator.vhd b/sn76489-1.0/sn76489_attenuator.vhd new file mode 100644 index 0000000..444064e --- /dev/null +++ b/sn76489-1.0/sn76489_attenuator.vhd @@ -0,0 +1,114 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_attenuator.vhd,v 1.7 2006/02/27 20:30:10 arnim Exp $ +-- +-- Attenuator Module +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sn76489_attenuator is + + port ( + attenuation_i : in std_logic_vector(0 to 3); + factor_i : in signed(0 to 1); + product_o : out signed(0 to 7) + ); + +end sn76489_attenuator; + + +architecture rtl of sn76489_attenuator is + +begin + + ----------------------------------------------------------------------------- + -- Process attenuate + -- + -- Purpose: + -- Determine the attenuation and generate the resulting product. + -- + -- The maximum attenuation value is 31 which corresponds to volume off. + -- As described in the data sheet, the maximum "playing" attenuation is + -- 28 = 16 + 8 + 4 + -- + -- The table for the volume constants is derived from the following + -- formula (each step is 2dB voltage): + -- v(0) = 31 + -- v(n+1) = v(n) * 0.79432823 + -- + attenuate: process (attenuation_i, + factor_i) + + type volume_t is array (natural range 0 to 15) of natural; + constant volume_c : volume_t := + (31, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 2, 2, 1, 0); + + variable attenuation_v : unsigned(attenuation_i'range); + variable volume_v : signed(product_o'range); + + begin + + attenuation_v := unsigned(attenuation_i); + + -- volume look-up table + volume_v := to_signed(volume_c(to_integer(attenuation_v)), + product_o'length); + + -- this replaces a multiplier and consumes a bit fewer + -- resources + case to_integer(factor_i) is + when +1 => + product_o <= volume_v; + when -1 => + product_o <= -volume_v; + when others => + product_o <= (others => '0'); + end case; + + end process attenuate; + -- + ----------------------------------------------------------------------------- + +end rtl; diff --git a/sn76489-1.0/sn76489_clock_div-c.vhd b/sn76489-1.0/sn76489_clock_div-c.vhd new file mode 100644 index 0000000..7dfd851 --- /dev/null +++ b/sn76489-1.0/sn76489_clock_div-c.vhd @@ -0,0 +1,14 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_clock_div-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_clock_div_rtl_c0 of sn76489_clock_div is + + for rtl + end for; + +end sn76489_clock_div_rtl_c0; diff --git a/sn76489-1.0/sn76489_clock_div.vhd b/sn76489-1.0/sn76489_clock_div.vhd new file mode 100644 index 0000000..eab86be --- /dev/null +++ b/sn76489-1.0/sn76489_clock_div.vhd @@ -0,0 +1,134 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_clock_div.vhd,v 1.4 2005/10/10 21:51:27 arnim Exp $ +-- +-- Clock Divider Circuit +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +entity sn76489_clock_div is + + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + clk_en_o : out boolean + ); + +end sn76489_clock_div; + + +library ieee; +use ieee.numeric_std.all; + +architecture rtl of sn76489_clock_div is + + signal cnt_s, + cnt_q : unsigned(3 downto 0); + +begin + + ----------------------------------------------------------------------------- + -- Process seq + -- + -- Purpose: + -- Implements the sequential counter element. + -- + seq: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + cnt_q <= (others => '0'); + elsif clock_i'event and clock_i = '1' then + cnt_q <= cnt_s; + end if; + end process seq; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Process comb + -- + -- Purpose: + -- Implements the combinational counter logic. + -- + comb: process (clock_en_i, + cnt_q) + begin + -- default assignments + cnt_s <= cnt_q; + clk_en_o <= false; + + if clock_en_i = '1' then + + if cnt_q = 0 then + clk_en_o <= true; + + if clock_div_16_g = 1 then + cnt_s <= to_unsigned(15, cnt_q'length); + elsif clock_div_16_g = 0 then + cnt_s <= to_unsigned( 1, cnt_q'length); + else + -- pragma translate_off + assert false + report "Generic clock_div_16_g must be either 0 or 1." + severity failure; + -- pragma translate_on + end if; + + else + cnt_s <= cnt_q - 1; + + end if; + + end if; + + end process comb; + -- + ----------------------------------------------------------------------------- + +end rtl; diff --git a/sn76489-1.0/sn76489_comp_pack-p.vhd b/sn76489-1.0/sn76489_comp_pack-p.vhd new file mode 100644 index 0000000..06b12c8 --- /dev/null +++ b/sn76489-1.0/sn76489_comp_pack-p.vhd @@ -0,0 +1,96 @@ +------------------------------------------------------------------------------- +-- +-- $Id: sn76489_comp_pack-p.vhd,v 1.6 2006/02/27 20:30:10 arnim Exp $ +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package sn76489_comp_pack is + + component sn76489_attenuator + port ( + attenuation_i : in std_logic_vector(0 to 3); + factor_i : in signed(0 to 1); + product_o : out signed(0 to 7) + ); + end component; + + component sn76489_tone + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + we_i : in boolean; + d_i : in std_logic_vector(0 to 7); + r2_i : in std_logic; + ff_o : out std_logic; + tone_o : out signed(0 to 7) + ); + end component; + + component sn76489_noise + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + we_i : in boolean; + d_i : in std_logic_vector(0 to 7); + r2_i : in std_logic; + tone3_ff_i : in std_logic; + noise_o : out signed(0 to 7) + ); + end component; + + component sn76489_latch_ctrl + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + d_i : in std_logic_vector(0 to 7); + ready_o : out std_logic; + tone1_we_o : out boolean; + tone2_we_o : out boolean; + tone3_we_o : out boolean; + noise_we_o : out boolean; + r2_o : out std_logic + ); + end component; + + component sn76489_clock_div + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + clk_en_o : out boolean + ); + end component; + + component sn76489_top + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + ready_o : out std_logic; + d_i : in std_logic_vector(0 to 7); + aout_o : out signed(0 to 7) + ); + end component; + +end sn76489_comp_pack; diff --git a/sn76489-1.0/sn76489_latch_ctrl-c.vhd b/sn76489-1.0/sn76489_latch_ctrl-c.vhd new file mode 100644 index 0000000..abc09e0 --- /dev/null +++ b/sn76489-1.0/sn76489_latch_ctrl-c.vhd @@ -0,0 +1,14 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_latch_ctrl-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_latch_ctrl_rtl_c0 of sn76489_latch_ctrl is + + for rtl + end for; + +end sn76489_latch_ctrl_rtl_c0; diff --git a/sn76489-1.0/sn76489_latch_ctrl.vhd b/sn76489-1.0/sn76489_latch_ctrl.vhd new file mode 100644 index 0000000..789720c --- /dev/null +++ b/sn76489-1.0/sn76489_latch_ctrl.vhd @@ -0,0 +1,138 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_latch_ctrl.vhd,v 1.6 2006/02/27 20:30:10 arnim Exp $ +-- +-- Latch Control Unit +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +entity sn76489_latch_ctrl is + + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + d_i : in std_logic_vector(0 to 7); + ready_o : out std_logic; + tone1_we_o : out boolean; + tone2_we_o : out boolean; + tone3_we_o : out boolean; + noise_we_o : out boolean; + r2_o : out std_logic + ); + +end sn76489_latch_ctrl; + + +library ieee; +use ieee.numeric_std.all; + +architecture rtl of sn76489_latch_ctrl is + + signal reg_q : std_logic_vector(0 to 2); + signal we_q : boolean; + signal ready_q : std_logic; + +begin + + ----------------------------------------------------------------------------- + -- Process seq + -- + -- Purpose: + -- Implements the sequential elements. + -- + seq: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + reg_q <= (others => '0'); + we_q <= false; + ready_q <= '0'; + + elsif clock_i'event and clock_i = '1' then + -- READY Flag Output ---------------------------------------------------- + if ready_q = '0' and we_q then + if clk_en_i then + -- assert READY when write access happened + ready_q <= '1'; + end if; + elsif ce_n_i = '1' then + -- deassert READY when access has finished + ready_q <= '0'; + end if; + + -- Register Selection --------------------------------------------------- + if ce_n_i = '0' and we_n_i = '0' then + if clk_en_i then + if d_i(0) = '1' then + reg_q <= d_i(1 to 3); + end if; + we_q <= true; + end if; + else + we_q <= false; + end if; + + end if; + end process seq; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Output mapping + ----------------------------------------------------------------------------- + tone1_we_o <= reg_q(0 to 1) = "00" and we_q; + tone2_we_o <= reg_q(0 to 1) = "01" and we_q; + tone3_we_o <= reg_q(0 to 1) = "10" and we_q; + noise_we_o <= reg_q(0 to 1) = "11" and we_q; + + r2_o <= reg_q(2); + + ready_o <= ready_q + when ce_n_i = '0' else + '1'; + +end rtl; diff --git a/sn76489-1.0/sn76489_noise-c.vhd b/sn76489-1.0/sn76489_noise-c.vhd new file mode 100644 index 0000000..28ded84 --- /dev/null +++ b/sn76489-1.0/sn76489_noise-c.vhd @@ -0,0 +1,19 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_noise-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_noise_rtl_c0 of sn76489_noise is + + for rtl + + for attenuator_b : sn76489_attenuator + use configuration work.sn76489_attenuator_rtl_c0; + end for; + + end for; + +end sn76489_noise_rtl_c0; diff --git a/sn76489-1.0/sn76489_noise.vhd b/sn76489-1.0/sn76489_noise.vhd new file mode 100644 index 0000000..8b2ee0e --- /dev/null +++ b/sn76489-1.0/sn76489_noise.vhd @@ -0,0 +1,281 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_noise.vhd,v 1.6 2006/02/27 20:30:10 arnim Exp $ +-- +-- Noise Generator +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sn76489_noise is + + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + we_i : in boolean; + d_i : in std_logic_vector(0 to 7); + r2_i : in std_logic; + tone3_ff_i : in std_logic; + noise_o : out signed(0 to 7) + ); + +end sn76489_noise; + + +use work.sn76489_comp_pack.sn76489_attenuator; + +architecture rtl of sn76489_noise is + + signal nf_q : std_logic_vector(0 to 1); + signal fb_q : std_logic; + signal a_q : std_logic_vector(0 to 3); + signal freq_cnt_q : unsigned(0 to 6); + signal freq_ff_q : std_logic; + + signal shift_source_s, + shift_source_q : std_logic; + signal shift_rise_edge_s : boolean; + + signal lfsr_q : std_logic_vector(0 to 15); + + signal freq_s : signed(0 to 1); + +begin + + ----------------------------------------------------------------------------- + -- Process cpu_regs + -- + -- Purpose: + -- Implements the registers writable by the CPU. + -- + cpu_regs: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + nf_q <= (others => '0'); + fb_q <= '0'; + a_q <= (others => '1'); + + elsif clock_i'event and clock_i = '1' then + if clk_en_i and we_i then + if r2_i = '0' then + -- access to control register + -- both access types can write to the control register! + nf_q <= d_i(6 to 7); + fb_q <= d_i(5); + + else + -- access to attenuator register + -- both access types can write to the attenuator register! + a_q <= d_i(4 to 7); + + end if; + end if; + end if; + end process cpu_regs; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Process freq_gen + -- + -- Purpose: + -- Implements the frequency generation components. + -- + freq_gen: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + freq_cnt_q <= (others => '0'); + freq_ff_q <= '0'; + + elsif clock_i'event and clock_i = '1' then + if clk_en_i then + if freq_cnt_q = 0 then + -- reload frequency counter according to NF setting + case nf_q is + when "00" => + freq_cnt_q <= to_unsigned(16 * 2 - 1, freq_cnt_q'length); + when "01" => + freq_cnt_q <= to_unsigned(16 * 4 - 1, freq_cnt_q'length); + when "10" => + freq_cnt_q <= to_unsigned(16 * 8 - 1, freq_cnt_q'length); + when others => + null; + end case; + + freq_ff_q <= not freq_ff_q; + + else + -- decrement frequency counter + freq_cnt_q <= freq_cnt_q - 1; + + end if; + + end if; + end if; + end process freq_gen; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Multiplex the source of the LFSR's shift enable + ----------------------------------------------------------------------------- + shift_source_s <= tone3_ff_i + when nf_q = "11" else + freq_ff_q; + + ----------------------------------------------------------------------------- + -- Process rise_edge + -- + -- Purpose: + -- Detect the rising edge of the selected LFSR shift source. + -- + rise_edge: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + shift_source_q <= '0'; + + elsif clock_i'event and clock_i = '1' then + if clk_en_i then + shift_source_q <= shift_source_s; + end if; + end if; + end process rise_edge; + -- + ----------------------------------------------------------------------------- + + -- detect rising edge on shift source + shift_rise_edge_s <= shift_source_q = '0' and shift_source_s = '1'; + + + ----------------------------------------------------------------------------- + -- Process lfsr + -- + -- Purpose: + -- Implements the LFSR that generates noise. + -- Note: This implementation shifts the register right, i.e. from index + -- 15 towards 0 => bit 15 is the input, bit 0 is the output + -- + -- Tapped bits according to MAME's sn76496.c, implemented in function + -- lfsr_tapped_f. + -- + lfsr: process (clock_i, res_n_i) + + function lfsr_tapped_f(lfsr : in std_logic_vector) return std_logic is + constant tapped_bits_c : std_logic_vector(0 to 15) + -- tapped bits are 0, 2, 15 + := "1010000000000001"; + variable parity_v : std_logic; + begin + parity_v := '0'; + + for idx in lfsr'low to lfsr'high loop + parity_v := parity_v xor (lfsr(idx) and tapped_bits_c(idx)); + end loop; + + return parity_v; + end; + + begin + if res_n_i = '0' then + -- reset LFSR to "0000000000000001" + lfsr_q <= (others => '0'); + lfsr_q(lfsr_q'right) <= '1'; + + elsif clock_i'event and clock_i = '1' then + if clk_en_i then + if we_i and r2_i = '0' then + -- write to noise register + -- -> reset LFSR + lfsr_q <= (others => '0'); + lfsr_q(lfsr_q'right) <= '1'; + + elsif shift_rise_edge_s then + + -- shift LFSR left towards MSB + for idx in lfsr_q'right-1 downto lfsr_q'left loop + lfsr_q(idx) <= lfsr_q(idx+1); + end loop; + + -- determine input bit + if fb_q = '0' then + -- "Periodic" Noise + -- -> input to LFSR is output + lfsr_q(lfsr_q'right) <= lfsr_q(lfsr_q'left); + else + -- "White" Noise + -- -> input to LFSR is parity of tapped bits + lfsr_q(lfsr_q'right) <= lfsr_tapped_f(lfsr_q); + end if; + + end if; + + end if; + end if; + end process lfsr; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Map output of LFSR to signed value for attenuator. + ----------------------------------------------------------------------------- + freq_s <= to_signed(+1, 2) + when lfsr_q(0) = '1' else + to_signed( 0, 2); + + + ----------------------------------------------------------------------------- + -- The attenuator itself + ----------------------------------------------------------------------------- + attenuator_b : sn76489_attenuator + port map ( + attenuation_i => a_q, + factor_i => freq_s, + product_o => noise_o + ); + +end rtl; diff --git a/sn76489-1.0/sn76489_tone-c.vhd b/sn76489-1.0/sn76489_tone-c.vhd new file mode 100644 index 0000000..119e2f3 --- /dev/null +++ b/sn76489-1.0/sn76489_tone-c.vhd @@ -0,0 +1,19 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_tone-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_tone_rtl_c0 of sn76489_tone is + + for rtl + + for attenuator_b : sn76489_attenuator + use configuration work.sn76489_attenuator_rtl_c0; + end for; + + end for; + +end sn76489_tone_rtl_c0; diff --git a/sn76489-1.0/sn76489_tone.vhd b/sn76489-1.0/sn76489_tone.vhd new file mode 100644 index 0000000..b71af8b --- /dev/null +++ b/sn76489-1.0/sn76489_tone.vhd @@ -0,0 +1,191 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_tone.vhd,v 1.5 2006/02/27 20:30:10 arnim Exp $ +-- +-- Tone Generator +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sn76489_tone is + + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + we_i : in boolean; + d_i : in std_logic_vector(0 to 7); + r2_i : in std_logic; + ff_o : out std_logic; + tone_o : out signed(0 to 7) + ); + +end sn76489_tone; + + +use work.sn76489_comp_pack.sn76489_attenuator; + +architecture rtl of sn76489_tone is + + signal f_q : std_logic_vector(0 to 9); + signal a_q : std_logic_vector(0 to 3); + signal freq_cnt_q : unsigned(0 to 9); + signal freq_ff_q : std_logic; + + signal freq_s : signed(0 to 1); + + function all_zero(a : in std_logic_vector) return boolean is + variable result_v : boolean; + begin + result_v := true; + + for idx in a'low to a'high loop + if a(idx) /= '0' then + result_v := false; + end if; + end loop; + + return result_v; + end; + +begin + + ----------------------------------------------------------------------------- + -- Process cpu_regs + -- + -- Purpose: + -- Implements the registers writable by the CPU. + -- + cpu_regs: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + f_q <= (others => '0'); + a_q <= (others => '1'); + + elsif clock_i'event and clock_i = '1' then + if clk_en_i and we_i then + if r2_i = '0' then + -- access to frequency register + if d_i(0) = '0' then + f_q(0 to 5) <= d_i(2 to 7); + else + f_q(6 to 9) <= d_i(4 to 7); + end if; + + else + -- access to attenuator register + -- both access types can write to the attenuator register! + a_q <= d_i(4 to 7); + + end if; + end if; + end if; + end process cpu_regs; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Process freq_gen + -- + -- Purpose: + -- Implements the frequency generation components. + -- + freq_gen: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + freq_cnt_q <= (others => '0'); + freq_ff_q <= '0'; + + elsif clock_i'event and clock_i = '1' then + if clk_en_i then + if freq_cnt_q = 0 then + -- update counter from frequency register + freq_cnt_q <= unsigned(f_q); + + -- and toggle the frequency flip-flop if enabled + if not all_zero(f_q) then + freq_ff_q <= not freq_ff_q; + else + -- if frequency setting is 0, then keep flip-flop at +1 + freq_ff_q <= '1'; + end if; + + else + -- decrement frequency counter + freq_cnt_q <= freq_cnt_q - 1; + + end if; + end if; + end if; + end process freq_gen; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Map frequency flip-flop to signed value for attenuator. + ----------------------------------------------------------------------------- + freq_s <= to_signed(+1, 2) + when freq_ff_q = '1' else + to_signed(-1, 2); + + + ----------------------------------------------------------------------------- + -- The attenuator itself + ----------------------------------------------------------------------------- + attenuator_b : sn76489_attenuator + port map ( + attenuation_i => a_q, + factor_i => freq_s, + product_o => tone_o + ); + + + ----------------------------------------------------------------------------- + -- Output mapping + ----------------------------------------------------------------------------- + ff_o <= freq_ff_q; + +end rtl; diff --git a/sn76489-1.0/sn76489_top-c.vhd b/sn76489-1.0/sn76489_top-c.vhd new file mode 100644 index 0000000..8f709b9 --- /dev/null +++ b/sn76489-1.0/sn76489_top-c.vhd @@ -0,0 +1,31 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_top-c.vhd,v 1.3 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_top_struct_c0 of sn76489_top is + + for struct + + for clock_div_b : sn76489_clock_div + use configuration work.sn76489_clock_div_rtl_c0; + end for; + + for latch_ctrl_b : sn76489_latch_ctrl + use configuration work.sn76489_latch_ctrl_rtl_c0; + end for; + + for all : sn76489_tone + use configuration work.sn76489_tone_rtl_c0; + end for; + + for noise_b : sn76489_noise + use configuration work.sn76489_noise_rtl_c0; + end for; + + end for; + +end sn76489_top_struct_c0; diff --git a/sn76489-1.0/sn76489_top.vhd b/sn76489-1.0/sn76489_top.vhd new file mode 100644 index 0000000..d08fc14 --- /dev/null +++ b/sn76489-1.0/sn76489_top.vhd @@ -0,0 +1,202 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_top.vhd,v 1.9 2006/02/27 20:30:10 arnim Exp $ +-- +-- Chip Toplevel +-- +-- References: +-- +-- * TI Data sheet SN76489.pdf +-- ftp://ftp.whtech.com/datasheets%20&%20manuals/SN76489.pdf +-- +-- * John Kortink's article on the SN76489: +-- http://web.inter.nl.net/users/J.Kortink/home/articles/sn76489/ +-- +-- * Maxim's "SN76489 notes" in +-- http://www.smspower.org/maxim/docs/SN76489.txt +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sn76489_top is + + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + ready_o : out std_logic; + d_i : in std_logic_vector(0 to 7); + aout_o : out signed(0 to 7) + ); + +end sn76489_top; + + +library ieee; +use ieee.numeric_std.all; +use work.sn76489_comp_pack.all; + +architecture struct of sn76489_top is + + signal clk_en_s : boolean; + + signal tone1_we_s, + tone2_we_s, + tone3_we_s, + noise_we_s : boolean; + signal r2_s : std_logic; + + signal tone1_s, + tone2_s, + tone3_s, + noise_s : signed(0 to 7); + + signal tone3_ff_s : std_logic; + +begin + + ----------------------------------------------------------------------------- + -- Clock Divider + ----------------------------------------------------------------------------- + clock_div_b : sn76489_clock_div + generic map ( + clock_div_16_g => clock_div_16_g + ) + port map ( + clock_i => clock_i, + clock_en_i => clock_en_i, + res_n_i => res_n_i, + clk_en_o => clk_en_s + ); + + + ----------------------------------------------------------------------------- + -- Latch Control = CPU Interface + ----------------------------------------------------------------------------- + latch_ctrl_b : sn76489_latch_ctrl + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + ce_n_i => ce_n_i, + we_n_i => we_n_i, + d_i => d_i, + ready_o => ready_o, + tone1_we_o => tone1_we_s, + tone2_we_o => tone2_we_s, + tone3_we_o => tone3_we_s, + noise_we_o => noise_we_s, + r2_o => r2_s + ); + + + ----------------------------------------------------------------------------- + -- Tone Channel 1 + ----------------------------------------------------------------------------- + tone1_b : sn76489_tone + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + we_i => tone1_we_s, + d_i => d_i, + r2_i => r2_s, + ff_o => open, + tone_o => tone1_s + ); + + ----------------------------------------------------------------------------- + -- Tone Channel 2 + ----------------------------------------------------------------------------- + tone2_b : sn76489_tone + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + we_i => tone2_we_s, + d_i => d_i, + r2_i => r2_s, + ff_o => open, + tone_o => tone2_s + ); + + ----------------------------------------------------------------------------- + -- Tone Channel 3 + ----------------------------------------------------------------------------- + tone3_b : sn76489_tone + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + we_i => tone3_we_s, + d_i => d_i, + r2_i => r2_s, + ff_o => tone3_ff_s, + tone_o => tone3_s + ); + + ----------------------------------------------------------------------------- + -- Noise Channel + ----------------------------------------------------------------------------- + noise_b : sn76489_noise + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + we_i => noise_we_s, + d_i => d_i, + r2_i => r2_s, + tone3_ff_i => tone3_ff_s, + noise_o => noise_s + ); + + + aout_o <= tone1_s + tone2_s + tone3_s + noise_s; + +end struct; -- cgit v1.2.3