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

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.