diff options
-rw-r--r-- | README | 6 | ||||
-rw-r--r-- | bbc_micro_de1.qsf | 25 | ||||
-rw-r--r-- | bbc_micro_de1.vhd | 123 | ||||
-rw-r--r-- | i2c_loader.vhd | 300 | ||||
-rw-r--r-- | i2s_intf.vhd | 191 | ||||
-rw-r--r-- | sn76489-1.0/COPYING | 340 | ||||
-rw-r--r-- | sn76489-1.0/README | 143 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_attenuator-c.vhd | 14 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_attenuator.vhd | 114 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_clock_div-c.vhd | 14 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_clock_div.vhd | 134 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_comp_pack-p.vhd | 96 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_latch_ctrl-c.vhd | 14 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_latch_ctrl.vhd | 138 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_noise-c.vhd | 19 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_noise.vhd | 281 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_tone-c.vhd | 19 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_tone.vhd | 191 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_top-c.vhd | 31 | ||||
-rw-r--r-- | sn76489-1.0/sn76489_top.vhd | 202 |
20 files changed, 2386 insertions, 9 deletions
@@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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. + + <signature of Ty Coon>, 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; |