--------------------------------------------------------------------------------- -- -- Entity: core_ctrl -- Filename: core_ctrl.vhd -- Description: the control unit for the TUD MB-Lite implementation -- -- Author: Huib Lincklaen Arriens -- Delft University of Technology -- Faculty EEMCS, Department ME&CE, Circuits and Systems -- Date: December, 2010 -- Modified: September, 2013: Core customization (Meloun) -- September, 2012: interrupt handling corrected to let -- a pending branch be taken first -- (with thanks to Matthis Meier, TU Dortmund, -- for detecting this errror). -- Remarks: -- -------------------------------------------------------------------------------- LIBRARY IEEE; USE IEEE.std_logic_1164.all; USE WORK.mbl_pkg.all; -------------------------------------------------------------------------------- ENTITY core_ctrl IS -------------------------------------------------------------------------------- GENERIC ( IMEM_ABITS_g : positive := 9; COMPATIBILITY_MODE_g : BOOLEAN := FALSE ); PORT ( clk_i : IN STD_LOGIC; rst_i : IN STD_LOGIC; halt_i : IN STD_LOGIC; int_i : IN STD_LOGIC; trace_i : IN STD_LOGIC; trace_kick_i : IN STD_LOGIC; core_clken_o : OUT STD_LOGIC; -- specific fetch i/o imem_addr_o : OUT STD_LOGIC_VECTOR ((IMEM_ABITS_g-1) DOWNTO 0); imem_clken_o : OUT STD_LOGIC; pc_ctrl_o : OUT STD_LOGIC; -- fetch to decode pipeline registers IF2ID_REG_i : IN IF2ID_Type; IF2ID_REG_o : OUT IF2ID_Type; -- decode to exeq pipeline registers ID2EX_REG_i : IN ID2EX_Type; ID2EX_REG_o : OUT ID2EX_Type; delay_bit_o : OUT STD_LOGIC; -- GPRF control gprf_clken_o : OUT STD_LOGIC; gprf_finish_wrb_mem_o : OUT STD_LOGIC; -- exeq to fetch feedback registers EX2IF_REG_i : IN EX2IF_Type; EX2IF_REG_o : OUT EX2IF_Type; EX2CTRL_REG_i : IN EX2CTRL_Type; -- exeq to core (halting) exeq_halt_i : IN STD_LOGIC; -- exeq to mem pipeline registers EX2MEM_REG_i : IN EX2MEM_Type; EX2MEM_REG_o : OUT EX2MEM_Type; -- mem pipeline register MEM_REG_i : IN MEM_REG_Type; MEM_REG_o : OUT MEM_REG_Type; -- decode control i/o ID2CTRL_i : IN ID2CTRL_Type; INT_CTRL_o : OUT INT_CTRL_Type; -- exeq control i/o EX_WRB_i : IN WRB_Type; EX_WRB_o : OUT WRB_Type; -- data hazard i/o HAZARD_WRB_i : IN HAZARD_WRB_Type; HAZARD_WRB_o : OUT HAZARD_WRB_Type; -- for handling the 'IMM' instruction IMM_LOCK_i : IN IMM_LOCK_Type; IMM_LOCK_o : OUT IMM_LOCK_Type; -- for handling the Machine Status Register MSR_i : IN MSR_Type; MSR_o : OUT MSR_Type; -- miscellaneous MEM2CTRL_i : IN MEM2CTRL_Type ); END ENTITY core_ctrl; -------------------------------------------------------------------------------- ARCHITECTURE rtl OF core_ctrl IS -------------------------------------------------------------------------------- SIGNAL rst_r : STD_LOGIC; SIGNAL reset_s : STD_LOGIC; SIGNAL core_clken_s : STD_LOGIC; SIGNAL EX2MEM_REG_r : EX2MEM_Type; SIGNAL MEM_REG_r : MEM_REG_Type; SIGNAL ID2EX_REG_r : ID2EX_Type; SIGNAL EX2IF_REG_r : EX2IF_Type; SIGNAL IMM_LOCK_r : IMM_LOCK_Type; SIGNAL HAZARD_WRB_r : HAZARD_WRB_Type; SIGNAL flush_first_r : STD_LOGIC; SIGNAL flush_second_r : STD_LOGIC; SIGNAL flush_second_2r : STD_LOGIC; SIGNAL ignore_state_r : STD_LOGIC; -- Please note: Flushing first is considered immediate! SIGNAL delayBit_r : STD_LOGIC; SIGNAL clken_s : STD_LOGIC; SIGNAL clken_pipe_s : STD_LOGIC; SIGNAL flush_ID2EX_s : STD_LOGIC; SIGNAL flush_ID2EX_r : STD_LOGIC; SIGNAL flush_EX2MEM_s : STD_LOGIC; SIGNAL setup_int_r : STD_LOGIC; SIGNAL int_busy_r : STD_LOGIC; SIGNAL wait_for_mem_s : STD_LOGIC; SIGNAL finish_wrb_mem_s: STD_LOGIC; BEGIN -- static connections reset_s <= rst_i OR rst_r; pc_ctrl_o <= NOT rst_r; -- Addressing is 32-bit, so omit two lowest bytes from PC imem_addr_o <= IF2ID_REG_i.program_counter((IMEM_ABITS_g+1) DOWNTO 2); -- Tracing -- Reset_s is 1 when rst_i is one and then gets deactivated core_clken_s <= reset_s OR (((NOT trace_i) AND (NOT exeq_halt_i)) OR trace_kick_i); core_clken_o <= core_clken_s; -- clock/wait control lines finish_wrb_mem_s <= NOT MEM2CTRL_i.bus_wait WHEN (MEM_REG_r.wrb_Action = WRB_MEM AND MEM2CTRL_i.bus_taken = '1') ELSE '0'; wait_for_mem_s <= MEM2CTRL_i.bus_wait OR MEM2CTRL_i.bus_taken; clken_s <= NOT wait_for_mem_s OR rst_i; clken_pipe_s <= clken_s AND (NOT HAZARD_WRB_i.hazard); imem_clken_o <= clken_pipe_s; gprf_clken_o <= clken_s or finish_wrb_mem_s; gprf_finish_wrb_mem_o <= finish_wrb_mem_s; -- signals for clearing the ID2EX and EX2MEM registers during branches flush_ID2EX_s <= ((EX2IF_REG_i.take_branch AND (NOT delayBit_r)) OR EX2IF_REG_r.take_branch) WHEN COMPATIBILITY_MODE_g = TRUE ELSE ((EX2IF_REG_i.take_branch AND (NOT delayBit_r)) OR EX2IF_REG_r.take_branch OR EX2CTRL_REG_i.flush_first OR flush_first_r OR ((NOT EX2CTRL_REG_i.ignore_state) AND (NOT ignore_state_r) AND flush_second_2r)); flush_EX2MEM_s <= HAZARD_WRB_i.hazard; -- outputs that need to be readable too, so needing shadowing signals EX2MEM_REG_o <= EX2MEM_REG_r; MEM_REG_o <= MEM_REG_r; ID2EX_REG_o <= ID2EX_REG_r; delay_bit_o <= delayBit_r; EX2IF_REG_o <= EX2IF_REG_r; IMM_LOCK_o <= IMM_LOCK_r; HAZARD_WRB_o <= HAZARD_WRB_r; -- INT_CTRL_o.setup_int <= setup_int_r; INT_CTRL_o.rti_target <= ID2EX_REG_r.program_counter; INT_CTRL_o.int_busy <= int_busy_r; regd_proc: PROCESS -- some local procedures PROCEDURE lp_rst_IF2ID_REG IS BEGIN IF2ID_REG_o.program_counter <= (OTHERS => '0'); END PROCEDURE; PROCEDURE lp_rst_ID2EX_REG IS BEGIN -- reset and handle ID2EX_REG_r.program_counter separately, -- since it will be needed during interrupt setup ID2EX_REG_r.rdix_rA <= (OTHERS => '0'); ID2EX_REG_r.rdix_rB <= (OTHERS => '0'); ID2EX_REG_r.curr_rD <= (OTHERS => '0'); ID2EX_REG_r.alu_Action <= A_NOP; ID2EX_REG_r.alu_Op1 <= ALU_IN_ZERO; ID2EX_REG_r.alu_Op2 <= ALU_IN_IMM; ID2EX_REG_r.alu_Cin <= CIN_ZERO; ID2EX_REG_r.IMM16 <= (OTHERS => '0'); ID2EX_REG_r.IMM_Lock <= '0'; ID2EX_REG_r.msr_Action <= KEEP_CARRY; ID2EX_REG_r.branch_Action <= NO_BR; ID2EX_REG_r.mem_Action <= NO_MEM; ID2EX_REG_r.transfer_Size <= WORD; ID2EX_REG_r.wrb_Action <= NO_WRB; ID2EX_REG_r.condition <= COND_ALL; ID2EX_REG_r.halt <= '0'; IF (COMPATIBILITY_MODE_g = FALSE) THEN ID2EX_REG_r.it_Action <= NO_IT; ELSE END IF; END PROCEDURE; PROCEDURE lp_rst_EX2IF_REG IS BEGIN EX2IF_REG_r.take_branch <= '0'; EX2IF_REG_r.branch_target <= (OTHERS => '0'); END PROCEDURE; PROCEDURE lp_rst_EX2MEM_REG IS BEGIN EX2MEM_REG_r.mem_Action <= NO_MEM; EX2MEM_REG_r.wrb_Action <= NO_WRB; EX2MEM_REG_r.exeq_result <= (OTHERS => '0'); EX2MEM_REG_r.data_rD <= (OTHERS => '0'); EX2MEM_REG_r.byte_Enable <= (OTHERS => '0'); EX2MEM_REG_r.wrix_rD <= (OTHERS => '0'); END PROCEDURE; PROCEDURE lp_rst_IMM_LOCK IS BEGIN IMM_LOCK_r.locked <= '0'; IMM_LOCK_r.IMM_hi16 <= (OTHERS => '0'); END PROCEDURE; PROCEDURE lp_rst_MSR IS BEGIN MSR_o.IE <= '0'; MSR_o.C <= '0'; END PROCEDURE; PROCEDURE lp_rst_EX_WRB IS BEGIN EX_WRB_o.wrb_Action <= NO_WRB; EX_WRB_o.wrix_rD <= (OTHERS => '0'); EX_WRB_o.data_rD <= (OTHERS => '0'); END PROCEDURE; PROCEDURE lp_rst_HAZARD_WRB IS BEGIN HAZARD_WRB_r.hazard <= '0'; HAZARD_WRB_r.save_rX <= NO_SAVE; HAZARD_WRB_r.data_rX <= (OTHERS => '0'); HAZARD_WRB_r.data_rD <= (OTHERS => '0'); END PROCEDURE; PROCEDURE lp_rst_MEM_REG IS BEGIN MEM_REG_r.wrb_Action <= NO_WRB; MEM_REG_r.exeq_result <= (OTHERS => '0'); MEM_REG_r.byte_Enable <= (OTHERS => '0'); MEM_REG_r.wrix_rD <= (OTHERS => '0'); END PROCEDURE; BEGIN WAIT UNTIL clk_i'event AND clk_i = '1'; IF (wait_for_mem_s = '0' AND halt_i = '0' AND core_clken_s = '1') OR rst_i = '1' THEN rst_r <= rst_i; IF (reset_s = '1') THEN -- synchronous reset ... lp_rst_IF2ID_REG; -- ... so lasts at least one clock_cycle lp_rst_MSR; lp_rst_HAZARD_WRB; lp_rst_MEM_REG; delayBit_r <= '0'; IF (COMPATIBILITY_MODE_g = FALSE) THEN flush_first_r <= '0'; flush_second_r <= '0'; flush_second_2r <= '0'; ignore_state_r <= '0'; END IF; flush_ID2EX_r <= '0'; setup_int_r <= '0'; int_busy_r <= '0'; ID2EX_REG_r.program_counter <= (OTHERS => '0'); ELSE IF (clken_pipe_s = '1') THEN IF2ID_REG_o <= IF2ID_REG_i; END IF; flush_ID2EX_r <= flush_ID2EX_s; IF (COMPATIBILITY_MODE_g = FALSE) THEN -- Flushing based on IT / ITE / ITT along with IMM locking IF (ID2EX_REG_i.IMM_Lock = '1') THEN IF (flush_second_2r = '0') THEN flush_second_2r <= flush_second_r; END IF; flush_first_r <= EX2CTRL_REG_i.flush_first; flush_second_r <= EX2CTRL_REG_i.flush_second; ignore_state_r <= EX2CTRL_REG_i.ignore_state; ELSE flush_first_r <= '0'; -- Directly to 2r (flushing the following instruction) flush_second_2r <= flush_second_r OR EX2CTRL_REG_i.flush_second; flush_second_r <= '0'; ignore_state_r <= '0'; END IF; END IF; HAZARD_WRB_r <= HAZARD_WRB_i; MEM_REG_r <= MEM_REG_i; int_busy_r <= ID2CTRL_i.int_busy; END IF; -- decode-to-exeq unit registers IF ((reset_s = '1') OR (flush_ID2EX_s = '1')) THEN setup_int_r <= '0'; lp_rst_ID2EX_REG; delayBit_r <= '0'; -- check for the need and possibility to handle active interrupt requests ELSIF (((int_i = '1') OR (MEM2CTRL_i.int = '1')) AND (MSR_i.IE = '1') AND (ID2CTRL_i.int_busy = '0') AND (int_busy_r = '0') AND -- pending branch should be taken before interrupt can be executed -- dectected by Matthis Meier, TU Dortmund (Sept 2012) -- -- Same goes for pending flushing - should be taken before interrupt can be executed (EX2IF_REG_i.take_branch = '0') AND (EX2IF_REG_r.take_branch = '0') AND ((COMPATIBILITY_MODE_g = TRUE) OR ((flush_first_r = '0') AND (flush_second_r = '0') AND (flush_second_2r = '0'))) AND (IMM_LOCK_i.locked = '0') AND (HAZARD_WRB_i.hazard = '0')) THEN setup_int_r <= '1'; ID2EX_REG_r.program_counter <= ID2EX_REG_i.program_counter; lp_rst_ID2EX_REG; ELSIF (clken_pipe_s = '1') THEN setup_int_r <= '0'; ID2EX_REG_r <= ID2EX_REG_i; delayBit_r <= ID2CTRL_i.delayBit; END IF; -- exeq-to-mem unit registers IF ((reset_s = '1') OR (flush_EX2MEM_s = '1')) THEN lp_rst_EX2IF_REG; lp_rst_EX2MEM_REG; lp_rst_EX_WRB; lp_rst_IMM_LOCK; ELSE IF (clken_pipe_s = '1') THEN EX2IF_REG_r <= EX2IF_REG_i; EX_WRB_o <= EX_WRB_i; END IF; IF (clken_s = '1') THEN -- next test to prevent a flush from disrupting -- the write-back pipeline IF (flush_ID2EX_r = '0') THEN EX2MEM_REG_r <= EX2MEM_REG_i; END IF; IMM_LOCK_r <= IMM_LOCK_i; MSR_o <= MSR_i; END IF; END IF; ELSE IF finish_wrb_mem_s = '1' THEN MEM_REG_r.wrb_Action <= NO_WRB; MEM_REG_r.wrix_rD <= (OTHERS => '0'); MEM_REG_r.byte_Enable <= (OTHERS => '0'); IF MEM2CTRL_i.need_keep = '1' THEN EX2MEM_REG_r.data_rD <= MEM2CTRL_i.read_data; END IF; END IF; END IF; -- rising edge clk_i ... END PROCESS regd_proc; END ARCHITECTURE rtl;