URL
https://opencores.org/ocsvn/rv01_riscv_core/rv01_riscv_core/trunk
Subversion Repositories rv01_riscv_core
[/] [rv01_riscv_core/] [trunk/] [VHDL/] [RV01_hltu.vhd] - Rev 2
Compare with Previous | Blame | View Log
----------------------------------------------------------------- -- -- ----------------------------------------------------------------- -- -- -- Copyright (C) 2017 Stefano Tonello -- -- -- -- This source file may be used and distributed without -- -- restriction provided that this copyright statement is not -- -- removed from the file and that any derivative work contains -- -- the original copyright notice and the associated disclaimer.-- -- -- -- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY -- -- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -- -- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -- -- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR -- -- OR CONTRIBUTORS 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. -- -- -- ----------------------------------------------------------------- --------------------------------------------------------------- -- RV01 Halt unit --------------------------------------------------------------- library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; library WORK; use WORK.RV01_CONSTS_PKG.all; use WORK.RV01_TYPES_PKG.all; use WORK.RV01_ARITH_PKG.all; use WORK.RV01_FUNCS_PKG.all; use WORK.RV01_OP_PKG.all; use WORK.RV01_CSR_PKG.all; entity RV01_HLTU is generic( PXE : std_logic := '1'; NW : natural := 2 ); port( CLK_i : in std_logic; RST_i : in std_logic; IX1_V_i : in std_logic_vector(NW-1 downto 0); IX2_V_i : in std_logic_vector(NW-1 downto 0); NOPR_i : in std_logic; -- no pending read (in sbuf) flag MMODE_i : in std_logic; -- machine mode flag HALT_i : in std_logic; -- halt flag HPC_i : in ADR_T; -- halt PC -- CSR interface CS_OP_i : in CS_OP_T; RS1_i : in RID_T; ADR_i : in signed(12-1 downto 0); WE_i : in std_logic; CSRD_i : in SDWORD_T; -- Control port CPRE_i : in std_logic; CPWE_i : in std_logic; CPADR_i : in std_logic_vector(17-1 downto 0); CPD_i : in std_logic_vector(SDLEN-1 downto 0); HMODE_o : out std_logic; -- halt mode flag STRT_o : out std_logic; -- start flag STRTPC_o : out ADR_T; -- start PC RSM_o : out std_logic; -- resume flag HLTURQ_o : out std_logic; -- halt request flag HLTOBRK_o : out std_logic; -- halt-on-break enable HLTOADR_o : out std_logic_vector(NW-1 downto 0); -- halt-on-address enable HLTADR_o : out ADR_T; -- halt address -- CSR interface CSRQ_o : out SDWORD_T; HCSR_o : out std_logic; ILLG_o : out std_logic; -- Control port HCP_o : out std_logic; CPQ_o : out std_logic_vector(SDLEN-1 downto 0) ); end RV01_HLTU; architecture ARC of RV01_HLTU is constant MAX_CNT : natural := 2147483647; --2**(SDLEN-1)-1; constant MRV01HC_HALTSTATE : natural := 0; constant MRV01HC_START : natural := 1; constant MRV01HC_RESUME : natural := 2; constant MRV01HC_HALTU : natural := 3; constant MRV01HC_HALTOBRK : natural := 4; constant MRV01HC_HALTOADR : natural := 5; signal CS_OP_q : CS_OP_T; signal CS_OP_V : std_logic; signal CSRD_q,ICSRD : SDWORD_T; signal RS1_q : RID_T; signal WE_q : std_logic; signal ADR_q : signed(12-1 downto 0); signal CSRQ,CSRQ_q : SDWORD_T; signal ILLG : std_logic; signal HCSR : std_logic; signal ADR_ERR : std_logic; signal IADR,IADR_q : unsigned(12-1 downto 0); signal CPQ : SDWORD_T; signal CP_RE_q : std_logic; signal CP_WE_q : std_logic; signal CP_IADR,CP_IADR_q : unsigned(12-1 downto 0); signal CP_D_q : SDWORD_T; signal MRV01HC_q : SDWORD_T; signal MRV01HA_q : SDWORD_T; signal MRV01RA_q : SDWORD_T; signal STRT,STRT_q : std_logic; signal RSM,RSM_q : std_logic; signal HLTURQ_q : std_logic; signal HALT_q : std_logic; signal HCP : std_logic; signal HOACLR_q : std_logic; function update_csr( PREVD : SDWORD_T; MSK : SDWORD_T; NEWD : SDWORD_T ) return SDWORD_T is variable TMP1,TMP2,TMP3 : SDWORD_T; begin -- clear new data non-writable bits TMP1 := NEWD and MSK; -- clear previous data writable bits TMP2 := PREVD and not(MSK); -- build merged data TMP3 := TMP1 or TMP2; return(TMP3); end function; begin ---------------------------------------------- -- Notes: -- From instruction execution perspective Halt -- module acts as an additional CSRU, executing -- CSR's manipulating instruction on its internal -- registers. -- Halt module registers can be accessed as -- regular CSR's, using CSR instructions, or -- through the control port. ---------------------------------------------- -- CS_OP_i, ADR_i, CSRD_i and RS1_i provide -- access to Halt module internal registers as -- CSR's. IADR <= to_unsigned(ADR_i); -- Input signal registers process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then WE_q <= '0'; else WE_q <= WE_i; end if; CS_OP_q <= CS_OP_i; IADR_q <= IADR; CSRD_q <= CSRD_i; RS1_q <= RS1_i; end if; end process; ---------------------------------------------- -- Perform operation specified by CSR manipulating -- instrucion. process(CS_OP_q,CSRD_q,RS1_q,CSRQ_q) variable ZXRS1 : SDWORD_T; begin -- zero-extended RS1 ZXRS1(log2(REGNUM)-1 downto 0) := to_signed(RS1_q,log2(REGNUM)); ZXRS1(SDLEN-1 downto log2(REGNUM)) := (others => '0'); case CS_OP_q is when CS_RW => ICSRD <= CSRD_q; when CS_RS => if(RS1_q = 0) then ICSRD <= CSRD_q; else ICSRD <= CSRQ_q or CSRD_q; end if; when CS_RC => if(RS1_q = 0) then ICSRD <= CSRD_q; else ICSRD <= CSRQ_q and not(CSRD_q); end if; when CS_RWI => ICSRD <= ZXRS1; when CS_RSI => if(RS1_q = 0) then ICSRD <= CSRD_q; else ICSRD <= CSRQ_q or ZXRS1; end if; when CS_RCI => if(RS1_q = 0) then ICSRD <= CSRD_q; else ICSRD <= CSRQ_q and not(ZXRS1); end if; when others => ICSRD <= CSRD_q; end case; end process; -- CSR instruction valid flag CS_OP_V <= IX1_V_i(0) when ( CS_OP_i = CS_RW or CS_OP_i = CS_RS or CS_OP_i = CS_RC or CS_OP_i = CS_RWI or CS_OP_i = CS_RSI or CS_OP_i = CS_RCI ) else '0'; ---------------------------------------------- -- CPADR_i, CPRE_i and CPWE_i provide access to -- Halt module internal registers from control -- port. CP_IADR <= to_unsigned(CPADR_i(14-1 downto 2)); process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then CP_RE_q <= '0'; CP_WE_q <= '0'; else CP_RE_q <= CPRE_i and CPADR_i(16); CP_WE_q <= CPWE_i and CPADR_i(16); end if; end if; end process; process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then CP_IADR_q <= CP_IADR; CP_D_q <= to_signed(CPD_i); end if; end process; ---------------------------------------------- -- RV01 Halt Control register (MRV01HC) ---------------------------------------------- -- This register manages halt interface, allowing -- to halt instruction execution: -- 1) immediately (i.e. on next executed -- instruction), OR -- 2) on the first break instruction to be -- executed, OR -- 3) on the first instruction which fetch address -- matches MRV01HA register content. -- When instruction execution halts, MRV01RA holds -- the address of the next NON executed instruction. -- Instruction execution starts from standard -- reset address when start=1 and resume=0 and -- from address stored in MRV01RA when start=1 and -- resume=1. -- At reset, haltstate=1. -- bit | description -- 0 | haltstate (R) -- 1 | start (W) -- 2 | resume (W) -- 3 | halt unconditionally (W) -- 4 | halt on break (R/W) -- 5 | halt on address (R/W) -- 6:31 | reserved -- This register "holds" HALT_i until -- store buffer gets emptied. process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then HALT_q <= '0'; elsif(HALT_i = '1' and NOPR_i = '0') then HALT_q <= '1'; elsif(HALT_q = '1' and NOPR_i = '1') then HALT_q <= '0'; end if; end if; end process; process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then MRV01HC_q <= MRV01HC_RST; elsif(CP_WE_q = '1' and CP_IADR_q = MRV01HC_ADR) then -- write register through control port MRV01HC_q <= update_csr(MRV01HC_q,MRV01HC_WMSK,CP_D_q); elsif(WE_q = '1' and IADR_q = MRV01HC_ADR and MMODE_i = '1') then -- write register through CSR instructions MRV01HC_q <= update_csr(MRV01HC_q,MRV01HC_WMSK,ICSRD); end if; -- register haltstate bit is updated by when halting -- and starting/resuming instruction execution. if((HALT_i = '1' or HALT_q = '1') and NOPR_i = '1') then MRV01HC_q(MRV01HC_HALTSTATE) <= '1'; elsif(STRT = '1') then MRV01HC_q(MRV01HC_HALTSTATE) <= '0'; end if; end if; end process; -- Output Halt mode flag HMODE_o <= MRV01HC_q(MRV01HC_HALTSTATE); ---------------------------------------------- -- Starting and resuming take place immediately, -- without the need to associate them to a valid -- instruction, so that it's not necessary to -- store them (they're just registered to break -- the timing path). ---------------------------------------------- -- Internal start flag STRT <= CP_WE_q when( CP_IADR_q = MRV01HC_ADR and CP_D_q(MRV01HC_START) = '1' ) else '0'; -- start flag register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then STRT_q <= '0'; else STRT_q <= STRT; end if; end if; end process; -- Output Start flag STRT_o <= STRT_q; -- Start address (set to reset vector, unless execution resumes from -- MRV01RA content. STRTPC_o <= RESET_VA_LO when RSM_q = '0' else to_unsigned(MRV01RA_q); ---------------------------------------------- -- Internal resume flag RSM <= CP_WE_q when( CP_IADR_q = MRV01HC_ADR and CP_D_q(MRV01HC_RESUME) = '1' ) else '0'; -- Resume flag register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then RSM_q <= '0'; else RSM_q <= RSM; end if; end if; end process; -- Output resume flag RSM_o <= RSM_q; -- Halt-On-Address Clear flag (used to force -- HLTOADR_o cleared when resuming execution). process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then HOACLR_q <= '0'; elsif(RSM = '1') then HOACLR_q <= '1'; elsif(IX2_V_i(0) = '1' or IX2_V_i(1) = '1') then HOACLR_q <= '0'; end if; end if; end process; ---------------------------------------------- -- Halting is associated to a valid instruction -- (otherwise there would be no address to -- resume from) and therefore halt flag must -- be stored until a valid instruction -- is retired (this event being marked by -- HALT_i = '1'). -- Halt request flag is set when a halt condition -- become true, and cleared when instruction -- execution halts (i.e. when the request is -- aknowledged by the core). process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then HLTURQ_q <= '0'; elsif( CP_WE_q = '1' and CP_IADR_q = MRV01HC_ADR and CP_D_q(MRV01HC_HALTU) = '1' ) then -- halt request flag is set by control port HLTURQ_q <= '1'; elsif( WE_q = '1' and IADR_q = MRV01HC_ADR and ICSRD(MRV01HC_HALTU) = '1' ) then -- halt request flag is set via CSR instruction HLTURQ_q <= '1'; elsif(HALT_i = '1') then -- core is halted: reset request flag HLTURQ_q <= '0'; end if; end if; end process; -- Output Halt request flag HLTURQ_o <= HLTURQ_q; ---------------------------------------------- -- Output Halt on break flag HLTOBRK_o <= MRV01HC_q(MRV01HC_HALTOBRK); ---------------------------------------------- -- Note: if halt address and resume address coincide, the first -- instruction to be executed (i.e. the instruction located -- at the resume address) must be prevented from triggering -- a halt-on-address, otherwise the resume would have no -- practical effect. -- This is accomplished by having separated halt-on-address -- flags for the two instructions checked for halting. -- Slot #0 flag is forced clear, after resuming, until the -- first valid instruction reaches IX2. -- Slot #1 flag is forced clear, after resuming, until the -- first valid instruction reaches IX2, but only if IX2 slot #0 -- instruction is not valid (this additional check is needed -- in the special case where halt address is equal to resume -- address plus 4 and the corresponding instructions reach -- IX2 at the same time, without it, the halt-on-address check -- on slot #1 instructions would be incorrectly disabled). -- Halt on address flag HLTOADR_o(0) <= MRV01HC_q(MRV01HC_HALTOADR) and not(HOACLR_q); -- Halt on address flag HLTOADR_o(1) <= MRV01HC_q(MRV01HC_HALTOADR) and (not(HOACLR_q) or IX2_V_i(0)); ---------------------------------------------- -- RV01 Halt Address register (MRV01HA) ---------------------------------------------- -- This register stores the address on which -- instruction execution halts when halt-on- -- address mode is enabled. process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then MRV01HA_q <= (others => '0'); elsif(CP_WE_q = '1' and CP_IADR_q = MRV01HA_ADR) then -- write register through control port MRV01HA_q <= CP_D_q; elsif(WE_q = '1' and IADR_q = MRV01HA_ADR and MMODE_i = '1') then -- write register through CSR instructions MRV01HA_q <= ICSRD; end if; end if; end process; -- Output halt-on address HLTADR_o <= to_unsigned(MRV01HA_q); ---------------------------------------------- -- RV01 Resume Address register (MRV01RA) ---------------------------------------------- -- This register also store the address at -- which instruction execution starts when -- start=1 and resume=1 (this allows, if -- needed, to start instruction execution -- from an arbitrary address). process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then MRV01RA_q <= (others => '0'); elsif(HALT_i = '1') then -- write register on halt MRV01RA_q <= to_signed(HPC_i); elsif(CP_WE_q = '1' and CP_IADR_q = MRV01RA_ADR) then -- write register through control port MRV01RA_q <= CP_D_q; elsif(WE_q = '1' and IADR_q = MRV01RA_ADR and MMODE_i = '1') then -- write register through CSR instructions MRV01RA_q <= ICSRD; end if; end if; end process; ---------------------------------------------- -- CSR output mux ---------------------------------------------- -- This mux selects Halt module output as a CSRU-like -- functional unit, sets address error flag in case -- an internal register access has been attempted -- while not in machine mode or to a non-existent -- registers and sets selector used to merge Halt -- module outputs with CSRU ones. process(IADR,MRV01HC_q,MRV01HA_q,MRV01RA_q,MMODE_i) begin case IADR is when MRV01HC_ADR => CSRQ <= MRV01HC_q; ADR_ERR <= not(MMODE_i); HCSR <= '1'; when MRV01HA_ADR => CSRQ <= MRV01HA_q; ADR_ERR <= not(MMODE_i); HCSR <= '1'; when MRV01RA_ADR => CSRQ <= MRV01RA_q; ADR_ERR <= not(MMODE_i); HCSR <= '1'; when others => CSRQ <= (others => '0'); ADR_ERR <= '1'; HCSR <= '0'; end case; end process; -- This register is needed because CSR's are accessed -- in pipelined fashion (read in ID stage, processed -- in IX1 stage) like GPR's. process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then CSRQ_q <= CSRQ; end if; end process; -- Illegal instruction exception must be raised if: -- 1) an un-existent CSR address is specified, OR -- 2) access is attempted on a CSR without appropriate -- privileges. ILLG <= CS_OP_V when (ADR_ERR = '1') else '0'; CSRQ_o <= CSRQ; ILLG_o <= ILLG; HCSR_o <= HCSR; ---------------------------------------------- -- Control port output mux ---------------------------------------------- -- This mux selects Halt module output as -- control port output. The selecting signal -- HCP is used to merge Halt module output -- with CSRU ones. process(CP_IADR,MRV01HC_q,MRV01HA_q,MRV01RA_q) begin case CP_IADR is when MRV01HC_ADR => CPQ <= MRV01HC_q; HCP <= '1'; when MRV01HA_ADR => CPQ <= MRV01HA_q; HCP <= '1'; when MRV01RA_ADR => CPQ <= MRV01RA_q; HCP <= '1'; when others => CPQ <= (others => '0'); HCP <= '0'; end case; end process; HCP_o <= HCP; CPQ_o <= to_std_logic_vector(CPQ); end ARC;