URL
https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk
Subversion Repositories open8_urisc
[/] [open8_urisc/] [trunk/] [VHDL/] [o8_cpu.vhd] - Rev 156
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. -- Seth Henry 07/27/11 Optimized logic for timing, merged blocks into -- single entity. 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 behave of Open8_CPU is 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) 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 ); 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_IDLE, DATA_REG, DATA_FLAG, DATA_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) 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"; type REGFILE_TYPE is array (0 to 7) of DATA_TYPE; subtype FLAG_TYPE is DATA_TYPE; signal Halt : std_logic; signal CPU_Next_State : CPU_STATES := PIPE_FILL_0; signal CPU_State : CPU_STATES := PIPE_FILL_0; signal Cache_Ctrl : CACHE_MODES := CACHE_IDLE; signal Opcode : OPCODE_TYPE := (others => '0'); signal SubOp, SubOp_p1 : SUBOP_TYPE := (others => '0'); signal Prefetch : DATA_TYPE := x"00"; signal Operand1, Operand2 : DATA_TYPE := x"00"; signal Instr_Prefetch : std_logic := '0'; signal PC_Ctrl : PC_CTRL_TYPE; signal Program_Ctr : ADDRESS_TYPE := x"0000"; signal SP_Ctrl : SP_CTRL_TYPE; signal Stack_Ptr : ADDRESS_TYPE := x"0000"; signal DP_Ctrl : DATA_CTRL_TYPE; 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; signal ALU_Ctrl : ALU_CTRL_TYPE; signal Regfile : REGFILE_TYPE; signal Flags : FLAG_TYPE; signal Mult : ADDRESS_TYPE := x"0000"; 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; ------------------------------------------------------------------------------- -- 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; 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; -- Address <= Program_Ctr; -- 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; -- 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_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( 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( 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; Address <= 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; 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'; end if; else Address <= (Regfile(Reg_1) & Regfile(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; Address <= 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; -- 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'; 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; -- 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'; 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; Address <= Stack_Ptr; SP_Ctrl.Oper <= SP_PUSH; when POP_C1 => CPU_Next_State <= POP_C2; Address <= Stack_Ptr; 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 Program_Ctr CPU_Next_State <= WAIT_FOR_INT; when ISR_C1 => CPU_Next_State <= ISR_C2; Address <= ISR_Addr; INT_Ctrl.Incr_ISR <= '1'; when ISR_C2 => CPU_Next_State <= ISR_C3; Address <= ISR_Addr; DP_Ctrl.Src <= DATA_FLAG; when ISR_C3 => CPU_Next_State <= JSR_C1; Cache_Ctrl <= CACHE_OPER1; Address <= Stack_Ptr; 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; Address <= Stack_Ptr; SP_Ctrl.Oper <= SP_PUSH; DP_Ctrl.Src <= DATA_PC; DP_Ctrl.Reg <= ACCUM; 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 RTS_C1 => CPU_Next_State <= RTS_C2; Address <= Stack_Ptr; SP_Ctrl.Oper <= SP_POP; 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_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_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; 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; -- 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 ------------------------------------------------------------------------------- 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'; Program_Ctr <= Program_Start_Addr; Stack_Ptr <= Stack_Start_Addr; Ack_Q <= '0'; Ack_Q1 <= '0'; Int_Ack <= '0'; Int_RTI <= '0'; 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; for i in 0 to 7 loop Regfile(i) <= (others => '0'); end loop; Flags <= x"00"; elsif( rising_edge(Clock) )then Wr_Enable <= '0'; Rd_Enable <= '0'; if( Halt = '0' )then Rd_Enable <= '1'; ------------------------------------------------------------------------------- -- Instruction/Operand caching for pipelined memory access ------------------------------------------------------------------------------- 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; ------------------------------------------------------------------------------- -- Program Counter ------------------------------------------------------------------------------- Offset_SX(15 downto 8) := (others => PC_Ctrl.Offset(7)); Offset_SX(7 downto 0) := PC_Ctrl.Offset; case PC_Ctrl.Oper is when PC_IDLE => null; when PC_REV1 => Program_Ctr <= Program_Ctr - 1; when PC_REV2 => Program_Ctr <= Program_Ctr - 2; when PC_INCR => Program_Ctr <= Program_Ctr + Offset_SX - 2; when PC_LOAD => Program_Ctr <= PC_Ctrl.Addr; when others => null; end case; ------------------------------------------------------------------------------- -- (Write) Data Path ------------------------------------------------------------------------------- case DP_Ctrl.Src is when DATA_IDLE => null; when DATA_REG => Wr_Enable <= '1'; Rd_Enable <= '0'; Wr_Data <= Regfile(conv_integer(DP_Ctrl.Reg)); when DATA_FLAG => Wr_Enable <= '1'; Rd_Enable <= '0'; Wr_Data <= Flags; when DATA_PC => Wr_Enable <= '1'; Rd_Enable <= '0'; Wr_Data <= Program_Ctr(15 downto 8); if( DP_Ctrl.Reg = ACCUM )then Wr_Data <= Program_Ctr(7 downto 0); end if; when others => null; end case; ------------------------------------------------------------------------------- -- Stack Pointer ------------------------------------------------------------------------------- case SP_Ctrl.Oper is when SP_IDLE => null; 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 SP_POP => Stack_Ptr <= Stack_Ptr + 1; when SP_PUSH => Stack_Ptr <= Stack_Ptr - 1; when others => null; end case; ------------------------------------------------------------------------------- -- Interrupt Controller ------------------------------------------------------------------------------- -- 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'; 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; Int_Req <= Wait_for_FSM and (not Int_Ack); Int_RTI <= Int_RTI_D; if( Int_RTI = '1' and Hst_Ptr > 0 )then Hst_Ptr <= Hst_Ptr - 1; 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_Addr <= ISR_Addr + 1; end if; ------------------------------------------------------------------------------- -- ALU (Arithmetic / Logic Unit) ------------------------------------------------------------------------------- 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; 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 architecture;
Go to most recent revision | Compare with Previous | Blame | View Log