This commit is contained in:
aschi54
2010-12-27 13:20:36 +00:00
commit 92c2ab95fc
427 changed files with 91737 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
----------------------------------------------------------------------
---- ----
---- YM2149 compatible sound generator. ----
---- ----
---- This file is part of the SUSKA ATARI clone project. ----
---- http://www.experiment-s.de ----
---- ----
---- Description: ----
---- Model of the ST or STE's YM2149 sound generator. ----
---- ----
---- This is the package file containing the component ----
---- declarations. ----
---- ----
---- ----
---- To Do: ----
---- - ----
---- ----
---- Author(s): ----
---- - Wolfgang Foerster, wf@experiment-s.de; wf@inventronik.de ----
---- ----
----------------------------------------------------------------------
---- ----
---- Copyright (C) 2006 - 2008 Wolfgang Foerster ----
---- ----
---- This source file may be used and distributed without ----
---- restriction provided that this copyright statement is not ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer. ----
---- ----
---- This source file is free software; you can redistribute it ----
---- and/or modify it under the terms of the GNU Lesser General ----
---- Public License as published by the Free Software Foundation; ----
---- either version 2.1 of the License, or (at your option) any ----
---- later version. ----
---- ----
---- This source 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 Lesser General Public License for more ----
---- details. ----
---- ----
---- You should have received a copy of the GNU Lesser General ----
---- Public License along with this source; if not, download it ----
---- from http://www.gnu.org/licenses/lgpl.html ----
---- ----
----------------------------------------------------------------------
--
-- Revision History
--
-- Revision 2K6A 2006/06/03 WF
-- Initial Release.
-- Revision 2K6B 2006/11/07 WF
-- Modified Source to compile with the Xilinx ISE.
-- Revision 2K8A 2008/07/14 WF
-- Minor changes.
--
library ieee;
use ieee.std_logic_1164.all;
package WF2149IP_PKG is
type BUSCYCLES is (INACTIVE, R_READ, R_WRITE, ADDRESS);
component WF2149IP_WAVE
port(
RESETn : in bit;
SYS_CLK : in bit;
WAV_STRB : in bit;
ADR : in bit_vector(3 downto 0);
DATA_IN : in std_logic_vector(7 downto 0);
DATA_OUT : out std_logic_vector(7 downto 0);
DATA_EN : out bit;
BUSCYCLE : in BUSCYCLES;
CTRL_REG : in bit_vector(5 downto 0);
OUT_A : out bit;
OUT_B : out bit;
OUT_C : out bit
);
end component;
end WF2149IP_PKG;

View File

@@ -0,0 +1,170 @@
----------------------------------------------------------------------
---- ----
---- YM2149 compatible sound generator. ----
---- ----
---- This file is part of the SUSKA ATARI clone project. ----
---- http://www.experiment-s.de ----
---- ----
---- Description: ----
---- Model of the ST or STE's YM2149 sound generator. ----
---- This IP core of the sound generator differs slightly from ----
---- the original. Firstly it is a synchronous design without any ----
---- latches (like assumed in the original chip). This required ----
---- the introduction of a system adequate clock. In detail this ----
---- SYS_CLK should on the one hand be fast enough to meet the ----
---- timing requirements of the system's bus cycle and should one ----
---- the other hand drive the PWM modules correctly. To meet both ----
---- a SYS_CLK of 16MHz or above is recommended. ----
---- Secondly, the original chip has an implemented DA converter. ----
---- This feature is not possible in today's FPGAs. Therefore the ----
---- converter is replaced by pulse width modulators. This solu- ----
---- tion is very simple in comparison to other approaches like ----
---- external DA converters with wave tables etc. The soltution ----
---- with the pulse width modulators is probably not as accurate ----
---- DAs with wavetables. For a detailed descrition of the hard- ----
---- ware PWM filter look at the end of the wave file, where the ----
---- pulse width modulators can be found. ----
---- For a proper operation it is required, that the wave clock ----
---- is lower than the system clock. A good choice is for example ----
---- 2MHz for the wave clock and 16MHz for the system clock. ----
---- ----
---- Main module file. ----
---- ----
---- ----
---- To Do: ----
---- - ----
---- ----
---- Author(s): ----
---- - Wolfgang Foerster, wf@experiment-s.de; wf@inventronik.de ----
---- ----
----------------------------------------------------------------------
---- ----
---- Copyright (C) 2006 Wolfgang Foerster ----
---- ----
---- This source file may be used and distributed without ----
---- restriction provided that this copyright statement is not ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer. ----
---- ----
---- This source file is free software; you can redistribute it ----
---- and/or modify it under the terms of the GNU Lesser General ----
---- Public License as published by the Free Software Foundation; ----
---- either version 2.1 of the License, or (at your option) any ----
---- later version. ----
---- ----
---- This source 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 Lesser General Public License for more ----
---- details. ----
---- ----
---- You should have received a copy of the GNU Lesser General ----
---- Public License along with this source; if not, download it ----
---- from http://www.gnu.org/licenses/lgpl.html ----
---- ----
----------------------------------------------------------------------
--
-- Revision History
--
-- Revision 2K6A 2006/06/03 WF
-- Initial Release.
-- Revision 2K6B 2006/11/07 WF
-- Modified Source to compile with the Xilinx ISE.
-- Revision 2K8B 2008/12/24 WF
-- Rewritten this top level file as a wrapper for the top_soc file.
--
library ieee;
use ieee.std_logic_1164.all;
use work.wf2149ip_pkg.all;
entity WF2149IP_TOP is
port(
SYS_CLK : in bit; -- Read the inforation in the header!
RESETn : in bit;
WAV_CLK : in bit; -- Read the inforation in the header!
SELn : in bit;
BDIR : in bit;
BC2, BC1 : in bit;
A9n, A8 : in bit;
DA : inout std_logic_vector(7 downto 0);
IO_A : inout std_logic_vector(7 downto 0);
IO_B : inout std_logic_vector(7 downto 0);
OUT_A : out bit; -- Analog (PWM) outputs.
OUT_B : out bit;
OUT_C : out bit
);
end WF2149IP_TOP;
architecture STRUCTURE of WF2149IP_TOP is
component WF2149IP_TOP_SOC
port(
SYS_CLK : in bit;
RESETn : in bit;
WAV_CLK : in bit;
SELn : in bit;
BDIR : in bit;
BC2, BC1 : in bit;
A9n, A8 : in bit;
DA_IN : in std_logic_vector(7 downto 0);
DA_OUT : out std_logic_vector(7 downto 0);
DA_EN : out bit;
IO_A_IN : in bit_vector(7 downto 0);
IO_A_OUT : out bit_vector(7 downto 0);
IO_A_EN : out bit;
IO_B_IN : in bit_vector(7 downto 0);
IO_B_OUT : out bit_vector(7 downto 0);
IO_B_EN : out bit;
OUT_A : out bit;
OUT_B : out bit;
OUT_C : out bit
);
end component;
--
signal DA_OUT : std_logic_vector(7 downto 0);
signal DA_EN : bit;
signal IO_A_IN : bit_vector(7 downto 0);
signal IO_A_OUT : bit_vector(7 downto 0);
signal IO_A_EN : bit;
signal IO_B_IN : bit_vector(7 downto 0);
signal IO_B_OUT : bit_vector(7 downto 0);
signal IO_B_EN : bit;
begin
IO_A_IN <= To_BitVector(IO_A);
IO_B_IN <= To_BitVector(IO_B);
IO_A <= To_StdLogicVector(IO_A_OUT) when IO_A_EN = '1' else (others => 'Z');
IO_B <= To_StdLogicVector(IO_B_OUT) when IO_B_EN = '1' else (others => 'Z');
DA <= DA_OUT when DA_EN = '1' else (others => 'Z');
I_SOUND: WF2149IP_TOP_SOC
port map(SYS_CLK => SYS_CLK,
RESETn => RESETn,
WAV_CLK => WAV_CLK,
SELn => SELn,
BDIR => BDIR,
BC2 => BC2,
BC1 => BC1,
A9n => A9n,
A8 => A8,
DA_IN => DA,
DA_OUT => DA_OUT,
DA_EN => DA_EN,
IO_A_IN => IO_A_IN,
IO_A_OUT => IO_A_OUT,
IO_A_EN => IO_A_EN,
IO_B_IN => IO_B_IN,
IO_B_OUT => IO_B_OUT,
IO_B_EN => IO_B_EN,
OUT_A => OUT_A,
OUT_B => OUT_B,
OUT_C => OUT_C
);
end STRUCTURE;

View File

@@ -0,0 +1,229 @@
----------------------------------------------------------------------
---- ----
---- YM2149 compatible sound generator. ----
---- ----
---- This file is part of the SUSKA ATARI clone project. ----
---- http://www.experiment-s.de ----
---- ----
---- Description: ----
---- Model of the ST or STE's YM2149 sound generator. ----
---- This IP core of the sound generator differs slightly from ----
---- the original. Firstly it is a synchronous design without any ----
---- latches (like assumed in the original chip). This required ----
---- the introduction of a system adequate clock. In detail this ----
---- SYS_CLK should on the one hand be fast enough to meet the ----
---- timing requirements of the system's bus cycle and should one ----
---- the other hand drive the PWM modules correctly. To meet both ----
---- a SYS_CLK of 16MHz or above is recommended. ----
---- Secondly, the original chip has an implemented DA converter. ----
---- This feature is not possible in today's FPGAs. Therefore the ----
---- converter is replaced by pulse width modulators. This solu- ----
---- tion is very simple in comparison to other approaches like ----
---- external DA converters with wave tables etc. The soltution ----
---- with the pulse width modulators is probably not as accurate ----
---- DAs with wavetables. For a detailed descrition of the hard- ----
---- ware PWM filter look at the end of the wave file, where the ----
---- pulse width modulators can be found. ----
---- For a proper operation it is required, that the wave clock ----
---- is lower than the system clock. A good choice is for example ----
---- 2MHz for the wave clock and 16MHz for the system clock. ----
---- ----
---- Main module file. ----
---- Top level file for use in systems on programmable chips. ----
---- ----
---- ----
---- To Do: ----
---- - ----
---- ----
---- Author(s): ----
---- - Wolfgang Foerster, wf@experiment-s.de; wf@inventronik.de ----
---- ----
----------------------------------------------------------------------
---- ----
---- Copyright (C) 2006 - 2008 Wolfgang Foerster ----
---- ----
---- This source file may be used and distributed without ----
---- restriction provided that this copyright statement is not ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer. ----
---- ----
---- This source file is free software; you can redistribute it ----
---- and/or modify it under the terms of the GNU Lesser General ----
---- Public License as published by the Free Software Foundation; ----
---- either version 2.1 of the License, or (at your option) any ----
---- later version. ----
---- ----
---- This source 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 Lesser General Public License for more ----
---- details. ----
---- ----
---- You should have received a copy of the GNU Lesser General ----
---- Public License along with this source; if not, download it ----
---- from http://www.gnu.org/licenses/lgpl.html ----
---- ----
----------------------------------------------------------------------
--
-- Revision History
--
-- Revision 2K6A 2006/06/03 WF
-- Initial Release.
-- Revision 2K6B 2006/11/07 WF
-- Modified Source to compile with the Xilinx ISE.
-- Top level file provided for SOC (systems on programmable chips).
-- Revision 2K8A 2008/07/14 WF
-- Minor changes.
--
library ieee;
use ieee.std_logic_1164.all;
use work.wf2149ip_pkg.all;
entity WF2149IP_TOP_SOC is
port(
SYS_CLK : in bit; -- Read the inforation in the header!
RESETn : in bit;
WAV_CLK : in bit; -- Read the inforation in the header!
SELn : in bit;
BDIR : in bit;
BC2, BC1 : in bit;
A9n, A8 : in bit;
DA_IN : in std_logic_vector(7 downto 0);
DA_OUT : out std_logic_vector(7 downto 0);
DA_EN : out bit;
IO_A_IN : in bit_vector(7 downto 0);
IO_A_OUT : out bit_vector(7 downto 0);
IO_A_EN : out bit;
IO_B_IN : in bit_vector(7 downto 0);
IO_B_OUT : out bit_vector(7 downto 0);
IO_B_EN : out bit;
OUT_A : out bit; -- Analog (PWM) outputs.
OUT_B : out bit;
OUT_C : out bit
);
end WF2149IP_TOP_SOC;
architecture STRUCTURE of WF2149IP_TOP_SOC is
signal BUSCYCLE : BUSCYCLES;
signal DATA_OUT_I : std_logic_vector(7 downto 0);
signal DATA_EN_I : bit;
signal WAV_STRB : bit;
signal ADR_I : bit_vector(3 downto 0);
signal CTRL_REG : bit_vector(7 downto 0);
signal PORT_A : bit_vector(7 downto 0);
signal PORT_B : bit_vector(7 downto 0);
begin
P_WAVSTRB: process(RESETn, SYS_CLK)
variable LOCK : boolean;
variable TMP : bit;
begin
if RESETn = '0' then
LOCK := false;
TMP := '0';
elsif SYS_CLK = '1' and SYS_CLK' event then
if WAV_CLK = '1' and LOCK = false then
LOCK := true;
TMP := not TMP; -- Divider by 2.
case SELn is
when '1' => WAV_STRB <= '1';
when others => WAV_STRB <= TMP;
end case;
elsif WAV_CLK = '0' then
LOCK := false;
WAV_STRB <= '0';
else
WAV_STRB <= '0';
end if;
end if;
end process P_WAVSTRB;
with BDIR & BC2 & BC1 select
BUSCYCLE <= INACTIVE when "000" | "010" | "101",
ADDRESS when "001" | "100" | "111",
R_READ when "011",
R_WRITE when "110";
ADDRESSLATCH: process(RESETn, SYS_CLK)
-- This process is responsible to store the desired register
-- address. The default (after reset) is channel A fine tone
-- adjustment.
begin
if RESETn = '0' then
ADR_I <= (others => '0');
elsif SYS_CLK = '1' and SYS_CLK' event then
if BUSCYCLE = ADDRESS and A9n = '0' and A8 = '1' and DA_IN(7 downto 4) = x"0" then
ADR_I <= To_BitVector(DA_IN(3 downto 0));
end if;
end if;
end process ADDRESSLATCH;
P_CTRL_REG: process(RESETn, SYS_CLK)
-- THIS is the Control register for the mixer and for the I/O ports.
begin
if RESETn = '0' then
CTRL_REG <= x"00";
elsif SYS_CLK = '1' and SYS_CLK' event then
if BUSCYCLE = R_WRITE and ADR_I = x"7" then
CTRL_REG <= To_BitVector(DA_IN);
end if;
end if;
end process P_CTRL_REG;
DIG_PORTS: process(RESETn, SYS_CLK)
begin
if RESETn = '0' then
PORT_A <= x"00";
PORT_B <= x"00";
elsif SYS_CLK = '1' and SYS_CLK' event then
if BUSCYCLE = R_WRITE and ADR_I = x"E" then
PORT_A <= To_BitVector(DA_IN);
elsif BUSCYCLE = R_WRITE and ADR_I = x"F" then
PORT_B <= To_BitVector(DA_IN);
end if;
end if;
end process DIG_PORTS;
-- Set port direction to input or to output:
IO_A_EN <= '1' when CTRL_REG(6) = '1' else '0';
IO_B_EN <= '1' when CTRL_REG(7) = '1' else '0';
IO_A_OUT <= PORT_A;
IO_B_OUT <= PORT_B;
I_PSG_WAVE: WF2149IP_WAVE
port map(
RESETn => RESETn,
SYS_CLK => SYS_CLK,
WAV_STRB => WAV_STRB,
ADR => ADR_I,
DATA_IN => DA_IN,
DATA_OUT => DATA_OUT_I,
DATA_EN => DATA_EN_I,
BUSCYCLE => BUSCYCLE,
CTRL_REG => CTRL_REG(5 downto 0),
OUT_A => OUT_A,
OUT_B => OUT_B,
OUT_C => OUT_C
);
-- Read the ports and registers:
DA_EN <= '1' when DATA_EN_I = '1' else
'1' when BUSCYCLE = R_READ and ADR_I = x"7" else
'1' when BUSCYCLE = R_READ and ADR_I = x"E" else
'1' when BUSCYCLE = R_READ and ADR_I = x"F" else '0';
DA_OUT <= DATA_OUT_I when DATA_EN_I = '1' else -- WAV stuff.
To_StdLogicVector(IO_A_IN) when BUSCYCLE = R_READ and ADR_I = x"E" else
To_StdLogicVector(IO_B_IN) when BUSCYCLE = R_READ and ADR_I = x"F" else
To_StdLogicVector(CTRL_REG) when BUSCYCLE = R_READ and ADR_I = x"7" else (others => '0');
end STRUCTURE;

View File

@@ -0,0 +1,533 @@
----------------------------------------------------------------------
---- ----
---- YM2149 compatible sound generator. ----
---- ----
---- This file is part of the SUSKA ATARI clone project. ----
---- http://www.experiment-s.de ----
---- ----
---- Description: ----
---- Model of the ST or STE's YM2149 sound generator. ----
---- ----
---- Waveform generator. ----
---- ----
---- ----
---- To Do: ----
---- - ----
---- ----
---- Author(s): ----
---- - Wolfgang Foerster, wf@experiment-s.de; wf@inventronik.de ----
---- ----
----------------------------------------------------------------------
---- ----
---- Copyright (C) 2006 - 2008 Wolfgang Foerster ----
---- ----
---- This source file may be used and distributed without ----
---- restriction provided that this copyright statement is not ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer. ----
---- ----
---- This source file is free software; you can redistribute it ----
---- and/or modify it under the terms of the GNU Lesser General ----
---- Public License as published by the Free Software Foundation; ----
---- either version 2.1 of the License, or (at your option) any ----
---- later version. ----
---- ----
---- This source 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 Lesser General Public License for more ----
---- details. ----
---- ----
---- You should have received a copy of the GNU Lesser General ----
---- Public License along with this source; if not, download it ----
---- from http://www.gnu.org/licenses/lgpl.html ----
---- ----
----------------------------------------------------------------------
--
-- Revision History
--
-- Revision 2K6A 2006/06/03 WF
-- Initial Release.
-- Revision 2K6B 2006/11/07 WF
-- Modified Source to compile with the Xilinx ISE.
-- Revision 2K8A 2008/07/14 WF
-- Minor changes.
-- Revision 2K9A 2009/06/20 WF
-- NOISE_OUT has now synchronous reset to meet preset requirement.
-- Fixed a bug in the envelope generator. Thanks to Lyndon Amsdon finding it.
-- Correction of the schematic given in the end of this file.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use work.wf2149ip_pkg.all;
entity WF2149IP_WAVE is
port(
RESETn : in bit;
SYS_CLK : in bit;
WAV_STRB : in bit;
ADR : in bit_vector(3 downto 0);
DATA_IN : in std_logic_vector(7 downto 0);
DATA_OUT : out std_logic_vector(7 downto 0);
DATA_EN : out bit;
BUSCYCLE : in BUSCYCLES;
CTRL_REG : in bit_vector(5 downto 0);
OUT_A : out bit;
OUT_B : out bit;
OUT_C : out bit
);
end entity WF2149IP_WAVE;
architecture BEHAVIOR of WF2149IP_WAVE is
signal FREQUENCY_A : std_logic_vector(11 downto 0);
signal FREQUENCY_B : std_logic_vector(11 downto 0);
signal FREQUENCY_C : std_logic_vector(11 downto 0);
signal NOISE_FREQ : std_logic_vector(4 downto 0);
signal LEVEL_A : std_logic_vector(4 downto 0);
signal LEVEL_B : std_logic_vector(4 downto 0);
signal LEVEL_C : std_logic_vector(4 downto 0);
signal ENV_FREQ : std_logic_vector(15 downto 0);
signal ENV_SHAPE : std_logic_vector(3 downto 0);
signal ENV_RESET : boolean;
signal ENV_STRB : bit;
signal OSC_A_OUT : bit;
signal OSC_B_OUT : bit;
signal OSC_C_OUT : bit;
signal NOISE_OUT : bit;
signal AUDIO_A : bit;
signal AUDIO_B : bit;
signal AUDIO_C : bit;
signal VOL_ENV : std_logic_vector(4 downto 0);
signal AMPLITUDE_A : std_logic_vector(4 downto 0);
signal AMPLITUDE_B : std_logic_vector(4 downto 0);
signal AMPLITUDE_C : std_logic_vector(4 downto 0);
signal VOLUME_A : std_logic_vector(7 downto 0);
signal VOLUME_B : std_logic_vector(7 downto 0);
signal VOLUME_C : std_logic_vector(7 downto 0);
signal PWM_RAMP : std_logic_vector(7 downto 0);
begin
REGISTERS: process(RESETn, SYS_CLK)
-- This process is responsible for initialisation
-- and write access to the configuration registers.
begin
if RESETn = '0' then
FREQUENCY_A <= x"000";
FREQUENCY_B <= x"000";
FREQUENCY_C <= x"000";
NOISE_FREQ <= "00000";
LEVEL_A <= "00000";
LEVEL_B <= "00000";
LEVEL_C <= "00000";
ENV_FREQ <= (others => '0');
ENV_SHAPE <= "0000";
elsif SYS_CLK = '1' and SYS_CLK' event then
ENV_RESET <= false; -- Initialize signal.
if BUSCYCLE = R_WRITE then
case ADR is
when x"0" => FREQUENCY_A(7 downto 0) <= DATA_IN;
when x"1" => FREQUENCY_A(11 downto 8) <= DATA_IN(3 downto 0);
when x"2" => FREQUENCY_B(7 downto 0) <= DATA_IN;
when x"3" => FREQUENCY_B(11 downto 8) <= DATA_IN(3 downto 0);
when x"4" => FREQUENCY_C(7 downto 0) <= DATA_IN;
when x"5" => FREQUENCY_C(11 downto 8) <= DATA_IN(3 downto 0);
when x"6" => NOISE_FREQ <= DATA_IN(4 downto 0);
when x"8" => LEVEL_A <= DATA_IN(4 downto 0);
when x"9" => LEVEL_B <= DATA_IN(4 downto 0);
when x"A" => LEVEL_C <= DATA_IN(4 downto 0);
when x"B" => ENV_FREQ(7 downto 0) <= DATA_IN;
when x"C" => ENV_FREQ(15 downto 8) <= DATA_IN;
ENV_RESET <= true; -- Initialize the envelope generator.
when x"D" => ENV_SHAPE <= DATA_IN(3 downto 0);
when others => null;
end case;
end if;
end if;
end process REGISTERS;
-- Read back the configuration registers:
DATA_OUT <= FREQUENCY_A(7 downto 0) when BUSCYCLE = R_READ and ADR = x"0" else
"0000" & FREQUENCY_A(11 downto 8) when BUSCYCLE = R_READ and ADR = x"1" else
FREQUENCY_B(7 downto 0) when BUSCYCLE = R_READ and ADR = x"2" else
"0000" & FREQUENCY_B(11 downto 8) when BUSCYCLE = R_READ and ADR = x"3" else
FREQUENCY_C(7 downto 0) when BUSCYCLE = R_READ and ADR = x"4" else
"0000" & FREQUENCY_C(11 downto 8) when BUSCYCLE = R_READ and ADR = x"5" else
"000" & NOISE_FREQ when BUSCYCLE = R_READ and ADR = x"6" else
"000" & LEVEL_A when BUSCYCLE = R_READ and ADR = x"8" else
"000" & LEVEL_B when BUSCYCLE = R_READ and ADR = x"9" else
"000" & LEVEL_C when BUSCYCLE = R_READ and ADR = x"A" else
ENV_FREQ(7 downto 0) when BUSCYCLE = R_READ and ADR = x"B" else
ENV_FREQ(15 downto 8) when BUSCYCLE = R_READ and ADR = x"C" else
x"0" & ENV_SHAPE when BUSCYCLE = R_READ and ADR = x"D" else (others => '0');
DATA_EN <= '1' when BUSCYCLE = R_READ and ADR >= x"0" and ADR <= x"6" else
'1' when BUSCYCLE = R_READ and ADR >= x"8" and ADR <= x"D" else '0';
MUSICGENERATOR: process(RESETn, SYS_CLK)
variable CLK_DIV : std_logic_vector(2 downto 0);
variable CNT_CH_A : std_logic_vector(11 downto 0);
variable CNT_CH_B : std_logic_vector(11 downto 0);
variable CNT_CH_C : std_logic_vector(11 downto 0);
begin
if RESETn = '0' then
CLK_DIV := "000";
CNT_CH_A := (others => '0');
CNT_CH_B := (others => '0');
CNT_CH_C := (others => '0');
OSC_A_OUT <= '0';
OSC_B_OUT <= '0';
OSC_C_OUT <= '0';
elsif SYS_CLK = '1' and SYS_CLK' event then
if WAV_STRB = '1' then
-- Divider by 8 for the oscillators brings in connection
-- with the toggle flip flops CH_x_OUT the required divider
-- ratio of 16.
CLK_DIV := CLK_DIV + '1';
if CLK_DIV = "000" then
if FREQUENCY_A = x"000" then
CNT_CH_A := (others => '0');
OSC_A_OUT <= '0';
elsif CNT_CH_A = x"000" then
CNT_CH_A := FREQUENCY_A - '1' ;
OSC_A_OUT <= not OSC_A_OUT;
else
CNT_CH_A := CNT_CH_A - '1';
end if;
if FREQUENCY_B = x"000" then
CNT_CH_B := (others => '0');
OSC_B_OUT <= '0';
elsif CNT_CH_B = x"000" then
CNT_CH_B := FREQUENCY_B - '1' ;
OSC_B_OUT <= not OSC_B_OUT;
else
CNT_CH_B := CNT_CH_B - '1';
end if;
if FREQUENCY_C = x"000" then
CNT_CH_C := (others => '0');
OSC_C_OUT <= '0';
elsif CNT_CH_C = x"000" then
CNT_CH_C := FREQUENCY_C - '1' ;
OSC_C_OUT <= not OSC_C_OUT;
else
CNT_CH_C := CNT_CH_C - '1';
end if;
end if;
end if;
end if;
end process MUSICGENERATOR;
NOISEGENERATOR: process
-- The noise shift polynomial is taken from a template of Kazuhiro TSUJIKAWA's
-- (ESE Artists' factory) approach for a 2149 equivalent. But the implementation
-- is done in another way.
-- LFSR (linear feedback shift register polynomial: f(x) = x^17 + x^14 + 1.
variable CLK_DIV : std_logic_vector(3 downto 0);
variable CNT_NOISE : std_logic_vector(4 downto 0);
variable N_SHFT : std_logic_vector(16 downto 0);
begin
wait until SYS_CLK = '1' and SYS_CLK' event;
if RESETn = '0' then
CLK_DIV := x"0";
CNT_NOISE := (others => '1'); -- Preset the polynomial shift register.
NOISE_OUT <= '1';
elsif WAV_STRB = '1' then
-- Divider by 16 for the noise generator.
CLK_DIV := CLK_DIV + '1';
if CLK_DIV = x"0" then
-- Noise frequency counter.
if NOISE_FREQ = "00000" then
CNT_NOISE := (others => '0');
elsif CNT_NOISE = "00000" then
CNT_NOISE := NOISE_FREQ - '1' ;
N_SHFT := N_SHFT(15 downto 14) & not(N_SHFT(16) xor N_SHFT(13)) &
N_SHFT(12 downto 0) & not N_SHFT(16);
else
CNT_NOISE := CNT_NOISE - '1';
end if;
end if;
end if;
NOISE_OUT <= To_Bit(N_SHFT(16));
end process NOISEGENERATOR;
ENVELOPE_PERIOD: process(RESETn, SYS_CLK)
-- The envelope period is controlled by the Envelope Frequency and the divider ratio which is
-- 256/32 = 8. For further information see the original data sheet.
variable ENV_CLK : std_logic_vector(18 downto 0);
variable LOCK : boolean;
begin
if RESETn = '0' then
ENV_STRB <= '0';
ENV_CLK := (others => '0');
LOCK := false;
elsif SYS_CLK = '1' and SYS_CLK' event then
if WAV_STRB = '1' and LOCK = false then
LOCK := true;
if ENV_FREQ = x"0000" then
ENV_STRB <= '0';
elsif ENV_CLK = x"0000" & "000" then
ENV_CLK := (ENV_FREQ & "111") - '1' ;
ENV_STRB <= '1';
else
ENV_CLK := ENV_CLK - '1';
ENV_STRB <= '0';
end if;
elsif WAV_STRB = '0' then
LOCK := false;
ENV_STRB <= '0';
else
ENV_STRB <= '0';
end if;
end if;
end process ENVELOPE_PERIOD;
ENVELOPE: process(RESETn, SYS_CLK)
-- Envelope shapes:
-- case ENV_SHAPE:
--
-- 0 0 x x \___
--
-- 0 1 x x /|___
--
-- 1 0 0 0 _|\|\|\|\|
--
-- 1 0 0 1 \___
--
-- 1 0 1 0 \/\/
-- ___
-- 1 0 1 1 \|
--
-- 1 1 0 0 /|/|/|/|
-- ___
-- 1 1 0 1 /
--
-- 1 1 1 0 /\/\
--
-- 1 1 1 1 /|___
--
variable ENV_STOP : boolean;
variable ENV_UP_DNn : bit;
begin
if RESETn = '0' then
VOL_ENV <= (others => '0');
ENV_UP_DNn := '0';
ENV_STOP := false;
elsif SYS_CLK = '1' and SYS_CLK' event then
if ENV_RESET = true then
ENV_STOP := false;
case ENV_SHAPE is
when "1011" | "1010" | "1001" | "1000" | "0011" | "0010" | "0001" | "0000" =>
VOL_ENV <= "11111"; -- Start on top.
ENV_UP_DNn := '0';
when others =>
VOL_ENV <= "00000"; -- Start at bottom.
ENV_UP_DNn := '1';
end case;
elsif ENV_STRB = '1' then
case ENV_SHAPE is
when "1001" | "0011" | "0010" | "0001" | "0000" =>
if VOL_ENV > "00000" then
VOL_ENV <= VOL_ENV - '1';
end if;
when "1111" | "0111" | "0110" | "0101" | "0100" =>
if VOL_ENV < "11111" and ENV_STOP = false then
VOL_ENV <= VOL_ENV + '1';
else
VOL_ENV <= "00000";
ENV_STOP := true;
end if;
when "1000" =>
VOL_ENV <= VOL_ENV - '1';
when "1110" | "1010" =>
if ENV_UP_DNn = '0' then
VOL_ENV <= VOL_ENV - '1';
else
VOL_ENV <= VOL_ENV + '1';
end if;
--
if VOL_ENV = "00001" then
ENV_UP_DNn := '1';
elsif VOL_ENV = "11110" then
ENV_UP_DNn := '0';
end if;
when "1011" =>
if VOL_ENV > "00000" and ENV_STOP = false then
VOL_ENV <= VOL_ENV - '1';
else
VOL_ENV <= "11111";
ENV_STOP := true;
end if;
when "1100" =>
VOL_ENV <= VOL_ENV + '1';
when "1101" =>
if VOL_ENV < "11111" then
VOL_ENV <= VOL_ENV + '1';
end if;
when others => null; -- Covers U, X, Z, W, H, L, -.
end case;
end if;
end if;
end process ENVELOPE;
--MIXER:
-- The mixer controls are dependant on the mixer settings and the output of the
-- audio data for all three channels. The noise generator and the square wave
-- generators A, B and C are mixed together by a simple boolean OR.
AUDIO_A <= (OSC_A_OUT and not CTRL_REG(0)) or (NOISE_OUT and not CTRL_REG(3));
AUDIO_B <= (OSC_B_OUT and not CTRL_REG(1)) or (NOISE_OUT and not CTRL_REG(4));
AUDIO_C <= (OSC_C_OUT and not CTRL_REG(2)) or (NOISE_OUT and not CTRL_REG(5));
--LEVEL (e.g. volume control):
-- The linear amplitude for the DA converters of channel A, B or C are fixed
-- (LEVEL(3 downto 0)) or delivered by the envelope generator.
-- The following behavior is taken from the 2149 IP core of Mike J (www.fpgaarcade.com):
-- "make sure level 31 (env) = level 15 (tone)"
-- Thus there is a resulting & '1' modeling if LEVEL amplitudes are selected.
AMPLITUDE_A <= LEVEL_A(3 downto 0) & '1' when LEVEL_A(4) = '0' and AUDIO_A = '1' else
VOL_ENV when LEVEL_A(4) = '1' and AUDIO_A = '1' else "00000";
AMPLITUDE_B <= LEVEL_B(3 downto 0) & '1' when LEVEL_B(4) = '0' and AUDIO_B = '1' else
VOL_ENV when LEVEL_B(4) = '1' and AUDIO_B = '1' else "00000";
AMPLITUDE_C <= LEVEL_C(3 downto 0) & '1' when LEVEL_C(4) = '0' and AUDIO_C = '1' else
VOL_ENV when LEVEL_C(4) = '1' and AUDIO_C = '1' else "00000";
-- The values for the logarithmic DA converter volume controls are taken from the linear
-- mixer of Mike J's 2149 IP core (www.fpgaarcade.com).
with AMPLITUDE_A select
VOLUME_A <= x"FF" when "11111",
x"D9" when "11110",
x"BA" when "11101",
x"9F" when "11100",
x"88" when "11011",
x"74" when "11010",
x"63" when "11001",
x"54" when "11000",
x"48" when "10111",
x"3D" when "10110",
x"34" when "10101",
x"2C" when "10100",
x"25" when "10011",
x"1F" when "10010",
x"1A" when "10001",
x"16" when "10000",
x"13" when "01111",
x"10" when "01110",
x"0D" when "01101",
x"0B" when "01100",
x"09" when "01011",
x"08" when "01010",
x"07" when "01001",
x"06" when "01000",
x"05" when "00111",
x"04" when "00110",
x"03" when "00101",
x"03" when "00100",
x"02" when "00011",
x"02" when "00010",
x"01" when "00001",
x"00" when others; -- Also covers U, X, Z, W, H, L, -.
with AMPLITUDE_B select
VOLUME_B <= x"FF" when "11111",
x"D9" when "11110",
x"BA" when "11101",
x"9F" when "11100",
x"88" when "11011",
x"74" when "11010",
x"63" when "11001",
x"54" when "11000",
x"48" when "10111",
x"3D" when "10110",
x"34" when "10101",
x"2C" when "10100",
x"25" when "10011",
x"1F" when "10010",
x"1A" when "10001",
x"16" when "10000",
x"13" when "01111",
x"10" when "01110",
x"0D" when "01101",
x"0B" when "01100",
x"09" when "01011",
x"08" when "01010",
x"07" when "01001",
x"06" when "01000",
x"05" when "00111",
x"04" when "00110",
x"03" when "00101",
x"03" when "00100",
x"02" when "00011",
x"02" when "00010",
x"01" when "00001",
x"00" when others; -- Also covers U, X, Z, W, H, L, -.
with AMPLITUDE_C select
VOLUME_C <= x"FF" when "11111",
x"D9" when "11110",
x"BA" when "11101",
x"9F" when "11100",
x"88" when "11011",
x"74" when "11010",
x"63" when "11001",
x"54" when "11000",
x"48" when "10111",
x"3D" when "10110",
x"34" when "10101",
x"2C" when "10100",
x"25" when "10011",
x"1F" when "10010",
x"1A" when "10001",
x"16" when "10000",
x"13" when "01111",
x"10" when "01110",
x"0D" when "01101",
x"0B" when "01100",
x"09" when "01011",
x"08" when "01010",
x"07" when "01001",
x"06" when "01000",
x"05" when "00111",
x"04" when "00110",
x"03" when "00101",
x"03" when "00100",
x"02" when "00011",
x"02" when "00010",
x"01" when "00001",
x"00" when others; -- Also covers U, X, Z, W, H, L, -.
DA_CONVERSION: process
-- The DA conversion for the three analog outputs is originally performed by a built in DA converter.
-- For this is not possible in current FPGA designs, the converter is replaced by three PWM units
-- operating at a frequency which is 100 times higher than the highest noise or music frequency which
-- is 2MHz/16 = 125kHz. So the PWM frequency requires about 12.5MHz or more. The design is done for
-- a PWM frequency of 16MHz).
begin
wait until SYS_CLK = '1' and SYS_CLK' event;
PWM_RAMP <= PWM_RAMP + '1';
end process DA_CONVERSION;
OUT_A <= '0' when VOLUME_A = x"00" else '1' when PWM_RAMP < VOLUME_A else '0';
OUT_B <= '0' when VOLUME_B = x"00" else '1' when PWM_RAMP < VOLUME_B else '0';
OUT_C <= '0' when VOLUME_C = x"00" else '1' when PWM_RAMP < VOLUME_C else '0';
--
-- To obtain proper analog output it is necessary to install analog RC filters to the pulse width
-- outputs. An example is given for the direct wiring of the three analog outputs and for a system
-- clock frequency of 16MHz. The output circuitry looks in this case as follows:
--
-- OUT_A ---------|1kOhm|-----------| |\ e.g. LM741
-- |----------------------|+\ ||
-- OUT_B ---------|1kOhm|-----------| | OP------||--- Analog Signal
-- | |-----|-/ | ||
-- OUT_C ---------|1kOhm|-----------| | |/ | 4u7
-- | |__________|
-- |
-- --- 10nF.
-- ---
-- |
-- |
-- ---
-- WF.
end architecture BEHAVIOR;