OpenCores
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;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.