OpenCores
URL https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk

Subversion Repositories open8_urisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /open8_urisc/trunk/VHDL
    from Rev 164 to Rev 167
    Reverse comparison

Rev 164 → Rev 167

/o8_pit.vhd
0,0 → 1,154
-- Copyright (c)2013 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary 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 binary 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,
-- where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``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 JEREMY SETH HENRY 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.
--
-- VHDL Units : o8_pit
-- Description: Provides an 8-bit microsecond resolution timer for generating
-- : periodic interrupts for the Open8 CPU.
--
-- Notes : It is possible to set the value to zero, resulting in the
-- : output staying high indefinitely. This may cause an issue if
-- : the output is connected to an interrupt input.
-- : Also provides uSec_Tick as an output
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_misc.all;
 
library work;
use work.open8_pkg.all;
 
entity o8_pit is
generic(
Sys_Freq : real;
Reset_Level : std_logic;
Address : ADDRESS_TYPE
);
port(
Clock : in std_logic;
Reset : in std_logic;
uSec_Tick : out std_logic;
--
Bus_Address : in ADDRESS_TYPE;
Wr_Enable : in std_logic;
Wr_Data : in DATA_TYPE;
Rd_Enable : in std_logic;
Rd_Data : out DATA_TYPE;
--
Tmr_Out : out std_logic
);
end entity;
 
architecture behave of o8_pit is
 
constant User_Addr : ADDRESS_TYPE := Address;
alias Comp_Addr is Bus_Address(15 downto 0);
signal Addr_Match : std_logic;
signal Wr_En : std_logic;
signal Wr_Data_q : DATA_TYPE;
signal Rd_En : std_logic;
signal Rd_En_q : std_logic;
 
signal Interval : DATA_TYPE;
signal Timer_Cnt : DATA_TYPE;
 
-- The ceil_log2 function returns the minimum register width required to
-- hold the supplied integer.
function ceil_log2 (x : in natural) return natural is
variable retval : natural;
begin
retval := 1;
while ((2**retval) - 1) < x loop
retval := retval + 1;
end loop;
return retval;
end ceil_log2;
 
constant DLY_1USEC_VAL: integer := integer(Sys_Freq / 1000000.0);
constant DLY_1USEC_WDT: integer := ceil_log2(DLY_1USEC_VAL - 1);
constant DLY_1USEC : std_logic_vector :=
conv_std_logic_vector( DLY_1USEC_VAL - 1, DLY_1USEC_WDT);
 
signal uSec_Cntr : std_logic_vector( DLY_1USEC_WDT - 1 downto 0 )
:= (others => '0');
signal uSec_Tick_i : std_logic;
begin
 
uSec_Tick <= uSec_Tick_i;
Addr_Match <= '1' when Comp_Addr = User_Addr else '0';
 
io_reg: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
Wr_En <= '0';
Wr_Data_q <= x"00";
Rd_En <= '0';
Rd_Data <= x"00";
Interval <= x"00";
elsif( rising_edge( Clock ) )then
Wr_En <= Addr_Match and Wr_Enable;
Wr_Data_q <= Wr_Data;
if( Wr_En = '1' )then
Interval <= Wr_Data_q;
end if;
 
Rd_Data <= (others => '0');
Rd_En <= Addr_Match and Rd_Enable;
if( Rd_En = '1' )then
Rd_Data <= Interval;
end if;
end if;
end process;
 
uSec_Tick_i_proc: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
uSec_Cntr <= (others => '0');
uSec_Tick_i <= '0';
elsif( rising_edge( Clock ) )then
uSec_Cntr <= uSec_Cntr - 1;
uSec_Tick_i <= '0';
if( uSec_Cntr = 0 )then
uSec_Cntr <= DLY_1USEC;
uSec_Tick_i <= or_reduce(Interval);
end if;
end if;
end process;
 
Interval_proc: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
Timer_Cnt <= x"00";
Tmr_Out <= '0';
elsif( rising_edge(Clock) )then
Tmr_Out <= '0';
Timer_Cnt <= Timer_Cnt - uSec_Tick_i;
if( or_reduce(Timer_Cnt) = '0' )then
Timer_Cnt <= Interval;
Tmr_Out <= or_reduce(Interval); -- Only issue output on Int > 0
end if;
end if;
end process;
 
end architecture;
/o8_gpin.vhd
0,0 → 1,78
-- Copyright (c)2013 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary 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 binary 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,
-- where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``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 JEREMY SETH HENRY 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.
--
-- VHDL Units : o8_gpin
-- Description: Provides a single 8-bit input register
 
library ieee;
use ieee.std_logic_1164.all;
 
library work;
use work.open8_pkg.all;
 
entity o8_gpin is
generic(
Reset_Level : std_logic;
Address : ADDRESS_TYPE
);
port(
Clock : in std_logic;
Reset : in std_logic;
--
Bus_Address : in ADDRESS_TYPE;
Rd_Enable : in std_logic;
Rd_Data : out DATA_TYPE;
--
GPIN : in DATA_TYPE
);
end entity;
 
architecture behave of o8_gpin is
 
constant User_Addr : std_logic_vector(15 downto 0) := Address;
alias Comp_Addr is Bus_Address(15 downto 0);
signal Addr_Match : std_logic;
signal Rd_En : std_logic;
signal User_In : DATA_TYPE;
 
begin
 
Addr_Match <= Rd_Enable when Comp_Addr = User_Addr else '0';
 
io_reg: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
Rd_En <= '0';
Rd_Data <= x"00";
elsif( rising_edge( Clock ) )then
User_In <= GPIN; -- first stage of double buffer
 
Rd_Data <= (others => '0');
Rd_En <= Addr_Match;
if( Rd_En = '1' )then
Rd_Data <= User_In;
end if;
end if;
end process;
 
end architecture;
/o8_gpio.vhd
0,0 → 1,144
-- Copyright (c)2013 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary 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 binary 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,
-- where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``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 JEREMY SETH HENRY 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.
--
-- VHDL Units : o8_gpio
-- Description: Provides a single 8-bit GPIO register
 
library ieee;
use ieee.std_logic_1164.all;
 
library work;
use work.open8_pkg.all;
 
entity o8_gpio is
generic(
Default_Out : DATA_TYPE := x"00";
Default_En : DATA_TYPE := x"00";
Reset_Level : std_logic := '1';
Input_Only : boolean := false;
Address : ADDRESS_TYPE
);
port(
Clock : in std_logic;
Reset : in std_logic;
--
Bus_Address : in ADDRESS_TYPE;
Wr_Enable : in std_logic;
Wr_Data : in DATA_TYPE;
Rd_Enable : in std_logic;
Rd_Data : out DATA_TYPE;
--
GPIO : inout DATA_TYPE
);
end entity;
 
architecture behave of o8_gpio is
 
constant User_Addr : std_logic_vector(15 downto 2)
:= Address(15 downto 2);
alias Comp_Addr is Bus_Address(15 downto 2);
alias Reg_Addr is Bus_Address(1 downto 0);
signal Reg_Sel : std_logic_vector(1 downto 0);
signal Addr_Match : std_logic;
signal Wr_En : std_logic;
signal Wr_Data_q : DATA_TYPE;
signal Rd_En : std_logic;
 
signal User_Out : DATA_TYPE;
signal User_En : DATA_TYPE;
signal User_In : DATA_TYPE;
 
begin
 
Addr_Match <= '1' when Comp_Addr = User_Addr else '0';
 
io_reg: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
Reg_Sel <= "00";
Rd_En <= '0';
Rd_Data <= x"00";
if( not Input_Only )then
Wr_En <= '0';
Wr_Data_q <= x"00";
User_Out <= Default_Out;
User_En <= Default_En;
end if;
elsif( rising_edge( Clock ) )then
Reg_Sel <= Reg_Addr;
 
if( not Input_Only )then
Wr_En <= Addr_Match and Wr_Enable;
Wr_Data_q <= Wr_Data;
if( Wr_En = '1' )then
case( Reg_Sel )is
when "00" =>
User_Out <= Wr_Data_q;
when "01" =>
User_En <= Wr_Data_q;
when others => null;
end case;
end if;
end if;
 
User_In <= GPIO;
 
Rd_Data <= (others => '0');
Rd_En <= Addr_Match and Rd_Enable;
if( Rd_En = '1' )then
if( Input_Only )then
Rd_Data <= User_In;
else
case( Reg_Sel )is
when "00" =>
Rd_Data <= User_Out;
when "01" =>
Rd_Data <= User_En;
when "10" =>
Rd_Data <= User_In;
when others => null;
end case;
end if;
end if;
end if;
end process;
 
Input_Only_is_True: if( Input_Only )generate
GPIO <= (others => 'Z');
end generate;
 
Input_Only_is_False: if( not Input_Only )generate
 
Output_Ctl_proc: process( User_Out, User_En )
begin
for i in 0 to 7 loop
GPIO(i) <= 'Z';
if( User_En(i) = '1' )then
GPIO(i) <= User_Out(i);
end if;
end loop;
end process;
 
end generate;
 
end architecture;
/o8_gpout.vhd
0,0 → 1,134
-- Copyright (c)2013 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary 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 binary 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,
-- where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``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 JEREMY SETH HENRY 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.
--
-- VHDL Units : o8_gpout
-- Description: Provides a single 8-bit GP output register with selectable
-- : tri-state control.
-- Notes : Requires 1 bit from the address bus (two locations).
-- : Sequential instantiations should be separated by 2.
 
library ieee;
use ieee.std_logic_1164.all;
 
library work;
use work.open8_pkg.all;
 
entity o8_gpout is
generic(
Default_Out : DATA_TYPE := x"00";
Default_En : DATA_TYPE := x"00";
Disable_Tristate : boolean := false;
Reset_Level : std_logic;
Address : ADDRESS_TYPE
);
port(
Clock : in std_logic;
Reset : in std_logic;
--
Bus_Address : in ADDRESS_TYPE;
Wr_Enable : in std_logic;
Wr_Data : in DATA_TYPE;
Rd_Enable : in std_logic;
Rd_Data : out DATA_TYPE;
--
GPO : out DATA_TYPE
);
end entity;
 
architecture behave of o8_gpout is
 
constant User_Addr : std_logic_vector(15 downto 1)
:= Address(15 downto 1);
alias Comp_Addr is Bus_Address(15 downto 1);
alias Reg_Addr is Bus_Address(0);
signal Reg_Sel : std_logic;
signal Addr_Match : std_logic;
signal Wr_En : std_logic;
signal Wr_Data_q : DATA_TYPE;
signal Rd_En : std_logic;
 
signal User_Out : DATA_TYPE;
signal User_En : DATA_TYPE;
 
begin
 
Addr_Match <= '1' when Comp_Addr = User_Addr else '0';
 
io_reg: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
Reg_Sel <= '0';
Wr_En <= '0';
Wr_Data_q <= x"00";
Rd_En <= '0';
Rd_Data <= x"00";
User_Out <= Default_Out;
if( not Disable_Tristate)then
User_En <= Default_En;
end if;
elsif( rising_edge( Clock ) )then
Reg_Sel <= Reg_Addr;
Wr_En <= Addr_Match and Wr_Enable;
Wr_Data_q <= Wr_Data;
if( Wr_En = '1' )then
if( Disable_Tristate )then
User_Out <= Wr_Data_q;
else
if( Reg_Sel = '0' )then
User_Out <= Wr_Data_q;
else
User_En <= Wr_Data_q;
end if;
end if;
end if;
 
Rd_Data <= (others => '0');
Rd_En <= Addr_Match and Rd_Enable;
if( Rd_En = '1' )then
Rd_Data <= User_Out;
if( (Reg_Sel = '1') and (not Disable_Tristate) )then
Rd_Data <= User_En;
end if;
end if;
end if;
end process;
 
No_Tristates: if( Disable_Tristate )generate
GPO <= User_Out;
end generate;
 
Tristates: if( not Disable_Tristate )generate
 
Output_Ctl_proc: process( User_Out, User_En )
begin
for i in 0 to 7 loop
GPO(i) <= 'Z';
if( User_En(i) = '1' )then
GPO(i) <= User_Out(i);
end if;
end loop;
end process;
 
end generate;
 
end architecture;
/o8_vdsm8.vhd
0,0 → 1,236
-- Copyright (c)2013 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary 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 binary 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,
-- where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``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 JEREMY SETH HENRY 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.
--
-- VHDL Units : o8_vdsm8
-- Description: 8-bit variable delta-sigma modulator. Requires Open8_pkg.vhd
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
 
library work;
use work.open8_pkg.all;
 
entity o8_vdsm8 is
generic(
Reset_Level : std_logic;
Address : ADDRESS_TYPE
);
port(
Clock : in std_logic;
Reset : in std_logic;
--
Bus_Address : in ADDRESS_TYPE;
Wr_Enable : in std_logic;
Wr_Data : in DATA_TYPE;
Rd_Enable : in std_logic;
Rd_Data : out DATA_TYPE;
--
DACout : out std_logic
);
end entity;
 
architecture behave of o8_vdsm8 is
 
function ceil_log2 (x : in natural) return natural is
variable retval : natural;
begin
retval := 1;
while ((2**retval) - 1) < x loop
retval := retval + 1;
end loop;
return retval;
end function;
 
constant User_Addr : std_logic_vector(15 downto 0) := Address;
alias Comp_Addr is Bus_Address(15 downto 0);
signal Addr_Match : std_logic;
signal Wr_En : std_logic;
signal Wr_Data_q : DATA_TYPE;
signal Rd_En : std_logic;
signal DACin : DATA_TYPE;
-- DAC WIDTH = 8 is fixed, with all constants normalized
-- against 256 (the MAX PERIOD)
constant DAC_WIDTH : integer := 8;
constant DELTA_1_I : integer := 1;
constant DELTA_2_I : integer := 5;
constant DELTA_3_I : integer := 25;
constant DELTA_4_I : integer := 75;
constant DELTA_5_I : integer := 125;
constant DELTA_6_I : integer := 195;
 
constant DELTA_1 : std_logic_vector(DAC_WIDTH - 1 downto 0) :=
conv_std_logic_vector(DELTA_1_I, DAC_WIDTH);
constant DELTA_2 : std_logic_vector(DAC_WIDTH - 1 downto 0) :=
conv_std_logic_vector(DELTA_2_I, DAC_WIDTH);
constant DELTA_3 : std_logic_vector(DAC_WIDTH - 1 downto 0) :=
conv_std_logic_vector(DELTA_3_I, DAC_WIDTH);
constant DELTA_4 : std_logic_vector(DAC_WIDTH - 1 downto 0) :=
conv_std_logic_vector(DELTA_4_I, DAC_WIDTH);
constant DELTA_5 : std_logic_vector(DAC_WIDTH - 1 downto 0) :=
conv_std_logic_vector(DELTA_5_I, DAC_WIDTH);
constant DELTA_6 : std_logic_vector(DAC_WIDTH - 1 downto 0) :=
conv_std_logic_vector(DELTA_6_I, DAC_WIDTH);
 
constant MAX_PERIOD : integer := 2**DAC_WIDTH;
constant DIV_WIDTH : integer := 2 * DAC_WIDTH;
 
constant PADJ_1_I : integer := DELTA_1_I * MAX_PERIOD;
constant PADJ_2_I : integer := DELTA_2_I * MAX_PERIOD;
constant PADJ_3_I : integer := DELTA_3_I * MAX_PERIOD;
constant PADJ_4_I : integer := DELTA_4_I * MAX_PERIOD;
constant PADJ_5_I : integer := DELTA_5_I * MAX_PERIOD;
constant PADJ_6_I : integer := DELTA_6_I * MAX_PERIOD;
 
constant PADJ_1 : std_logic_vector(DIV_WIDTH-1 downto 0) :=
conv_std_logic_vector(PADJ_1_I,DIV_WIDTH);
constant PADJ_2 : std_logic_vector(DIV_WIDTH-1 downto 0) :=
conv_std_logic_vector(PADJ_2_I,DIV_WIDTH);
constant PADJ_3 : std_logic_vector(DIV_WIDTH-1 downto 0) :=
conv_std_logic_vector(PADJ_3_I,DIV_WIDTH);
constant PADJ_4 : std_logic_vector(DIV_WIDTH-1 downto 0) :=
conv_std_logic_vector(PADJ_4_I,DIV_WIDTH);
constant PADJ_5 : std_logic_vector(DIV_WIDTH-1 downto 0) :=
conv_std_logic_vector(PADJ_5_I,DIV_WIDTH);
constant PADJ_6 : std_logic_vector(DIV_WIDTH-1 downto 0) :=
conv_std_logic_vector(PADJ_6_I,DIV_WIDTH);
 
signal DACin_q : DATA_TYPE;
 
signal Divisor : std_logic_vector(DIV_WIDTH-1 downto 0);
signal Dividend : std_logic_vector(DIV_WIDTH-1 downto 0);
 
signal q : std_logic_vector(DIV_WIDTH*2-1 downto 0);
signal diff : std_logic_vector(DIV_WIDTH downto 0);
 
constant CB : integer := ceil_log2(DIV_WIDTH);
signal count : std_logic_vector(CB-1 downto 0);
 
signal Next_Width : DATA_TYPE;
signal Next_Period : DATA_TYPE;
 
signal PWM_Width : DATA_TYPE;
signal PWM_Period : DATA_TYPE;
 
signal Width_Ctr : DATA_TYPE;
signal Period_Ctr : DATA_TYPE;
 
begin
 
Addr_Match <= '1' when Comp_Addr = User_Addr else '0';
 
io_reg: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
Wr_En <= '0';
Wr_Data_q <= x"00";
Rd_En <= '0';
Rd_Data <= x"00";
DACin <= x"00";
elsif( rising_edge( Clock ) )then
Wr_En <= Addr_Match and Wr_Enable;
Wr_Data_q <= Wr_Data;
if( Wr_En = '1' )then
DACin <= Wr_Data_q;
end if;
 
Rd_Data <= (others => '0');
Rd_En <= Addr_Match and Rd_Enable;
if( Rd_En = '1' )then
Rd_Data <= DACin;
end if;
end if;
end process;
 
diff <= ('0' & q(DIV_WIDTH*2-2 downto DIV_WIDTH-1)) -
('0' & Divisor);
 
Dividend <= PADJ_2 when DACin_q >= DELTA_2_I and DACin_q < DELTA_3_I else
PADJ_3 when DACin_q >= DELTA_3_I and DACin_q < DELTA_4_I else
PADJ_4 when DACin_q >= DELTA_4_I and DACin_q < DELTA_5_I else
PADJ_5 when DACin_q >= DELTA_5_I and DACin_q < DELTA_6_I else
PADJ_6 when DACin_q >= DELTA_6_I else
PADJ_1;
 
Next_Width <= DELTA_1 when DACin_q >= DELTA_1_I and DACin_q < DELTA_2_I else
DELTA_2 when DACin_q >= DELTA_2_I and DACin_q < DELTA_3_I else
DELTA_3 when DACin_q >= DELTA_3_I and DACin_q < DELTA_4_I else
DELTA_4 when DACin_q >= DELTA_4_I and DACin_q < DELTA_5_I else
DELTA_5 when DACin_q >= DELTA_5_I and DACin_q < DELTA_6_I else
DELTA_6 when DACin_q >= DELTA_6_I else
(others => '0');
 
Next_Period <= q(7 downto 0) - 1;
vDSM_proc: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
q <= (others => '0');
count <= (others => '1');
Divisor <= (others => '0');
DACin_q <= (others => '0');
PWM_Width <= (others => '0');
PWM_Period <= (others => '0');
Period_Ctr <= (others => '0');
Width_Ctr <= (others => '0');
DACout <= '0';
elsif( rising_edge(Clock) )then
q <= diff(DIV_WIDTH-1 downto 0) &
q(DIV_WIDTH-2 downto 0) & '1';
if( diff(DIV_WIDTH) = '1' )then
q <= q(DIV_WIDTH*2-2 downto 0) & '0';
end if;
 
count <= count + 1;
if( count = DIV_WIDTH )then
PWM_Width <= Next_Width;
PWM_Period <= Next_Period;
DACin_q <= DACin;
Divisor <= (others => '0');
Divisor(7 downto 0) <= DACin_q;
q <= conv_std_logic_vector(0,DIV_WIDTH) & Dividend;
count <= (others => '0');
end if;
 
Period_Ctr <= Period_Ctr - 1;
Width_Ctr <= Width_Ctr - 1;
 
DACout <= '1';
if( Width_Ctr = 0 )then
DACout <= '0';
Width_Ctr <= (others => '0');
end if;
 
if( Period_Ctr = 0 )then
Period_Ctr <= PWM_Period;
Width_Ctr <= PWM_Width;
end if;
 
end if;
end process;
 
end architecture;
/Open8.vhd
1,4 → 1,4
-- Copyright (c)2006, Jeremy Seth Henry
-- Copyright (c)2006,2013 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
20,10 → 20,20
-- 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.
 
--
-- VHDL Units : Open8_CPU
-- Description: VHDL model of the V8 uRISC 8-bit processor core
-- Description: VHDL model of a RISC 8-bit processor core loosely based on the
-- : V8/ARC uRISC instruction set. Requires Open8_pkg.vhd
-- :
-- Notes : Generic definitions
-- :
-- : Program_Start_Addr - Determines the initial value of the
-- : program counter.
-- :
-- : ISR_Start_Addr - determines the location of the interrupt
-- : service vector table. There are 8 service vectors, or 16
-- : bytes, which must be allocated to either ROM or RAM.
-- :
-- : Stack_Start_Address - determines the initial (reset) value of
-- : the stack pointer. Also used for the RSP instruction if
-- : Allow_Stack_Address_Move is 0.
32,68 → 42,94
-- : programmed via thet RSP instruction. If enabled, the contents
-- : of R1:R0 are used to initialize the stack pointer.
-- :
-- : ISR_Start_Addr - determines the location of the interrupt
-- : service vector table. There are 8 service vectors, or 16
-- : bytes, which must be allocated to either ROM or RAM.
-- : The Enable_Auto_Increment generic can be used to modify the
-- : indexed instructions such that specifying an odd register
-- : will use the next lower register pair, post-incrementing the
-- : value in that pair. IOW, specifying STX R1 will instead
-- : result in STX R0++, or R0 = {R1:R0}; {R1:R0} + 1
-- :
-- : Program_Start_Addr - Determines the initial value of the
-- : program counter.
-- : BRK_Implements_WAI modifies the BRK instruction such that it
-- : triggers the wait for interrupt state, but without triggering
-- : a soft interrupt in lieu of its normal behavior, which is to
-- : insert several dead clock cycles - essentially a long NOP
-- :
-- : Enable_NMI overrides the mask bit for interrupt 0, creating a
-- : non-maskable interrupt at the highest priority.
-- :
-- : Default_Interrupt_Mask - Determines the intial value of the
-- : interrupt mask. To remain true to the original core, which
-- : had no interrupt mask, this should be set to x"FF". Otherwise
-- : it can be initialized to any value.
-- : it can be initialized to any value. Enable_NMI will logically
-- : force the LSB high.
-- :
-- : Reset_Level - Determines whether the processor registers reset
-- : on a high or low level from higher logic.
-- :
-- : Enable_CPU_Halt - determines whether the CPU_Halt pin is
-- : connected or not. This signal is typically used to halt the
-- : processor for a few cycles when accessing slower peripherals,
-- : but may also be used to single step the processor. If this
-- : feature isn't used, it can be disabled to increase Fmax.
-- : Architecture notes
-- : This model deviates from the original ISA in a few important
-- : ways.
-- :
-- : The CPU_Halt signal can be used to access slower peripherals
-- : by allowing the device to "pause" the CPU. This can be used,
-- : for example, to write to a standard LCD panel, which requires
-- : a 4MHz interface, by halting on writes. Alternately, devices
-- : such as SDRAM controllers, can pause the processor until the
-- : data is ready to be presented.
-- : First, there is only one set of registers. Interrupt service
-- : routines must explicitely preserve context since the the
-- : hardware doesn't. This was done to decrease size and code
-- : complexity. Older code that assumes this behavior will not
-- : execute correctly on this processor model.
-- :
-- : The Enable_Auto_Increment generic can be used to modify the
-- : indexed instructions such that specifying an odd register
-- : will use the next lower register pair, post-incrementing the
-- : value in that pair. IOW, specifying STX R1 will instead
-- : result in STX R0++, or R0 = {R1:R0}; {R1:R0} + 1
-- : Second, this model adds an additional pipeline stage between
-- : the instruction decoder and the ALU. Unfortunately, this
-- : means that the instruction stream has to be restarted after
-- : any math instruction is executed, implying that any ALU
-- : instruction now has a latency of 2 instead of 0. The
-- : advantage is that the maximum frequency has gone up
-- : significantly, as the ALU code is vastly more efficient.
-- : As an aside, this now means that all math instructions,
-- : including MUL (see below) and UPP have the same instruction
-- : latency.
-- :
-- : Instructions USR and USR2 have been replaced with DBNZ, and MUL
-- : respectively. DBNZ decrements the specified register, and will
-- : branch if the result is non-zero (Zero flag is not set). MUL
-- : places the result of R0 * Rn into R1:R0, and executes in two
-- : cycles. (R1:R0 = R0 * Rn)
-- : Third, the original ISA, also a soft core, had two reserved
-- : instructions, USR and USR2. These have been implemented as
-- : DBNZ, and MUL respectively.
-- :
-- : BRK_Implements_WAI modifies the BRK instruction such that it
-- : triggers the wait for interrupt state, but without triggering
-- : a soft interrupt in lieu of its normal behavior, which is to
-- : insert several dead clock cycles - essentially a long NOP
-- Revision History
-- Author Date Change
------------------ -------- ---------------------------------------------------
-- Seth Henry 07/19/06 Design Start
-- Seth Henry 01/18/11 Fixed BTT instruction to match V8
-- Seth Henry 07/22/11 Fixed interrupt transition logic to avoid data
-- corruption issues.
-- Seth Henry 07/26/11 Optimized logic in ALU, stack pointer, and data
-- path sections.
-- Seth Henry 07/27/11 Optimized logic for timing, merged blocks into
-- single entity.
-- Seth Henry 09/20/11 Added BRK_Implements_WAI option, allowing the
-- processor to wait for an interrupt instead of the
-- normal BRK behavior.
-- Seth Henry 12/20/11 Modified core to allow WAIT_FOR_INT state to idle
-- the bus entirely (Rd_Enable is low)
-- : DBNZ decrements the specified register and branches if the
-- : result is non-zero. The instruction effectively executes a
-- : DEC Rn instruction prior to branching, so the same flags will
-- : be set.
-- :
-- : MUL places the result of R0 * Rn into R1:R0. Instruction
-- : latency is identical to other ALU instructions. Only the Z
-- : flag is set, since there is no defined overflow or "negative
-- : 16-bit values"
-- :
-- : Fourth, indexed load/store instructions now have an (optional)
-- : ability to post-increment their index registers. If enabled,
-- : using an odd operand for LDO,LDX, STO, STX will cause the
-- : register pair to be incremented after the storage access.
-- :
-- : Fifth, the RSP instruction has been (optionally) altered to
-- : allow the stack pointer to be sourced from R1:R0.
-- :
-- : Sixth, the BRK instruction can optionally implement a WAI,
-- : which is the same as the INT instruction without the soft
-- : interrupt, as a way to put the processor to "sleep" until the
-- : next interrupt.
-- :
-- : Seventh, the original CPU model had 8 non-maskable interrupts
-- : with priority. This model has the same 8 interrupts, but
-- : allows software to mask them (with an additional option to
-- : override the highest priority interrupt, making it the NMI.)
-- : The interrupt code will retain memory of lower priority
-- : interrupts, and execute them as it can.
-- :
-- : Lastly, previous unmapped instructions in the OP_STK opcode
-- : were repurposed to support a new interrupt mask.
-- : SMSK and GMSK transfer the contents of R0 (accumulator)
-- : to/from the interrupt mask register. SMSK is immediate, while
-- : GMSK has the same overhead as a math instruction.
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_misc.all;
 
library work;
use work.Open8_pkg.all;
100,19 → 136,18
 
entity Open8_CPU is
generic(
Program_Start_Addr : ADDRESS_TYPE := x"0090"; -- Initial PC location
ISR_Start_Addr : ADDRESS_TYPE := x"0080"; -- Bottom of ISR vec's
Stack_Start_Addr : ADDRESS_TYPE := x"007F"; -- Top of Stack
Allow_Stack_Address_Move : boolean := false; -- Use Normal v8 RSP
Enable_Auto_Increment : boolean := false; -- Modify indexed instr
BRK_Implements_WAI : boolean := false; -- BRK -> Wait for Int
ISR_Start_Addr : ADDRESS_TYPE := x"0080"; -- Bottom of ISR vec's
Program_Start_Addr : ADDRESS_TYPE := x"0090"; -- Initial PC location
Enable_NMI : boolean := true; -- force mask for int 0
Default_Interrupt_Mask : DATA_TYPE := x"FF"; -- Enable all Ints
Enable_CPU_Halt : boolean := false; -- Disable HALT pin
Enable_Auto_Increment : boolean := false; -- Modify indexed instr
Reset_Level : std_logic := '0' ); -- Active reset level
port(
Clock : in std_logic;
Reset : in std_logic;
CPU_Halt : in std_logic := '0';
Interrupts : in INTERRUPT_BUNDLE;
--
Address : out ADDRESS_TYPE;
127,7 → 162,8
subtype OPCODE_TYPE is std_logic_vector(4 downto 0);
subtype SUBOP_TYPE is std_logic_vector(2 downto 0);
 
-- These are all the primary instructions/op-codes (upper 5-bits)
-- All opcodes should be identical to the opcode used by the assembler
-- In this case, they match the original V8/ARC uRISC ISA
constant OP_INC : OPCODE_TYPE := "00000";
constant OP_ADC : OPCODE_TYPE := "00001";
constant OP_TX0 : OPCODE_TYPE := "00010";
148,9 → 184,9
constant OP_POP : OPCODE_TYPE := "10001";
constant OP_BR0 : OPCODE_TYPE := "10010";
constant OP_BR1 : OPCODE_TYPE := "10011";
constant OP_DBNZ : OPCODE_TYPE := "10100"; -- USR
constant OP_DBNZ : OPCODE_TYPE := "10100";
constant OP_INT : OPCODE_TYPE := "10101";
constant OP_MUL : OPCODE_TYPE := "10110"; -- USR2
constant OP_MUL : OPCODE_TYPE := "10110";
constant OP_STK : OPCODE_TYPE := "10111";
constant OP_UPP : OPCODE_TYPE := "11000";
constant OP_STA : OPCODE_TYPE := "11001";
161,7 → 197,9
constant OP_LDX : OPCODE_TYPE := "11110";
constant OP_LDO : OPCODE_TYPE := "11111";
 
-- These are all specific sub-opcodes for OP_STK / 0xB8 (lower 3-bits)
-- OP_STK uses the lower 3 bits to further refine the instruction by
-- repurposing the source register field. These "sub opcodes" are
-- take the place of the register select for the OP_STK opcode
constant SOP_RSP : SUBOP_TYPE := "000";
constant SOP_RTS : SUBOP_TYPE := "001";
constant SOP_RTI : SUBOP_TYPE := "010";
171,18 → 209,18
constant SOP_GMSK : SUBOP_TYPE := "110";
constant SOP_JSR : SUBOP_TYPE := "111";
 
-- Preinitialization is for simulation only - check actual reset conditions
type CPU_STATES is (
-- Instruction fetch & Decode
PIPE_FILL_0, PIPE_FILL_1, PIPE_FILL_2, INSTR_DECODE,
-- Branching
BRN_C1, DBNZ_C1, JMP_C1, JMP_C2,
BRN_C1, DBNZ_C1, DBNZ_C2, JMP_C1, JMP_C2,
-- Loads
LDA_C1, LDA_C2, LDA_C3, LDA_C4, LDI_C1, LDO_C1, LDX_C1, LDX_C2, LDX_C3,
LDA_C1, LDA_C2, LDA_C3, LDA_C4, LDI_C1,
LDO_C1, LDX_C1, LDX_C2, LDX_C3, LDX_C4,
-- Stores
STA_C1, STA_C2, STA_C3, STO_C1, STO_C2, STX_C1, STX_C2,
-- 2-cycle math
MUL_C1, UPP_C1,
STA_C1, STA_C2, STO_C1, STO_C2, STX_C1, STX_C2,
-- math
MATH_C1, GMSK_C1, MUL_C1, UPP_C1,
-- Stack
PSH_C1, POP_C1, POP_C2, POP_C3, POP_C4,
-- Subroutines & Interrupts
191,53 → 229,11
-- Debugging
BRK_C1 );
 
type CACHE_MODES is (CACHE_IDLE, CACHE_INSTR, CACHE_OPER1, CACHE_OPER2,
CACHE_PREFETCH );
 
type PC_MODES is ( PC_IDLE, PC_REV1, PC_REV2, PC_INCR, PC_LOAD );
 
type PC_CTRL_TYPE is record
Oper : PC_MODES;
Offset : DATA_TYPE;
Addr : ADDRESS_TYPE;
end record;
 
type SP_MODES is ( SP_IDLE, SP_RSET, SP_POP, SP_PUSH );
 
type SP_CTRL_TYPE is record
Oper : SP_MODES;
Addr : ADDRESS_TYPE;
end record;
 
type DP_MODES is ( DATA_BUS_IDLE, DATA_RD_MEM,
DATA_WR_REG, DATA_WR_FLAG, DATA_WR_PC );
 
type DATA_CTRL_TYPE is record
Src : DP_MODES;
Reg : SUBOP_TYPE;
end record;
 
-- Preinitialization is for simulation only - check actual reset conditions
constant INT_VECTOR_0 : ADDRESS_TYPE := ISR_Start_Addr;
constant INT_VECTOR_1 : ADDRESS_TYPE := ISR_Start_Addr+2;
constant INT_VECTOR_2 : ADDRESS_TYPE := ISR_Start_Addr+4;
constant INT_VECTOR_3 : ADDRESS_TYPE := ISR_Start_Addr+6;
constant INT_VECTOR_4 : ADDRESS_TYPE := ISR_Start_Addr+8;
constant INT_VECTOR_5 : ADDRESS_TYPE := ISR_Start_Addr+10;
constant INT_VECTOR_6 : ADDRESS_TYPE := ISR_Start_Addr+12;
constant INT_VECTOR_7 : ADDRESS_TYPE := ISR_Start_Addr+14;
 
type INT_CTRL_TYPE is record
Mask_Set : std_logic;
Soft_Ints : INTERRUPT_BUNDLE;
Incr_ISR : std_logic;
end record;
 
type INT_HIST is array (0 to 8) of integer range 0 to 7;
 
-- Most of the ALU instructions are the same as their Opcode equivalents with
-- three exceptions (for IDLE, UPP2, and MUL2)
-- To simplify the logic, the first 16 of these should exactly match their
-- corresponding Opcodes. This allows the state logic to simply pass the
-- opcode field to the ALU for most math operations.
constant ALU_INC : OPCODE_TYPE := "00000"; -- x"00"
constant ALU_UPP1 : OPCODE_TYPE := "00000"; -- Alias of ALU_INC
constant ALU_ADC : OPCODE_TYPE := "00001"; -- x"01"
constant ALU_TX0 : OPCODE_TYPE := "00010"; -- x"02"
constant ALU_OR : OPCODE_TYPE := "00011"; -- x"03"
253,16 → 249,12
constant ALU_CLP : OPCODE_TYPE := "01101"; -- x"0D"
constant ALU_T0X : OPCODE_TYPE := "01110"; -- x"0E"
constant ALU_CMP : OPCODE_TYPE := "01111"; -- x"0F"
constant ALU_POP : OPCODE_TYPE := "10001"; -- x"11"
constant ALU_IDLE : OPCODE_TYPE := "10000"; -- x"10"
constant ALU_UPP2 : OPCODE_TYPE := "10010"; -- x"11"
constant ALU_RFLG : OPCODE_TYPE := "10011"; -- x"12"
constant ALU_MUL : OPCODE_TYPE := "10110"; -- x"16"
constant ALU_UPP : OPCODE_TYPE := "11000"; -- x"18"
constant ALU_LDI : OPCODE_TYPE := "11100"; -- x"1C"
constant ALU_LDX : OPCODE_TYPE := "11110"; -- x"1E"
 
constant ALU_IDLE : OPCODE_TYPE := "10000"; -- x"10"
constant ALU_UPP2 : OPCODE_TYPE := "10010"; -- x"12"
constant ALU_RFLG : OPCODE_TYPE := "10011"; -- x"13"
 
constant FL_ZERO : integer := 0;
constant FL_CARRY : integer := 1;
constant FL_NEG : integer := 2;
272,1082 → 264,1033
constant FL_GP3 : integer := 6;
constant FL_GP4 : integer := 7;
 
type ALU_CTRL_TYPE is record
Oper : OPCODE_TYPE;
Reg : SUBOP_TYPE;
Data : DATA_TYPE;
end record;
 
constant ACCUM : SUBOP_TYPE := "000";
constant INT_FLAG : SUBOP_TYPE := "011";
 
type REGFILE_TYPE is array (0 to 7) of DATA_TYPE;
 
subtype FLAG_TYPE is DATA_TYPE;
 
signal Halt : std_logic;
constant INT_VECTOR_0 : ADDRESS_TYPE := ISR_Start_Addr + 0;
constant INT_VECTOR_1 : ADDRESS_TYPE := ISR_Start_Addr + 2;
constant INT_VECTOR_2 : ADDRESS_TYPE := ISR_Start_Addr + 4;
constant INT_VECTOR_3 : ADDRESS_TYPE := ISR_Start_Addr + 6;
constant INT_VECTOR_4 : ADDRESS_TYPE := ISR_Start_Addr + 8;
constant INT_VECTOR_5 : ADDRESS_TYPE := ISR_Start_Addr + 10;
constant INT_VECTOR_6 : ADDRESS_TYPE := ISR_Start_Addr + 12;
constant INT_VECTOR_7 : ADDRESS_TYPE := ISR_Start_Addr + 14;
 
signal CPU_Next_State : CPU_STATES := PIPE_FILL_0;
signal CPU_State : CPU_STATES := PIPE_FILL_0;
type CPU_CTRL_TYPE is record
State : CPU_STATES;
LS_Address : ADDRESS_TYPE;
Program_Ctr : ADDRESS_TYPE;
Stack_Ptr : ADDRESS_TYPE;
Opcode : OPCODE_TYPE;
SubOp_p0 : SUBOP_TYPE;
SubOp_p1 : SUBOP_TYPE;
Cache_Valid : std_logic;
Prefetch : DATA_TYPE;
Operand1 : DATA_TYPE;
Operand2 : DATA_TYPE;
AutoIncr : std_logic;
A_Oper : OPCODE_TYPE;
A_Reg : SUBOP_TYPE;
A_Data : DATA_TYPE;
A_NoFlags : std_logic;
M_Reg : SUBOP_TYPE;
M_Prod : ADDRESS_TYPE;
Regfile : REGFILE_TYPE;
Flags : FLAG_TYPE;
Int_Mask : DATA_TYPE;
Int_Addr : ADDRESS_TYPE;
Int_Pending : DATA_TYPE;
Int_Level : integer range 0 to 7;
Wait_for_FSM : std_logic;
end record;
 
signal Cache_Ctrl : CACHE_MODES := CACHE_IDLE;
signal CPU : CPU_CTRL_TYPE;
 
signal Opcode : OPCODE_TYPE := (others => '0');
signal SubOp, SubOp_p1 : SUBOP_TYPE := (others => '0');
alias Accumulator is CPU.Regfile(0);
alias Flags is CPU.Flags;
 
signal Prefetch : DATA_TYPE := x"00";
signal Operand1, Operand2 : DATA_TYPE := x"00";
signal Ack_Q, Ack_Q1 : std_logic;
signal Int_Req, Int_Ack : std_logic;
 
signal Instr_Prefetch : std_logic := '0';
type IC_MODES is ( CACHE_IDLE, CACHE_INSTR, CACHE_OPER1, CACHE_OPER2,
CACHE_PREFETCH, CACHE_PFFLUSH, CACHE_INVALIDATE );
 
signal PC_Ctrl : PC_CTRL_TYPE;
signal Program_Ctr : ADDRESS_TYPE := x"0000";
type PC_MODES is ( PC_INCR, PC_IDLE, PC_REV1, PC_REV2, PC_REV3,
PC_BRANCH, PC_LOAD );
 
signal SP_Ctrl : SP_CTRL_TYPE;
signal Stack_Ptr : ADDRESS_TYPE := x"0000";
type SP_MODES is ( SP_IDLE, SP_RSET, SP_POP, SP_PUSH );
 
signal DP_Ctrl : DATA_CTRL_TYPE;
type DP_MODES is ( DATA_BUS_IDLE, DATA_RD_MEM,
DATA_WR_REG, DATA_WR_FLAG, DATA_WR_PC );
 
signal INT_Ctrl : INT_CTRL_TYPE;
signal Ack_D, Ack_Q, Ack_Q1: std_logic := '0';
signal Int_RTI_D, Int_RTI : std_logic := '0';
signal Int_Req, Int_Ack : std_logic := '0';
signal Int_Mask : DATA_TYPE := x"00";
signal ISR_Addr : ADDRESS_TYPE := x"0000";
signal i_Ints : INTERRUPT_BUNDLE := x"00";
signal Pending : INTERRUPT_BUNDLE := x"00";
signal Wait_for_FSM : std_logic := '0';
signal History : INT_HIST := (others => 0);
signal Hst_Ptr : integer range 0 to 8 := 0;
type DP_CTRL_TYPE is record
Src : DP_MODES;
Reg : SUBOP_TYPE;
end record;
 
signal ALU_Ctrl : ALU_CTRL_TYPE;
signal Regfile : REGFILE_TYPE;
signal Flags : FLAG_TYPE;
signal Mult : ADDRESS_TYPE := x"0000";
type INT_CTRL_TYPE is record
Mask_Set : std_logic;
Soft_Ints : INTERRUPT_BUNDLE;
Incr_ISR : std_logic;
end record;
 
begin
 
Halt_Disabled_fn: if( not Enable_CPU_Halt )generate
Halt <= '0';
end generate;
Address_Sel: process( CPU )
variable Offset_SX : ADDRESS_TYPE;
begin
Offset_SX(15 downto 8) := (others => CPU.Operand1(7));
Offset_SX(7 downto 0) := CPU.Operand1;
 
Halt_Enabled_fn: if( Enable_CPU_Halt )generate
Halt <= CPU_Halt;
end generate;
case( CPU.State )is
when LDO_C1 | LDX_C1 | STO_C1 | STX_C1 =>
Address <= CPU.LS_Address + Offset_SX;
when LDA_C2 | STA_C2 =>
Address <= (CPU.Operand2 & CPU.Operand1);
when PSH_C1 | POP_C1 | ISR_C3 | JSR_C1 | JSR_C2 |
RTS_C1 | RTS_C2 | RTS_C3 =>
Address <= CPU.Stack_Ptr;
when ISR_C1 | ISR_C2 =>
Address <= CPU.Int_Addr;
when others =>
Address <= CPU.Program_Ctr;
end case;
end process;
 
-------------------------------------------------------------------------------
-- State Logic / Instruction Decoding & Execution
-- Combinatorial portion of CPU finite state machine
-------------------------------------------------------------------------------
 
State_Logic: process(CPU_State, Regfile, Flags, Int_Mask, Opcode,
SubOp , SubOp_p1, Operand1, Operand2, Int_Req,
Program_Ctr, Stack_Ptr, ISR_Addr )
variable Reg, Reg_1 : integer range 0 to 7 := 0;
CPU_Proc: process( Clock, Reset )
variable IC : IC_MODES;
variable PC : PC_MODES;
variable SP : SP_MODES;
variable DP : DP_CTRL_TYPE;
variable INT : INT_CTRL_TYPE;
variable RegSel : integer range 0 to 7;
variable Reg_l, Reg_u : integer range 0 to 7;
variable Ack_D : std_logic;
variable Offset_SX : ADDRESS_TYPE;
variable Index : integer range 0 to 7;
variable Temp : std_logic_vector(8 downto 0);
begin
CPU_Next_State <= CPU_State;
Cache_Ctrl <= CACHE_IDLE;
--
ALU_Ctrl.Oper <= ALU_IDLE;
ALU_Ctrl.Reg <= ACCUM;
ALU_Ctrl.Data <= x"00";
--
PC_Ctrl.Oper <= PC_IDLE;
PC_Ctrl.Offset <= x"03";
PC_Ctrl.Addr <= x"0000";
--
SP_Ctrl.Oper <= SP_IDLE;
--
Address <= Program_Ctr;
--
DP_Ctrl.Src <= DATA_RD_MEM;
DP_Ctrl.Reg <= ACCUM;
--
INT_Ctrl.Mask_Set <= '0';
INT_Ctrl.Soft_Ints <= x"00";
INT_Ctrl.Incr_ISR <= '0';
Ack_D <= '0';
Int_RTI_D <= '0';
if( Reset = Reset_Level )then
CPU.State <= PIPE_FILL_0;
CPU.LS_Address <= (others => '0');
CPU.Program_Ctr <= Program_Start_Addr;
CPU.Stack_Ptr <= Stack_Start_Addr;
CPU.Opcode <= (others => '0');
CPU.SubOp_p0 <= (others => '0');
CPU.SubOp_p1 <= (others => '0');
CPU.Prefetch <= (others => '0');
CPU.Operand1 <= (others => '0');
CPU.Operand2 <= (others => '0');
CPU.AutoIncr <= '0';
CPU.Cache_Valid <= '0';
CPU.A_Oper <= ALU_IDLE;
CPU.A_Reg <= ACCUM;
CPU.A_Data <= x"00";
CPU.A_NoFlags <= '0';
CPU.M_Reg <= (others => '0');
for i in 0 to 7 loop
CPU.Regfile(i) <= x"00";
end loop;
CPU.Flags <= (others => '0');
if( Enable_NMI )then
CPU.Int_Mask <= Default_Interrupt_Mask(7 downto 1) & '1';
else
CPU.Int_Mask <= Default_Interrupt_Mask;
end if;
CPU.Int_Addr <= (others => '0');
CPU.Int_Pending <= (others => '0');
CPU.Int_Level <= 7;
CPU.Wait_for_FSM <= '0';
 
-- Assign the most common value of Reg and Reg1 outside the case structure
-- to simplify things.
Reg := conv_integer(SubOp);
Reg_1 := conv_integer(SubOp_p1);
Offset_SX(15 downto 0) := (others => Operand1(7));
Offset_SX(7 downto 0) := Operand1;
Ack_Q <= '0';
Ack_Q1 <= '0';
Int_Ack <= '0';
Int_Req <= '0';
 
case CPU_State is
-------------------------------------------------------------------------------
-- Initial Instruction fetch & decode
-------------------------------------------------------------------------------
when PIPE_FILL_0 =>
CPU_Next_State <= PIPE_FILL_1;
PC_Ctrl.Oper <= PC_INCR;
Wr_Data <= x"00";
Wr_Enable <= '0';
Rd_Enable <= '1';
elsif( rising_edge(Clock) )then
 
when PIPE_FILL_1 =>
CPU_Next_State <= PIPE_FILL_2;
PC_Ctrl.Oper <= PC_INCR;
IC := CACHE_IDLE;
SP := SP_IDLE;
DP.Src := DATA_RD_MEM;
DP.Reg := ACCUM;
Ack_D := '0';
INT.Mask_Set := '0';
INT.Soft_Ints := x"00";
INT.Incr_ISR := '0';
RegSel := conv_integer(CPU.SubOp_p0);
 
when PIPE_FILL_2 =>
CPU_Next_State <= INSTR_DECODE;
Cache_Ctrl <= CACHE_INSTR;
PC_Ctrl.Oper <= PC_INCR;
if( Enable_Auto_Increment )then
Reg_l := conv_integer(CPU.SubOp_p0(2 downto 1) & '0');
Reg_u := conv_integer(CPU.SubOp_p0(2 downto 1) & '1');
else
Reg_l := conv_integer(CPU.SubOp_p0);
Reg_u := conv_integer(CPU.SubOp_p1);
end if;
 
when INSTR_DECODE =>
CPU_Next_State <= INSTR_DECODE;
Cache_Ctrl <= CACHE_INSTR;
CPU.LS_Address <= CPU.Regfile(Reg_u) & CPU.Regfile(Reg_l);
 
case Opcode is
when OP_PSH =>
CPU_Next_State <= PSH_C1;
Cache_Ctrl <= CACHE_PREFETCH;
PC_Ctrl.Oper <= PC_REV1;
DP_Ctrl.Src <= DATA_WR_REG;
DP_Ctrl.Reg <= SubOp;
CPU.AutoIncr <= '0';
if( Enable_Auto_Increment )then
CPU.AutoIncr <= CPU.SubOp_p0(0);
end if;
 
when OP_POP =>
CPU_Next_State <= POP_C1;
Cache_Ctrl <= CACHE_PREFETCH;
PC_Ctrl.Oper <= PC_REV2;
SP_Ctrl.Oper <= SP_POP;
CPU.A_Oper <= ALU_IDLE;
CPU.A_Reg <= ACCUM;
CPU.A_Data <= x"00";
CPU.A_NoFlags <= '0';
 
when OP_BR0 | OP_BR1 =>
CPU_Next_State <= BRN_C1;
Cache_Ctrl <= CACHE_OPER1;
PC_Ctrl.Oper <= PC_INCR;
case( CPU.State )is
when PIPE_FILL_0 =>
PC := PC_INCR;
CPU.State <= PIPE_FILL_1;
 
when OP_DBNZ =>
CPU_Next_State <= DBNZ_C1;
Cache_Ctrl <= CACHE_OPER1;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_DEC;
ALU_Ctrl.Reg <= SubOp;
when PIPE_FILL_1 =>
PC := PC_INCR;
CPU.State <= PIPE_FILL_2;
 
when OP_INT =>
PC_Ctrl.Oper <= PC_INCR;
-- Make sure the requested interrupt is actually enabled first
if( Int_Mask(Reg) = '1' )then
CPU_Next_State <= WAIT_FOR_INT;
INT_Ctrl.Soft_Ints(Reg) <= '1';
end if;
when PIPE_FILL_2 =>
IC := CACHE_INSTR;
PC := PC_INCR;
CPU.State <= INSTR_DECODE;
 
when OP_STK =>
case SubOp is
when SOP_RSP =>
PC_Ctrl.Oper <= PC_INCR;
SP_Ctrl.Oper <= SP_RSET;
-------------------------------------------------------------------------------
-- Instruction Decode and dispatch
-------------------------------------------------------------------------------
 
when SOP_RTS | SOP_RTI =>
CPU_Next_State <= RTS_C1;
Cache_Ctrl <= CACHE_IDLE;
SP_Ctrl.Oper <= SP_POP;
when INSTR_DECODE =>
IC := CACHE_INSTR;
PC := PC_INCR;
case CPU.Opcode is
when OP_PSH =>
IC := CACHE_PREFETCH;
PC := PC_IDLE;
DP.Src := DATA_WR_REG;
DP.Reg := CPU.SubOp_p0;
CPU.State <= PSH_C1;
 
when SOP_BRK =>
CPU_Next_State <= BRK_C1;
PC_Ctrl.Oper <= PC_REV2;
-- If Break implements Wait for Interrupt
-- Replace normal flow with a modified
-- version of INT instruction
if( BRK_Implements_WAI )then
CPU_Next_State <= WAIT_FOR_INT;
PC_Ctrl.Oper <= PC_INCR;
end if;
when OP_POP =>
IC := CACHE_PREFETCH;
PC := PC_REV2;
SP := SP_POP;
CPU.State <= POP_C1;
 
when SOP_JMP =>
CPU_Next_State <= JMP_C1;
Cache_Ctrl <= CACHE_OPER1;
when OP_BR0 | OP_BR1 =>
IC := CACHE_OPER1;
CPU.State <= BRN_C1;
 
when SOP_SMSK =>
PC_Ctrl.Oper <= PC_INCR;
INT_Ctrl.Mask_Set <= '1';
when OP_DBNZ =>
IC := CACHE_OPER1;
CPU.A_Oper <= ALU_DEC;
CPU.A_Reg <= CPU.SubOp_p0;
CPU.A_Data <= CPU.Regfile(RegSel);
CPU.State <= DBNZ_C1;
 
when SOP_GMSK =>
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper<= ALU_LDI;
ALU_Ctrl.Reg <= ACCUM;
ALU_Ctrl.Data<= Int_Mask;
when OP_INT =>
if( CPU.Int_Mask(RegSel) = '1' )then
CPU.State <= WAIT_FOR_INT;
INT.Soft_Ints(RegSel) := '1';
end if;
 
when SOP_JSR =>
CPU_Next_State <= JSR_C1;
Cache_Ctrl <= CACHE_OPER1;
DP_Ctrl.Src <= DATA_WR_PC;
DP_Ctrl.Reg <= ACCUM+1;
when OP_STK =>
case CPU.SubOp_p0 is
when SOP_RSP =>
SP := SP_RSET;
 
when others => null;
end case;
when SOP_RTS | SOP_RTI =>
IC := CACHE_IDLE;
PC := PC_IDLE;
SP := SP_POP;
CPU.State <= RTS_C1;
 
when OP_MUL =>
CPU_Next_State <= MUL_C1;
Cache_Ctrl <= CACHE_PREFETCH;
when SOP_BRK =>
if( BRK_Implements_WAI )then
CPU.State<= WAIT_FOR_INT;
else
PC := PC_REV2;
CPU.State<= BRK_C1;
end if;
 
-- We need to back the PC up by 1, and allow it to refill. An
-- unfortunate consequence of the pipelining. We can get away with
-- only 1 extra clock by pre-fetching the next instruction, though
PC_Ctrl.Oper <= PC_REV1;
-- Multiplication is automatic, but requires a single clock cycle.
-- We need to specify the register for Rn (R1:R0 = R0 * Rn) now,
-- but will issue the multiply command on the next clock to copy
-- the results to the specified register.
ALU_Ctrl.Oper <= ALU_IDLE;
ALU_Ctrl.Reg <= SubOp; -- Rn
when SOP_JMP =>
IC := CACHE_OPER1;
PC := PC_IDLE;
CPU.State <= JMP_C1;
 
when OP_UPP =>
CPU_Next_State <= UPP_C1;
Cache_Ctrl <= CACHE_PREFETCH;
PC_Ctrl.Oper <= PC_REV1;
ALU_Ctrl.Oper <= Opcode;
ALU_Ctrl.Reg <= SubOp;
when SOP_SMSK =>
INT.Mask_Set := '1';
 
when OP_LDA =>
CPU_Next_State <= LDA_C1;
Cache_Ctrl <= CACHE_OPER1;
when SOP_GMSK =>
IC := CACHE_PREFETCH;
PC := PC_REV1;
CPU.State <= GMSK_C1;
 
when OP_LDI =>
CPU_Next_State <= LDI_C1;
Cache_Ctrl <= CACHE_OPER1;
PC_Ctrl.Oper <= PC_INCR;
when SOP_JSR =>
IC := CACHE_OPER1;
PC := PC_IDLE;
DP.Src := DATA_WR_PC;
DP.Reg := ACCUM+1;
CPU.State <= JSR_C1;
 
when OP_LDO =>
CPU_Next_State <= LDO_C1;
Cache_Ctrl <= CACHE_OPER1;
PC_Ctrl.Oper <= PC_REV2;
when others => null;
end case;
 
when OP_LDX =>
CPU_Next_State <= LDX_C1;
PC_Ctrl.Oper <= PC_REV2;
-- If auto-increment is disabled, use the specified register pair,
-- otherwise, for an odd:even pair, and issue the first half of
-- a UPP instruction to the ALU
if( not Enable_Auto_Increment )then
Address <= Regfile(Reg_1) & Regfile(Reg);
else
Reg := conv_integer(SubOp(2 downto 1) & '0');
Reg_1 := conv_integer(SubOp(2 downto 1) & '1');
Address <= Regfile(Reg_1) & Regfile(Reg);
if( SubOp(0) = '1' )then
ALU_Ctrl.Oper<= ALU_UPP;
ALU_Ctrl.Reg <= SubOp(2 downto 1) & '0';
end if;
end if;
when OP_MUL =>
IC := CACHE_PREFETCH;
PC := PC_REV1;
CPU.M_Reg <= CPU.SubOp_p0;
CPU.State <= MUL_C1;
 
when OP_STA =>
CPU_Next_State <= STA_C1;
Cache_Ctrl <= CACHE_OPER1;
when OP_UPP =>
IC := CACHE_PREFETCH;
PC := PC_REV1;
CPU.A_Oper <= ALU_UPP1;
CPU.A_NoFlags <= '1';
CPU.A_Reg <= CPU.SubOp_p0;
CPU.A_Data <= CPU.Regfile(RegSel);
CPU.State <= UPP_C1;
 
when OP_STO =>
CPU_Next_State <= STO_C1;
Cache_Ctrl <= CACHE_OPER1;
PC_Ctrl.Oper <= PC_REV2;
DP_Ctrl.Src <= DATA_WR_REG;
DP_Ctrl.Reg <= ACCUM;
when OP_LDA =>
IC := CACHE_OPER1;
PC := PC_IDLE;
CPU.State <= LDA_C1;
 
when OP_STX =>
CPU_Next_State <= STX_C1;
Cache_Ctrl <= CACHE_PREFETCH;
PC_Ctrl.Oper <= PC_REV2;
DP_Ctrl.Src <= DATA_WR_REG;
DP_Ctrl.Reg <= ACCUM;
when OP_LDI =>
IC := CACHE_OPER1;
PC := PC_IDLE;
CPU.State <= LDI_C1;
 
when others =>
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= Opcode;
ALU_Ctrl.Reg <= SubOp;
when OP_LDO =>
IC := CACHE_OPER1;
PC := PC_IDLE;
CPU.State <= LDO_C1;
 
end case;
when OP_LDX =>
IC := CACHE_PFFLUSH;
PC := PC_REV1;
CPU.State <= LDX_C1;
 
when OP_STA =>
IC := CACHE_OPER1;
PC := PC_IDLE;
CPU.State <= STA_C1;
 
when OP_STO =>
IC := CACHE_OPER1;
PC := PC_REV2;
DP.Src := DATA_WR_REG;
DP.Reg := ACCUM;
CPU.State <= STO_C1;
 
when OP_STX =>
IC := CACHE_PFFLUSH;
PC := PC_REV2;
DP.Src := DATA_WR_REG;
DP.Reg := ACCUM;
CPU.State <= STX_C1;
 
when others =>
IC := CACHE_PREFETCH;
PC := PC_REV1;
CPU.State <= MATH_C1;
 
end case;
 
-------------------------------------------------------------------------------
-- Program Control (BR0_C1, BR1_C1, DBNZ_C1, JMP )
-------------------------------------------------------------------------------
 
when BRN_C1 =>
CPU_Next_State <= INSTR_DECODE;
Cache_Ctrl <= CACHE_INSTR;
PC_Ctrl.Oper <= PC_INCR;
if( Flags(Reg) = Opcode(0) )then
CPU_Next_State <= PIPE_FILL_0;
Cache_Ctrl <= CACHE_IDLE;
PC_Ctrl.Offset <= Operand1;
end if;
when BRN_C1 =>
if( Flags(RegSel) = CPU.Opcode(0) )then
IC := CACHE_IDLE;
PC := PC_BRANCH;
CPU.State <= PIPE_FILL_0;
else
IC := CACHE_INSTR;
CPU.State <= INSTR_DECODE;
end if;
 
when DBNZ_C1 =>
CPU_Next_State <= INSTR_DECODE;
Cache_Ctrl <= CACHE_INSTR;
PC_Ctrl.Oper <= PC_INCR;
if( Flags(FL_ZERO) = '0' )then
CPU_Next_State <= PIPE_FILL_0;
Cache_Ctrl <= CACHE_IDLE;
PC_Ctrl.Offset <= Operand1;
end if;
when DBNZ_C1 =>
IC := CACHE_PREFETCH;
PC := PC_IDLE;
CPU.State <= DBNZ_C2;
 
when JMP_C1 =>
CPU_Next_State <= JMP_C2;
Cache_Ctrl <= CACHE_OPER2;
when DBNZ_C2 =>
if( Flags(FL_ZERO) = '0' )then
IC := CACHE_INVALIDATE;
PC := PC_BRANCH;
CPU.State <= PIPE_FILL_0;
else
PC := PC_REV1;
CPU.State <= PIPE_FILL_1;
end if;
 
when JMP_C2 =>
CPU_Next_State <= PIPE_FILL_0;
PC_Ctrl.Oper <= PC_LOAD;
PC_Ctrl.Addr <= Operand2 & Operand1;
when JMP_C1 =>
IC := CACHE_OPER2;
PC := PC_IDLE;
CPU.State <= JMP_C2;
 
when JMP_C2 =>
PC := PC_LOAD;
CPU.State <= PIPE_FILL_0;
 
-------------------------------------------------------------------------------
-- Data Storage - Load from memory (LDA, LDI, LDO, LDX)
-------------------------------------------------------------------------------
 
when LDA_C1 =>
CPU_Next_State <= LDA_C2;
Cache_Ctrl <= CACHE_OPER2;
when LDA_C1 =>
IC := CACHE_OPER2;
PC := PC_IDLE;
CPU.State <= LDA_C2;
 
when LDA_C2 =>
CPU_Next_State <= LDA_C3;
Address <= Operand2 & Operand1;
when LDA_C2 =>
PC := PC_IDLE;
CPU.State <= LDA_C3;
 
when LDA_C3 =>
CPU_Next_State <= LDA_C4;
PC_Ctrl.Oper <= PC_INCR;
when LDA_C3 =>
PC := PC_IDLE;
CPU.State <= LDA_C4;
 
when LDA_C4 =>
CPU_Next_State <= LDI_C1;
Cache_Ctrl <= CACHE_OPER1;
PC_Ctrl.Oper <= PC_INCR;
when LDA_C4 =>
IC := CACHE_OPER1;
PC := PC_INCR;
CPU.State <= LDI_C1;
 
when LDI_C1 =>
CPU_Next_State <= INSTR_DECODE;
Cache_Ctrl <= CACHE_INSTR;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_LDI;
ALU_Ctrl.Reg <= SubOp;
ALU_Ctrl.Data <= Operand1;
when LDI_C1 =>
IC := CACHE_PREFETCH;
PC := PC_INCR;
CPU.A_Oper <= ALU_LDI;
CPU.A_Reg <= CPU.SubOp_p0;
CPU.A_Data <= CPU.Operand1;
CPU.State <= PIPE_FILL_2;
 
when LDO_C1 =>
CPU_Next_State <= LDX_C1;
PC_Ctrl.Oper <= PC_INCR;
if( Enable_Auto_Increment )then
Reg := conv_integer(SubOp(2 downto 1) & '0');
Reg_1 := conv_integer(SubOp(2 downto 1) & '1');
Address <= (Regfile(Reg_1) & Regfile(Reg)) + Offset_SX;
if( SubOp(0) = '1' )then
ALU_Ctrl.Oper<= ALU_UPP;
ALU_Ctrl.Reg <= SubOp(2 downto 1) & '0';
when LDO_C1 =>
IC := CACHE_PREFETCH;
PC := PC_REV2;
RegSel := conv_integer(CPU.SubOp_p0(2 downto 1) & '0');
if( Enable_Auto_Increment and CPU.AutoIncr = '1' )then
CPU.A_Oper <= ALU_UPP1;
CPU.A_Reg <= CPU.SubOp_p0(2 downto 1) & '0';
CPU.A_NoFlags <= '1';
CPU.A_Data <= CPU.RegFile(RegSel);
end if;
else
Address <= (Regfile(Reg_1) & Regfile(Reg)) + Offset_SX;
end if;
CPU.State <= LDX_C2;
 
when LDX_C1 =>
CPU_Next_State <= LDX_C2;
PC_Ctrl.Oper <= PC_INCR;
when LDX_C1 =>
PC := PC_REV2;
RegSel := conv_integer(CPU.SubOp_p0(2 downto 1) & '0');
if( Enable_Auto_Increment and CPU.AutoIncr = '1' )then
CPU.A_Oper <= ALU_UPP1;
CPU.A_Reg <= CPU.SubOp_p0(2 downto 1) & '0';
CPU.A_NoFlags <= '1';
CPU.A_Data <= CPU.Regfile(RegSel);
end if;
CPU.State <= LDX_C2;
 
when LDX_C2 =>
CPU_Next_State <= LDX_C3;
PC_Ctrl.Oper <= PC_INCR;
Cache_Ctrl <= CACHE_OPER1;
when LDX_C2 =>
PC := PC_INCR;
RegSel := conv_integer(CPU.SubOp_p0(2 downto 1) & '1');
if( Enable_Auto_Increment and CPU.AutoIncr = '1' )then
CPU.A_Oper <= ALU_UPP2;
CPU.A_Reg <= CPU.SubOp_p0(2 downto 1) & '1';
CPU.A_Data <= CPU.Regfile(RegSel);
end if;
CPU.State <= LDX_C3;
 
when LDX_C3 =>
CPU_Next_State <= INSTR_DECODE;
Cache_Ctrl <= CACHE_INSTR;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_LDX;
ALU_Ctrl.Reg <= ACCUM;
ALU_Ctrl.Data <= Operand1;
when LDX_C3 =>
IC := CACHE_OPER1;
PC := PC_INCR;
CPU.State <= LDX_C4;
 
when LDX_C4 =>
PC := PC_INCR;
CPU.A_Oper <= ALU_LDI;
CPU.A_Reg <= ACCUM;
CPU.A_Data <= CPU.Operand1;
CPU.State <= PIPE_FILL_2;
 
-------------------------------------------------------------------------------
-- Data Storage - Store to memory (STA, STO, STX)
-------------------------------------------------------------------------------
when STA_C1 =>
CPU_Next_State <= STA_C2;
Cache_Ctrl <= CACHE_OPER2;
DP_Ctrl.Src <= DATA_WR_REG;
DP_Ctrl.Reg <= SubOp;
when STA_C1 =>
IC := CACHE_OPER2;
PC := PC_IDLE;
DP.Src := DATA_WR_REG;
DP.Reg := CPU.SubOp_p0;
CPU.State <= STA_C2;
 
when STA_C2 =>
CPU_Next_State <= STA_C3;
Address <= Operand2 & Operand1;
PC_Ctrl.Oper <= PC_INCR;
when STA_C2 =>
IC := CACHE_PREFETCH;
PC := PC_INCR;
CPU.State <= PIPE_FILL_1;
 
when STA_C3 =>
CPU_Next_State <= PIPE_FILL_2;
Cache_Ctrl <= CACHE_PREFETCH;
PC_Ctrl.Oper <= PC_INCR;
 
when STO_C1 =>
Cache_Ctrl <= CACHE_PREFETCH;
PC_Ctrl.Oper <= PC_INCR;
-- If auto-increment is disabled, just load the registers normally
if( not Enable_Auto_Increment )then
CPU_Next_State <= PIPE_FILL_1;
Address <= (Regfile(Reg_1) & Regfile(Reg)) + Offset_SX;
-- Otherwise, enforce the even register rule, and check the LSB to see
-- if we should perform the auto-increment on the register pair
else
CPU_Next_State <= PIPE_FILL_0;
Reg := conv_integer(SubOp(2 downto 1) & '0');
Reg_1 := conv_integer(SubOp(2 downto 1) & '1');
Address <= (Regfile(Reg_1) & Regfile(Reg)) + Offset_SX;
if( SubOp(0) = '1' )then
CPU_Next_State <= STO_C2;
ALU_Ctrl.Oper <= ALU_UPP;
ALU_Ctrl.Reg <= SubOp(2 downto 1) & '0';
when STO_C1 =>
IC := CACHE_PREFETCH;
PC := PC_INCR;
RegSel := conv_integer(CPU.SubOp_p0(2 downto 1) & '0');
if( not Enable_Auto_Increment )then
CPU.State <= PIPE_FILL_1;
else
CPU.State <= PIPE_FILL_0;
if( CPU.AutoIncr = '1' )then
CPU.A_Oper <= ALU_UPP1;
CPU.A_Reg <= CPU.SubOp_p0(2 downto 1) & '0';
CPU.A_NoFlags <= '1';
CPU.A_Data <= CPU.Regfile(RegSel);
CPU.State <= STO_C2;
end if;
end if;
end if;
 
when STO_C2 =>
CPU_Next_State <= PIPE_FILL_1;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_UPP2;
ALU_Ctrl.Reg <= SubOp(2 downto 1) & '1';
when STO_C2 =>
PC := PC_INCR;
RegSel := conv_integer(CPU.SubOp_p0(2 downto 1) & '1');
CPU.A_Oper <= ALU_UPP2;
CPU.A_Reg <= CPU.SubOp_p0(2 downto 1) & '1';
CPU.A_Data <= CPU.Regfile(RegSel);
CPU.State <= PIPE_FILL_1;
 
when STX_C1 =>
PC_Ctrl.Oper <= PC_INCR;
-- If auto-increment is disabled, just load the registers normally
if( not Enable_Auto_Increment )then
CPU_Next_State <= PIPE_FILL_1;
Address <= (Regfile(Reg_1) & Regfile(Reg));
-- Otherwise, enforce the even register rule, and check the LSB to see
-- if we should perform the auto-increment on the register pair
else
CPU_Next_State <= PIPE_FILL_1;
Reg := conv_integer(SubOp(2 downto 1) & '0');
Reg_1 := conv_integer(SubOp(2 downto 1) & '1');
Address <= (Regfile(Reg_1) & Regfile(Reg));
if( SubOp(0) = '1' )then
CPU_Next_State <= STX_C2;
ALU_Ctrl.Oper <= ALU_UPP;
ALU_Ctrl.Reg <= SubOp(2 downto 1) & '0';
when STX_C1 =>
PC := PC_INCR;
if( not Enable_Auto_Increment )then
CPU.State <= PIPE_FILL_1;
else
RegSel := conv_integer(CPU.SubOp_p0(2 downto 1) & '0');
CPU.State <= PIPE_FILL_1;
if( CPU.AutoIncr = '1' )then
CPU.A_Oper <= ALU_UPP1;
CPU.A_Reg <= CPU.SubOp_p0(2 downto 1) & '0';
CPU.A_NoFlags <= '1';
CPU.A_Data <= CPU.Regfile(RegSel);
CPU.State <= STX_C2;
end if;
end if;
end if;
 
when STX_C2 =>
CPU_Next_State <= PIPE_FILL_2;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_UPP2;
ALU_Ctrl.Reg <= SubOp(2 downto 1) & '1';
when STX_C2 =>
PC := PC_INCR;
RegSel := conv_integer(CPU.SubOp_p0(2 downto 1) & '1');
CPU.A_Oper <= ALU_UPP2;
CPU.A_Reg <= CPU.SubOp_p0(2 downto 1) & '1';
CPU.A_Data <= CPU.Regfile(RegSel);
CPU.State <= PIPE_FILL_2;
 
-------------------------------------------------------------------------------
-- Multi-Cycle Math Operations (UPP, MUL)
-- Multi-Cycle Math Operations
-------------------------------------------------------------------------------
-- Because we have to backup the pipeline by 1 to refetch the 2nd
-- instruction/first operand, we have to return through PF2
 
when MUL_C1 =>
CPU_Next_State <= PIPE_FILL_2;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_MUL;
when MATH_C1 =>
PC := PC_INCR;
CPU.A_Oper <= CPU.Opcode;
CPU.A_Reg <= CPU.SubOp_p0;
CPU.A_Data <= CPU.Regfile(RegSel);
CPU.State <= PIPE_FILL_2;
 
when UPP_C1 =>
CPU_Next_State <= PIPE_FILL_2;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_UPP2;
ALU_Ctrl.Reg <= SubOp_p1;
when GMSK_C1 =>
PC := PC_INCR;
CPU.A_Oper <= ALU_LDI;
CPU.A_Data <= CPU.Int_Mask;
CPU.State <= PIPE_FILL_2;
 
when MUL_C1 =>
PC := PC_INCR;
CPU.A_Oper <= ALU_MUL;
CPU.State <= PIPE_FILL_2;
 
when UPP_C1 =>
PC := PC_INCR;
RegSel := conv_integer(CPU.SubOp_p1);
CPU.A_Oper <= ALU_UPP2;
CPU.A_Reg <= CPU.SubOp_p1;
CPU.A_Data <= CPU.Regfile(RegSel);
CPU.State <= PIPE_FILL_2;
 
-------------------------------------------------------------------------------
-- Basic Stack Manipulation (PSH, POP, RSP)
-------------------------------------------------------------------------------
when PSH_C1 =>
CPU_Next_State <= PIPE_FILL_1;
Address <= Stack_Ptr;
SP_Ctrl.Oper <= SP_PUSH;
when PSH_C1 =>
PC := PC_REV1;
SP := SP_PUSH;
CPU.State <= PIPE_FILL_1;
 
when POP_C1 =>
CPU_Next_State <= POP_C2;
Address <= Stack_Ptr;
when POP_C1 =>
PC := PC_IDLE;
CPU.State <= POP_C2;
 
when POP_C2 =>
CPU_Next_State <= POP_C3;
PC_Ctrl.Oper <= PC_INCR;
when POP_C2 =>
PC := PC_IDLE;
CPU.State <= POP_C3;
 
when POP_C3 =>
CPU_Next_State <= POP_C4;
Cache_Ctrl <= CACHE_OPER1;
PC_Ctrl.Oper <= PC_INCR;
when POP_C3 =>
IC := CACHE_OPER1;
PC := PC_INCR;
CPU.State <= POP_C4;
 
when POP_C4 =>
CPU_Next_State <= INSTR_DECODE;
Cache_Ctrl <= CACHE_INSTR;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_POP;
ALU_Ctrl.Reg <= SubOp;
ALU_Ctrl.Data <= Operand1;
when POP_C4 =>
PC := PC_INCR;
CPU.A_Oper <= ALU_LDI;
CPU.A_Reg <= CPU.SubOp_p0;
CPU.A_NoFlags <= '1';
CPU.A_Data <= CPU.Operand1;
CPU.State <= PIPE_FILL_2;
 
-------------------------------------------------------------------------------
-- Subroutines & Interrupts (RTS, JSR)
-------------------------------------------------------------------------------
when WAIT_FOR_INT => -- For soft interrupts only, halt the Program_Ctr
DP_Ctrl.Src <= DATA_BUS_IDLE;
CPU_Next_State <= WAIT_FOR_INT;
when WAIT_FOR_INT =>
PC := PC_IDLE;
DP.Src := DATA_BUS_IDLE;
CPU.State <= WAIT_FOR_INT;
 
when ISR_C1 =>
CPU_Next_State <= ISR_C2;
Address <= ISR_Addr;
INT_Ctrl.Incr_ISR <= '1';
when ISR_C1 =>
PC := PC_IDLE;
INT.Incr_ISR := '1';
CPU.State <= ISR_C2;
 
when ISR_C2 =>
CPU_Next_State <= ISR_C3;
Address <= ISR_Addr;
DP_Ctrl.Src <= DATA_WR_FLAG;
when ISR_C2 =>
PC := PC_IDLE;
DP.Src := DATA_WR_FLAG;
CPU.State <= ISR_C3;
 
when ISR_C3 =>
CPU_Next_State <= JSR_C1;
Cache_Ctrl <= CACHE_OPER1;
Address <= Stack_Ptr;
SP_Ctrl.Oper <= SP_PUSH;
DP_Ctrl.Src <= DATA_WR_PC;
DP_Ctrl.Reg <= ACCUM+1;
ALU_Ctrl.Oper <= ALU_STP;
ALU_Ctrl.Reg <= INT_FLAG;
Ack_D <= '1';
when ISR_C3 =>
IC := CACHE_OPER1;
PC := PC_IDLE;
SP := SP_PUSH;
DP.Src := DATA_WR_PC;
DP.Reg := ACCUM+1;
Ack_D := '1';
CPU.A_Oper <= ALU_STP;
CPU.A_Reg <= INT_FLAG;
CPU.State <= JSR_C1;
 
when JSR_C1 =>
CPU_Next_State <= JSR_C2;
Cache_Ctrl <= CACHE_OPER2;
Address <= Stack_Ptr;
SP_Ctrl.Oper <= SP_PUSH;
DP_Ctrl.Src <= DATA_WR_PC;
DP_Ctrl.Reg <= ACCUM;
when JSR_C1 =>
IC := CACHE_OPER2;
PC := PC_IDLE;
SP := SP_PUSH;
DP.Src := DATA_WR_PC;
DP.Reg := ACCUM;
CPU.State <= JSR_C2;
 
when JSR_C2 =>
CPU_Next_State <= PIPE_FILL_0;
Address <= Stack_Ptr;
SP_Ctrl.Oper <= SP_PUSH;
PC_Ctrl.Oper <= PC_LOAD;
PC_Ctrl.Addr <= Operand2 & Operand1;
when JSR_C2 =>
SP := SP_PUSH;
PC := PC_LOAD;
CPU.State <= PIPE_FILL_0;
 
when RTS_C1 =>
CPU_Next_State <= RTS_C2;
Address <= Stack_Ptr;
SP_Ctrl.Oper <= SP_POP;
when RTS_C1 =>
PC := PC_IDLE;
SP := SP_POP;
CPU.State <= RTS_C2;
 
when RTS_C2 =>
CPU_Next_State <= RTS_C3;
Address <= Stack_Ptr;
-- if this is an RTI, then we need to POP the flags
if( SubOp = SOP_RTI )then
SP_Ctrl.Oper <= SP_POP;
end if;
when RTS_C2 =>
PC := PC_IDLE;
if( CPU.SubOp_p0 = SOP_RTI )then
SP := SP_POP;
end if;
CPU.State <= RTS_C3;
 
when RTS_C3 =>
CPU_Next_State <= RTS_C4;
Cache_Ctrl <= CACHE_OPER1;
-- It doesn't really matter what is on the address bus for RTS, while
-- it does for RTI, so we make this the default
Address <= Stack_Ptr;
when RTS_C3 =>
IC := CACHE_OPER1;
PC := PC_IDLE;
CPU.State <= RTS_C4;
 
when RTS_C4 =>
CPU_Next_State <= RTS_C5;
Cache_Ctrl <= CACHE_OPER2;
when RTS_C4 =>
IC := CACHE_OPER2;
PC := PC_IDLE;
CPU.State <= RTS_C5;
 
when RTS_C5 =>
CPU_Next_State <= PIPE_FILL_0;
PC_Ctrl.Oper <= PC_LOAD;
PC_Ctrl.Addr <= Operand2 & Operand1;
if( SubOp = SOP_RTI )then
CPU_Next_State <= RTI_C6;
Cache_Ctrl <= CACHE_OPER1;
end if;
when RTS_C5 =>
PC := PC_LOAD;
CPU.State <= PIPE_FILL_0;
if( CPU.SubOp_p0 = SOP_RTI )then
IC := CACHE_OPER1;
CPU.State <= RTI_C6;
end if;
 
when RTI_C6 =>
CPU_Next_State <= PIPE_FILL_1;
PC_Ctrl.Oper <= PC_INCR;
ALU_Ctrl.Oper <= ALU_RFLG;
ALU_Ctrl.Data <= Operand1;
Int_RTI_D <= '1';
when RTI_C6 =>
PC := PC_INCR;
CPU.Int_Level <= 7;
CPU.A_Oper <= ALU_RFLG;
CPU.A_Data <= CPU.Operand1;
CPU.State <= PIPE_FILL_1;
 
-------------------------------------------------------------------------------
-- Debugging (BRK) Performs a 5-clock NOP
-------------------------------------------------------------------------------
when BRK_C1 =>
CPU_Next_State <= PIPE_FILL_0;
 
when others =>
null;
end case;
when BRK_C1 =>
PC := PC_IDLE;
CPU.State <= PIPE_FILL_0;
 
-- Interrupt service routines can only begin during the decode and wait
-- states to avoid corruption due to incomplete instruction execution
when others =>
null;
 
end case;
 
-------------------------------------------------------------------------------
-- Interrupt Override Logic
-------------------------------------------------------------------------------
 
if( Int_Req = '1' )then
if( CPU_State = INSTR_DECODE or CPU_State = WAIT_FOR_INT )then
-- Reset all of the sub-block controls to IDLE, to avoid unintended
-- operation due to the current instruction
ALU_Ctrl.Oper <= ALU_IDLE;
Cache_Ctrl <= CACHE_IDLE;
SP_Ctrl.Oper <= SP_IDLE;
DP_Ctrl.Src <= DATA_RD_MEM; -- JSH 7/20
INT_Ctrl.Soft_Ints <= (others => '0'); -- JSH 7/22
-- Rewind the PC by 3 to compensate for the pipeline registers
PC_Ctrl.Oper <= PC_INCR;
PC_Ctrl.Offset <= x"FF";
CPU_Next_State <= ISR_C1;
if( CPU.State = INSTR_DECODE or CPU.State = WAIT_FOR_INT )then
IC := CACHE_IDLE;
PC := PC_REV3;
SP := SP_IDLE;
DP.Src := DATA_RD_MEM;
INT.Soft_Ints := (others => '0');
CPU.A_Oper <= ALU_IDLE;
CPU.State <= ISR_C1;
 
end if;
end if;
 
end process;
 
-- We need to infer a hardware multipler, so we create a special clocked
-- process with no reset or clock enable
Multiplier_proc: process( Clock )
begin
if( rising_edge(Clock) )then
Mult <= Regfile(0) *
Regfile(conv_integer(ALU_Ctrl.Reg));
end if;
end process;
 
-------------------------------------------------------------------------------
-- Registered portion of CPU finite state machine
-- Vectored Interrupt Controller
-------------------------------------------------------------------------------
CPU_Regs: process( Reset, Clock )
variable Offset_SX : ADDRESS_TYPE;
variable i_Ints : INTERRUPT_BUNDLE := (others => '0');
variable Sum : std_logic_vector(8 downto 0) := "000000000";
variable Index : integer range 0 to 7 := 0;
variable Temp : std_logic_vector(8 downto 0);
begin
if( Reset = Reset_Level )then
CPU_State <= PIPE_FILL_0;
Opcode <= OP_INC;
SubOp <= ACCUM;
SubOp_p1 <= ACCUM;
Operand1 <= x"00";
Operand2 <= x"00";
Instr_Prefetch <= '0';
Prefetch <= x"00";
 
Wr_Data <= (others => '0');
Wr_Enable <= '0';
Rd_Enable <= '1';
CPU.Int_Pending <= ((Interrupts or INT.Soft_Ints) and
CPU.Int_Mask) or CPU.Int_Pending;
 
Program_Ctr <= Program_Start_Addr;
Stack_Ptr <= Stack_Start_Addr;
if( CPU.Wait_for_FSM = '0' )then
if( CPU.Int_Pending(0) = '1' )then
CPU.Int_Addr <= INT_VECTOR_0;
CPU.Int_Level <= 0;
CPU.Int_Pending(0) <= '0';
CPU.Wait_for_FSM <= '1';
elsif( CPU.Int_Pending(1) = '1' and CPU.Int_Level > 0 )then
CPU.Int_Addr <= INT_VECTOR_1;
CPU.Int_Level <= 1;
CPU.Int_Pending(1) <= '0';
CPU.Wait_for_FSM <= '1';
elsif( CPU.Int_Pending(2) = '1' and CPU.Int_Level > 1 )then
CPU.Int_Addr <= INT_VECTOR_2;
CPU.Int_Level <= 2;
CPU.Int_Pending(2) <= '0';
CPU.Wait_for_FSM <= '1';
elsif( CPU.Int_Pending(3) = '1' and CPU.Int_Level > 2 )then
CPU.Int_Addr <= INT_VECTOR_3;
CPU.Int_Level <= 3;
CPU.Int_Pending(3) <= '0';
CPU.Wait_for_FSM <= '1';
elsif( CPU.Int_Pending(4) = '1' and CPU.Int_Level > 3 )then
CPU.Int_Addr <= INT_VECTOR_4;
CPU.Int_Level <= 4;
CPU.Int_Pending(4) <= '0';
CPU.Wait_for_FSM <= '1';
elsif( CPU.Int_Pending(5) = '1' and CPU.Int_Level > 4 )then
CPU.Int_Addr <= INT_VECTOR_5;
CPU.Int_Level <= 5;
CPU.Int_Pending(5) <= '0';
CPU.Wait_for_FSM <= '1';
elsif( CPU.Int_Pending(6) = '1' and CPU.Int_Level > 6 )then
CPU.Int_Addr <= INT_VECTOR_6;
CPU.Int_Level <= 6;
CPU.Int_Pending(6) <= '0';
CPU.Wait_for_FSM <= '1';
elsif( CPU.Int_Pending(7) = '1' )then
CPU.Int_Addr <= INT_VECTOR_7;
CPU.Int_Level <= 7;
CPU.Int_Pending(7) <= '0';
CPU.Wait_for_FSM <= '1';
end if;
end if;
 
Ack_Q <= '0';
Ack_Q1 <= '0';
Int_Ack <= '0';
Int_RTI <= '0';
Ack_Q <= Ack_D;
Ack_Q1 <= Ack_Q;
Int_Ack <= Ack_Q1;
if( Int_Ack = '1' )then
CPU.Wait_for_FSM <= '0';
end if;
 
Int_Req <= '0';
Pending <= x"00";
Wait_for_FSM <= '0';
Int_Mask <= Default_Interrupt_Mask(7 downto 1) & '1';
ISR_Addr <= INT_VECTOR_0;
for i in 0 to 8 loop
History(i) <= 0;
end loop;
Hst_Ptr <= 0;
Int_Req <= CPU.Wait_for_FSM and (not Int_Ack);
 
for i in 0 to 7 loop
Regfile(i) <= (others => '0');
end loop;
Flags <= x"00";
if( INT.Mask_Set = '1' )then
if( Enable_NMI )then
CPU.Int_Mask <= Accumulator(7 downto 1) & '1';
else -- Disable NMI override
CPU.Int_Mask <= Accumulator;
end if;
end if;
 
elsif( rising_edge(Clock) )then
Wr_Enable <= '0';
Wr_Data <= x"00";
Rd_Enable <= '0';
if( INT.Incr_ISR = '1' )then
CPU.Int_Addr <= CPU.Int_Addr + 1;
end if;
 
if( Halt = '0' )then
-------------------------------------------------------------------------------
-- Instruction/Operand caching for pipelined memory access
-- ALU (Arithmetic / Logic Unit)
-------------------------------------------------------------------------------
CPU_State <= CPU_Next_State;
case Cache_Ctrl is
when CACHE_INSTR =>
Opcode <= Rd_Data(7 downto 3);
SubOp <= Rd_Data(2 downto 0);
SubOp_p1 <= Rd_Data(2 downto 0) + 1;
if( Instr_Prefetch = '1' )then
Opcode <= Prefetch(7 downto 3);
SubOp <= Prefetch(2 downto 0);
SubOp_p1 <= Prefetch(2 downto 0) + 1;
Instr_Prefetch <= '0';
end if;
Index := conv_integer(CPU.A_Reg);
 
when CACHE_OPER1 =>
Operand1 <= Rd_Data;
CPU.M_Prod <= Accumulator *
CPU.Regfile(conv_integer(CPU.M_Reg));
 
when CACHE_OPER2 =>
Operand2 <= Rd_Data;
case( CPU.A_Oper )is
when ALU_INC => -- Rn = Rn + 1 : CPU.Flags N,C,Z
Temp := ("0" & x"01") +
("0" & CPU.A_Data);
Flags(FL_CARRY) <= Temp(8);
CPU.Regfile(Index) <= Temp(7 downto 0);
if( CPU.A_NoFlags = '0' )then
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_NEG) <= Temp(7);
end if;
 
when CACHE_PREFETCH =>
Prefetch <= Rd_Data;
Instr_Prefetch <= '1';
when ALU_UPP2 => -- Rn = Rn + C : Flags C
Temp := ("0" & x"00") +
("0" & CPU.A_Data) +
Flags(FL_CARRY);
Flags(FL_CARRY) <= Temp(8);
CPU.Regfile(Index) <= Temp(7 downto 0);
 
when CACHE_IDLE =>
null;
end case;
when ALU_ADC => -- R0 = R0 + Rn + C : N,C,Z
Temp := ("0" & Accumulator) +
("0" & CPU.A_Data) +
Flags(FL_CARRY);
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_CARRY) <= Temp(8);
Flags(FL_NEG) <= Temp(7);
Accumulator <= Temp(7 downto 0);
 
-------------------------------------------------------------------------------
-- Program Counter
-------------------------------------------------------------------------------
Offset_SX(15 downto 8) := (others => PC_Ctrl.Offset(7));
Offset_SX(7 downto 0) := PC_Ctrl.Offset;
when ALU_TX0 => -- R0 = Rn : Flags N,Z
Temp := "0" & CPU.A_Data;
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_NEG) <= Temp(7);
Accumulator <= Temp(7 downto 0);
 
case PC_Ctrl.Oper is
when PC_IDLE =>
null;
when ALU_OR => -- R0 = R0 | Rn : Flags N,Z
Temp(7 downto 0) := Accumulator or CPU.A_Data;
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_NEG) <= Temp(7);
Accumulator <= Temp(7 downto 0);
 
when PC_REV1 =>
Program_Ctr <= Program_Ctr - 1;
when ALU_AND => -- R0 = R0 & Rn : Flags N,Z
Temp(7 downto 0) := Accumulator and CPU.A_Data;
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_NEG) <= Temp(7);
Accumulator <= Temp(7 downto 0);
 
when PC_REV2 =>
Program_Ctr <= Program_Ctr - 2;
when ALU_XOR => -- R0 = R0 ^ Rn : Flags N,Z
Temp(7 downto 0) := Accumulator xor CPU.A_Data;
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_NEG) <= Temp(7);
Accumulator <= Temp(7 downto 0);
 
when PC_INCR =>
Program_Ctr <= Program_Ctr + Offset_SX - 2;
when ALU_ROL => -- Rn = Rn<<1,C : Flags N,C,Z
Temp := CPU.A_Data & Flags(FL_CARRY);
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_CARRY) <= Temp(8);
Flags(FL_NEG) <= Temp(7);
CPU.Regfile(Index) <= Temp(7 downto 0);
 
when PC_LOAD =>
Program_Ctr <= PC_Ctrl.Addr;
when ALU_ROR => -- Rn = C,Rn>>1 : Flags N,C,Z
Temp := CPU.A_Data(0) & Flags(FL_CARRY) &
CPU.A_Data(7 downto 1);
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_CARRY) <= Temp(8);
Flags(FL_NEG) <= Temp(7);
CPU.Regfile(Index) <= Temp(7 downto 0);
 
when others =>
null;
end case;
when ALU_DEC => -- Rn = Rn - 1 : Flags N,C,Z
Temp := ("0" & CPU.A_Data) +
("0" & x"FF");
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_CARRY) <= Temp(8);
Flags(FL_NEG) <= Temp(7);
CPU.Regfile(Index) <= Temp(7 downto 0);
 
-------------------------------------------------------------------------------
-- (Write) Data Path
-------------------------------------------------------------------------------
case DP_Ctrl.Src is
when DATA_BUS_IDLE =>
null;
when ALU_SBC => -- Rn = R0 - Rn - C : Flags N,C,Z
Temp := ("0" & Accumulator) +
("0" & (not CPU.A_Data)) +
Flags(FL_CARRY);
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_CARRY) <= Temp(8);
Flags(FL_NEG) <= Temp(7);
Accumulator <= Temp(7 downto 0);
 
when DATA_RD_MEM =>
Rd_Enable <= '1';
when ALU_ADD => -- R0 = R0 + Rn : Flags N,C,Z
Temp := ("0" & Accumulator) +
("0" & CPU.A_Data);
Flags(FL_CARRY) <= Temp(8);
Accumulator <= Temp(7 downto 0);
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_NEG) <= Temp(7);
 
when DATA_WR_REG =>
Wr_Enable <= '1';
Wr_Data <= Regfile(conv_integer(DP_Ctrl.Reg));
when ALU_STP => -- Sets bit(n) in the CPU.Flags register
Flags(Index) <= '1';
 
when DATA_WR_FLAG =>
Wr_Enable <= '1';
Wr_Data <= Flags;
when ALU_BTT => -- Z = !R0(N), N = R0(7)
Flags(FL_ZERO) <= not Accumulator(Index);
Flags(FL_NEG) <= Accumulator(7);
 
when DATA_WR_PC =>
Wr_Enable <= '1';
Wr_Data <= Program_Ctr(15 downto 8);
if( DP_Ctrl.Reg = ACCUM )then
Wr_Data <= Program_Ctr(7 downto 0);
end if;
when ALU_CLP => -- Clears bit(n) in the Flags register
Flags(Index) <= '0';
 
when others =>
null;
end case;
when ALU_T0X => -- Rn = R0 : Flags N,Z
Temp := "0" & Accumulator;
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_NEG) <= Temp(7);
CPU.Regfile(Index) <= Temp(7 downto 0);
 
-------------------------------------------------------------------------------
-- Stack Pointer
-------------------------------------------------------------------------------
case SP_Ctrl.Oper is
when SP_IDLE =>
null;
when ALU_CMP => -- Sets CPU.Flags on R0 - Rn : Flags N,C,Z
Temp := ("0" & Accumulator) +
("0" & (not CPU.A_Data)) +
'1';
Flags(FL_ZERO) <= nor_reduce(Temp(7 downto 0));
Flags(FL_CARRY) <= Temp(8);
Flags(FL_NEG) <= Temp(7);
 
when SP_RSET =>
-- The original RSP instruction simply reset the stack pointer to the preset
-- address set at compile time. However, with little extra effort, we can
-- modify the instruction to allow the stack pointer to be moved anywhere in
-- the memory map. Since RSP can't have an sub-opcode, R1:R0 was chosen as
-- a fixed source
Stack_Ptr <= Stack_Start_Addr;
if( Allow_Stack_Address_Move )then
Stack_Ptr <= Regfile(1) & Regfile(0);
end if;
when ALU_MUL => -- Stage 1 of 2 {R1:R0} = R0 * Rn : Flags Z
CPU.Regfile(0) <= CPU.M_Prod(7 downto 0);
CPU.Regfile(1) <= CPU.M_Prod(15 downto 8);
Flags(FL_ZERO) <= nor_reduce(CPU.M_Prod);
 
when SP_POP =>
Stack_Ptr <= Stack_Ptr + 1;
when ALU_LDI => -- Rn <= Data : Flags N,Z
if( CPU.A_NoFlags = '0' )then
Flags(FL_ZERO) <= nor_reduce(CPU.A_Data);
Flags(FL_NEG) <= CPU.A_Data(7);
end if;
CPU.Regfile(Index) <= CPU.A_Data;
 
when SP_PUSH =>
Stack_Ptr <= Stack_Ptr - 1;
when ALU_RFLG =>
Flags <= CPU.A_Data;
 
when others =>
null;
when others =>
null;
end case;
 
end case;
 
-------------------------------------------------------------------------------
-- Interrupt Controller
-- Instruction/Operand caching for pipelined memory access
-------------------------------------------------------------------------------
-- The interrupt control mask is always sourced out of R0
if( INT_Ctrl.Mask_Set = '1' )then
Int_Mask <= Regfile(conv_integer(ACCUM))(7 downto 1) & '1';
end if;
 
-- Combine external and internal interrupts, and mask the OR or the two
-- with the mask. Record any incoming interrupts to the pending buffer
i_Ints := (Interrupts or INT_Ctrl.Soft_Ints) and
Int_Mask;
if( i_Ints > 0 )then
Pending <= i_Ints;
end if;
 
-- Only mess with interrupt signals while the CPU core is not currently
-- working with, or loading, an ISR address
if( Wait_for_FSM = '0' and Pending > 0 )then
if( Pending(0) = '1' and (Hst_Ptr = 0 or History(Hst_Ptr) > 0))then
ISR_Addr <= INT_VECTOR_0;
Pending(0) <= '0';
History(Hst_Ptr+1) <= 0;
Hst_Ptr <= Hst_Ptr + 1;
Wait_for_FSM <= '1';
elsif(Pending(1) = '1' and (Hst_Ptr = 0 or History(Hst_Ptr) > 1))then
ISR_Addr <= INT_VECTOR_1;
Pending(1) <= '0';
History(Hst_Ptr+1) <= 1;
Hst_Ptr <= Hst_Ptr + 1;
Wait_for_FSM <= '1';
elsif(Pending(2) = '1' and (Hst_Ptr = 0 or History(Hst_Ptr) > 2))then
ISR_Addr <= INT_VECTOR_2;
Pending(2) <= '0';
History(Hst_Ptr+1) <= 1;
Hst_Ptr <= Hst_Ptr + 1;
Wait_for_FSM <= '1';
elsif(Pending(3) = '1' and (Hst_Ptr = 0 or History(Hst_Ptr) > 3))then
ISR_Addr <= INT_VECTOR_3;
Pending(3) <= '0';
History(Hst_Ptr+1) <= 3;
Hst_Ptr <= Hst_Ptr + 1;
Wait_for_FSM <= '1';
elsif(Pending(4) = '1' and (Hst_Ptr = 0 or History(Hst_Ptr) > 4))then
ISR_Addr <= INT_VECTOR_4;
Pending(4) <= '0';
History(Hst_Ptr+1) <= 4;
Hst_Ptr <= Hst_Ptr + 1;
Wait_for_FSM <= '1';
elsif(Pending(5) = '1' and (Hst_Ptr = 0 or History(Hst_Ptr) > 5))then
ISR_Addr <= INT_VECTOR_5;
Pending(5) <= '0';
History(Hst_Ptr+1) <= 5;
Hst_Ptr <= Hst_Ptr + 1;
Wait_for_FSM <= '1';
elsif(Pending(6) = '1' and (Hst_Ptr = 0 or History(Hst_Ptr) > 6))then
ISR_Addr <= INT_VECTOR_6;
Pending(6) <= '0';
History(Hst_Ptr+1) <= 6;
Hst_Ptr <= Hst_Ptr + 1;
Wait_for_FSM <= '1';
elsif(Pending(7) = '1' and (Hst_Ptr = 0 or History(Hst_Ptr) > 7))then
ISR_Addr <= INT_VECTOR_7;
Pending(7) <= '0';
History(Hst_Ptr+1) <= 7;
Hst_Ptr <= Hst_Ptr + 1;
Wait_for_FSM <= '1';
case( IC )is
when CACHE_INSTR =>
CPU.Opcode <= Rd_Data(7 downto 3);
CPU.SubOp_p0 <= Rd_Data(2 downto 0);
CPU.SubOp_p1 <= Rd_Data(2 downto 0) + 1;
if( CPU.Cache_Valid = '1' )then
CPU.Opcode <= CPU.Prefetch(7 downto 3);
CPU.SubOp_p0 <= CPU.Prefetch(2 downto 0);
CPU.SubOp_p1 <= CPU.Prefetch(2 downto 0) + 1;
CPU.Cache_Valid <= '0';
end if;
end if;
 
-- Reset the Wait_for_FSM flag on Int_Ack
Ack_Q <= Ack_D;
Ack_Q1 <= Ack_Q;
Int_Ack <= Ack_Q1;
if( Int_Ack = '1' )then
Wait_for_FSM <= '0';
end if;
when CACHE_OPER1 =>
CPU.Operand1 <= Rd_Data;
 
Int_Req <= Wait_for_FSM and (not Int_Ack);
when CACHE_OPER2 =>
CPU.Operand2 <= Rd_Data;
 
Int_RTI <= Int_RTI_D;
if( Int_RTI = '1' and Hst_Ptr > 0 )then
Hst_Ptr <= Hst_Ptr - 1;
end if;
when CACHE_PREFETCH =>
CPU.Prefetch <= Rd_Data;
CPU.Cache_Valid <= '1';
 
-- Incr_ISR allows the CPU Core to advance the vector address to pop the
-- lower half of the address.
if( INT_Ctrl.Incr_ISR = '1' )then
ISR_Addr <= ISR_Addr + 1;
end if;
when CACHE_PFFLUSH =>
CPU.Prefetch <= Rd_Data;
CPU.Operand1 <= x"00";
CPU.Operand2 <= x"00";
CPU.Cache_Valid <= '1';
 
when CACHE_INVALIDATE =>
CPU.Cache_Valid <= '0';
 
when CACHE_IDLE =>
null;
end case;
 
-------------------------------------------------------------------------------
-- ALU (Arithmetic / Logic Unit)
-- Program Counter
-------------------------------------------------------------------------------
Temp := (others => '0');
Index := conv_integer(ALU_Ctrl.Reg);
 
case ALU_Ctrl.Oper is
when ALU_INC | ALU_UPP => -- Rn = Rn + 1 : Flags N,C,Z
Sum := ("0" & x"01") +
("0" & Regfile(Index));
Flags(FL_CARRY) <= Sum(8);
Regfile(Index) <= Sum(7 downto 0);
-- ALU_INC and ALU_UPP are essentially the same, except that ALU_UPP
-- doesn't set the N or Z flags. Note that the MSB can be used to
-- distinguish between the two ALU modes.
if( ALU_Ctrl.Oper(4) = '0' )then
Flags(FL_ZERO) <= '0';
if( Sum(7 downto 0) = 0 )then
Flags(FL_ZERO)<= '1';
end if;
Flags(FL_NEG) <= Sum(7);
end if;
Offset_SX(15 downto 8) := (others => CPU.Operand1(7));
Offset_SX(7 downto 0) := CPU.Operand1;
 
when ALU_UPP2 => -- Rn = Rn + C
Sum := ("0" & x"00") +
("0" & Regfile(Index)) +
Flags(FL_CARRY);
Flags(FL_CARRY) <= Sum(8);
Regfile(Index) <= Sum(7 downto 0);
case( PC )is
 
when ALU_ADC => -- R0 = R0 + Rn + C : Flags N,C,Z
Sum := ("0" & Regfile(0)) +
("0" & Regfile(Index)) +
Flags(FL_CARRY);
Flags(FL_ZERO) <= '0';
if( Sum(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_CARRY) <= Sum(8);
Flags(FL_NEG) <= Sum(7);
Regfile(0) <= Sum(7 downto 0);
when PC_INCR =>
CPU.Program_Ctr <= CPU.Program_ctr + 1;
when PC_IDLE =>
--CPU.Program_Ctr <= CPU.Program_Ctr + 0;
null;
 
when ALU_TX0 => -- R0 = Rn : Flags N,Z
Temp := "0" & Regfile(Index);
Flags(FL_ZERO) <= '0';
if( Temp(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_NEG) <= Temp(7);
Regfile(0) <= Temp(7 downto 0);
when PC_REV1 =>
CPU.Program_Ctr <= CPU.Program_Ctr - 1;
 
when ALU_OR => -- R0 = R0 | Rn : Flags N,Z
Temp(7 downto 0) := Regfile(0) or Regfile(Index);
Flags(FL_ZERO) <= '0';
if( Temp(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_NEG) <= Temp(7);
Regfile(0) <= Temp(7 downto 0);
when PC_REV2 =>
CPU.Program_Ctr <= CPU.Program_Ctr - 2;
 
when ALU_AND => -- R0 = R0 & Rn : Flags N,Z
Temp(7 downto 0) := Regfile(0) and Regfile(Index);
Flags(FL_ZERO) <= '0';
if( Temp(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_NEG) <= Temp(7);
Regfile(0) <= Temp(7 downto 0);
when PC_REV3 =>
CPU.Program_Ctr <= CPU.Program_Ctr - 3;
 
when ALU_XOR => -- R0 = R0 ^ Rn : Flags N,Z
Temp(7 downto 0) := Regfile(0) xor Regfile(Index);
Flags(FL_ZERO) <= '0';
if( Temp(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_NEG) <= Temp(7);
Regfile(0) <= Temp(7 downto 0);
when PC_BRANCH =>
CPU.Program_Ctr <= CPU.Program_Ctr + Offset_SX - 2;
 
when ALU_ROL => -- Rn = Rn<<1,C : Flags N,C,Z
Temp := Regfile(Index) & Flags(FL_CARRY);
Flags(FL_ZERO) <= '0';
if( Temp(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_CARRY) <= Temp(8);
Flags(FL_NEG) <= Temp(7);
Regfile(Index) <= Temp(7 downto 0);
when PC_LOAD =>
CPU.Program_Ctr <= CPU.Operand2 & CPU.Operand1;
 
when ALU_ROR => -- Rn = C,Rn>>1 : Flags N,C,Z
Temp := Regfile(Index)(0) & Flags(FL_CARRY) &
Regfile(Index)(7 downto 1);
Flags(FL_ZERO) <= '0';
if( Temp(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_CARRY) <= Temp(8);
Flags(FL_NEG) <= Temp(7);
Regfile(Index) <= Temp(7 downto 0);
when others =>
null;
end case;
 
when ALU_DEC => -- Rn = Rn - 1 : Flags N,C,Z
Sum := ("0" & Regfile(Index)) +
("0" & x"FF");
Flags(FL_ZERO) <= '0';
if( Sum(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_CARRY) <= Sum(8);
Flags(FL_NEG) <= Sum(7);
Regfile(Index) <= Sum(7 downto 0);
-------------------------------------------------------------------------------
-- (Write) Data Path
-------------------------------------------------------------------------------
 
when ALU_SBC => -- Rn = R0 - Rn - C : Flags N,C,Z
Sum := ("0" & Regfile(0)) +
("0" & (not Regfile(Index))) +
Flags(FL_CARRY);
Flags(FL_ZERO) <= '0';
if( Sum(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_CARRY) <= Sum(8);
Flags(FL_NEG) <= Sum(7);
Regfile(0) <= Sum(7 downto 0);
Wr_Data <= x"00";
Wr_Enable <= '0';
Rd_Enable <= '0';
 
when ALU_ADD => -- R0 = R0 + Rn : Flags N,C,Z
Sum := ("0" & Regfile(0)) +
("0" & Regfile(Index));
Flags(FL_CARRY) <= Sum(8);
Regfile(0) <= Sum(7 downto 0);
Flags(FL_ZERO) <= '0';
if( Sum(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_NEG) <= Sum(7);
case( DP.Src )is
when DATA_BUS_IDLE =>
null;
 
when ALU_STP => -- Sets bit(n) in the Flags register
Flags(Index) <= '1';
when DATA_RD_MEM =>
Rd_Enable <= '1';
 
when ALU_BTT => -- Z = !R0(N), N = R0(7)
Flags(FL_ZERO) <= not Regfile(0)(Index);
Flags(FL_NEG) <= Regfile(0)(7);
when DATA_WR_REG =>
Wr_Enable <= '1';
Wr_Data <= CPU.Regfile(conv_integer(DP.Reg));
 
when ALU_CLP => -- Clears bit(n) in the Flags register
Flags(Index) <= '0';
when DATA_WR_FLAG =>
Wr_Enable <= '1';
Wr_Data <= Flags;
 
when ALU_T0X => -- Rn = R0 : Flags N,Z
Temp := "0" & Regfile(0);
Flags(FL_ZERO) <= '0';
if( Temp(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_NEG) <= Temp(7);
Regfile(Index) <= Temp(7 downto 0);
when DATA_WR_PC =>
Wr_Enable <= '1';
Wr_Data <= CPU.Program_Ctr(15 downto 8);
if( DP.Reg = ACCUM )then
Wr_Data <= CPU.Program_Ctr(7 downto 0);
end if;
 
when ALU_CMP => -- Sets Flags on R0 - Rn : Flags N,C,Z
Sum := ("0" & Regfile(0)) +
("0" & (not Regfile(Index))) +
'1';
Flags(FL_ZERO) <= '0';
if( Sum(7 downto 0) = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_CARRY) <= Sum(8);
Flags(FL_NEG) <= Sum(7);
when others =>
null;
end case;
 
when ALU_MUL => -- Stage 1 of 2 {R1:R0} = R0 * Rn : Flags Z
Regfile(0) <= Mult(7 downto 0);
Regfile(1) <= Mult(15 downto 8);
Flags(FL_ZERO) <= '0';
if( Mult = 0 )then
Flags(FL_ZERO) <= '1';
end if;
-------------------------------------------------------------------------------
-- Stack Pointer
-------------------------------------------------------------------------------
case( SP )is
when SP_IDLE =>
null;
 
when ALU_LDI | ALU_POP => -- Rn <= Data : Flags N,Z
-- The POP instruction doesn't alter the flags, so we need to check
if( ALU_Ctrl.Oper = ALU_LDI )then
Flags(FL_ZERO) <= '0';
if( ALU_Ctrl.Data = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_NEG) <= ALU_Ctrl.Data(7);
end if;
Regfile(Index) <= ALU_Ctrl.Data;
when SP_RSET =>
CPU.Stack_Ptr <= Stack_Start_Addr;
if( Allow_Stack_Address_Move )then
CPU.Stack_Ptr <= CPU.Regfile(1) & CPU.Regfile(0);
end if;
 
when ALU_LDX => -- R0 <= Data : Flags N,Z
Flags(FL_ZERO) <= '0';
if( ALU_Ctrl.Data = 0 )then
Flags(FL_ZERO) <= '1';
end if;
Flags(FL_NEG) <= ALU_Ctrl.Data(7);
Regfile(0) <= ALU_Ctrl.Data;
when SP_POP =>
CPU.Stack_Ptr <= CPU.Stack_Ptr + 1;
 
when ALU_RFLG =>
Flags <= ALU_Ctrl.Data;
when SP_PUSH =>
CPU.Stack_Ptr <= CPU.Stack_Ptr - 1;
 
when others =>
null;
end case;
when others =>
null;
 
end if;
end case;
 
end if;
end process;
 
/o8_alu16.vhd
0,0 → 1,797
-- Copyright (c)2013 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary 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 binary 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,
-- where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``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 JEREMY SETH HENRY 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.
--
-- VHDL Units : o8_alu16
-- Description: Provides a mix of common 16-bit math functions to accelerate
-- : math operations on the Open8 microprocessor core.
--
-- Notes : All output registers are updated by writing an instruction to
-- : offset 0x1F.
-- : The math unit is busy when the MSB of the status
-- : register is high, and done/ready when it reads low.
-- : Almost Equal checks to see if Addend 1 is no more, or less
-- : addend 2 within the specified tolerance. For example,
-- : addend_1 = 2 is almost equal to addend_2 = -1 with a
-- : tolerance of 3, but not a tolerance of 1. Actual function is
-- : AE = '1' when (A1 <= A2 + T) and (A1 >= A2 - T) else '0'
-- : This is an inherently signed function.
-- : Signed Overflow/Underflow is logically equivalent to
-- : S_Sum/Dif(16) xor S_Sum/Dif(15), since the apparent result
-- : changes sign while the internal sign bit does not. For example
-- : |8000| will result in (-)8000 due to the way the internal
-- : logic handles sign extension. Thus, the O bit should be
-- : checked when performing signed math
-- : Decimal Adjust converts the contents of a register into its BCD
-- : equivalent. This can be used to get the base 10 representation
-- : of a value for conversion to ASCII. There are two variants,
-- : Byte and Word. Note that conversion times are fairly long,
-- : since we are repeatedly issuing division commands, but it is
-- : still faster than software emulation.
-- : The Byte conversion only operates on the lower 8 bits, and sets
-- : the Z and N flags. The N flag is only set when SDAB is used
-- : and the signed value of the register is negative. The O bit is
-- : set if the upper byte of the register is non-zero, but does
-- : not actually result in an calculation error.
-- : Examples:
-- : UDAB 0x00FE -> 0x0254, Flags -> 0x0
-- : SDAB 0x00FE -> 0x0002, Flags -> 0x4
-- : The Word conversion uses the entire 16-bit word, and uses the
-- : Flags register to hold the Tens of Thousands place. Note that
-- : the N flag is still used for signed conversions, while it may
-- : be used as a data bit for unsigned conversions.
-- : Examples:
-- : UDAW 0xD431 -> 0x4321, Flags -> 0x5 ('0', MSB, MSB-1, MSB-2)
-- : SDAW 0xD431 -> 0x1216, Flags -> 0x5 ('0', N , MSB , MSB-1)
 
-- Register Map:
-- Offset Bitfield Description Read/Write
-- 0x00 AAAAAAAA Register 0 ( 7:0) (RW)
-- 0x01 AAAAAAAA Register 0 (15:8) (RW)
-- 0x02 AAAAAAAA Register 1 ( 7:0) (RW)
-- 0x03 AAAAAAAA Register 1 (15:8) (RW)
-- 0x04 AAAAAAAA Register 2 ( 7:0) (RW)
-- 0x05 AAAAAAAA Register 2 (15:8) (RW)
-- 0x06 AAAAAAAA Register 3 ( 7:0) (RW)
-- 0x07 AAAAAAAA Register 3 (15:8) (RW)
-- 0x08 AAAAAAAA Register 4 ( 7:0) (RW)
-- 0x09 AAAAAAAA Register 4 (15:8) (RW)
-- 0x0A AAAAAAAA Register 5 ( 7:0) (RW)
-- 0x0B AAAAAAAA Register 5 (15:8) (RW)
-- 0x0C AAAAAAAA Register 6 ( 7:0) (RW)
-- 0x0D AAAAAAAA Register 6 (15:8) (RW)
-- 0x0E AAAAAAAA Register 7 ( 7:0) (RW)
-- 0x0F AAAAAAAA Register 7 (15:8) (RW)
-- 0x10 -------- Reserved (--)
-- 0x11 -------- Reserved (--)
-- 0x12 -------- Reserved (--)
-- 0x13 -------- Reserved (--)
-- 0x14 -------- Reserved (--)
-- 0x15 -------- Reserved (--)
-- 0x16 -------- Reserved (--)
-- 0x17 -------- Reserved (--)
-- 0x18 -------- Reserved (--)
-- 0x19 -------- Reserved (--)
-- 0x1A -------- Reserved (--)
-- 0x1B -------- Reserved (--)
-- 0x1C AAAAAAAA Tolerance ( 7:0) (RW)
-- 0x1D AAAAAAAA Tolerance (15:8) (RW)
-- 0x1E E---DCBA Status & Flags (RW)
-- A = Zero Flag
-- B = Carry Flag
-- C = Negative Flag
-- D = Overflow / Error Flag
-- E = Busy Flag (1 = busy, 0 = idle)
-- 0x1F BBBBBAAA Instruction Register (RW)
-- A = Operand (register select)
-- B = Opcode (instruction select)
--
-- Instruction Map:
-- OP_T0X "0000 0xxx" : Transfer R0 to Rx R0 -> Rx (Sets Z,N)
-- OP_TX0 "0000 1xxx" : Transfer Rx to R0 Rx -> R0 (Sets Z,N)
-- OP_CLR "0001 0xxx" : Set Rx to 0 0x00 -> Rx (Sets Z,N)
--
-- OP_IDIV "0001 1xxx" : Integer Division R0/Rx -> Q:R0, R:Rx
--
-- OP_UMUL "0010 0xxx" : Unsigned Multiply R0*Rx -> R1:R0 (Sets Z)
-- OP_UADD "0010 1xxx" : Unsigned Addition R0+Rx -> R0 (Sets N,Z,C)
-- OP_UADC "0011 0xxx" : Unsigned Add w/Carry R0+Rx+C -> R0 (Sets N,Z,C)
-- OP_USUB "0011 1xxx" : Unsigned Subtraction R0-Rx -> R0 (Sets N,Z,C)
-- OP_USBC "0100 0xxx" : Unsigned Sub w/Carry R0-Rx-C -> R0 (Sets N,Z,C)
-- OP_UCMP "0100 1xxx" : Unsigned Compare R0-Rx - Sets N,Z,C only
--
-- OP_SMUL "0101 0xxx" : Signed Multiply R0*Rx -> R1:R0 (Sets N,Z)
-- OP_SADD "0101 1xxx" : Signed Addition R0+Rx -> R0 (Sets N,Z,O)
-- OP_SSUB "0110 0xxx" : Signed Subtraction R0-Rx -> R0 (Sets N,Z,O)
-- OP_SCMP "0110 1xxx" : Signed Compare R0-Rx - Sets N,Z,O only
-- OP_SMAG "0111 0xxx" : Signed Magnitude |Rx| -> R0 (Sets Z,O)
-- OP_SNEG "0111 1xxx" : Signed Negation -Rx -> R0 (Sets N,Z,O)
--
-- OP_ACMP "1000 0xxx" : Signed Almost Equal (see description)
-- OP_SCRY "1000 1---" : Set the carry bit (ignores operand)
--
-- OP_UDAB "1001 0xxx" : Decimal Adjust Byte (see description)
-- OP_SDAB "1001 1xxx" : Decimal Adjust Byte (see description)
-- OP_UDAW "1010 0xxx" : Decimal Adjust Word (see description)
-- OP_SDAW "1010 1xxx" : Decimal Adjust Word (see description)
 
-- OP_RSVD "1011 0---" : Reserved
 
-- OP_BSWP "1011 1xxx" : Byte Swap (Swaps upper and lower bytes)
 
-- OP_BOR "1100 0xxx" : Bitwise Logical OR Rx or R0 -> R0
-- OP_BAND "1100 1xxx" : Bitwise Logical AND Rx and R0 -> R0
-- OP_BXOR "1101 0xxx" : Bitwise Logical XOR Rx xor R0 -> R0
--
-- OP_BINV "1101 1xxx" : Bitwise logical NOT #Rx -> Rx
-- OP_BSFL "1110 0xxx" : Logical Shift Left Rx<<1,0 -> Rx
-- OP_BROL "1110 1xxx" : Logical Rotate Left Rx<<1,C -> Rx,C
-- OP_BSFR "1111 0xxx" : Logical Shift Right 0,Rx>>1 -> Rx
-- OP_BROR "1111 1xxx" : Logical Rotate Right C,Rx>>1 -> Rx,C
 
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;
 
library work;
use work.open8_pkg.all;
 
entity o8_alu16 is
generic(
Reset_Level : std_logic;
Address : ADDRESS_TYPE
);
port(
Clock : in std_logic;
Reset : in std_logic;
--
Bus_Address : in ADDRESS_TYPE;
Wr_Enable : in std_logic;
Wr_Data : in DATA_TYPE;
Rd_Enable : in std_logic;
Rd_Data : out DATA_TYPE;
Interrupt : out std_logic
);
end entity;
 
architecture behave of o8_alu16 is
 
-------------------------------------------------------------------
-- Opcode Definitions (should match the table above)
-- Register Manipulation
constant OP_T0X : std_logic_vector(4 downto 0) := "00000";
constant OP_TX0 : std_logic_vector(4 downto 0) := "00001";
constant OP_CLR : std_logic_vector(4 downto 0) := "00010";
 
-- Integer Division
constant OP_IDIV : std_logic_vector(4 downto 0) := "00011";
 
-- Unsigned Math Operations
constant OP_UMUL : std_logic_vector(4 downto 0) := "00100";
constant OP_UADD : std_logic_vector(4 downto 0) := "00101";
constant OP_UADC : std_logic_vector(4 downto 0) := "00110";
constant OP_USUB : std_logic_vector(4 downto 0) := "00111";
constant OP_USBC : std_logic_vector(4 downto 0) := "01000";
constant OP_UCMP : std_logic_vector(4 downto 0) := "01001";
 
-- Signed Math Operations
constant OP_SMUL : std_logic_vector(4 downto 0) := "01010";
constant OP_SADD : std_logic_vector(4 downto 0) := "01011";
constant OP_SSUB : std_logic_vector(4 downto 0) := "01100";
constant OP_SCMP : std_logic_vector(4 downto 0) := "01101";
constant OP_SMAG : std_logic_vector(4 downto 0) := "01110";
constant OP_SNEG : std_logic_vector(4 downto 0) := "01111";
 
-- Signed Almost Equal
constant OP_ACMP : std_logic_vector(4 downto 0) := "10000";
 
-- Carry Flag set/clear
constant OP_SCRY : std_logic_vector(4 downto 0) := "10001";
 
-- (Un)Signed Decimal Adjust Byte
constant OP_UDAB : std_logic_vector(4 downto 0) := "10010";
constant OP_SDAB : std_logic_vector(4 downto 0) := "10011";
 
-- (Un)Signed Decimal Adjust Word
constant OP_UDAW : std_logic_vector(4 downto 0) := "10100";
constant OP_SDAW : std_logic_vector(4 downto 0) := "10101";
 
-- Reserved for future use
constant OP_RSVD : std_logic_vector(4 downto 0) := "10110";
-- Byte Swap ( U <> L )
constant OP_BSWP : std_logic_vector(4 downto 0) := "10111";
 
-- Bitwise Boolean Operations (two operand)
constant OP_BOR : std_logic_vector(4 downto 0) := "11000";
constant OP_BAND : std_logic_vector(4 downto 0) := "11001";
constant OP_BXOR : std_logic_vector(4 downto 0) := "11010";
 
-- In-place Bitwise Boolean Operations (single operand)
constant OP_BINV : std_logic_vector(4 downto 0) := "11011";
constant OP_BSFL : std_logic_vector(4 downto 0) := "11100";
constant OP_BROL : std_logic_vector(4 downto 0) := "11101";
constant OP_BSFR : std_logic_vector(4 downto 0) := "11110";
constant OP_BROR : std_logic_vector(4 downto 0) := "11111";
-------------------------------------------------------------------
 
constant User_Addr : std_logic_vector(15 downto 5):= Address(15 downto 5);
alias Comp_Addr is Bus_Address(15 downto 5);
signal Reg_Addr : std_logic_vector(4 downto 0);
 
signal Addr_Match : std_logic;
signal Wr_En : std_logic;
signal Wr_Data_q : DATA_TYPE;
signal Rd_En : std_logic;
 
type REG_ARRAY is array( 0 to 7 ) of std_logic_vector(15 downto 0);
signal regfile : REG_ARRAY;
 
signal Start : std_logic;
signal Opcode : std_logic_vector(4 downto 0);
signal Operand_Sel : std_logic_vector(2 downto 0);
 
signal Tolerance : std_logic_vector(15 downto 0);
signal High_Tol : signed(16 downto 0);
signal Low_Tol : signed(16 downto 0);
signal Almost_Equal : std_logic;
 
constant FLAG_Z : integer := 0;
constant FLAG_C : integer := 1;
constant FLAG_N : integer := 2;
constant FLAG_O : integer := 3;
 
signal Flags : std_logic_vector(3 downto 0);
 
type ALU_STATES is ( IDLE, LOAD, EXECUTE, IDIV_INIT, IDIV_WAIT,
DAW_INIT, DAB_INIT, DAA_WAIT1, DAA_STEP2,
DAA_WAIT2, DAA_STEP3, DAA_WAIT3, DAA_STEP4,
STORE );
signal alu_ctrl : ALU_STATES;
 
signal Busy : std_logic;
signal Busy_q : std_logic;
 
signal Operand_1 : std_logic_vector(15 downto 0);
signal Operand_2 : std_logic_vector(15 downto 0);
 
alias Dividend is Operand_1;
alias Divisor is Operand_2;
 
alias u_Operand_1 is Operand_1;
alias u_Operand_2 is Operand_2;
 
alias u_Addend_1 is Operand_1;
alias u_Addend_2 is Operand_2;
 
signal s_Operand_1 : signed(16 downto 0);
signal s_Operand_2 : signed(16 downto 0);
 
alias s_Addend_1 is S_Operand_1;
alias s_Addend_2 is S_Operand_2;
 
signal u_accum : std_logic_vector(16 downto 0);
alias u_data is u_accum(15 downto 0);
alias u_sign is u_accum(15);
alias u_carry is u_accum(16);
 
signal u_prod : std_logic_vector(31 downto 0);
 
signal s_accum : signed(16 downto 0);
alias s_data is s_accum(15 downto 0);
alias s_sign is s_accum(15);
alias s_ovf is s_accum(16);
 
signal s_prod : signed(33 downto 0);
 
signal IDIV_Start : std_logic;
signal IDIV_Busy : std_logic;
 
constant N : integer := 16; -- Width of Operands
 
signal q : std_logic_vector(N*2-1 downto 0);
signal diff : std_logic_vector(N downto 0);
signal count : integer range 0 to N + 1;
 
signal Quotient_i : std_logic_vector(15 downto 0);
signal Quotient : std_logic_vector(15 downto 0);
 
signal Remainder_i : std_logic_vector(15 downto 0);
signal Remainder : std_logic_vector(15 downto 0);
 
signal DAA_intreg : std_logic_vector(15 downto 0);
signal DAA_mode : std_logic;
signal DAA_sign : std_logic;
signal DAA_p4 : std_logic_vector(3 downto 0);
signal DAA_p3 : std_logic_vector(3 downto 0);
signal DAA_p2 : std_logic_vector(3 downto 0);
alias DAA_p1 is Quotient(3 downto 0);
alias DAA_p0 is Remainder(3 downto 0);
signal DAA_result : std_logic_vector(19 downto 0);
 
begin
 
Addr_Match <= '1' when Comp_Addr = User_Addr else '0';
 
-- Sign-extend the base operands to created operands for signed math
S_Operand_1 <= signed(Operand_1(15) & Operand_1);
S_Operand_2 <= signed(Operand_2(15) & Operand_2);
 
-- Compute the tolerance bounds for the Almost Equal function
High_Tol <= S_Operand_2 + signed('0' & Tolerance);
Low_Tol <= S_Operand_2 - signed('0' & Tolerance);
 
-- Combinational logic for the Decimal Adjust logic
DAA_result <= DAA_p4 & DAA_p3 & DAA_p2 & DAA_p1 & DAA_p0;
 
-- Combinational logic for the division logic
diff <= ('0' & Q(N*2-2 downto N-1)) - ('0' & Divisor);
Quotient_i <= q(N-1 downto 0);
Remainder_i <= q(N*2-1 downto N);
 
ALU_proc: process( Clock, Reset )
variable Reg_Sel : integer;
variable Oper_Sel : integer;
begin
if( Reset = Reset_Level )then
Wr_En <= '0';
Wr_Data_q <= (others => '0');
Rd_En <= '0';
Rd_Data <= (others => '0');
Reg_Addr <= (others => '0');
Opcode <= (others => '0');
Operand_Sel <= (others => '0');
Tolerance <= (others => '0');
Start <= '0';
Busy_q <= '0';
Interrupt <= '0';
for i in 0 to 7 loop
regfile(i) <= (others => '0');
end loop;
alu_ctrl <= IDLE;
Operand_1 <= (others => '0');
Operand_2 <= (others => '0');
u_accum <= (others => '0');
u_prod <= (others => '0');
s_accum <= (others => '0');
s_prod <= (others => '0');
Quotient <= (others => '0');
Remainder <= (others => '0');
Flags <= (others => '0');
Almost_Equal <= '0';
Busy <= '0';
DAA_mode <= '0';
DAA_sign <= '0';
DAA_intreg <= (others => '0');
DAA_p4 <= (others => '0');
DAA_p3 <= (others => '0');
DAA_p2 <= (others => '0');
IDIV_Start <= '0';
q <= (others => '0');
count <= N;
IDIV_Busy <= '0';
elsif( rising_edge(Clock) )then
-- For convenience, convert these to integers and assign them to
-- variables
Reg_Sel := conv_integer(Reg_Addr(3 downto 1));
Oper_Sel := conv_integer(Operand_Sel);
 
Wr_En <= Addr_Match and Wr_Enable;
Wr_Data_q <= Wr_Data;
Reg_Addr <= Bus_Address(4 downto 0);
 
Start <= '0';
if( Wr_En = '1' )then
case( Reg_Addr )is
-- Even addresses go to the lower byte of the register
when "00000" | "00010" | "00100" | "00110" |
"01000" | "01010" | "01100" | "01110" =>
regfile(Reg_Sel)(7 downto 0) <= Wr_Data_q;
 
-- Odd addresses go to the upper byte of the register
when "00001" | "00011" | "00101" | "00111" |
"01001" | "01011" | "01101" | "01111" =>
regfile(Reg_Sel)(15 downto 8)<= Wr_Data_q;
 
when "11100" => -- 0x1C -> Tolerance.l
Tolerance(7 downto 0) <= Wr_Data_q;
 
when "11101" => -- 0x1D -> Tolerance.u
Tolerance(15 downto 8) <= Wr_Data_q;
 
when "11110" => -- 0x1E -> Status register
null;
 
when "11111" => -- 0x1F -> Control register
Start <= '1';
Opcode <= Wr_Data_q(7 downto 3);
Operand_Sel <= Wr_Data_q(2 downto 0);
 
when others => null;
end case;
end if;
 
Rd_Data <= (others => '0');
Rd_En <= Addr_Match and Rd_Enable;
 
if( Rd_En = '1' )then
case( Reg_Addr )is
when "00000" | "00010" | "00100" | "00110" |
"01000" | "01010" | "01100" | "01110" =>
Rd_Data <= regfile(Reg_Sel)(7 downto 0);
when "00001" | "00011" | "00101" | "00111" |
"01001" | "01011" | "01101" | "01111" =>
Rd_Data <= regfile(Reg_Sel)(15 downto 8);
when "11100" => -- 0x1C -> Tolerance.l
Rd_Data <= Tolerance(7 downto 0);
when "11101" => -- 0x1D -> Tolerance.u
Rd_Data <= Tolerance(15 downto 8);
when "11110" => -- 0x1E -> Flags & Status register
Rd_Data <= Busy & "000" & Flags;
when "11111" => -- 0x1F -> Control register
Rd_Data <= Opcode & Operand_Sel;
when others => null;
end case;
end if;
 
Busy <= '1';
IDIV_Start <= '0';
case( alu_ctrl )is
when IDLE =>
Busy <= '0';
if( Start = '1' )then
alu_ctrl <= LOAD;
end if;
 
-- Load the operands from the register file. We also check for specific
-- opcodes to set the DAA mode (signed vs unsigned). This is the only
-- place where we READ the register file outside of the bus interface
when LOAD =>
Operand_1 <= regfile(0);
Operand_2 <= regfile(Oper_Sel);
DAA_mode <= '0';
if( Opcode = OP_SDAW or Opcode = OP_SDAB )then
DAA_mode <= '1';
end if;
alu_ctrl <= EXECUTE;
 
-- Now that the operands are loaded, we can execute the actual math
-- operations. We do it with separate operand registers to pipeline
-- the logic.
when EXECUTE =>
alu_ctrl <= STORE;
case( Opcode)is
when OP_T0X =>
u_accum <= '0' & Operand_1;
 
when OP_TX0 =>
u_accum <= '0' & Operand_2;
 
when OP_CLR | OP_SCRY =>
u_accum <= (others => '0');
 
when OP_BSWP =>
u_accum <= '0' &
Operand_2(7 downto 0) &
Operand_2(15 downto 8);
when OP_SMAG =>
s_accum <= S_Operand_2;
if( S_Operand_2 < 0)then
s_accum <= -S_Operand_2;
end if;
 
when OP_SNEG =>
s_accum <= -S_Operand_2;
 
when OP_SMUL =>
s_prod <= S_Operand_1 * S_Operand_2;
 
when OP_UMUL =>
u_prod <= U_Operand_1 * U_Operand_2;
 
when OP_SADD =>
s_accum <= S_Addend_1 + S_Addend_2;
 
when OP_UADD =>
u_accum <= ('0' & Operand_1) + ('0' & Operand_2);
 
when OP_UADC =>
u_accum <= ('0' & Operand_1) + ('0' & Operand_2) + Flags(FLAG_C);
 
when OP_SSUB | OP_SCMP =>
s_accum <= S_Addend_1 - S_Addend_2;
 
when OP_USUB | OP_UCMP =>
u_accum <= ('0' & U_Addend_1) - ('0' & U_Addend_2);
 
when OP_USBC =>
u_accum <= ('0' & U_Addend_1) - ('0' & U_Addend_2) - Flags(FLAG_C);
 
when OP_ACMP =>
-- Perform the function
-- AE = '1' when (A1 <= A2 + T) and (A1 >= A2 - T) else '0'
Almost_Equal <= '0';
if( (S_Addend_1 <= High_Tol) and
(S_Addend_1 >= Low_Tol) )then
Almost_Equal <= '1';
end if;
 
when OP_BINV =>
u_accum <= '0' & (not U_Operand_1);
 
when OP_BSFL =>
u_accum <= U_Operand_1 & '0';
 
when OP_BROL =>
u_accum <= U_Operand_1 & Flags(FLAG_C);
 
when OP_BSFR =>
u_accum <= "00" & U_Operand_1(15 downto 1);
 
when OP_BROR =>
u_accum <= U_Operand_1(0) & Flags(FLAG_C) &
U_Operand_1(15 downto 1);
 
when OP_BOR =>
u_accum <= '0' & (U_Operand_1 or U_Operand_2);
 
when OP_BAND =>
u_accum <= '0' & (U_Operand_1 and U_Operand_2);
 
when OP_BXOR =>
u_accum <= '0' & (U_Operand_1 xor U_Operand_2);
 
-- Division unit has a longer latency, so we need to wait for its busy
-- signal to return low before storing results. Trigger the engine,
-- and then jump to the wait state for it to finish
when OP_IDIV =>
IDIV_Start<= '1';
alu_ctrl <= IDIV_INIT;
 
-- Decimal Adjust Word initialization
-- Stores the sign bit for later use setting the N flag
-- Assigns Operand_1 to register as-is
-- If the sign bit is set, do a 2's complement of the register
when OP_UDAW | OP_SDAW =>
IDIV_Start<= '1';
DAA_sign <= Operand_2(15);
Operand_1 <= Operand_2;
if( (Operand_2(15) and DAA_mode) = '1' )then
Operand_1 <= (not Operand_2) + 1;
end if;
Operand_2 <= x"2710";
alu_ctrl <= DAW_INIT;
 
-- Decimal Adjust Byte initialization
-- Stores the sign bit for later use setting the N flag
-- Assigns Operand_1 to the lower byte of the register
-- If the sign bit is set, do a 2's complement of the register
when OP_UDAB | OP_SDAB =>
IDIV_Start<= '1';
DAA_p4 <= (others => '0');
DAA_p3 <= (others => '0');
DAA_sign <= Operand_2(7);
Operand_1 <= x"00" & Operand_2(7 downto 0);
if( (Operand_2(7) and DAA_mode) = '1' )then
Operand_1 <= ((not Operand_2) + 1) and x"00FF";
end if;
Operand_2 <= x"0064";
alu_ctrl <= DAB_INIT;
 
when others => null;
end case;
 
-- These three states look superfluous, but simplify the state machine
-- logic enough to improve performance. Leave them.
when IDIV_INIT =>
if( IDIV_Busy = '1' )then
alu_ctrl <= IDIV_WAIT;
end if;
 
when DAW_INIT =>
if( IDIV_Busy = '1' )then
alu_ctrl <= DAA_WAIT1;
end if;
 
when DAB_INIT =>
if( IDIV_Busy = '1' )then
alu_ctrl <= DAA_WAIT3;
end if;
 
when DAA_WAIT1 =>
if( IDIV_Busy = '0' )then
DAA_p4 <= Quotient_i(3 downto 0);
DAA_intreg <= Remainder_i;
alu_ctrl <= DAA_STEP2;
end if;
 
when DAA_STEP2 =>
Operand_1 <= DAA_intreg;
Operand_2 <= x"03E8";
IDIV_Start <= '1';
if( IDIV_Busy = '1' )then
alu_ctrl <= DAA_WAIT2;
end if;
 
when DAA_WAIT2 =>
if( IDIV_Busy = '0' )then
DAA_p3 <= Quotient_i(3 downto 0);
DAA_intreg <= Remainder_i;
alu_ctrl <= DAA_STEP3;
end if;
 
when DAA_STEP3 =>
Operand_1 <= DAA_intreg;
Operand_2 <= x"0064";
IDIV_Start <= '1';
if( IDIV_Busy = '1' )then
alu_ctrl <= DAA_WAIT3;
end if;
 
when DAA_WAIT3 =>
if( IDIV_Busy = '0' )then
DAA_p2 <= Quotient_i(3 downto 0);
DAA_intreg <= Remainder_i;
alu_ctrl <= DAA_STEP4;
end if;
 
when DAA_STEP4 =>
Operand_1 <= DAA_intreg;
Operand_2 <= x"000A";
IDIV_Start <= '1';
if( IDIV_Busy = '1' )then
alu_ctrl <= IDIV_WAIT;
end if;
 
when IDIV_WAIT =>
if( IDIV_Busy = '0' )then
Quotient <= Quotient_i;
Remainder <= Remainder_i;
alu_ctrl <= STORE;
end if;
 
-- All ALU writes to the register file go through here. This is also
-- where the flag register gets updated. This should be the only
-- place where the register file gets WRITTEN outside of the bus
-- interface.
when STORE =>
Flags <= (others => '0');
case( Opcode)is
when OP_T0X | OP_CLR | OP_BSWP =>
regfile(Oper_Sel) <= u_data;
Flags(FLAG_Z) <= nor_reduce(u_data);
Flags(FLAG_N) <= u_sign;
 
when OP_TX0 =>
regfile(0) <= u_data;
Flags(FLAG_Z) <= nor_reduce(u_data);
Flags(FLAG_N) <= u_sign;
 
when OP_SCRY =>
Flags(FLAG_C) <= '0';
if( Oper_Sel > 0 )then
Flags(FLAG_C)<= '1';
end if;
 
when OP_IDIV =>
regfile(0) <= Quotient;
regfile(Oper_Sel) <= Remainder;
Flags(FLAG_Z) <= nor_reduce(Quotient);
 
when OP_SMAG | OP_SNEG | OP_SADD | OP_SSUB =>
regfile(0) <= std_logic_vector(s_data);
Flags(FLAG_N) <= s_sign;
Flags(FLAG_Z) <= nor_reduce(std_logic_vector(s_data));
Flags(FLAG_O) <= s_ovf xor s_sign;
 
when OP_SMUL =>
regfile(0) <= std_logic_vector(s_prod(15 downto 0));
regfile(1) <= std_logic_vector(s_prod(31 downto 16));
Flags(FLAG_N) <= s_prod(33) or s_prod(32);
Flags(FLAG_Z) <= nor_reduce(std_logic_vector(s_prod));
 
when OP_UMUL =>
regfile(0) <= u_prod(15 downto 0);
regfile(1) <= u_prod(31 downto 16);
Flags(FLAG_N) <= u_prod(31);
Flags(FLAG_Z) <= nor_reduce(u_prod);
 
when OP_UADD | OP_USUB =>
regfile(0) <= u_data;
Flags(FLAG_Z) <= nor_reduce(u_data);
Flags(FLAG_N) <= u_sign;
Flags(FLAG_C) <= u_carry;
 
when OP_SCMP =>
Flags(FLAG_N) <= s_ovf;
Flags(FLAG_Z) <= nor_reduce(std_logic_vector(s_data));
Flags(FLAG_O) <= s_accum(16) xor s_accum(15);
 
when OP_UCMP =>
Flags(FLAG_Z) <= nor_reduce(u_data);
Flags(FLAG_C) <= u_carry;
 
when OP_ACMP =>
Flags(FLAG_Z) <= Almost_Equal;
 
when OP_UDAB | OP_SDAB =>
regfile(Oper_Sel) <= DAA_result(15 downto 0);
Flags(FLAG_Z) <= nor_reduce(DAA_result);
Flags(FLAG_N) <= DAA_sign;
 
when OP_UDAW | OP_SDAW =>
regfile(Oper_Sel) <= DAA_result(15 downto 0);
Flags(3 downto 0) <= DAA_result(19 downto 16);
if( DAA_mode = '1' )then
Flags(FLAG_N) <= DAA_sign;
end if;
 
when OP_BOR | OP_BAND | OP_BXOR =>
regfile(0) <= u_data;
Flags(FLAG_Z) <= nor_reduce(u_data);
Flags(FLAG_N) <= u_sign;
 
when OP_BINV =>
regfile(Oper_Sel) <= u_data;
Flags(FLAG_Z) <= nor_reduce(u_data);
Flags(FLAG_N) <= u_sign;
 
when OP_BSFL | OP_BROL | OP_BSFR | OP_BROR =>
regfile(Oper_Sel) <= u_data;
Flags(FLAG_Z) <= nor_reduce(u_data);
Flags(FLAG_N) <= u_sign;
Flags(FLAG_C) <= u_carry;
 
when others => null;
end case;
alu_ctrl <= IDLE;
 
when others =>
null;
 
end case;
 
IDIV_Busy <= '0';
if( IDIV_Start = '1' )then
IDIV_Busy <= '1';
count <= 0;
q <= conv_std_logic_vector(0,N) & Dividend;
elsif( count < N )then
IDIV_Busy <= '1';
count <= count + 1;
q <= diff(N-1 downto 0) & q(N-2 downto 0) & '1';
if( diff(N) = '1' )then
q <= q(N*2-2 downto 0) & '0';
end if;
end if;
 
-- Fire on the falling edge of Busy
Busy_q <= Busy;
Interrupt <= not Busy and Busy_q;
 
end if;
end process;
 
end architecture;
/o8_rtc.vhd
0,0 → 1,348
-- Copyright (c)2013 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary 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 binary 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,
-- where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``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 JEREMY SETH HENRY 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.
--
-- VHDL Units : realtime_clock
-- Description: Provides automatically updated registers that maintain the
-- : time of day. Keeps track of the day of week, hours, minutes
-- : seconds, and tenths of a second. Module is doubled buffered
-- : to ensure time consistency during accesses. Also provides
-- : a programmable periodic interrupt timer, as well as a uSec
-- : tick for external use.
--
-- Register Map:
-- Offset Bitfield Description Read/Write
-- 0x0 AAAAAAAA Periodic Interval Timer in uS (RW)
-- 0x1 -AAAAAAA Tenths (0x00 - 0x63) (RW)
-- 0x2 --AAAAAA Seconds (0x00 - 0x3B) (RW)
-- 0x3 --AAAAAA Minutes (0x00 - 0x3B) (RW)
-- 0x4 ---AAAAA Hours (0x00 - 0x17) (RW)
-- 0x5 -----AAA Day of Week (0x00 - 0x06) (RW)
-- 0x6 -------- Update RTC regs from Shadow Regs (WO)
-- 0x7 A------- Update Shadow Regs from RTC regs (RW)
-- A = Update is Busy
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_misc.all;
 
library work;
use work.open8_pkg.all;
 
entity o8_rtc is
generic(
Sys_Freq : real;
Reset_Level : std_logic;
Address : ADDRESS_TYPE
);
port(
Clock : in std_logic;
Reset : in std_logic;
uSec_Tick : out std_logic;
--
Bus_Address : in ADDRESS_TYPE;
Wr_Enable : in std_logic;
Wr_Data : in DATA_TYPE;
Rd_Enable : in std_logic;
Rd_Data : out DATA_TYPE;
--
Interrupt_PIT : out std_logic;
Interrupt_RTC : out std_logic
);
end entity;
 
architecture behave of o8_rtc is
 
-- The ceil_log2 function returns the minimum register width required to
-- hold the supplied integer.
function ceil_log2 (x : in natural) return natural is
variable retval : natural;
begin
retval := 1;
while ((2**retval) - 1) < x loop
retval := retval + 1;
end loop;
return retval;
end ceil_log2;
 
constant User_Addr : std_logic_vector(15 downto 3)
:= Address(15 downto 3);
alias Comp_Addr is Bus_Address(15 downto 3);
signal Addr_Match : std_logic;
 
alias Reg_Addr is Bus_Address(2 downto 0);
signal Reg_Addr_q : std_logic_vector(2 downto 0);
 
signal Wr_En : std_logic;
signal Wr_Data_q : DATA_TYPE;
signal Rd_En : std_logic;
 
constant DLY_1USEC_VAL: integer := integer(Sys_Freq / 1000000.0);
constant DLY_1USEC_WDT: integer := ceil_log2(DLY_1USEC_VAL - 1);
constant DLY_1USEC : std_logic_vector :=
conv_std_logic_vector( DLY_1USEC_VAL - 1, DLY_1USEC_WDT);
 
signal uSec_Cntr : std_logic_vector( DLY_1USEC_WDT - 1 downto 0 )
:= (others => '0');
signal uSec_Tick_i : std_logic;
 
signal interval : DATA_TYPE;
signal timer_cnt : DATA_TYPE;
signal timer_ro : std_logic;
 
signal rtc_frac : std_logic_vector(15 downto 0);
signal frac_ro : std_logic;
 
signal rtc_tens : DATA_TYPE;
signal tens_ro : std_logic;
 
signal rtc_secs : DATA_TYPE;
signal secs_ro : std_logic;
 
signal rtc_mins : DATA_TYPE;
signal mins_ro : std_logic;
 
signal rtc_hours : DATA_TYPE;
signal hours_ro : std_logic;
 
signal rtc_dow : DATA_TYPE;
 
signal shd_tens : DATA_TYPE;
signal shd_secs : DATA_TYPE;
signal shd_mins : DATA_TYPE;
signal shd_hours : DATA_TYPE;
signal shd_dow : DATA_TYPE;
 
signal update_rtc : std_logic;
signal update_shd : std_logic;
signal update_ctmr : std_logic_vector(3 downto 0);
 
begin
 
uSec_Tick <= uSec_Tick_i;
Addr_Match <= '1' when Comp_Addr = User_Addr else '0';
 
Interrupt_PIT <= timer_ro;
Interrupt_RTC <= frac_ro;
 
io_reg: process( Clock, Reset )
begin
if( Reset = Reset_Level )then
uSec_Cntr <= (others => '0');
uSec_Tick_i <= '0';
 
interval <= x"00";
timer_cnt <= x"00";
timer_ro <= '0';
 
rtc_frac <= (others => '0');
frac_ro <= '0';
 
rtc_tens <= (others => '0');
tens_ro <= '0';
 
rtc_secs <= (others => '0');
secs_ro <= '0';
 
rtc_mins <= (others => '0');
mins_ro <= '0';
 
rtc_hours <= (others => '0');
hours_ro <= '0';
 
rtc_dow <= (others => '0');
 
shd_tens <= (others => '0');
shd_secs <= (others => '0');
shd_mins <= (others => '0');
shd_hours <= (others => '0');
shd_dow <= (others => '0');
 
update_rtc <= '0';
update_shd <= '0';
update_ctmr <= (others => '0');
 
Wr_Data_q <= (others => '0');
Reg_Addr_q <= (others => '0');
Wr_En <= '0';
Rd_En <= '0';
Rd_Data <= x"00";
 
elsif( rising_edge( Clock ) )then
 
uSec_Cntr <= uSec_Cntr - 1;
uSec_Tick_i <= '0';
if( uSec_Cntr = 0 )then
uSec_Cntr <= DLY_1USEC;
uSec_Tick_i <= or_reduce(Interval);
end if;
 
timer_ro <= '0';
frac_ro <= '0';
tens_ro <= '0';
secs_ro <= '0';
mins_ro <= '0';
hours_ro <= '0';
 
-- Periodic Interval Timer
timer_cnt <= timer_cnt - uSec_Tick_i;
if( or_reduce(timer_cnt) = '0' )then
timer_cnt <= interval;
timer_ro <= or_reduce(interval); -- Only issue output on Int > 0
end if;
 
-- Fractional decisecond counter - cycles every 10k microseconds
rtc_frac <= rtc_frac - uSec_Tick_i;
if( or_reduce(rtc_frac) = '0' or update_rtc = '1' )then
rtc_frac <= x"2710";
frac_ro <= not update_rtc;
end if;
 
-- Decisecond counter
rtc_tens <= rtc_tens + frac_ro;
if( update_rtc = '1' )then
rtc_tens <= shd_tens;
elsif( rtc_tens = x"64")then
rtc_tens <= (others => '0');
tens_ro <= '1';
end if;
 
-- Second counter
rtc_secs <= rtc_secs + tens_ro;
if( update_rtc = '1' )then
rtc_secs <= shd_secs;
elsif( rtc_secs = x"3C")then
rtc_secs <= (others => '0');
secs_ro <= '1';
end if;
 
-- Minute counter
rtc_mins <= rtc_mins + secs_ro;
if( update_rtc = '1' )then
rtc_mins <= shd_mins;
elsif( rtc_mins = x"3C")then
rtc_mins <= (others => '0');
mins_ro <= '1';
end if;
 
-- Hour counter
rtc_hours <= rtc_hours + mins_ro;
if( update_rtc = '1' )then
rtc_hours <= shd_hours;
elsif( rtc_hours = x"18")then
rtc_hours <= (others => '0');
hours_ro <= '1';
end if;
 
-- Day of Week counter
rtc_dow <= rtc_dow + hours_ro;
if( update_rtc = '1' )then
rtc_dow <= shd_dow;
elsif( rtc_dow = x"07")then
rtc_dow <= (others => '0');
end if;
 
-- Coherency timer - ensures that the shadow registers are updated with
-- valid time data by delaying updates until the rtc registers have
-- finished cascading.
update_rtc <= '0';
update_ctmr <= update_ctmr - or_reduce(update_ctmr);
if( frac_ro = '1' )then
update_ctmr <= x"9";
end if;
 
-- Copy the RTC registers to the shadow registers when the coherency
-- timer is zero (RTC registers are static)
if( update_shd = '1' and or_reduce(update_ctmr) = '0' )then
shd_tens <= rtc_tens;
shd_secs <= rtc_secs;
shd_mins <= rtc_mins;
shd_hours <= rtc_hours;
shd_dow <= rtc_dow;
update_shd <= '0';
end if;
 
Reg_Addr_q <= Reg_Addr;
Wr_Data_q <= Wr_Data;
 
Wr_En <= Addr_Match and Wr_Enable;
if( Wr_En = '1' )then
case( Reg_Addr_q )is
when "000" =>
interval <= Wr_Data_q;
 
when "001" =>
shd_tens <= '0' & Wr_Data_q(6 downto 0);
 
when "010" =>
shd_secs <= "00" & Wr_Data_q(5 downto 0);
 
when "011" =>
shd_mins <= "00" & Wr_Data_q(5 downto 0);
 
when "100" =>
shd_hours <= "000" & Wr_Data_q(4 downto 0);
 
when "101" =>
shd_dow <= "00000" & Wr_Data_q(2 downto 0);
 
when "110" =>
update_rtc <= '1';
 
when "111" =>
 
update_shd <= '1';
 
when others => null;
end case;
end if;
 
Rd_Data <= (others => '0');
Rd_En <= Addr_Match and Rd_Enable;
if( Rd_En = '1' )then
case( Reg_Addr_q )is
when "000" =>
Rd_Data <= interval;
when "001" =>
Rd_Data <= shd_tens;
when "010" =>
Rd_Data <= shd_secs;
when "011" =>
Rd_Data <= shd_mins;
when "100" =>
Rd_Data <= shd_hours;
when "101" =>
Rd_Data <= shd_dow;
when "110" =>
null;
when "111" =>
Rd_Data <= update_shd & "0000000";
when others => null;
end case;
end if;
 
end if;
end process;
 
end architecture;

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.