// Copyright (c) 2013-2020 Bluespec, Inc. All Rights Reserved
//-
// RVFI_DII + CHERI modifications:
//     Copyright (c) 2020 Jonathan Woodruff
//     All rights reserved.
//
//     This software was developed by SRI International and the University of
//     Cambridge Computer Laboratory (Department of Computer Science and
//     Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
//     DARPA SSITH research programme.
//
//     This work was supported by NCSC programme grant 4212611/RFA 15971 ("SafeBet").
//-

// ================================================================
// ISA defs for UC Berkeley RISC V
//
// References (from riscv.org):
//     The RISC-V Instruction Set Manual
//     Volume I: Unprivileged ISA
//     Document Version 20181106-Base-Ratification
//     November 6, 2018
//
//     The RISC-V Instruction Set Manual
//     Volume II: Privileged Architecture
//     Document Version 20181203-Base-Ratification
//     December 3, 2018
//
// ================================================================

package ISA_Decls;

// ================================================================
// BSV library imports

import DefaultValue :: *;
import Vector       :: *;
import BuildVector  :: *;

// ================================================================
// BSV project imports

// None

// ================================================================

typedef 3 NO_OF_PRIVMODES;

// ================================================================
// XLEN and related constants

`ifdef RV32

typedef 32 XLEN;

`elsif RV64

typedef 64 XLEN;

`endif

typedef TMul #(2, XLEN)  XLEN_2;      // Double-width for multiplications
typedef TSub #(XLEN, 2)  XLEN_MINUS_2;// XLEN-2 for MTVEC base width

Integer xlen = valueOf (XLEN);

typedef enum { RV32, RV64 } RV_Version deriving (Eq, Bits);

RV_Version rv_version = ( (valueOf (XLEN) == 32) ? RV32 : RV64 );

// ----------------
// We're evolving the code to use WordXL/IntXL instead of Word/Word_S
// because of the widespread and inconsistent use of 'word' in the field.
// All existing uses of 'Word/Word_S' should migrate towards WordXL/IntXL.
// All new code should only use WordXL/IntXL
// Eventually, we should remove Word and Word_S

typedef  Bit #(XLEN)  WordXL;    // Raw (unsigned) register data
typedef  Int #(XLEN)  IntXL;     // Signed register data

typedef  Bit #(XLEN)  Word;      // Raw (unsigned) register data    // OLD: migrate to WordXL
typedef  Int #(XLEN)  Word_S;    // Signed register data            // OLD: migrate to IntXL

typedef  WordXL       Addr;      // addresses/pointers

// ----------------

typedef  8                                     Bits_per_Byte;
typedef  Bit #(Bits_per_Byte)                  Byte;

typedef  XLEN                                  Bits_per_Word;            // REDUNDANT to XLEN

typedef  TDiv #(Bits_per_Word, Bits_per_Byte)  Bytes_per_Word;           // OLD ('WordXL')
typedef  TLog #(Bytes_per_Word)                Bits_per_Byte_in_Word;    // OLD ('WordXL')
typedef  Bit #(Bits_per_Byte_in_Word)          Byte_in_Word;             // OLD ('WordXL')
typedef  Vector #(Bytes_per_Word, Byte)        Word_B;                   // OLD ('WordXL')

typedef  TDiv #(XLEN, Bits_per_Byte)           Bytes_per_WordXL;
typedef  TLog #(Bytes_per_WordXL)              Bits_per_Byte_in_WordXL;
typedef  Bit #(Bits_per_Byte_in_WordXL)        Byte_in_WordXL;
typedef  Vector #(Bytes_per_WordXL, Byte)      WordXL_B;

typedef  XLEN                                  Bits_per_Addr;
typedef  TDiv #(Bits_per_Addr, Bits_per_Byte)  Bytes_per_Addr;

Integer  bits_per_byte           = valueOf (Bits_per_Byte);

Integer  bytes_per_wordxl        = valueOf (Bytes_per_WordXL);
Integer  bits_per_byte_in_wordxl = valueOf (Bits_per_Byte_in_WordXL);

Integer  addr_lo_byte_in_wordxl = 0;
Integer  addr_hi_byte_in_wordxl = addr_lo_byte_in_wordxl + bits_per_byte_in_wordxl - 1;

function  Byte_in_Word  fn_addr_to_byte_in_wordxl (Addr a);
   return a [addr_hi_byte_in_wordxl : addr_lo_byte_in_wordxl ];
endfunction

// ================================================================
// FLEN and related constants, for floating point data
// Can have one or two fpu sizes (should they be merged sooner than later ?).

// Cannot define ISA_D unless ISA_F is also defined
// ISA_F - 32 bit FPU
// ISA_D - 64 bit FPU

`ifdef ISA_F

`ifdef ISA_D   // ISA_D precludes ISA_F
typedef 64 FLEN;
Bool hasFpu32 = False;
Bool hasFpu64 = True;
`else
typedef 32 FLEN;
Bool hasFpu32 = True;
Bool hasFpu64 = False;
`endif

typedef  Bit #(FLEN) FP_Value;
typedef  Bit #(FLEN)  WordFL;    // Floating point data

typedef  TDiv #(FLEN, Bits_per_Byte)           Bytes_per_WordFL;
typedef  TLog #(Bytes_per_WordFL)              Bits_per_Byte_in_WordFL;
typedef  Bit #(Bits_per_Byte_in_WordFL)        Byte_in_WordFL;
typedef  Vector #(Bytes_per_WordFL, Byte)      WordFL_B;

`endif

`ifdef ISA_F
`ifdef ISA_D
`define ISA_F_AND_D
`endif
`endif

// ================================================================
// Tokens are used for signalling/synchronization, and have no payload

typedef Bit #(0) Token;

// ================================================================
// Instruction fields

// This is used for encoding Tandem Verifier traces
typedef enum { ISIZE16BIT, ISIZE32BIT
   } ISize deriving (Bits, Eq, FShow);

typedef  Bit #(32)  Instr;
typedef  Bit #(7)   Opcode;
typedef  Bit #(5)   RegName;       // 32 registers, 0..31
typedef  32         NumRegs;
Integer  numRegs = valueOf (NumRegs);

Instr  illegal_instr = 32'h0000_0000;

function  Opcode     instr_opcode   (Instr x); return x [6:0]; endfunction

function  Bit #(2)   instr_funct2   (Instr x); return x [26:25]; endfunction
function  Bit #(3)   instr_funct3   (Instr x); return x [14:12]; endfunction
function  Bit #(5)   instr_funct5   (Instr x); return x [31:27]; endfunction
function  Bit #(7)   instr_funct7   (Instr x); return x [31:25]; endfunction
function  Bit #(10)  instr_funct10  (Instr x); return { x [31:25], x [14:12] }; endfunction
function  Bit #(2)   instr_fmt      (Instr x); return x [26:25]; endfunction

function  RegName    instr_rd       (Instr x); return x [11:7]; endfunction
function  RegName    instr_rs1      (Instr x); return x [19:15]; endfunction
function  RegName    instr_rs2      (Instr x); return x [24:20]; endfunction
function  RegName    instr_rs3      (Instr x); return x [31:27]; endfunction
function  CSR_Addr   instr_csr      (Instr x); return unpack(x [31:20]); endfunction

function  Bit #(12)  instr_I_imm12  (Instr x);
   return x [31:20];
endfunction

function  Bit #(12)  instr_S_imm12  (Instr x);
   return { x [31:25], x [11:7] };
endfunction

function  Bit #(13)  instr_SB_imm13 (Instr x);
   return { x [31], x [7], x [30:25], x [11:8], 1'b0 };
endfunction

function  Bit #(20)  instr_U_imm20  (Instr x);
   return x [31:12];
endfunction

function  Bit #(21)  instr_UJ_imm21 (Instr x);
   return { x [31], x [19:12], x [20], x [30:21], 1'b0 };
endfunction

// For FENCE decode
function  Bit #(4)   instr_pred (Instr x); return x [27:24]; endfunction
function  Bit #(4)   instr_succ (Instr x); return x [23:20]; endfunction

// For AMO decode
function  Bit #(2)   instr_aqrl (Instr x); return x [26:25]; endfunction

// ----------------
// Decoded instructions

typedef struct {
   Opcode    opcode;

   RegName   rd;
   RegName   rs1;
   RegName   rs2;
   RegName   rs3;
   CSR_Addr  csr;

   Bit #(3)  funct3;
   Bit #(5)  funct5;
   Bit #(7)  funct7;
   Bit #(10) funct10;

   Bit #(12) imm12_I;
   Bit #(12) imm12_S;
   Bit #(13) imm13_SB;
   Bit #(20) imm20_U;
   Bit #(21) imm21_UJ;

   Bit #(4)  pred;
   Bit #(4)  succ;

   Bit #(2)  aqrl;
   } Decoded_Instr
deriving (FShow, Bits);

function Decoded_Instr fv_decode (Instr instr);
   return Decoded_Instr {opcode:    instr_opcode (instr),

                         rd:        instr_rd       (instr),
                         rs1:       instr_rs1      (instr),
                         rs2:       instr_rs2      (instr),
                         rs3:       instr_rs3      (instr),
                         csr:       instr_csr      (instr),

                         funct3:    instr_funct3   (instr),
                         funct5:    instr_funct5   (instr),
                         funct7:    instr_funct7   (instr),
                         funct10:   instr_funct10  (instr),

                         imm12_I:   instr_I_imm12  (instr),
                         imm12_S:   instr_S_imm12  (instr),
                         imm13_SB:  instr_SB_imm13 (instr),
                         imm20_U:   instr_U_imm20  (instr),
                         imm21_UJ:  instr_UJ_imm21 (instr),

                         pred:      instr_pred     (instr),
                         succ:      instr_succ     (instr),

                         aqrl:      instr_aqrl     (instr)
                         };
endfunction

// Decodes if we need to read the GPR register file. This step becomes necessary
// on integrating the FPU as certain instruction now do not require the GPR
// anymore
//                IsFP, GPRRd
// function Tuple2# (Bool, Bool) fv_decode_gpr_read (Decoded_Instr di);
// `ifdef ISA_F
//    // FP_LD and FP_ST are treated as non-FP operation as far as GPR reads
//    // are concerned
//    if (di.opcode != op_FP) begin
//       return (tuple2 (False, True));   // Regular op with GPR read
//    end
//
//    // This is an FP operation. The following f5 values would work for F and
//    // D subsets
//    else begin
//       if (   (di.funct5 == f5_FCVT_F_X)
//           || (di.funct5 == f5_FMV_W_X))
//          return (tuple2 (True, True)); // FP op with GPR read
//       else
//          return (tuple2 (True, False));// FP op with no GPR read
//    end
// `else
//    return (tuple2 (False, True));      // Regular op with GPR read
// `endif
// endfunction

// ================================================================
// Instruction constructors
// Used in 'C' decode to construct equivalent 32-bit instructions

// R-type
function Instr  mkInstr_R_type (Bit #(7) funct7, RegName rs2, RegName rs1, Bit #(3) funct3, RegName rd, Bit #(7) opcode);
   let instr = { funct7, rs2, rs1, funct3, rd, opcode };
   return instr;
endfunction

// I-type
function Instr  mkInstr_I_type (Bit #(12) imm12, RegName rs1, Bit #(3) funct3, RegName rd, Bit #(7) opcode);
   let instr = { imm12, rs1, funct3, rd, opcode };
   return instr;
endfunction

// S-type

function Instr  mkInstr_S_type (Bit #(12) imm12, RegName rs2, RegName rs1, Bit #(3) funct3, Bit #(7) opcode);
   let instr = { imm12 [11:5], rs2, rs1, funct3, imm12 [4:0], opcode };
   return instr;
endfunction

// B-type
function Instr  mkInstr_B_type (Bit #(13) imm13, RegName rs2, RegName rs1, Bit #(3) funct3, Bit #(7) opcode);
   let instr = { imm13 [12], imm13 [10:5], rs2, rs1, funct3, imm13 [4:1], imm13 [11], opcode };
   return instr;
endfunction

// U-type
function Instr  mkInstr_U_type (Bit #(20) imm20, RegName rd, Bit #(7) opcode);
   let instr = { imm20, rd, opcode };
   return instr;
endfunction

// J-type
function Instr  mkInstr_J_type (Bit #(21) imm21, RegName rd, Bit #(7) opcode);
   let instr = { imm21 [20], imm21 [10:1], imm21 [11], imm21 [19:12], rd, opcode };
   return instr;
endfunction

// ================================================================
// Symbolic register names

RegName x0  =  0;    RegName x1  =  1;    RegName x2  =  2;    RegName x3  =  3;
RegName x4  =  4;    RegName x5  =  5;    RegName x6  =  6;    RegName x7  =  7;
RegName x8  =  8;    RegName x9  =  9;    RegName x10 = 10;    RegName x11 = 11;
RegName x12 = 12;    RegName x13 = 13;    RegName x14 = 14;    RegName x15 = 15;
RegName x16 = 16;    RegName x17 = 17;    RegName x18 = 18;    RegName x19 = 19;
RegName x20 = 20;    RegName x21 = 21;    RegName x22 = 22;    RegName x23 = 23;
RegName x24 = 24;    RegName x25 = 25;    RegName x26 = 26;    RegName x27 = 27;
RegName x28 = 28;    RegName x29 = 29;    RegName x30 = 30;    RegName x31 = 31;

// Register names used in calling convention

RegName reg_zero =  0;
RegName reg_ra   =  1;
RegName reg_sp   =  2;
RegName reg_gp   =  3;
RegName reg_tp   =  4;

RegName reg_t0   =  5; RegName reg_t1  =  6; RegName reg_t2 =  7;
RegName reg_fp   =  8;
RegName reg_s0   =  8; RegName reg_s1  =  9;

RegName reg_a0   = 10; RegName reg_a1  = 11;
RegName reg_v0   = 10; RegName reg_v1  = 11;

RegName reg_a2   = 12; RegName reg_a3  = 13; RegName reg_a4 = 14; RegName reg_a5 = 15;
RegName reg_a6   = 16; RegName reg_a7  = 17;

RegName reg_s2   = 18; RegName reg_s3  = 19; RegName reg_s4 = 20; RegName reg_s5 = 21;
RegName reg_s6   = 22; RegName reg_s7  = 23; RegName reg_s8 = 24; RegName reg_s9 = 25;
RegName reg_s10  = 26; RegName reg_s11 = 27;

RegName reg_t3   = 28; RegName reg_t4  = 29; RegName reg_t5 = 30; RegName reg_t6 = 31;

// ----------------
// Is 'r' a standard register for PC save/restore on call/return?
// This function is used in branch-predictors for managing the return-address stack.

function Bool fn_reg_is_link (RegName  r);
   return ((r == x1) || (r == x5));
endfunction

// ================================================================
// Kinds of memory access (excluding AMOs)

typedef enum { Access_RWX_R, Access_RWX_W, Access_RWX_X } Access_RWX
deriving (Eq, Bits, FShow);

// ================================================================
// Data sizes for LOAD/STORE

typedef enum {BITS8,
              BITS16,
              BITS32,
              BITS64    // Even in RV32, to allow for Double (floating point)
   } Mem_Data_Size
deriving (Eq, Bits, FShow);

// ================================================================
// LOAD/STORE instructions

typedef Bit #(2) MemReqSize;

MemReqSize f3_SIZE_B = 2'b00;
MemReqSize f3_SIZE_H = 2'b01;
MemReqSize f3_SIZE_W = 2'b10;
MemReqSize f3_SIZE_D = 2'b11;

// ----------------
// Load instructions

Opcode op_LOAD = 7'b00_000_11;

Bit #(3) f3_LB  = 3'b000;
Bit #(3) f3_LH  = 3'b001;
Bit #(3) f3_LW  = 3'b010;
Bit #(3) f3_LD  = 3'b011;
Bit #(3) f3_LBU = 3'b100;
Bit #(3) f3_LHU = 3'b101;
Bit #(3) f3_LWU = 3'b110;

// ----------------
// Store instructions

Opcode op_STORE = 7'b01_000_11;

Bit #(3) f3_SB  = 3'b000;
Bit #(3) f3_SH  = 3'b001;
Bit #(3) f3_SW  = 3'b010;
Bit #(3) f3_SD  = 3'b011;

// ================================================================
// Memory Model

Opcode op_MISC_MEM = 7'b00_011_11;

Bit #(3) f3_FENCE   = 3'b000;
Bit #(3) f3_FENCE_I = 3'b001;

typedef struct {
   // Predecessors
   Bool pi;    // IO reads
   Bool po;    // IO writes
   Bool pr;    // Mem reads
   Bool pw;    // Mem writes
   // Successors
   Bool si;
   Bool so;
   Bool sr;
   Bool sw;
   } Fence_Ordering
deriving (FShow);

instance Bits #(Fence_Ordering, 8);
   function Bit #(8) pack (Fence_Ordering fo);
      return {pack (fo.pi),
              pack (fo.po),
              pack (fo.pr),
              pack (fo.pw),
              pack (fo.si),
              pack (fo.so),
              pack (fo.sr),
              pack (fo.sw) };
   endfunction
   function Fence_Ordering unpack (Bit #(8) b8);
      return Fence_Ordering {pi: unpack (b8 [7]),
                             po: unpack (b8 [6]),
                             pr: unpack (b8 [5]),
                             pw: unpack (b8 [4]),
                             si: unpack (b8 [3]),
                             so: unpack (b8 [2]),
                             sr: unpack (b8 [1]),
                             sw: unpack (b8 [0]) };
   endfunction
endinstance

// ================================================================
// Atomic Memory Operation Instructions

Opcode op_AMO = 7'b01_011_11;

// NOTE: bit [4] for aq, and [3] for rl, are here set to zero

Bit #(3)    f3_AMO_W     = 3'b010;
Bit #(3)    f3_AMO_D     = 3'b011;

Bit #(5)    f5_AMO_LR     = 5'b00010;
Bit #(5)    f5_AMO_SC     = 5'b00011;
Bit #(5)    f5_AMO_ADD    = 5'b00000;
Bit #(5)    f5_AMO_SWAP   = 5'b00001;
Bit #(5)    f5_AMO_XOR    = 5'b00100;
Bit #(5)    f5_AMO_AND    = 5'b01100;
Bit #(5)    f5_AMO_OR     = 5'b01000;
Bit #(5)    f5_AMO_MIN    = 5'b10000;
Bit #(5)    f5_AMO_MAX    = 5'b10100;
Bit #(5)    f5_AMO_MINU   = 5'b11000;
Bit #(5)    f5_AMO_MAXU   = 5'b11100;

Bit #(10) f10_LR_W       = 10'b00010_00_010;
Bit #(10) f10_SC_W       = 10'b00011_00_010;
Bit #(10) f10_AMOADD_W   = 10'b00000_00_010;
Bit #(10) f10_AMOSWAP_W  = 10'b00001_00_010;
Bit #(10) f10_AMOXOR_W   = 10'b00100_00_010;
Bit #(10) f10_AMOAND_W   = 10'b01100_00_010;
Bit #(10) f10_AMOOR_W    = 10'b01000_00_010;
Bit #(10) f10_AMOMIN_W   = 10'b10000_00_010;
Bit #(10) f10_AMOMAX_W   = 10'b10100_00_010;
Bit #(10) f10_AMOMINU_W  = 10'b11000_00_010;
Bit #(10) f10_AMOMAXU_W  = 10'b11100_00_010;

Bit #(10) f10_LR_D       = 10'b00010_00_011;
Bit #(10) f10_SC_D       = 10'b00011_00_011;
Bit #(10) f10_AMOADD_D   = 10'b00000_00_011;
Bit #(10) f10_AMOSWAP_D  = 10'b00001_00_011;
Bit #(10) f10_AMOXOR_D   = 10'b00100_00_011;
Bit #(10) f10_AMOAND_D   = 10'b01100_00_011;
Bit #(10) f10_AMOOR_D    = 10'b01000_00_011;
Bit #(10) f10_AMOMIN_D   = 10'b10000_00_011;
Bit #(10) f10_AMOMAX_D   = 10'b10100_00_011;
Bit #(10) f10_AMOMINU_D  = 10'b11000_00_011;
Bit #(10) f10_AMOMAXU_D  = 10'b11100_00_011;

// ================================================================
// Integer Register-Immediate Instructions

Opcode op_OP_IMM = 7'b00_100_11;

Bit #(3) f3_ADDI  = 3'b000;
Bit #(3) f3_SLLI  = 3'b001;
Bit #(3) f3_SLTI  = 3'b010;
Bit #(3) f3_SLTIU = 3'b011;
Bit #(3) f3_XORI  = 3'b100;
Bit #(3) f3_SRxI  = 3'b101; Bit #(3) f3_SRLI  = 3'b101; Bit #(3) f3_SRAI  = 3'b101;
Bit #(3) f3_ORI   = 3'b110;
Bit #(3) f3_ANDI  = 3'b111;

// ================================================================
// Integer Register-Immediate 32b Instructions for RV64

Opcode op_OP_IMM_32 = 7'b00_110_11;

Bit #(3) f3_ADDIW = 3'b000;
Bit #(3) f3_SLLIW = 3'b001;
Bit #(3) f3_SRxIW = 3'b101; Bit #(3) f3_SRLIW = 3'b101; Bit #(3) f3_SRAIW = 3'b101;

// OP_IMM.SLLI/SRLI/SRAI for RV32
Bit #(7)  msbs7_SLLI = 7'b_000_0000;
Bit #(7)  msbs7_SRLI = 7'b_000_0000;
Bit #(7)  msbs7_SRAI = 7'b_010_0000;

// OP_IMM.SLLI/SRLI/SRAI for RV64
Bit #(6)  msbs6_SLLI = 6'b_00_0000;
Bit #(6)  msbs6_SRLI = 6'b_00_0000;
Bit #(6)  msbs6_SRAI = 6'b_01_0000;

// ================================================================
// Integer Register-Register Instructions

Opcode op_OP = 7'b01_100_11;

Bit #(10) f10_ADD    = 10'b000_0000_000;
Bit #(10) f10_SUB    = 10'b010_0000_000;
Bit #(10) f10_SLL    = 10'b000_0000_001;
Bit #(10) f10_SLT    = 10'b000_0000_010;
Bit #(10) f10_SLTU   = 10'b000_0000_011;
Bit #(10) f10_XOR    = 10'b000_0000_100;
Bit #(10) f10_SRL    = 10'b000_0000_101;
Bit #(10) f10_SRA    = 10'b010_0000_101;
Bit #(10) f10_OR     = 10'b000_0000_110;
Bit #(10) f10_AND    = 10'b000_0000_111;

Bit #(7) funct7_ADD  = 7'b_000_0000;    Bit #(3) funct3_ADD = 3'b_000;
Bit #(7) funct7_SUB  = 7'b_010_0000;    Bit #(3) funct3_SUB = 3'b_000;
Bit #(7) funct7_XOR  = 7'b_000_0000;    Bit #(3) funct3_XOR = 3'b_100;
Bit #(7) funct7_OR   = 7'b_000_0000;    Bit #(3) funct3_OR  = 3'b_110;
Bit #(7) funct7_AND  = 7'b_000_0000;    Bit #(3) funct3_AND = 3'b_111;

// ----------------
// MUL/DIV/REM family

Bit #(7) f7_MUL_DIV_REM = 7'b000_0001;

function Bool f7_is_OP_MUL_DIV_REM (Bit #(7) f7);
   return (f7 == f7_MUL_DIV_REM);
endfunction

Bit #(3) f3_MUL    = 3'b000;
Bit #(3) f3_MULH   = 3'b001;
Bit #(3) f3_MULHSU = 3'b010;
Bit #(3) f3_MULHU  = 3'b011;
Bit #(3) f3_DIV    = 3'b100;
Bit #(3) f3_DIVU   = 3'b101;
Bit #(3) f3_REM    = 3'b110;
Bit #(3) f3_REMU   = 3'b111;

Bit #(10) f10_MUL    = 10'b000_0001_000;
Bit #(10) f10_MULH   = 10'b000_0001_001;
Bit #(10) f10_MULHSU = 10'b000_0001_010;
Bit #(10) f10_MULHU  = 10'b000_0001_011;
Bit #(10) f10_DIV    = 10'b000_0001_100;
Bit #(10) f10_DIVU   = 10'b000_0001_101;
Bit #(10) f10_REM    = 10'b000_0001_110;
Bit #(10) f10_REMU   = 10'b000_0001_111;

// ================================================================
// Integer Register-Register 32b Instructions for RV64

Opcode op_OP_32 = 7'b01_110_11;

Bit #(10) f10_ADDW   = 10'b000_0000_000;
Bit #(10) f10_SUBW   = 10'b010_0000_000;
Bit #(10) f10_SLLW   = 10'b000_0000_001;
Bit #(10) f10_SRLW   = 10'b000_0000_101;
Bit #(10) f10_SRAW   = 10'b010_0000_101;

Bit #(7) funct7_ADDW = 7'b_000_0000;    Bit #(3) funct3_ADDW  = 3'b_000;
Bit #(7) funct7_SUBW = 7'b_010_0000;    Bit #(3) funct3_SUBW  = 3'b_000;

Bit #(10) f10_MULW   = 10'b000_0001_000;
Bit #(10) f10_DIVW   = 10'b000_0001_100;
Bit #(10) f10_DIVUW  = 10'b000_0001_101;
Bit #(10) f10_REMW   = 10'b000_0001_110;
Bit #(10) f10_REMUW  = 10'b000_0001_111;

function Bool is_OP_32_MUL_DIV_REM (Bit #(10) f10);
   return (   (f10 == f10_MULW)
           || (f10 == f10_DIVW)
           || (f10 == f10_DIVUW)
           || (f10 == f10_REMW)
           || (f10 == f10_REMUW));
endfunction

// ================================================================
// LUI, AUIPC

Opcode op_LUI   = 7'b01_101_11;
Opcode op_AUIPC = 7'b00_101_11;

// ================================================================
// Control transfer

Opcode  op_BRANCH = 7'b11_000_11;

Bit #(3) f3_BEQ   = 3'b000;
Bit #(3) f3_BNE   = 3'b001;
Bit #(3) f3_BLT   = 3'b100;
Bit #(3) f3_BGE   = 3'b101;
Bit #(3) f3_BLTU  = 3'b110;
Bit #(3) f3_BGEU  = 3'b111;

Opcode op_JAL  = 7'b11_011_11;

Opcode op_JALR = 7'b11_001_11;
Bit #(3) funct3_JALR = 3'b000;

`ifdef ISA_F
// ================================================================
// Floating Point Instructions
// TODO: these are implementation choices; should not be in ISA_Decls
// Enumeration of floating point opcodes for decode within the FPU
typedef enum {
     FPAdd
   , FPSub
   , FPMul
   , FPDiv
   , FPSqrt
   , FPMAdd
   , FPMSub
   , FPNMAdd
   , FPNMSub } FpuOp deriving (Bits, Eq, FShow);

// Enumeration of rounding modes
typedef enum {
     Rnd_Nearest_Even
   , Rnd_Zero
   , Rnd_Minus_Inf
   , Rnd_Plus_Inf
   , Rnd_Nearest_Max_Mag
} RoundMode deriving (Bits, Eq, FShow);

// Funct2 encoding
Bit #(2) f2_S           = 2'b00;
Bit #(2) f2_D           = 2'b01;
Bit #(2) f2_Q           = 2'b11;

// Floating point Load-Store
Opcode   op_LOAD_FP     = 7'b_00_001_11;
Opcode   op_STORE_FP    = 7'b_01_001_11;
Bit #(3) f3_FSW         = 3'b010;
Bit #(3) f3_FSD         = 3'b011;
Bit #(3) f3_FLW         = 3'b010;
Bit #(3) f3_FLD         = 3'b011;

// Fused FP Multiply Add/Sub instructions
Opcode   op_FMADD       = 7'b10_00_011;
Opcode   op_FMSUB       = 7'b10_00_111;
Opcode   op_FNMSUB      = 7'b10_01_011;
Opcode   op_FNMADD      = 7'b10_01_111;

// All other FP intructions
Opcode   op_FP          = 7'b10_10_011;

Bit #(7) f7_FADD_D      = 7'h1 ;
Bit #(7) f7_FSUB_D      = 7'h5 ;
Bit #(7) f7_FMUL_D      = 7'h9 ;
Bit #(7) f7_FDIV_D      = 7'hD ;
Bit #(7) f7_FSQRT_D     = 7'h2D;
Bit #(7) f7_FCMP_D      = 7'h51;
Bit #(7) f7_FMIN_D      = 7'h15;
Bit #(7) f7_FMAX_D      = 7'h15;
Bit #(7) f7_FSGNJ_D     = 7'h11;

Bit #(7) f7_FADD_S      = 7'h0 ;
Bit #(7) f7_FSUB_S      = 7'h4 ;
Bit #(7) f7_FMUL_S      = 7'h8 ;
Bit #(7) f7_FDIV_S      = 7'hC ;
Bit #(7) f7_FSQRT_S     = 7'h2C;
Bit #(7) f7_FCMP_S      = 7'h50;
Bit #(7) f7_FMIN_S      = 7'h14;
Bit #(7) f7_FMAX_S      = 7'h14;
Bit #(7) f7_FSGNJ_S     = 7'h10;

Bit #(7) f7_FCVT_W_S    = 7'h60;
Bit #(7) f7_FCVT_WU_S   = 7'h60;
Bit #(7) f7_FCVT_S_W    = 7'h68;
Bit #(7) f7_FCVT_S_WU   = 7'h68;

Bit #(7) f7_FCVT_L_S    = 7'h60;
Bit #(7) f7_FCVT_LU_S   = 7'h60;
Bit #(7) f7_FCVT_S_L    = 7'h68;
Bit #(7) f7_FCVT_S_LU   = 7'h68;

Bit #(7) f7_FCVT_S_D    = 7'h20;
Bit #(7) f7_FCVT_D_S    = 7'h21;
Bit #(7) f7_FCVT_W_D    = 7'h61;
Bit #(7) f7_FCVT_WU_D   = 7'h61;
Bit #(7) f7_FCVT_D_W    = 7'h69;
Bit #(7) f7_FCVT_D_WU   = 7'h69;

Bit #(7) f7_FCVT_L_D    = 7'h61;
Bit #(7) f7_FCVT_LU_D   = 7'h61;
Bit #(7) f7_FCVT_D_L    = 7'h69;
Bit #(7) f7_FCVT_D_LU   = 7'h69;

Bit #(7) f7_FMV_X_D     = 7'h71;
Bit #(7) f7_FMV_D_X     = 7'h79;
Bit #(7) f7_FCLASS_D    = 7'h71;
Bit #(7) f7_FMV_X_W     = 7'h70;
Bit #(7) f7_FMV_W_X     = 7'h78;
Bit #(7) f7_FCLASS_S    = 7'h70;

// fv_is_rd_in_GPR: Checks if the request generates a result which
// should be written into the GPR
function Bool fv_is_rd_in_GPR (Bit #(7) funct7, RegName rs2);

`ifdef ISA_D
    let is_FCVT_W_D  =    (funct7 == f7_FCVT_W_D)
                       && (rs2 == 0);
    let is_FCVT_WU_D =    (funct7 == f7_FCVT_WU_D)
                       && (rs2 == 1);
`ifdef RV64
    let is_FCVT_L_D  =    (funct7 == f7_FCVT_L_D)
                       && (rs2 == 2);
    let is_FCVT_LU_D =    (funct7 == f7_FCVT_LU_D)
                       && (rs2 == 3);

`endif
   // FCLASS.D also maps to this -- both write to GPR
   let is_FMV_X_D    =    (funct7 == f7_FMV_X_D);
   // FEQ.D, FLE.D, FLT.D map to this
   let is_FCMP_D     =    (funct7 == f7_FCMP_D);
`endif

    let is_FCVT_W_S  =    (funct7 == f7_FCVT_W_S)
                       && (rs2 == 0);
    let is_FCVT_WU_S =    (funct7 == f7_FCVT_WU_S)
                       && (rs2 == 1);
`ifdef RV64
    let is_FCVT_L_S  =    (funct7 == f7_FCVT_L_S)
                       && (rs2 == 2);
    let is_FCVT_LU_S =    (funct7 == f7_FCVT_LU_S)
                       && (rs2 == 3);
`endif

   // FCLASS.S also maps to this -- both write to GPR
   let is_FMV_X_W    =    (funct7 == f7_FMV_X_W);

   // FEQ.S, FLE.S, FLT.S map to this
   let is_FCMP_S     =    (funct7 == f7_FCMP_S);

    return (
          False
`ifdef ISA_D
       || is_FCVT_W_D
       || is_FCVT_WU_D
`ifdef RV64
       || is_FCVT_L_D
       || is_FCVT_LU_D
`endif
       || is_FMV_X_D
       || is_FCMP_D
`endif
`ifdef RV64
       || is_FCVT_L_S
       || is_FCVT_LU_S
`endif
       || is_FCVT_W_S
       || is_FCVT_WU_S
       || is_FMV_X_W
       || is_FCMP_S
    );
endfunction

// Check if a rounding mode value in the FCSR.FRM is valid
function Bool fv_fcsr_frm_valid (Bit #(3) frm);
   return (   (frm != 3'b101)
           && (frm != 3'b110)
           && (frm != 3'b111)
          );
endfunction

// Check if a rounding mode value in the instr is valid
function Bool fv_inst_frm_valid (Bit #(3) frm);
   return (   (frm != 3'b101)
           && (frm != 3'b110)
          );
endfunction

// fv_rounding_mode_check
// Returns the correct rounding mode considering the values in the
// FCSR and the instruction and checks legality
function Tuple2# (Bit #(3), Bool) fv_rmode_check (
   Bit #(3) inst_frm, Bit #(3) fcsr_frm);
   let rm = (inst_frm == 3'h7) ? fcsr_frm : inst_frm;
   let rm_is_legal  = (inst_frm == 3'h7) ? fv_fcsr_frm_valid (fcsr_frm)
                                         : fv_inst_frm_valid (inst_frm);
   return (tuple2 (rm, rm_is_legal));
endfunction

// A D instruction requires misa.f to be set as well as misa.d
function Bool fv_is_fp_instr_legal (
   Bit #(7) f7, Bit #(3) rm, RegName rs2, Opcode fopc);
   Bit #(2) f2 = f7[1:0];
   Bool is_legal = True;
   if (   (fopc == op_FMADD )
       || (fopc == op_FMSUB )
       || (fopc == op_FNMADD)
       || (fopc == op_FNMSUB))
`ifdef ISA_D
      return ((f2 == f2_S) || (f2 == f2_D)); // Both SP and DP are legal
`else
      return (f2 == f2_S);                   // Only SP is legal
`endif
   else
      if (    (f7 == f7_FADD_S)
          ||  (f7 == f7_FSUB_S)
          ||  (f7 == f7_FMUL_S)
`ifdef ISA_FD_DIV
          ||  (f7 == f7_FDIV_S)
          ||  (f7 == f7_FSQRT_S)
`endif
          || ((f7 == f7_FSGNJ_S)  && ( rm == 0))
          || ((f7 == f7_FSGNJ_S)  && ( rm == 1))
          || ((f7 == f7_FSGNJ_S)  && ( rm == 2))
          || ((f7 == f7_FCVT_W_S) && (rs2 == 0))
          || ((f7 == f7_FCVT_WU_S)&& (rs2 == 1))
`ifdef RV64
          || ((f7 == f7_FCVT_L_S) && (rs2 == 2))
          || ((f7 == f7_FCVT_LU_S)&& (rs2 == 3))
`endif
          || ((f7 == f7_FCVT_S_W) && (rs2 == 0))
          || ((f7 == f7_FCVT_S_WU)&& (rs2 == 1))
`ifdef RV64
          || ((f7 == f7_FCVT_S_L) && (rs2 == 2))
          || ((f7 == f7_FCVT_S_LU)&& (rs2 == 3))
`endif
          || ((f7 == f7_FMIN_S)   && ( rm == 0))
          || ((f7 == f7_FMAX_S)   && ( rm == 1))
          || ((f7 == f7_FCMP_S)   && ( rm == 0))
          || ((f7 == f7_FCMP_S)   && ( rm == 1))
          || ((f7 == f7_FCMP_S)   && ( rm == 2))
          || ((f7 == f7_FMV_X_W)  && ( rm == 0))
          || ((f7 == f7_FMV_W_X)  && ( rm == 0))
          || ((f7 == f7_FCLASS_S) && ( rm == 1))
`ifdef ISA_D
          ||  (f7 == f7_FADD_D)
          ||  (f7 == f7_FSUB_D)
          ||  (f7 == f7_FMUL_D)
`ifdef ISA_FD_DIV
          ||  (f7 == f7_FDIV_D)
          ||  (f7 == f7_FSQRT_D)
`endif
          || ((f7 == f7_FSGNJ_D)  && ( rm == 0))
          || ((f7 == f7_FSGNJ_D)  && ( rm == 1))
          || ((f7 == f7_FSGNJ_D)  && ( rm == 2))
          || ((f7 == f7_FCVT_W_D) && (rs2 == 0))
          || ((f7 == f7_FCVT_WU_D)&& (rs2 == 1))
`ifdef RV64
          || ((f7 == f7_FCVT_L_D) && (rs2 == 2))
          || ((f7 == f7_FCVT_LU_D)&& (rs2 == 3))
`endif
          || ((f7 == f7_FCVT_D_W) && (rs2 == 0))
          || ((f7 == f7_FCVT_D_WU)&& (rs2 == 1))
`ifdef RV64
          || ((f7 == f7_FCVT_D_L) && (rs2 == 2))
          || ((f7 == f7_FCVT_D_LU)&& (rs2 == 3))
`endif
          || ((f7 == f7_FCVT_D_S) && (rs2 == 0))
          || ((f7 == f7_FCVT_S_D) && (rs2 == 1))
          || ((f7 == f7_FMIN_D)   && ( rm == 0))
          || ((f7 == f7_FMAX_D)   && ( rm == 1))
          || ((f7 == f7_FCMP_D)   && ( rm == 0))
          || ((f7 == f7_FCMP_D)   && ( rm == 1))
          || ((f7 == f7_FCMP_D)   && ( rm == 2))
          || ((f7 == f7_FMV_X_D)  && ( rm == 0))
          || ((f7 == f7_FMV_D_X)  && ( rm == 0))
          || ((f7 == f7_FCLASS_D) && ( rm == 1))
`endif
         ) return True;
      else return False;
endfunction

// Returns True if the first operand (val1) should be taken from the GPR
// instead of the FPR for a FP opcode
function Bool fv_fp_val1_from_gpr (Opcode opcode, Bit#(7) f7, RegName rs2);
   return (
         (opcode == op_FP)
      && (   False
`ifdef ISA_D
          || ((f7 == f7_FCVT_D_W)  && (rs2 == 0))
          || ((f7 == f7_FCVT_D_WU) && (rs2 == 1))
`ifdef RV64
          || ((f7 == f7_FCVT_D_L)  && (rs2 == 2))
          || ((f7 == f7_FCVT_D_LU) && (rs2 == 3))
`endif
          || ((f7 == f7_FMV_D_X))
`endif
          || ((f7 == f7_FCVT_S_W)  && (rs2 == 0))
          || ((f7 == f7_FCVT_S_WU) && (rs2 == 1))
`ifdef RV64
          || ((f7 == f7_FCVT_S_L)  && (rs2 == 2))
          || ((f7 == f7_FCVT_S_LU) && (rs2 == 3))
`endif
          || ((f7 == f7_FMV_W_X))
          )
   );
endfunction
`endif

// ================================================================
// System Instructions
Opcode op_SYSTEM = 7'b11_100_11;

// sub-opcodes: (in funct3 field)
Bit #(3)   f3_PRIV           = 3'b000;
Bit #(3)   f3_CSRRW          = 3'b001;
Bit #(3)   f3_CSRRS          = 3'b010;
Bit #(3)   f3_CSRRC          = 3'b011;
Bit #(3)   f3_SYSTEM_ILLEGAL = 3'b100;
Bit #(3)   f3_CSRRWI         = 3'b101;
Bit #(3)   f3_CSRRSI         = 3'b110;
Bit #(3)   f3_CSRRCI         = 3'b111;

// sub-sub-opcodes for f3_PRIV

Bit #(12) f12_ECALL     = 12'b_0000_0000_0000;
Bit #(12) f12_EBREAK    = 12'b_0000_0000_0001;

Bit #(12) f12_URET      = 12'b_0000_0000_0010;
Bit #(12) f12_SRET      = 12'b_0001_0000_0010;
Bit #(12) f12_HRET      = 12'b_0010_0000_0010;
Bit #(12) f12_MRET      = 12'b_0011_0000_0010;
Bit #(12) f12_WFI       = 12'b_0001_0000_0101;

// v1.10 sub-sub-opcode for SFENCE_VMA
Bit #(7)  f7_SFENCE_VMA = 7'b_0001_001;

Instr break_instr = { f12_EBREAK, 5'b00000, 3'b000, 5'b00000, op_SYSTEM };

function Bool fn_instr_is_csrrx (Instr  instr);
   let decoded_instr = fv_decode (instr);
   let opcode        = decoded_instr.opcode;
   let funct3        = decoded_instr.funct3;
   let csr           = decoded_instr.csr;
   return ((opcode == op_SYSTEM) && f3_is_CSRR_any (funct3));
endfunction

function Bool f3_is_CSRR_any (Bit #(3) f3);
   return (f3_is_CSRR_W (f3) || f3_is_CSRR_S_or_C (f3));
endfunction

function Bool f3_is_CSRR_W (Bit #(3) f3);
   return ((f3 == f3_CSRRW) || (f3 == f3_CSRRWI));
endfunction

function Bool f3_is_CSRR_S_or_C (Bit #(3) f3);
   return ((f3 == f3_CSRRS) || (f3 == f3_CSRRSI) ||
           (f3 == f3_CSRRC) || (f3 == f3_CSRRCI));
endfunction

// ================================================================
// Privilege Modes

typedef 4 Num_Priv_Modes;

typedef Bit #(2) Priv_Mode;

Priv_Mode         u_Priv_Mode = 2'b00;
Priv_Mode         s_Priv_Mode = 2'b01;
Priv_Mode  reserved_Priv_Mode = 2'b10;
Priv_Mode         m_Priv_Mode = 2'b11;

function Fmt fshow_Priv_Mode (Priv_Mode pm);
   return case (pm)
             u_Priv_Mode: $format ("U");
             s_Priv_Mode: $format ("S");
             m_Priv_Mode: $format ("M");
             default: $format ("RESERVED");
          endcase;
endfunction

// ================================================================
// Control/Status registers

typedef Bit #(12) CSR_Addr;

function Bool fn_csr_addr_can_write (CSR_Addr csr_addr);
   return (csr_addr [11:10] != 2'b11);
endfunction

function Bool fn_csr_addr_priv_ok (CSR_Addr csr_addr, Priv_Mode priv_mode);
   return (priv_mode >= csr_addr [9:8]);
endfunction

// ----------------
// User-level CSR addresses

CSR_Addr   csr_addr_ustatus        = 12'h000;    // User status
CSR_Addr   csr_addr_uie            = 12'h004;    // User interrupt-enable
CSR_Addr   csr_addr_utvec          = 12'h005;    // User trap handler base address

CSR_Addr   csr_addr_uscratch       = 12'h040;    // Scratch register for trap handlers
CSR_Addr   csr_addr_uepc           = 12'h041;    // User exception program counter
CSR_Addr   csr_addr_ucause         = 12'h042;    // User trap cause
CSR_Addr   csr_addr_utval          = 12'h043;    // User bad address or instruction
CSR_Addr   csr_addr_uip            = 12'h044;    // User interrupt pending

CSR_Addr   csr_addr_fflags         = 12'h001;    // Floating-point accrued exceptions
CSR_Addr   csr_addr_frm            = 12'h002;    // Floating-point Dynamic Rounding Mode
CSR_Addr   csr_addr_fcsr           = 12'h003;    // Floating-point Control and Status Register (frm + fflags)

CSR_Addr   csr_addr_cycle          = 12'hC00;    // Cycle counter for RDCYCLE
CSR_Addr   csr_addr_time           = 12'hC01;    // Timer for RDTIME
CSR_Addr   csr_addr_instret        = 12'hC02;    // Instructions retired counter for RDINSTRET

CSR_Addr   csr_addr_hpmcounter3    = 12'hC03;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter4    = 12'hC04;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter5    = 12'hC05;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter6    = 12'hC06;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter7    = 12'hC07;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter8    = 12'hC08;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter9    = 12'hC09;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter10   = 12'hC0A;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter11   = 12'hC0B;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter12   = 12'hC0C;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter13   = 12'hC0D;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter14   = 12'hC0E;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter15   = 12'hC0F;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter16   = 12'hC10;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter17   = 12'hC11;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter18   = 12'hC12;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter19   = 12'hC13;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter20   = 12'hC14;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter21   = 12'hC15;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter22   = 12'hC16;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter23   = 12'hC17;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter24   = 12'hC18;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter25   = 12'hC19;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter26   = 12'hC1A;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter27   = 12'hC1B;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter28   = 12'hC1C;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter29   = 12'hC1D;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter30   = 12'hC1E;    // Performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter31   = 12'hC1F;    // Performance-monitoring counter

CSR_Addr   csr_addr_cycleh         = 12'hC80;    // Upper 32 bits of csr_cycle (RV32I only)
CSR_Addr   csr_addr_timeh          = 12'hC81;    // Upper 32 bits of csr_time (RV32I only)
CSR_Addr   csr_addr_instreth       = 12'hC82;    // Upper 32 bits of csr_instret (RV32I only)

CSR_Addr   csr_addr_hpmcounter3h   = 12'hC83;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter4h   = 12'hC84;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter5h   = 12'hC85;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter6h   = 12'hC86;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter7h   = 12'hC87;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter8h   = 12'hC88;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter9h   = 12'hC89;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter10h  = 12'hC8A;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter11h  = 12'hC8B;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter12h  = 12'hC8C;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter13h  = 12'hC8D;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter14h  = 12'hC8E;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter15h  = 12'hC8F;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter16h  = 12'hC90;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter17h  = 12'hC91;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter18h  = 12'hC92;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter19h  = 12'hC93;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter20h  = 12'hC94;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter21h  = 12'hC95;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter22h  = 12'hC96;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter23h  = 12'hC97;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter24h  = 12'hC98;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter25h  = 12'hC99;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter26h  = 12'hC9A;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter27h  = 12'hC9B;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter28h  = 12'hC9C;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter29h  = 12'hC9D;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter30h  = 12'hC9E;    // Upper 32 bits of performance-monitoring counter
CSR_Addr   csr_addr_hpmcounter31h  = 12'hC9F;    // Upper 32 bits of performance-monitoring counter

// Information from the CSR on a new trap.
typedef struct {
   Addr        pc;
   WordXL      mstatus;
   WordXL      mcause;
   Priv_Mode   priv;
} Trap_Info deriving (Bits, Eq, FShow);

// ================================================================
// 'C' Extension ("compressed" instructions)

`include "ISA_Decls_C.bsv"

// ================================================================
// Supervisor-Level ISA defs

`include "ISA_Decls_Priv_S.bsv"

// ================================================================
// Hypervisor-Level ISA defs

// `include "ISA_Decls_Priv_H.bsv"

// ================================================================
// Machine-Level ISA defs

`include "ISA_Decls_Priv_M.bsv"

// ================================================================

endpackage
