URL
https://opencores.org/ocsvn/rv01_riscv_core/rv01_riscv_core/trunk
Subversion Repositories rv01_riscv_core
[/] [rv01_riscv_core/] [trunk/] [VHDL/] [RV01_plic_core.vhd] - Rev 2
Compare with Previous | Blame | View Log
----------------------------------------------------------------- -- -- ----------------------------------------------------------------- -- -- -- Copyright (C) 2016 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 REQERRUPTION) 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 PLIC core --------------------------------------------------------------- library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; library work; use work.RV01_CONSTS_PKG.all; use work.RV01_FUNCS_PKG.all; use work.RV01_PLIC_PKG.all; entity RV01_PLIC_CORE is generic( SRC_CNT : natural := 8 ); port( CLK_i : in std_logic; RST_i : in std_logic; REG_A_i : in std_logic_vector(log2(SRC_CNT+1)-1 downto 0); REG_WE_i : in std_logic; REG_D_i : in std_logic_vector(SDLEN-1 downto 0); IP_i : in std_logic_vector(SRC_CNT-1 downto 0); REG_Q_o : out std_logic_vector(SDLEN-1 downto 0); EIP_o : out std_logic; IS_o : out std_logic_vector(SRC_CNT-1 downto 0) ); end RV01_PLIC_CORE; architecture ARC of RV01_PLIC_CORE is -- interrupt Id number of bits. constant ID_WIDTH : natural := 8; -- interrupt priority number of bits. constant PRI_WIDTH : natural := 8; subtype PRIORITY_TYPE is unsigned(PRI_WIDTH-1 downto 0); subtype ID_TYPE is unsigned(ID_WIDTH-1 downto 0); -- interrupt-claim bit index constant ICLM_NDX : natural := 8; -- interrupt-complete bit index constant ICMPLT_NDX : natural := 9; -- interrupt id. hi/lo bit bounds constant ID_LO : natural := 0; constant ID_HI : natural := ID_LO + ID_WIDTH - 1; -- interrupt priority hi/lo bit bounds constant PRI_LO : natural := 8; constant PRI_HI : natural := PRI_LO + PRI_WIDTH - 1; -- interrupt priority threshold hi/lo bit bounds constant PRIT_LO : natural := 0; constant PRIT_HI : natural := PRIT_LO + PRI_WIDTH - 1; constant IPTR_ADR : unsigned(log2(SRC_CNT+3)-1 downto 0) := to_unsigned(SRC_CNT,log2(SRC_CNT+3)); constant IEBR_ADR : unsigned(log2(SRC_CNT+3)-1 downto 0) := to_unsigned(SRC_CNT+1,log2(SRC_CNT+3)); constant ICR_ADR : unsigned(log2(SRC_CNT+3)-1 downto 0) := to_unsigned(SRC_CNT+2,log2(SRC_CNT+3)); component RV01_RAM_1RW1R is generic( -- I/O data bus width DWIDTH : integer := 16; -- word count WCOUNT : integer := 256 ); port( CLK_i : in std_logic; A_i : in unsigned(log2(WCOUNT)-1 downto 0); DPRA_i : in unsigned(log2(WCOUNT)-1 downto 0); D_i : in std_logic_vector(DWIDTH-1 downto 0); WE_i : in std_logic; Q_o : out std_logic_vector(DWIDTH-1 downto 0); DPQ_o : out std_logic_vector(DWIDTH-1 downto 0) ); end component; --------------------------------------------------------- -- Interrupt Source Registers (ISR) --------------------------------------------------------- -- 3322 2222 2222 1111 1111 1100 0000 0000 -- 1098 7654 3210 9876 5432 1098 7654 3210 Descr. -- dddd dddd Int. ID (R/W) -- dddd dddd Int. priority (R/W) --------------------------------------------------------- -- Interrupt Priority Threshold Register (IPTR) --------------------------------------------------------- -- 3322 2222 2222 1111 1111 1100 0000 0000 -- 1098 7654 3210 9876 5432 1098 7654 3210 Descr. -- dddd dddd Int. priority thresh. (R/W) --------------------------------------------------------- -- Interrupt Enable Bits Register (IEBR) --------------------------------------------------------- -- 3322 2222 2222 1111 1111 1100 0000 0000 -- 1098 7654 3210 9876 5432 1098 7654 3210 Descr. -- dddd dddd dddd dddd dddd dddd dddd dddd IE bits (R/W) --------------------------------------------------------- -- Interrupt Control Register (ICR) --------------------------------------------------------- -- 3322 2222 2222 1111 1111 1100 0000 0000 -- 1098 7654 3210 9876 5432 1098 7654 3210 Descr. -- dddd dddd Int. ID (R) -- d Int. Claim (W) -- d Int. Complete (W) type STATE_TYPE is ( S_IDLE, S_WAIT, S_EVAL1, S_EVAL2, S_WCLM, S_WCMPLT ); signal ISR_A,ISR_DPRA : unsigned(log2(SRC_CNT)-1 downto 0); signal ISR_D,ISR_Q,ISR_DPQ : std_logic_vector(ID_WIDTH+PRI_WIDTH-1 downto 0); signal ISR_WE : std_logic; signal IPTR_q : PRIORITY_TYPE; signal IPTR_WE : std_logic; signal IEBR_q : std_logic_vector(SRC_CNT-1 downto 0); signal IEBR_WE : std_logic; signal EVAL,EVAL_q : std_logic; signal S,S_q : STATE_TYPE; signal ICLM : std_logic; signal ICMPLT : std_logic; signal SCNT_RST,SCNT_INC,SCNT_END : std_logic; signal SCNT_q,SCNT_q2 : natural range 0 to SRC_CNT-1; signal EIP_q : std_logic; signal EIP_SET,EIP_CLR : std_logic; signal MAX_PRI_q : PRIORITY_TYPE; signal MAX_PRI_WE : std_logic; signal MIN_ID_WE : std_logic; signal PRI_GT_MAX,PRI_EQ_MAX,PRI_MAX_GT_TRSH : std_logic; signal MIN_ID_q : ID_TYPE; signal ID_LT_MIN : std_logic; signal RSEL,RSEL_q : std_logic_vector(2-1 downto 0); signal CURR_IP,CURR_IP_q : std_logic; signal CURR_ID : ID_TYPE; signal CURR_PRI : PRIORITY_TYPE; signal IPSRC_q : natural range 0 to SRC_CNT-1; signal IP_q : std_logic_vector(SRC_CNT-1 downto 0); begin --------------------------------------------------------- -- Interrupt Source Registers --------------------------------------------------------- U_ISR : RV01_RAM_1RW1R generic map( DWIDTH => (ID_WIDTH + PRI_WIDTH), WCOUNT => SRC_CNT ) port map( CLK_i => CLK_i, A_i => ISR_A, DPRA_i => ISR_DPRA, D_i => ISR_D, WE_i => ISR_WE, Q_o => ISR_Q, DPQ_o => ISR_DPQ ); ISR_A <= to_unsigned(REG_A_i(log2(SRC_CNT)-1 downto 0)); ISR_DPRA <= to_unsigned(SCNT_q,log2(SRC_CNT)); ISR_D <= REG_D_i(PRI_WIDTH+ID_WIDTH-1 downto 0); ISR_WE <= REG_WE_i when (to_unsigned(REG_A_i) < SRC_CNT) else '0'; CURR_ID <= to_unsigned(ISR_DPQ(ID_HI downto ID_LO)); CURR_PRI <= to_unsigned(ISR_DPQ(PRI_HI downto PRI_LO)); --------------------------------------------------------- -- Interrupt Priority Threshold Register --------------------------------------------------------- process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then IPTR_q <= (others => '0'); elsif(IPTR_WE = '1') then IPTR_q <= to_unsigned(REG_D_i(PRIT_HI downto PRIT_LO)); end if; end if; end process; IPTR_WE <= REG_WE_i when (to_unsigned(REG_A_i) = IPTR_ADR) else '0'; --------------------------------------------------------- -- Interrupt Enable Bits Register --------------------------------------------------------- process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then IEBR_q <= (others => '0'); elsif(IEBR_WE = '1') then IEBR_q <= REG_D_i(SRC_CNT-1 downto 0); end if; end if; end process; IEBR_WE <= REG_WE_i when (to_unsigned(REG_A_i) = IEBR_ADR) else '0'; CURR_IP <= IEBR_q(SCNT_q) and IP_q(SCNT_q); process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then CURR_IP_q <= CURR_IP; end if; end process; --------------------------------------------------------- -- Interrupt Control Register --------------------------------------------------------- -- There's no physical storage for this register as its -- bits (Int. Claim and Int. Complete) are write-only. -- Interrupt claim flag ICLM <= (REG_WE_i and REG_D_i(ICLM_NDX)) when (to_unsigned(REG_A_i) = ICR_ADR) else '0'; -- Interrupt complete flag ICMPLT <= (REG_WE_i and REG_D_i(ICMPLT_NDX)) when (to_unsigned(REG_A_i) = ICR_ADR) else '0'; --------------------------------------------------------- -- Refresh Signal --------------------------------------------------------- -- Pending interrup status must be re-evaluated every time -- PLIC core registers are written or pending interrupt -- inputs change. process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then IP_q <= (others => '0'); else IP_q <= IP_i; end if; end if; end process; process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then EVAL_q <= '0'; else EVAL_q <= EVAL; end if; end if; end process; EVAL <= not(EIP_q) when (REG_WE_i = '1' or not(IP_i = IP_q)) else '0'; --------------------------------------------------------- -- Control FSM --------------------------------------------------------- process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then S_q <= S_IDLE; else S_q <= S; end if; end if; end process; process(S_q,EVAL_q,SCNT_END,PRI_GT_MAX,PRI_EQ_MAX,ID_LT_MIN, PRI_MAX_GT_TRSH,CURR_IP_q,ICLM,ICMPLT) begin SCNT_RST <= '0'; SCNT_INC <= '0'; MAX_PRI_WE <= '0'; MIN_ID_WE <= '0'; EIP_SET <= '0'; EIP_CLR <= '0'; case S_q is -- Wait for next pending interrupt evaluation when S_IDLE => if(EVAL_q = '1') then -- Reset source count SCNT_RST <= '1'; -- Start evaluation S <= S_WAIT; end if; -- A wait state allows is needed for register RAM -- output to update after source count is reset. when S_WAIT => if(EVAL_q = '1') then -- Reset source count SCNT_RST <= '1'; -- Re-start evaluation S <= S_WAIT; else -- Increment source count SCNT_INC <= '1'; -- Go on with evaluation S <= S_EVAL1; end if; -- Perform pending interrupt evaluation when S_EVAL1 => if(EVAL_q = '1') then -- Reset source count SCNT_RST <= '1'; -- Re-start evaluation S <= S_WAIT; elsif(SCNT_END = '1') then -- end evaluation, there's no pending -- interrupt, go back to idle state. S <= S_IDLE; else -- Increment source count SCNT_INC <= '1'; -- Check for pending interrupts... if(CURR_IP_q = '1') then -- If current interrupt priority is higher than -- max. priority, update max. priority. MAX_PRI_WE <= PRI_GT_MAX; -- If current interrupt priority is higher than -- max. priority, OR -- if current interrupt priority is equal to -- max. priority and current id. is -- lower then min. id, update min. id. MIN_ID_WE <= PRI_GT_MAX or (PRI_EQ_MAX and ID_LT_MIN); -- Go on with evaluation S <= S_EVAL2; else -- Go on with evaluation S <= S_EVAL1; end if; end if; -- Perform pending interrupt evaluation when S_EVAL2 => if(EVAL_q = '1') then -- Reset source count SCNT_RST <= '1'; -- Re-start evaluation S <= S_WAIT; elsif(SCNT_END = '1') then -- end evaluation, there's a pending -- interrupt: check if its priority -- exceed treshold value... if(PRI_MAX_GT_TRSH = '1') then -- it does: SET EIP register EIP_SET <= '1'; S <= S_WCLM; else -- it doesn't: go back to idle state. S <= S_IDLE; end if; else -- Increment source count SCNT_INC <= '1'; -- Check for pending interrupts... if(CURR_IP_q = '1') then -- If current interrupt priority is higher than -- max. priority, update max. priority. MAX_PRI_WE <= PRI_GT_MAX; -- If current interrupt priority is higher than -- max. priority, OR -- if current interrupt priority is equal to -- max. priority and current id. is -- lower then min. id, update min. id. MIN_ID_WE <= PRI_GT_MAX or (PRI_EQ_MAX and ID_LT_MIN); -- Go on with evaluation S <= S_EVAL2; else -- Go on with evaluation S <= S_EVAL2; end if; end if; -- Wait for claim signal from target when S_WCLM => if(ICLM = '1') then -- Clear EIP register EIP_CLR <= '1'; S <= S_WCMPLT; else S <= S_WCLM; end if; -- Wait for completion signal from target when S_WCMPLT => if(ICMPLT = '1') then EIP_CLR <= '1'; S <= S_IDLE; else S <= S_WCMPLT; end if; when others => S <= S_IDLE; end case; end process; --------------------------------------------------------- -- --------------------------------------------------------- -- Max. source priority register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then MAX_PRI_q <= to_unsigned(1,PRI_WIDTH); elsif(MAX_PRI_WE = '1') then MAX_PRI_q <= CURR_PRI; end if; end if; end process; -- Current source priority greater-than max. priority flag PRI_GT_MAX <= '1' when CURR_PRI > MAX_PRI_q else '0'; -- Current source priority equal-to max. priority flag PRI_EQ_MAX <= '1' when CURR_PRI > MAX_PRI_q else '0'; -- Max. source priority greater-than priority treshold flag PRI_MAX_GT_TRSH <= '1' when MAX_PRI_q > IPTR_q else '0'; -- Min. source id. register process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1') then MIN_ID_q <= (others => '1'); elsif(MIN_ID_WE = '1') then MIN_ID_q <= CURR_ID; end if; end if; end process; -- Current source id. lower-than min. id. flag. ID_LT_MIN <= '1' when CURR_ID < MIN_ID_q else '0'; --------------------------------------------------------- -- Pending Interrupt register --------------------------------------------------------- process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(RST_i = '1' or EIP_CLR = '1') then EIP_q <= '0'; elsif(EIP_SET = '1') then EIP_q <= '1'; end if; end if; end process; EIP_o <= EIP_q; --------------------------------------------------------- -- Pending Interrupt Source register --------------------------------------------------------- process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(MIN_ID_WE = '1') then IPSRC_q <= SCNT_q2; end if; end if; end process; process(ICMPLT,IPSRC_q) variable TMP : std_logic_vector(SRC_CNT-1 downto 0); begin TMP := (others => '0'); TMP(IPSRC_q) := ICMPLT; IS_o <= TMP; end process; --------------------------------------------------------- -- Source counter --------------------------------------------------------- process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then if(SCNT_RST = '1') then SCNT_q <= 0; SCNT_q2 <= 0; elsif(SCNT_INC = '1' and SCNT_END = '0') then SCNT_q <= SCNT_q + 1; SCNT_q2 <= SCNT_q; end if; end if; end process; SCNT_END <= '1' when (SCNT_q = SRC_CNT-1) else '0'; --------------------------------------------------------- -- Output mux --------------------------------------------------------- process(REG_A_i) begin if(to_unsigned(REG_A_i) <= SRC_CNT) then RSEL <= "00"; elsif(to_unsigned(REG_A_i) = IPTR_ADR) then RSEL <= "01"; elsif(to_unsigned(REG_A_i) = IEBR_ADR) then RSEL <= "10"; else RSEL <= "11"; end if; end process; -- Delay selector value by one cycle to match -- ISR sync.RAM read delay. process(CLK_i) begin if(CLK_i = '1' and CLK_i'event) then RSEL_q <= RSEL; end if; end process; process(RSEL_q,ISR_Q,IPTR_q,IEBR_q) variable TMP : std_logic_vector(SDLEN-1 downto 0); begin TMP := (others => '0'); case RSEL_q is when "00" => TMP(ID_HI downto ID_LO) := ISR_q(ID_WIDTH-1 downto 0); TMP(PRI_HI downto PRI_LO) := ISR_q(PRI_WIDTH+ID_WIDTH-1 downto ID_WIDTH); REG_Q_o <= TMP; when "01" => TMP(PRI_HI downto PRI_LO) := to_std_logic_vector(IPTR_q); REG_Q_o <= TMP; when "10" => TMP(SRC_CNT-1 downto 0) := IEBR_q(ID_WIDTH-1 downto 0); REG_Q_o <= TMP; when others => REG_Q_o <= (others => '0'); end case; end process; --------------------------------------------------------- -- Notes --------------------------------------------------------- -- The PLIC core has an address space of log2(SRC_CNT+2) -- bit, with addresses ranging from 0 to SRC_CNT+1. -- Address SRC_CNT+1 selects the Interrupt Enable Bit -- Registers. -- Address SRC_CNT selects the Interrupt Priority -- Threshold Register. -- Addresses 0:SRC_CNT-1 select the Interrupt registers -- (one per interrupt source). -- Each Interrupt register stores interrupt id and -- priority for an interrupt source. -- Interrupt registers are read and written by the RV01 -- core through a memory-mapped interface consisting of -- the REG_ADR_i, REG_WE_i, REG_D_i and REG_Q_o ports. end ARC;