URL
https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk
Subversion Repositories open8_urisc
[/] [open8_urisc/] [trunk/] [VHDL/] [o8_cpu.vhd] - Rev 155
Go to most recent revision | Compare with Previous | Blame | View Log
-- Copyright (c)2006, 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 : Open8_CPU -- Description: VHDL model of the V8 uRISC 8-bit processor core -- Notes : Generic definitions -- : 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. -- : -- : Allow_Stack_Address_Move - When set to 1, allows the RSP to be -- : 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. -- : -- : Program_Start_Addr - Determines the initial value of the -- : program counter. -- : -- : 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. -- : -- : 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. -- : -- : 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. -- : -- : 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 -- : -- : 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) -- : -- 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. 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 Open8_CPU is generic( Stack_Start_Addr : ADDRESS_TYPE := x"007F"; -- Top of Stack Allow_Stack_Address_Move : boolean := false; -- Use Normal v8 RSP ISR_Start_Addr : ADDRESS_TYPE := x"0080"; -- Bottom of ISR vec's Program_Start_Addr : ADDRESS_TYPE := x"0090"; -- Initial PC location 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; Rd_Data : in DATA_TYPE; Rd_Enable : out std_logic; Wr_Data : out DATA_TYPE; Wr_Enable : out std_logic ); end entity; architecture rtl of Open8_CPU is subtype OPCODE_TYPE is std_logic_vector(4 downto 0); subtype SUBOP_TYPE is std_logic_vector(2 downto 0); -- Most of the ALU instructions are the same as their Opcode equivalents with -- three exceptions (for IDLE, UPP2, and MUL2) constant ALU_INC : OPCODE_TYPE := "00000"; -- x"00" constant ALU_ADC : OPCODE_TYPE := "00001"; -- x"01" constant ALU_TX0 : OPCODE_TYPE := "00010"; -- x"02" constant ALU_OR : OPCODE_TYPE := "00011"; -- x"03" constant ALU_AND : OPCODE_TYPE := "00100"; -- x"04" constant ALU_XOR : OPCODE_TYPE := "00101"; -- x"05" constant ALU_ROL : OPCODE_TYPE := "00110"; -- x"06" constant ALU_ROR : OPCODE_TYPE := "00111"; -- x"07" constant ALU_DEC : OPCODE_TYPE := "01000"; -- x"08" constant ALU_SBC : OPCODE_TYPE := "01001"; -- x"09" constant ALU_ADD : OPCODE_TYPE := "01010"; -- x"0A" constant ALU_STP : OPCODE_TYPE := "01011"; -- x"0B" constant ALU_BTT : OPCODE_TYPE := "01100"; -- x"0C" 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_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; constant FL_INT_EN : integer := 3; constant FL_GP1 : integer := 4; constant FL_GP2 : integer := 5; 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"; -- There are only 8 byte-wide registers - and the write register is always 0, -- so there is little point in making a RAM out of this type REGFILE_TYPE is array (0 to 7) of DATA_TYPE; subtype FLAG_TYPE is DATA_TYPE; 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 INT_CTRL_TYPE is record Mask_Set : std_logic; Mask_Data : DATA_TYPE; Soft_Ints : INTERRUPT_BUNDLE; Incr_ISR : std_logic; end record; type AS_MODES is ( ADDR_PC, ADDR_SP, ADDR_IMM, ADDR_ISR); type ADDR_CTRL_TYPE is record Src : AS_MODES; end record; type DP_MODES is ( DATA_IDLE, DATA_REG, DATA_FLAG, DATA_PC ); type DATA_CTRL_TYPE is record Src : DP_MODES; Reg : SUBOP_TYPE; end record; signal Halt : std_logic; signal ALU_Ctrl : ALU_CTRL_TYPE; signal ALU_Regs : REGFILE_TYPE; signal ALU_Flags : FLAG_TYPE; signal PC_Ctrl : PC_CTRL_TYPE; signal SP_Ctrl : SP_CTRL_TYPE; signal AS_Ctrl : ADDR_CTRL_TYPE; signal DP_Ctrl : DATA_CTRL_TYPE; signal INT_Ctrl : INT_CTRL_TYPE; signal Int_Req, Int_Ack : std_logic; signal Int_RTI : std_logic; signal Int_Mask : DATA_TYPE; signal PC : ADDRESS_TYPE; signal SP : ADDRESS_TYPE; signal ISR : ADDRESS_TYPE; signal IMM : ADDRESS_TYPE; begin Halt_Disabled_fn: if( not Enable_CPU_Halt )generate Halt <= '0'; end generate; Halt_Enabled_fn: if( Enable_CPU_Halt )generate Halt <= CPU_Halt; end generate; ------------------------------------------------------------------------------- -- ALU (Arithmetic / Logic Unit) -- Notes: -- 1) Infers a multiplier in Xilinx/Altera parts - should be checked in others ------------------------------------------------------------------------------- Open8_ALU : block is -- Preinitialization is for simulation only - check actual reset conditions signal Regfile : REGFILE_TYPE := (others => (others => '0') ); signal Flags : FLAG_TYPE := (others => '0'); signal Mult : ADDRESS_TYPE := (others => '0'); begin ALU_Regs <= Regfile; ALU_Flags <= Flags; -- We need to infer a hardware multipler, so we create a special clocked -- process with no reset or clock enable Multiplier: process( Clock ) begin if( rising_edge(Clock) )then Mult <= Regfile(0) * Regfile(conv_integer(ALU_Ctrl.Reg)); end if; end process; ALU: process( Reset, Clock ) 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 for i in 0 to 7 loop Regfile(i) <= (others => '0'); end loop; Flags <= x"00"; elsif( rising_edge(Clock) )then Temp := (others => '0'); Index := conv_integer(ALU_Ctrl.Reg); if( Halt = '0' )then 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; 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); 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 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 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 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 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 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 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 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); 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); 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); when ALU_STP => -- Sets bit(n) in the Flags register Flags(Index) <= '1'; when ALU_BTT => -- Z = !R0(N), N = R0(7) Flags(FL_ZERO) <= not Regfile(0)(Index); Flags(FL_NEG) <= Regfile(0)(7); when ALU_CLP => -- Clears bit(n) in the Flags register Flags(Index) <= '0'; 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 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 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; 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 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 ALU_RFLG => Flags <= ALU_Ctrl.Data; when others => null; end case; end if; end if; end process; end block; ------------------------------------------------------------------------------- -- Program Counter ------------------------------------------------------------------------------- Open8_PC : block is -- Preinitialization is for simulation only - check actual reset conditions signal PC_Q : ADDRESS_TYPE := (others => '0'); begin PC <= PC_Q; Program_Counter: process( Reset, Clock ) variable PC_Offset_SX : ADDRESS_TYPE := x"0000"; begin if( Reset = Reset_Level )then PC_Q <= Program_Start_Addr; elsif( rising_edge(Clock) )then PC_Offset_SX(15 downto 8):= (others => PC_Ctrl.Offset(7)); PC_Offset_SX(7 downto 0) := PC_Ctrl.Offset; if( Halt = '0' )then case PC_Ctrl.Oper is when PC_IDLE => null; when PC_REV1 => PC_Q <= PC_Q - 1; when PC_REV2 => PC_Q <= PC_Q - 2; when PC_INCR => PC_Q <= PC_Q + PC_Offset_SX - 2; when PC_LOAD => PC_Q <= PC_Ctrl.Addr; end case; end if; end if; end process; end block; ------------------------------------------------------------------------------- -- Stack Pointer ------------------------------------------------------------------------------- Open8_SP : block is -- Preinitialization is for simulation only - check actual reset conditions signal SP_Q : ADDRESS_TYPE := (others => '0'); begin SP <= SP_Q; Stack_Pointer: process( Reset, Clock ) begin if( Reset = Reset_Level )then SP_Q <= Stack_Start_Addr; elsif( rising_edge(Clock) )then if( Halt = '0' )then case SP_Ctrl.Oper is when SP_IDLE => null; when SP_RSET => SP_Q <= SP_Ctrl.Addr; when SP_POP => SP_Q <= SP_Q + 1; when SP_PUSH => SP_Q <= SP_Q - 1; end case; end if; end if; end process; end block; ------------------------------------------------------------------------------- -- Address Source Mux ------------------------------------------------------------------------------- Open8_AS : block is begin Address_Select: process( AS_Ctrl, PC, SP, IMM, ISR ) begin Address <= (others => '0'); case AS_Ctrl.Src is when ADDR_PC => Address <= PC; when ADDR_SP => Address <= SP; when ADDR_IMM => Address <= IMM; when ADDR_ISR => Address <= ISR; end case; end process; end block; ------------------------------------------------------------------------------- -- (Write) Data Path ------------------------------------------------------------------------------- Open8_DP : block is begin Data_Path: process( Reset, Clock ) begin if( Reset = Reset_Level )then Wr_Data <= (others => '0'); Wr_Enable <= '0'; Rd_Enable <= '1'; elsif( rising_edge(Clock) )then if( Halt = '0' )then Wr_Enable <= '0'; Rd_Enable <= '1'; case DP_Ctrl.Src is when DATA_IDLE => null; when DATA_REG => Wr_Enable <= '1'; Rd_Enable <= '0'; Wr_Data <= ALU_Regs(conv_integer(DP_Ctrl.Reg)); when DATA_FLAG => Wr_Enable <= '1'; Rd_Enable <= '0'; Wr_Data <= ALU_Flags; when DATA_PC => Wr_Enable <= '1'; Rd_Enable <= '0'; Wr_Data <= PC(15 downto 8); if( DP_Ctrl.Reg = ACCUM )then Wr_Data <= PC(7 downto 0); end if; when others => null; end case; end if; end if; end process; end block; ------------------------------------------------------------------------------- -- Interrupt Controller ------------------------------------------------------------------------------- Open8_INT : block is -- 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; signal i_Ints : INTERRUPT_BUNDLE := (others => '0'); signal Pending_D : INTERRUPT_BUNDLE := (others => '0'); signal Pending : INTERRUPT_BUNDLE := (others => '0'); signal Wait_for_FSM : std_logic := '0'; signal ISR_D, ISR_Q : ADDRESS_TYPE := (others => '0'); type INT_HIST is array (0 to 8) of integer range 0 to 7; signal History : INT_HIST := (others => 0); signal Int_Trig : std_logic := '0'; signal Hist_Level : integer range 0 to 7 := 0; signal Hist_Ptr : integer range 0 to 8 := 0; begin ISR <= ISR_Q; Int_Mask_proc: process( Int_Mask, Interrupts, INT_Ctrl ) variable S_Mask : std_logic_vector(7 downto 0); begin S_Mask := Int_Mask; for i in 0 to 7 loop i_Ints(i) <= (Interrupts(i) or INT_Ctrl.Soft_Ints(i)) and S_Mask(i); end loop; end process; Int_Ctrl_proc: process( i_Ints, Pending, Wait_for_FSM, ISR_Q, INT_Ctrl, History, Hist_Ptr ) begin ISR_D <= ISR_Q; Pending_D <= Pending; Int_Trig <= '0'; Hist_Level <= 0; -- Record any incoming interrupts to the pending buffer if( i_Ints > 0 )then Pending_D <= i_Ints; end if; -- 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_D <= ISR_Q + 1; end if; -- Only mess with interrupt signals while the CPU core is not currently -- working with the ISR address (ie, not loading a new service vector) if( Wait_for_FSM = '0' and Pending > 0 )then if( Pending(0) = '1' and (Hist_Ptr = 0 or History(Hist_Ptr) > 0) )then ISR_D <= INT_VECTOR_0; Pending_D(0) <= '0'; Hist_Level <= 0; Int_Trig <= '1'; elsif( Pending(1) = '1' and (Hist_Ptr = 0 or History(Hist_Ptr) > 1) )then ISR_D <= INT_VECTOR_1; Pending_D(1) <= '0'; Hist_Level <= 1; Int_Trig <= '1'; elsif( Pending(2) = '1' and (Hist_Ptr = 0 or History(Hist_Ptr) > 2) )then ISR_D <= INT_VECTOR_2; Pending_D(2) <= '0'; Hist_Level <= 2; Int_Trig <= '1'; elsif( Pending(3) = '1' and (Hist_Ptr = 0 or History(Hist_Ptr) > 3) )then ISR_D <= INT_VECTOR_3; Pending_D(3) <= '0'; Hist_Level <= 3; Int_Trig <= '1'; elsif( Pending(4) = '1' and (Hist_Ptr = 0 or History(Hist_Ptr) > 4) )then ISR_D <= INT_VECTOR_4; Pending_D(4) <= '0'; Hist_Level <= 4; Int_Trig <= '1'; elsif( Pending(5) = '1' and (Hist_Ptr = 0 or History(Hist_Ptr) > 5) )then ISR_D <= INT_VECTOR_5; Pending_D(5) <= '0'; Hist_Level <= 5; Int_Trig <= '1'; elsif( Pending(6) = '1' and (Hist_Ptr = 0 or History(Hist_Ptr) > 6) )then ISR_D <= INT_VECTOR_6; Pending_D(6) <= '0'; Hist_Level <= 6; Int_Trig <= '1'; elsif( Pending(7) = '1' and (Hist_Ptr = 0 or History(Hist_Ptr) > 7) )then ISR_D <= INT_VECTOR_7; Pending_D(7) <= '0'; Hist_Level <= 7; Int_Trig <= '1'; end if; end if; end process; S_Regs: process( Reset, Clock ) begin if( Reset = Reset_Level )then Int_Req <= '0'; Pending <= x"00"; Wait_for_FSM <= '0'; Int_Mask <= Default_Interrupt_Mask(7 downto 1) & '1'; ISR_Q <= INT_VECTOR_0; for i in 0 to 8 loop History(i) <= 0; end loop; Hist_Ptr <= 0; elsif( rising_edge(Clock) )then if( Halt = '0' )then Int_Req <= Wait_for_FSM and (not Int_Ack); Pending <= Pending_D; -- Reset the Wait_for_FSM flag on Int_Ack if( Int_Ack = '1' )then Wait_for_FSM <= '0'; -- Set the Wait_for_FSM flag on Int_Trig elsif( Int_Trig = '1' )then Wait_for_FSM <= '1'; end if; if( INT_Ctrl.Mask_Set = '1' )then Int_Mask <= INT_Ctrl.Mask_Data(7 downto 1) & '1'; end if; ISR_Q <= ISR_D; if( Int_Trig = '1' )then History(Hist_Ptr+1) <= Hist_Level; Hist_Ptr <= Hist_Ptr + 1; elsif( Int_RTI = '1' and Hist_Ptr > 0 )then Hist_Ptr <= Hist_Ptr - 1; end if; end if; end if; end process; end block; ------------------------------------------------------------------------------- -- State Logic / Instruction Decoding & Execution ------------------------------------------------------------------------------- Open8_FSM : block is -- These are all the primary instructions/op-codes (upper 5-bits) constant OP_INC : OPCODE_TYPE := "00000"; constant OP_ADC : OPCODE_TYPE := "00001"; constant OP_TX0 : OPCODE_TYPE := "00010"; constant OP_OR : OPCODE_TYPE := "00011"; constant OP_AND : OPCODE_TYPE := "00100"; constant OP_XOR : OPCODE_TYPE := "00101"; constant OP_ROL : OPCODE_TYPE := "00110"; constant OP_ROR : OPCODE_TYPE := "00111"; constant OP_DEC : OPCODE_TYPE := "01000"; constant OP_SBC : OPCODE_TYPE := "01001"; constant OP_ADD : OPCODE_TYPE := "01010"; constant OP_STP : OPCODE_TYPE := "01011"; constant OP_BTT : OPCODE_TYPE := "01100"; constant OP_CLP : OPCODE_TYPE := "01101"; constant OP_T0X : OPCODE_TYPE := "01110"; constant OP_CMP : OPCODE_TYPE := "01111"; constant OP_PSH : OPCODE_TYPE := "10000"; 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_INT : OPCODE_TYPE := "10101"; constant OP_MUL : OPCODE_TYPE := "10110"; -- USR2 constant OP_STK : OPCODE_TYPE := "10111"; constant OP_UPP : OPCODE_TYPE := "11000"; constant OP_STA : OPCODE_TYPE := "11001"; constant OP_STX : OPCODE_TYPE := "11010"; constant OP_STO : OPCODE_TYPE := "11011"; constant OP_LDI : OPCODE_TYPE := "11100"; constant OP_LDA : OPCODE_TYPE := "11101"; 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) constant SOP_RSP : SUBOP_TYPE := "000"; constant SOP_RTS : SUBOP_TYPE := "001"; constant SOP_RTI : SUBOP_TYPE := "010"; constant SOP_BRK : SUBOP_TYPE := "011"; constant SOP_JMP : SUBOP_TYPE := "100"; constant SOP_SMSK : SUBOP_TYPE := "101"; 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, -- Loads LDA_C1, LDA_C2, LDA_C3, LDA_C4, LDI_C1, LDO_C1, LDX_C1, LDX_C2, LDX_C3, -- Stores STA_C1, STA_C2, STA_C3, STO_C1, STO_C2, STX_C1, STX_C2, -- 2-cycle math MUL_C1, UPP_C1, -- Stack PSH_C1, POP_C1, POP_C2, POP_C3, POP_C4, -- Subroutines & Interrupts WAIT_FOR_INT, ISR_C1, ISR_C2, ISR_C3, JSR_C1, JSR_C2, RTS_C1, RTS_C2, RTS_C3, RTS_C4, RTS_C5, RTI_C6, -- Debugging BRK_C1 ); signal CPU_Next_State : CPU_STATES := PIPE_FILL_0; signal CPU_State : CPU_STATES := PIPE_FILL_0; type CACHE_MODES is (CACHE_IDLE, CACHE_INSTR, CACHE_OPER1, CACHE_OPER2, CACHE_PREFETCH ); signal Cache_Ctrl : CACHE_MODES := CACHE_IDLE; signal Opcode : OPCODE_TYPE := (others => '0'); signal SubOp, SubOp_p1 : SUBOP_TYPE := (others => '0'); -- synthesis translate_off signal Instruction : DATA_TYPE := (others => '0'); -- synthesis translate_on signal Prefetch : DATA_TYPE := (others => '0'); signal Operand1, Operand2 : DATA_TYPE := (others => '0'); signal Instr_Prefetch : std_logic := '0'; signal Ack_D, Ack_Q, Ack_Q1: std_logic := '0'; signal Int_RTI_D : std_logic := '0'; begin -- synthesis translate_off Instruction <= Opcode & SubOp; -- synthesis translate_on State_Logic: process(CPU_State, ALU_Regs, ALU_Flags, Int_Mask, Opcode, SubOp , SubOp_p1, Operand1, Operand2, Int_Req ) variable Reg, Reg_1 : integer range 0 to 7 := 0; variable Offset_SX : ADDRESS_TYPE; 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; -- AS_Ctrl.Src <= ADDR_PC; IMM <= x"0000"; -- DP_Ctrl.Src <= DATA_IDLE; 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'; -- 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; case CPU_State is ------------------------------------------------------------------------------- -- Initial Instruction fetch & decode ------------------------------------------------------------------------------- when PIPE_FILL_0 => CPU_Next_State <= PIPE_FILL_1; PC_Ctrl.Oper <= PC_INCR; when PIPE_FILL_1 => CPU_Next_State <= PIPE_FILL_2; PC_Ctrl.Oper <= PC_INCR; when PIPE_FILL_2 => CPU_Next_State <= INSTR_DECODE; Cache_Ctrl <= CACHE_INSTR; PC_Ctrl.Oper <= PC_INCR; when INSTR_DECODE => CPU_Next_State <= INSTR_DECODE; Cache_Ctrl <= CACHE_INSTR; case Opcode is when OP_PSH => CPU_Next_State <= PSH_C1; Cache_Ctrl <= CACHE_PREFETCH; PC_Ctrl.Oper <= PC_REV1; DP_Ctrl.Src <= DATA_REG; DP_Ctrl.Reg <= SubOp; when OP_POP => CPU_Next_State <= POP_C1; Cache_Ctrl <= CACHE_PREFETCH; PC_Ctrl.Oper <= PC_REV2; SP_Ctrl.Oper <= SP_POP; when OP_BR0 | OP_BR1 => CPU_Next_State <= BRN_C1; Cache_Ctrl <= CACHE_OPER1; PC_Ctrl.Oper <= PC_INCR; 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 OP_INT => PC_Ctrl.Oper <= PC_INCR; if( Int_Mask(Reg) = '1' )then CPU_Next_State <= WAIT_FOR_INT; INT_Ctrl.Soft_Ints(Reg) <= '1'; end if; when OP_STK => case SubOp is when SOP_RSP => PC_Ctrl.Oper <= PC_INCR; SP_Ctrl.Oper <= SP_RSET; when SOP_RTS | SOP_RTI => CPU_Next_State <= RTS_C1; Cache_Ctrl <= CACHE_IDLE; SP_Ctrl.Oper <= SP_POP; when SOP_BRK => CPU_Next_State <= BRK_C1; PC_Ctrl.Oper <= PC_REV2; when SOP_JMP => CPU_Next_State <= JMP_C1; Cache_Ctrl <= CACHE_OPER1; when SOP_SMSK => PC_Ctrl.Oper <= PC_INCR; INT_Ctrl.Mask_Set <= '1'; when SOP_GMSK => PC_Ctrl.Oper <= PC_INCR; ALU_Ctrl.Oper<= ALU_LDI; ALU_Ctrl.Reg <= ACCUM; ALU_Ctrl.Data<= Int_Mask; when SOP_JSR => CPU_Next_State <= JSR_C1; Cache_Ctrl <= CACHE_OPER1; DP_Ctrl.Src <= DATA_PC; DP_Ctrl.Reg <= ACCUM+1; when others => null; end case; when OP_MUL => CPU_Next_State <= MUL_C1; Cache_Ctrl <= CACHE_PREFETCH; -- 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 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 OP_LDA => CPU_Next_State <= LDA_C1; Cache_Ctrl <= CACHE_OPER1; when OP_LDI => CPU_Next_State <= LDI_C1; Cache_Ctrl <= CACHE_OPER1; PC_Ctrl.Oper <= PC_INCR; when OP_LDO => CPU_Next_State <= LDO_C1; Cache_Ctrl <= CACHE_OPER1; PC_Ctrl.Oper <= PC_REV2; when OP_LDX => CPU_Next_State <= LDX_C1; PC_Ctrl.Oper <= PC_REV2; AS_Ctrl.Src <= ADDR_IMM; -- 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 IMM <= ALU_Regs(Reg_1) & ALU_Regs(Reg); else Reg := conv_integer(SubOp(2 downto 1) & '0'); Reg_1 := conv_integer(SubOp(2 downto 1) & '1'); IMM <= ALU_Regs(Reg_1) & ALU_Regs(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_STA => CPU_Next_State <= STA_C1; Cache_Ctrl <= CACHE_OPER1; when OP_STO => CPU_Next_State <= STO_C1; Cache_Ctrl <= CACHE_OPER1; PC_Ctrl.Oper <= PC_REV2; DP_Ctrl.Src <= DATA_REG; DP_Ctrl.Reg <= ACCUM; when OP_STX => CPU_Next_State <= STX_C1; Cache_Ctrl <= CACHE_PREFETCH; PC_Ctrl.Oper <= PC_REV2; DP_Ctrl.Src <= DATA_REG; DP_Ctrl.Reg <= ACCUM; when others => PC_Ctrl.Oper <= PC_INCR; ALU_Ctrl.Oper <= Opcode; ALU_Ctrl.Reg <= SubOp; 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( ALU_Flags(Reg) = Opcode(0) )then CPU_Next_State <= PIPE_FILL_0; Cache_Ctrl <= CACHE_IDLE; PC_Ctrl.Offset <= Operand1; end if; when DBNZ_C1 => CPU_Next_State <= INSTR_DECODE; Cache_Ctrl <= CACHE_INSTR; PC_Ctrl.Oper <= PC_INCR; if( ALU_Flags(FL_ZERO) = '0' )then CPU_Next_State <= PIPE_FILL_0; Cache_Ctrl <= CACHE_IDLE; PC_Ctrl.Offset <= Operand1; end if; when JMP_C1 => CPU_Next_State <= JMP_C2; Cache_Ctrl <= CACHE_OPER2; when JMP_C2 => CPU_Next_State <= PIPE_FILL_0; PC_Ctrl.Oper <= PC_LOAD; PC_Ctrl.Addr <= Operand2 & Operand1; ------------------------------------------------------------------------------- -- Data Storage - Load from memory (LDA, LDI, LDO, LDX) ------------------------------------------------------------------------------- when LDA_C1 => CPU_Next_State <= LDA_C2; Cache_Ctrl <= CACHE_OPER2; when LDA_C2 => CPU_Next_State <= LDA_C3; AS_Ctrl.Src <= ADDR_IMM; IMM <= Operand2 & Operand1; when LDA_C3 => CPU_Next_State <= LDA_C4; PC_Ctrl.Oper <= PC_INCR; when LDA_C4 => CPU_Next_State <= LDI_C1; Cache_Ctrl <= CACHE_OPER1; PC_Ctrl.Oper <= PC_INCR; 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 LDO_C1 => CPU_Next_State <= LDX_C1; AS_Ctrl.Src <= ADDR_IMM; 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'); IMM <= (ALU_Regs(Reg_1) & ALU_Regs(Reg)) + Offset_SX; if( SubOp(0) = '1' )then ALU_Ctrl.Oper<= ALU_UPP; ALU_Ctrl.Reg <= SubOp(2 downto 1) & '0'; end if; else IMM <= (ALU_Regs(Reg_1) & ALU_Regs(Reg)) + Offset_SX; end if; when LDX_C1 => CPU_Next_State <= LDX_C2; PC_Ctrl.Oper <= PC_INCR; when LDX_C2 => CPU_Next_State <= LDX_C3; PC_Ctrl.Oper <= PC_INCR; Cache_Ctrl <= CACHE_OPER1; 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; ------------------------------------------------------------------------------- -- Data Storage - Store to memory (STA, STO, STX) ------------------------------------------------------------------------------- when STA_C1 => CPU_Next_State <= STA_C2; Cache_Ctrl <= CACHE_OPER2; DP_Ctrl.Src <= DATA_REG; DP_Ctrl.Reg <= SubOp; when STA_C2 => CPU_Next_State <= STA_C3; AS_Ctrl.Src <= ADDR_IMM; IMM <= Operand2 & Operand1; PC_Ctrl.Oper <= PC_INCR; 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; AS_Ctrl.Src <= ADDR_IMM; -- If auto-increment is disabled, just load the registers normally if( not Enable_Auto_Increment )then CPU_Next_State <= PIPE_FILL_1; IMM <= (ALU_Regs(Reg_1) & ALU_Regs(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'); IMM <= (ALU_Regs(Reg_1) & ALU_Regs(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'; 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 STX_C1 => PC_Ctrl.Oper <= PC_INCR; AS_Ctrl.Src <= ADDR_IMM; -- If auto-increment is disabled, just load the registers normally if( not Enable_Auto_Increment )then CPU_Next_State <= PIPE_FILL_1; IMM <= (ALU_Regs(Reg_1) & ALU_Regs(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'); IMM <= (ALU_Regs(Reg_1) & ALU_Regs(Reg)); if( SubOp(0) = '1' )then CPU_Next_State <= STX_C2; ALU_Ctrl.Oper <= ALU_UPP; ALU_Ctrl.Reg <= SubOp(2 downto 1) & '0'; 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'; ------------------------------------------------------------------------------- -- Multi-Cycle Math Operations (UPP, MUL) ------------------------------------------------------------------------------- -- 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 UPP_C1 => CPU_Next_State <= PIPE_FILL_2; PC_Ctrl.Oper <= PC_INCR; ALU_Ctrl.Oper <= ALU_UPP2; ALU_Ctrl.Reg <= SubOp_p1; ------------------------------------------------------------------------------- -- Basic Stack Manipulation (PSH, POP, RSP) ------------------------------------------------------------------------------- when PSH_C1 => CPU_Next_State <= PIPE_FILL_1; AS_Ctrl.Src <= ADDR_SP; SP_Ctrl.Oper <= SP_PUSH; when POP_C1 => CPU_Next_State <= POP_C2; AS_Ctrl.Src <= ADDR_SP; when POP_C2 => CPU_Next_State <= POP_C3; PC_Ctrl.Oper <= PC_INCR; when POP_C3 => CPU_Next_State <= POP_C4; Cache_Ctrl <= CACHE_OPER1; PC_Ctrl.Oper <= PC_INCR; 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; ------------------------------------------------------------------------------- -- Subroutines & Interrupts (RTS, JSR) ------------------------------------------------------------------------------- when WAIT_FOR_INT => -- For soft interrupts only, halt the PC CPU_Next_State <= WAIT_FOR_INT; when ISR_C1 => CPU_Next_State <= ISR_C2; AS_Ctrl.Src <= ADDR_ISR; INT_Ctrl.Incr_ISR <= '1'; when ISR_C2 => CPU_Next_State <= ISR_C3; AS_Ctrl.Src <= ADDR_ISR; DP_Ctrl.Src <= DATA_FLAG; when ISR_C3 => CPU_Next_State <= JSR_C1; Cache_Ctrl <= CACHE_OPER1; AS_Ctrl.Src <= ADDR_SP; SP_Ctrl.Oper <= SP_PUSH; DP_Ctrl.Src <= DATA_PC; DP_Ctrl.Reg <= ACCUM+1; ALU_Ctrl.Oper <= ALU_STP; ALU_Ctrl.Reg <= INT_FLAG; Ack_D <= '1'; when JSR_C1 => CPU_Next_State <= JSR_C2; Cache_Ctrl <= CACHE_OPER2; AS_Ctrl.Src <= ADDR_SP; SP_Ctrl.Oper <= SP_PUSH; DP_Ctrl.Src <= DATA_PC; DP_Ctrl.Reg <= ACCUM; when JSR_C2 => CPU_Next_State <= PIPE_FILL_0; AS_Ctrl.Src <= ADDR_SP; SP_Ctrl.Oper <= SP_PUSH; PC_Ctrl.Oper <= PC_LOAD; PC_Ctrl.Addr <= Operand2 & Operand1; when RTS_C1 => CPU_Next_State <= RTS_C2; AS_Ctrl.Src <= ADDR_SP; SP_Ctrl.Oper <= SP_POP; when RTS_C2 => CPU_Next_State <= RTS_C3; AS_Ctrl.Src <= ADDR_SP; -- 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_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 AS_Ctrl.Src <= ADDR_SP; when RTS_C4 => CPU_Next_State <= RTS_C5; Cache_Ctrl <= CACHE_OPER2; 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 RTI_C6 => CPU_Next_State <= PIPE_FILL_1; PC_Ctrl.Oper <= PC_INCR; ALU_Ctrl.Oper <= ALU_RFLG; ALU_Ctrl.Data <= Operand1; PC_Ctrl.Oper <= PC_INCR; Int_RTI_D <= '1'; ------------------------------------------------------------------------------- -- Debugging (BRK) Performs a 5-clock NOP ------------------------------------------------------------------------------- when BRK_C1 => CPU_Next_State <= PIPE_FILL_0; when others => null; end case; -- Interrupt service routines can only begin during the decode and wait -- states to avoid corruption due to incomplete instruction execution 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_IDLE; -- 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; end if; end if; end process; S_Regs: process( Reset, Clock ) 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"; Ack_Q <= '0'; Ack_Q1 <= '0'; Int_Ack <= '0'; Int_RTI <= '0'; elsif( rising_edge(Clock) )then if( Halt = '0' )then 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; when CACHE_OPER1 => Operand1 <= Rd_Data; when CACHE_OPER2 => Operand2 <= Rd_Data; when CACHE_PREFETCH => Prefetch <= Rd_Data; Instr_Prefetch <= '1'; when CACHE_IDLE => null; end case; -- Interrupt signalling registers Ack_Q <= Ack_D; Ack_Q1 <= Ack_Q; Int_Ack <= Ack_Q1; Int_RTI <= Int_RTI_D; end if; end if; end process; ------------------------------------------------------------------------------- -- Fixed in-line statements for the interrupt mask, and stack pointer address ------------------------------------------------------------------------------- -- The interrupt control mask is always sourced out of R0 INT_Ctrl.Mask_Data <= ALU_Regs(conv_integer(ACCUM)); -- 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 Prog_Stack_Addr_Move_fn: if( Allow_Stack_Address_Move )generate SP_Ctrl.Addr <= ALU_Regs(1) & ALU_Regs(0); end generate; Normal_Stack_Reset_fn: if( not Allow_Stack_Address_Move )generate SP_Ctrl.Addr <= Stack_Start_Addr; end generate; end block; end rtl;
Go to most recent revision | Compare with Previous | Blame | View Log