-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- Copyright 2025 VLSI Solution Oy
-- SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
--
-- Licensed under the Solderpad Hardware License v 2.1 (the "License");
-- you may not use this file except in compliance with the License, or,
-- at your option, the Apache License version 2.0.
-- You may obtain a copy of the License at
--
--    https://solderpad.org/licenses/SHL-2.1/
--
-- Unless required by applicable law or agreed to in writing, any work
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--                               RISCV core
--                                 V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- - - - - - - -
--  libraries
-- - - - - - - -
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

package rv_pkg is


  subtype sl_t      is std_logic;
  subtype slv_t     is std_logic_vector;

  subtype slv_127_00 is std_logic_vector(127 downto  0);

  subtype slv_68_00  is std_logic_vector(68 downto  0);
  subtype slv_65_00  is std_logic_vector(65 downto  0);
  subtype slv_63_00  is std_logic_vector(63 downto  0);
  subtype slv_62_00  is std_logic_vector(62 downto  0);
  subtype slv_45_00  is std_logic_vector(45 downto  0);
  subtype slv_38_00  is std_logic_vector(38 downto  0);
  subtype slv_36_00  is std_logic_vector(36 downto  0);
  subtype slv_32_00  is std_logic_vector(32 downto  0);
  subtype slv_31_28  is std_logic_vector(31 downto 28);
  subtype slv_31_16  is std_logic_vector(31 downto 16);
  subtype slv_31_12  is std_logic_vector(31 downto 12);
  subtype slv_31_02  is std_logic_vector(31 downto  2);
  subtype slv_31_00  is std_logic_vector(31 downto  0);
  subtype slv_30_22  is std_logic_vector(30 downto 22);
  subtype slv_29_00  is std_logic_vector(29 downto  0);
  subtype slv_27_24  is std_logic_vector(27 downto 24);
  subtype slv_24_00  is std_logic_vector(24 downto  0);
  subtype slv_23_00  is std_logic_vector(23 downto  0);
  subtype slv_21_00  is std_logic_vector(21 downto  0);
  subtype slv_20_00  is std_logic_vector(20 downto  0);
  subtype slv_19_00  is std_logic_vector(19 downto  0);
  subtype slv_16_00  is std_logic_vector(16 downto  0);
  subtype slv_15_00  is std_logic_vector(15 downto  0);
  subtype slv_14_00  is std_logic_vector(14 downto  0);
  subtype slv_13_00  is std_logic_vector(13 downto  0);
  subtype slv_12_11  is std_logic_vector(12 downto 11);
  subtype slv_12_00  is std_logic_vector(12 downto  0);
  subtype slv_11_00  is std_logic_vector(11 downto  0);
  subtype slv_11_07  is std_logic_vector(11 downto  7);
  subtype slv_10_00  is std_logic_vector(10 downto  0);
  subtype slv_09_08  is std_logic_vector(09 downto  8);
  subtype slv_09_00  is std_logic_vector(09 downto  0);
  subtype slv_08_00  is std_logic_vector(08 downto  0);
  subtype slv_07_00  is std_logic_vector( 7 downto  0);
  subtype slv_05_00  is std_logic_vector( 5 downto  0);
  subtype slv_04_00  is std_logic_vector( 4 downto  0);
  subtype slv_03_00  is std_logic_vector( 3 downto  0);
  subtype slv_02_00  is std_logic_vector( 2 downto  0);
  subtype slv_01_00  is std_logic_vector( 1 downto  0);
  subtype slv_00_00  is std_logic_vector( 0 downto  0);


  subtype isa_t  is  slv_t(31 downto 0);

  subtype FUNC5_R   is  slv_t(31 downto 27);   -- func5                4:0
  subtype RS3_R     is  slv_t(31 downto 27);   -- rf source 3 addr     4:0
  subtype FUNC7_R   is  slv_t(31 downto 25);   -- func7                6:0
  subtype FUNC12_R  is  slv_t(31 downto 20);   -- func12              11:0
  subtype IMM12_R   is  slv_t(31 downto 20);   -- immediate12         11:0
  subtype IMM20_R   is  slv_t(31 downto 12);   -- immediate20         21:0
  subtype RS2_R     is  slv_t(24 downto 20);   -- rf source 2 addr     4:0
  subtype RS1_R     is  slv_t(19 downto 15);   -- rf source 1 addr     4:0
  subtype FUNC3_R   is  slv_t(14 downto 12);   -- func3 bits           2:0
  subtype RD_R      is  slv_t(11 downto  7);   -- rf destination addr  4:0
  subtype OPCODE_R  is  slv_t( 6 downto  0);   -- opdoce bit           6:0


  type rfile_t is array (0 to 31) of slv_31_00;


  constant HW_VERSION           : slv_31_00 := X"76727601"; -- hardware version
  constant VRV_ARCHID           : slv_31_00 := X"00000000";


  -- - - - - - - - - - - - - - - - - - - -
  --  ALU Operations
  -- - - - - - - - - - - - - - - - - - - -

  -- Using bits 14:12 to alu func decode
  constant ALU_ADD              : slv_03_00 := "0000";
  constant ALU_SUB              : slv_03_00 := "1000";  -- !
  constant ALU_SHIFTL           : slv_03_00 := "0001";
  constant ALU_LESS_THAN_SIGNED : slv_03_00 := "0010";
  constant ALU_LESS_THAN        : slv_03_00 := "0011";
  constant ALU_XOR              : slv_03_00 := "0100";
  constant ALU_SHIFTR           : slv_03_00 := "0101";
  constant ALU_SHIFTR_ARITH     : slv_03_00 := "1101";  -- !
  constant ALU_OR               : slv_03_00 := "0110";
  constant ALU_AND              : slv_03_00 := "0111";
  constant ALU_NONE             : slv_03_00 := "1111";
  -- 1001 NA
  -- 1010 NA
  -- 1011 NA
  -- 1100 NA
  -- 1110 NA

  type alu_i_zb_t is ( aNULL, aAND, aOR, aXOR, aSLL, aSLR, aSAR );   --  0 -  6



  type mul_op_t is              -- multiplier ops
  record
    i_mul       : std_logic;
    i_mulh      : std_logic;
    i_mulhsu    : std_logic;
    i_mulhu     : std_logic;
  end record;

  type div_op_t is              -- divider ops
  record
    i_remu      : std_logic;
    i_rem       : std_logic;
    i_divu      : std_logic;
    i_div       : std_logic;
  end record;

  type alu_op_t is
  record
    -- ALU op is valid
    -- i_valid                                            : std_logic;
    alu_func4                                             : slv_03_00;
    -- alu ops
    i_add, i_sub,  i_slt,  i_sltu,  i_or,  i_xor,  i_and  : std_logic;
    -- alu shift ops
    i_sll, i_srl, i_sra                                   : std_logic;
    -- ALU op with 2 operands
    i_2operand                                            : std_logic;
    -- alu immediate ops
    i_immediate                                           : std_logic;
    -- i_addi, i_slti, i_sltiu, i_ori, i_xori, i_andi     : std_logic;
    -- i_slli, i_srli, i_srai                             : std_logic;
    -- alu load upper immediate, add upper immediate to pc
    i_lui, i_auipc                                        : std_logic;
    -- alu branch not taken / jump taken
    i_jal, i_jalr                                         : std_logic;
    --
    -- Zba - address generation
    i_sh1add, i_sh2add, i_sh3add                          : std_logic;
    --
    zb_valid                                              : std_logic;
    zb_func5                                              : slv_04_00;
  end record;

  type csr_op_t is              -- csr ops (control and status register)
  record
    i_ecall     : std_logic;
    i_ebreak    : std_logic;
    i_eret      : std_logic;
    i_csrrw     : std_logic;
    i_csrrs     : std_logic;
    i_csrrc     : std_logic;
    i_csrrwi    : std_logic;
    i_csrrsi    : std_logic;
    i_csrrci    : std_logic;
    i_wfi       : std_logic;
    i_fence     : std_logic;
    i_sfence    : std_logic;
    i_ifence    : std_logic;
  end record;

  type lsu_op_t is              -- lsu ops (load store unit)
  record
    i_lb        : std_logic;
    i_lh        : std_logic;
    i_lw        : std_logic;
    i_lbu       : std_logic;    -- load byte unsiged
    i_lhu       : std_logic;    -- load half word unsiged
    i_lwu       : std_logic;    -- load word unsiged
    i_sb        : std_logic;    -- store byte
    i_sh        : std_logic;    -- store half word
    i_sw        : std_logic;    -- store word
    i_csrrw     : std_logic;
    i_atomic    : std_logic;    -- lr/sc
    i_amo       : std_logic;    -- atomic memory op
  end record;

  type branch_op_t is           -- branch ops
  record
    i_jal       : std_logic;    -- jump and link
    i_jalr      : std_logic;    -- indirect jump -> jump and link register
    i_beq       : std_logic;
    i_bne       : std_logic;
    i_blt       : std_logic;
    i_bge       : std_logic;
    i_bltu      : std_logic;
    i_bgeu      : std_logic;
  end record;


  type amo_op_t is              -- amo ops (atomic memory operation)
  record
    i_lr    : std_logic;        -- lr (load locked)
    i_sc    : std_logic;        -- sc (store conditional)
    -- pragma translate_off;
    lr_sc   : std_logic;
    i_swap  : std_logic;
    i_add   : std_logic;
    i_xor   : std_logic;
    i_and   : std_logic;
    i_or    : std_logic;
    i_min   : std_logic;
    i_max   : std_logic;
    i_minu  : std_logic;
    i_maxu  : std_logic;
    -- pragma translate_on;
    amofunc : slv_03_00;
  end record;


  type i_group_t is             -- type for instr groups (csr, mul, div, ...)
  record
    invalid     : std_logic;
    lsu         : std_logic;
    mul         : std_logic;
    div         : std_logic;
    csr         : std_logic;
    csr_reg     : std_logic;
    branch      : std_logic;
    amo         : std_logic;
    rd_valid    : std_logic;
  end record;


  -- dmem read response type
  type dmem_ldtyp_t is
  record
--  ack         : std_logic;
--  err         : std_logic;
--  exp         : std_logic;
--  din         : slv_31_00;
    ualigned_e2 : std_logic;
    exp_addr    : slv_31_00;
    ld          : std_logic;    -- load (w / h / b)
    ldb         : std_logic;
    ldh         : std_logic;
    sint        : std_logic;    -- signed load
  end record;


  -- - - - - - - - - - - - - - - - - - - -
  --  Instructions Masks
  -- - - - - - - - - - - - - - - - - - - -

  -- rv_i ALU instructions
  -- addi    rd rs1 imm12           14..12=0 6..2=0x04 1..0=3
  -- slti    rd rs1 imm12           14..12=2 6..2=0x04 1..0=3
  -- sltiu   rd rs1 imm12           14..12=3 6..2=0x04 1..0=3
  -- xori    rd rs1 imm12           14..12=4 6..2=0x04 1..0=3
  -- ori     rd rs1 imm12           14..12=6 6..2=0x04 1..0=3
  -- andi    rd rs1 imm12           14..12=7 6..2=0x04 1..0=3
  -- add     rd rs1 rs2 31..25=0  14..12=0 6..2=0x0C 1..0=3
  -- sub     rd rs1 rs2 31..25=32 14..12=0 6..2=0x0C 1..0=3
  -- sll     rd rs1 rs2 31..25=0  14..12=1 6..2=0x0C 1..0=3
  -- slt     rd rs1 rs2 31..25=0  14..12=2 6..2=0x0C 1..0=3
  -- sltu    rd rs1 rs2 31..25=0  14..12=3 6..2=0x0C 1..0=3
  -- xor     rd rs1 rs2 31..25=0  14..12=4 6..2=0x0C 1..0=3
  -- srl     rd rs1 rs2 31..25=0  14..12=5 6..2=0x0C 1..0=3
  -- sra     rd rs1 rs2 31..25=32 14..12=5 6..2=0x0C 1..0=3
  -- or      rd rs1 rs2 31..25=0  14..12=6 6..2=0x0C 1..0=3
  -- and     rd rs1 rs2 31..25=0  14..12=7 6..2=0x0C 1..0=3

  -- Zba extension instructions
  -- sh1add     rd rs1 rs2 31..25=16 14..12=2 6..2=0x0C 1..0=3
  -- sh2add     rd rs1 rs2 31..25=16 14..12=4 6..2=0x0C 1..0=3
  -- sh3add     rd rs1 rs2 31..25=16 14..12=6 6..2=0x0C 1..0=3

  constant INST_NOP          : slv_31_00 := X"00000013";   -- == ADDI

  --  andi (AND immediate)
  constant INST_ANDI         : slv_31_00 := X"00007013";   --  mask X"0000707f"
  constant ANDIo             : isa_t := "-----------------111-----0010011";

  --  addi (ADD immediate)
  constant INST_ADDI         : slv_31_00 := X"00000013";   --  mask X"0000707f"
  constant ADDIo             : isa_t := "-----------------000-----0010011";

  --  slti (LESS_THAN_SIGNED immediate)
  constant INST_SLTI         : slv_31_00 := X"00002013";   --  mask X"0000707f"
  constant SLTIo             : isa_t := "-----------------010-----0010011";

  --  sltiu (LESS_THAN_UNSIGNED immediate)
  constant INST_SLTIU        : slv_31_00 := X"00003013";   --  mask X"0000707f"
  constant SLTIUo            : isa_t := "-----------------011-----0010011";

  --  ori ( immediate)
  constant INST_ORI          : slv_31_00 := X"00006013";   --  mask X"0000707f";
  constant ORIo              : isa_t := "-----------------110-----0010011";

  --  xori ( immediate)
  constant INST_XORI         : slv_31_00 := X"00004013";   --  mask X"0000707f"
  constant XORIo             : isa_t := "-----------------100-----0010011";

  --  slli ( immediate)
  constant INST_SLLI         : slv_31_00 := X"00001013";   --  mask X"fe00707f"
  constant SLLIo             : isa_t := "0000000----------001-----0010011";

  --  srli ( immediate)
  constant INST_SRLI         : slv_31_00 := X"00005013";   --  mask X"fe00707f"
  constant SRLIo             : isa_t := "0000000----------101-----0010011";

  --  srai ( immediate)
  constant INST_SRAI         : slv_31_00 := X"40005013";   --  mask X"fe00707f"
  constant SRAIo             : isa_t := "0100000----------101-----0010011";



  --  lui  (load upper immediate)
  constant INST_LUI          : slv_31_00 := X"00000037";   --  mask X"0000007f"
  constant LUIo              : isa_t := "-------------------------0110111";

  --  auipc  (add 20-bit upper immediate to PC)
  constant INST_AUIPC        : slv_31_00 := X"00000017";   --  mask X"0000007F"
  constant AUIPCo            : isa_t := "-------------------------0010111";



  --  add
  constant INST_ADD          : slv_31_00 := X"00000033";   --  mask X"FE00707F"
  constant ADDo              : isa_t := "0000000----------000-----0110011";

  --  sub
  constant INST_SUB          : slv_31_00 := X"40000033";
  constant INST_SUB_MASK     : slv_31_00 := X"FE00707F";
  constant SUBo              : isa_t := "0100000----------000-----0110011";

  --  slt (LESS_THAN_SIGNED)
  constant INST_SLT          : slv_31_00 := X"00002033";
  constant INST_SLT_MASK     : slv_31_00 := X"FE00707F";
  constant SLTo              : isa_t := "0000000----------010-----0110011";

  --  sltu (LESS_THAN_UNSIGNED)
  constant INST_SLTU         : slv_31_00 := X"00003033";
  constant INST_SLTU_MASK    : slv_31_00 := X"FE00707F";
  constant SLTUo             : isa_t := "0000000----------011-----0110011";

  --  xor
  constant INST_XOR          : slv_31_00 := X"00004033";
  constant INST_XOR_MASK     : slv_31_00 := X"FE00707F";
  constant XORo              : isa_t := "0000000----------100-----0110011";

  --  or
  constant INST_OR           : slv_31_00 := X"00006033";
  constant INST_OR_MASK      : slv_31_00 := X"FE00707F";
  constant ORo               : isa_t := "0000000----------110-----0110011";

  --  and
  constant INST_AND          : slv_31_00 := X"00007033";
  constant INST_AND_MASK     : slv_31_00 := X"FE00707F";
  constant ANDo              : isa_t := "0000000----------111-----0110011";

  --  sll
  constant INST_SLL          : slv_31_00 := X"00001033";
  constant INST_SLL_MASK     : slv_31_00 := X"FE00707F";
  constant SLLo              : isa_t := "0000000----------001-----0110011";

  --  srl
  constant INST_SRL          : slv_31_00 := X"00005033";
  constant INST_SRL_MASK     : slv_31_00 := X"FE00707F";
  constant SRLo              : isa_t := "0000000----------101-----0110011";

  --  sra
  constant INST_SRA          : slv_31_00 := X"40005033";
  constant INST_SRA_MASK     : slv_31_00 := X"FE00707F";
  constant SRAo              : isa_t := "0100000----------101-----0110011";



  -- Zba
  --  sh1add
  constant INST_SH1ADD       : slv_31_00 := X"20002033";
  constant INST_SH1ADD_MASK  : slv_31_00 := X"FE00707F";
  constant SH1ADDo           : isa_t := "0010000----------010-----0110011";
  --  sh2add
  constant INST_SH2ADD       : slv_31_00 := X"20004033";
  constant INST_SH2ADD_MASK  : slv_31_00 := X"FE00707F";
  constant SH2ADDo           : isa_t := "0010000----------100-----0110011";
  --  sh3add
  constant INST_SH3ADD       : slv_31_00 := X"20006033";
  constant INST_SH3ADD_MASK  : slv_31_00 := X"FE00707F";
  constant SH3ADDo           : isa_t := "0010000----------110-----0110011";

  -- https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zba.adoc
  --   sh1add rd, rs1, rs2
  --   sh2add rd, rs1, rs2
  --   h3add rd, rs1, rs2

  -- https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbb.adoc
  -- Zbb: Basic bit-manipulation
  --   Logical with negate
  --     andn
  --     orn
  --     xnor
  --   Count leading/trailing zero bits
  --     clz
  --     ctz
  --   Count population
  --     cpop
  --   Integer minimum/maximum
  --     max
  --     maxu
  --     min
  --     minu
  --   Sign- and zero-extension
  --     sext.b
  --     sext.h
  --     zext.h
  --   Bitwise rotation
  --     rol
  --     ror
  --     rori
  --   OR Combine
  --     orc.b
  --   Byte-reverse
  --     rev8
  --
  --
  -- https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbs.adoc
  --    Zbs: Single-bit instructions
  --    bclr rd, rs1, rs2
  --    bclri rd, rs1, imm
  --    bext rd, rs1, rs2
  --    bexti rd, rs1, imm
  --    binv rd, rs1, rs2
  --    binvi rd, rs1, imm
  --    bset rd, rs1, rs2
  --    bseti rd, rs1, imm

  -- https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbkb.adoc
  -- Zbkb: Bit-manipulation for Cryptography
  --    rol
  --    ror
  --    rori
  --    andn
  --    orn
  --    xnor
  --    pack
  --    packh
  --    rev.b
  --    rev8
  --    zip
  --    unzip
  --
  -- https://github.com/riscvarchive/riscv-zicond/blob/main/zicondops.adoc
  -- Zicond specification
  --    czero.eqz rd, rs1, rs2
  --    czero.nez rd, rs1, rs2


  -- Zbb                                                          mask
  constant INST_SEXTB        : slv_31_00 := X"60401013";   --  X"FFF0707F"
  constant INST_SEXTH        : slv_31_00 := X"60501013";   --  X"FFF0707F"
  constant INST_ZEXTH        : slv_31_00 := X"08004033";   --! X"FFF0707F"    !
  constant INST_MAX          : slv_31_00 := X"0a006033";   --    X"FE00707F"
  constant INST_MAXU         : slv_31_00 := X"0a007033";   --    X"FE00707F"
  constant INST_MIN          : slv_31_00 := X"0a004033";   --    X"FE00707F"
  constant INST_MINU         : slv_31_00 := X"0a005033";   --    X"FE00707F"
  constant INST_ANDN         : slv_31_00 := X"40007033";   --    X"FE00707F"
  constant INST_ORN          : slv_31_00 := X"40006033";   --    X"FE00707F"
  constant INST_XORN         : slv_31_00 := X"40004033";   --    X"FE00707F"
  constant INST_ROL          : slv_31_00 := X"60001033";   --    X"FE00707F"
  constant INST_ROR          : slv_31_00 := X"60005033";   --    X"FE00707F"
  constant INST_RORI         : slv_31_00 := X"60005013";   --    X"FE00707F"
  constant INST_CLZ          : slv_31_00 := X"60001013";   --  X"FFF0707F"
  constant INST_CTZ          : slv_31_00 := X"60101013";   --  X"FFF0707F"
  constant INST_CPOP         : slv_31_00 := X"60201013";   --  X"FFF0707F"
  constant INST_ORCB         : slv_31_00 := X"28705013";   --  X"FFF0707F"
  constant INST_REV8         : slv_31_00 := X"69805013";   --  X"FFF0707F"

  -- Zbs : Single-bit instructions                                mask
  constant INST_BCLR         : slv_31_00 := X"48001033";   --    X"FE00707F"
  constant INST_BCLRI        : slv_31_00 := X"48001013";   --    X"FE00707F"
  constant INST_BEXT         : slv_31_00 := X"48005033";   --    X"FE00707F"
  constant INST_BEXTI        : slv_31_00 := X"48005013";   --    X"FE00707F"
  constant INST_BINV         : slv_31_00 := X"68001033";   --    X"FE00707F"
  constant INST_BINVI        : slv_31_00 := X"68001013";   --    X"FE00707F"
  constant INST_BSET         : slv_31_00 := X"28001033";   --    X"FE00707F"
  constant INST_BSETI        : slv_31_00 := X"28001013";   --    X"FE00707F"

  -- Zbkb                                                         mask
  constant INST_PACK         : slv_31_00 := X"08004033";   --!   X"FE00707F"  !
  constant INST_PACKH        : slv_31_00 := X"08007033";   --    X"FE00707F"
  constant INST_ZIP          : slv_31_00 := X"08F01013";   --  X"FFF0707F"
  constant INST_UNZIP        : slv_31_00 := X"08F05013";   --  X"FFF0707F"
  constant INST_REVB         : slv_31_00 := X"68705013";   --  X"FFF0707F"

  -- Zicond                                                       mask
  constant INST_CZEROEQZ     : slv_31_00 := X"0E005033";   --    X"FE00707F"
  constant INST_CZERONEZ     : slv_31_00 := X"0E007033";   --    X"FE00707F"

  type alu_zb_t is ( zbSEXTB,  zbSEXTH,              zbMAX,   zbMAXU,  --  0- 3
                     zbMIN,    zbMINU,      zbANDN,  zbORN,   zbXORN,  --  4- 8
                     zbROL,    zbROR,       zbRORI,  zbCLZ,   zbCTZ,   --  9-13
                     zbCPOP,   zbORCB,      zbREV8,  zbBCLR,  zbBCLRI, -- 14-18
                     zbBEXT,   zbBEXTI,     zbBINV,  zbBINVI, zbBSET,  -- 19-23
                     zbBSETI, zbZEXTH_PACK, zbPACKH, zbZIP,   zbUNZIP, -- 24-27
                     zbREVB,  zbCZEROEQZ,  zbCZERONEZ );               -- 28-32
  --                 NOTE:  zbZEXTH == zbPACK ==>  zbZEXTH_PACK



  --                                   31:20          14:12     6:0
  -- INST_SEXTB      X"6040 1013"    011000000100-----001-----00100 11
  -- INST_SEXTH      X"6050 1013"    011000000101-----001-----00100 11
  -- INST_ZEXTH      X"0800 4033"    000010000000-----100-----01100 11  !
  --                                  31:25           14:12     6:0
  -- INST_MAX        X"0a00 6033"    0000101----------110-----01100 11
  -- INST_MAXU       X"0a00 7033"    0000101----------111-----01100 11
  -- INST_MIN        X"0a00 4033"    0000101----------100-----01100 11
  -- INST_MINU       X"0a00 5033"    0000101----------101-----01100 11
  -- INST_ANDN       X"4000 7033"    0100000----------111-----01100 11
  -- INST_ORN        X"4000 6033"    0100000----------110-----01100 11
  -- INST_XORN       X"4000 4033"    0100000----------100-----01100 11
  -- INST_ROL        X"6000 1033"    0110000----------001-----01100 11
  -- INST_ROR        X"6000 5033"    0110000----------101-----01100 11
  -- INST_RORI       X"6000 5013"    0110000----------101-----00100 11

  -- INST_CLZ        X"6000 1013"    011000000000-----001-----00100 11
  -- INST_CTZ        X"6010 1013"    011000000001-----001-----00100 11
  -- INST_CPOP       X"6020 1013"    011000000010-----001-----00100 11
  -- INST_ORCB       X"2870 5013"    001010000111-----101-----00100 11
  -- INST_REV8       X"6980 5013"    011010011000-----101-----00100 11

  -- INST_BCLR       X"4800 1033"    0100100----------001-----01100 11
  -- INST_BCLRI      X"4800 1013"    0100100----------001-----00100 11
  -- INST_BEXT       X"4800 5033"    0100100----------101-----01100 11
  -- INST_BEXTI      X"4800 5013"    0100100----------101-----00100 11
  -- INST_BINV       X"6800 1033"    0110100----------001-----01100 11
  -- INST_BINVI      X"6800 1013"    0110100----------001-----00100 11
  -- INST_BSET       X"2800 1033"    0010100----------001-----01100 11
  -- INST_BSETI      X"2800 1013"    0010100----------001-----00100 11
  -- INST_PACK       X"0800 4033"    0000100----------100-----01100 11  !
  -- INST_PACKH      X"0800 7033"    0000100----------111-----01100 11

  -- INST_ZIP        X"08F0 1013"    000010001111-----001-----00100 11
  -- INST_UNZIP      X"08F0 5013"    000010001111-----101-----00100 11
  -- INST_REVB       X"6870 5013"    011010000111-----101-----00100 11

  -- INST_CZEROEQZ   X"0E00 5033"    0000111----------101-----01100 11
  -- INST_CZERONEZ   X"0E00 7033"    0000111----------111-----01100 11



  --  jal  (jump and link)
  constant INST_JAL          : slv_31_00 := X"0000006f";
  constant INST_JAL_MASK     : slv_31_00 := X"0000007F";
  constant JALo              : isa_t := "-------------------------1101111";

  --  jalr (indirect jump -> jump and link register)
  constant INST_JALR         : slv_31_00 := X"00000067";
  constant INST_JALR_MASK    : slv_31_00 := X"0000707F";
  constant JALRo             : isa_t := "-----------------000-----1100111";

  --  beq
  constant INST_BEQ          : slv_31_00 := X"00000063";
  constant INST_BEQ_MASK     : slv_31_00 := X"0000707F";
  constant BEQo              : isa_t := "-----------------000-----1100011";

  --  bne
  constant INST_BNE          : slv_31_00 := X"00001063";
  constant INST_BNE_MASK     : slv_31_00 := X"0000707F";
  constant BNEo              : isa_t := "-----------------001-----1100011";

  --  blt
  constant INST_BLT          : slv_31_00 := X"00004063";
  constant INST_BLT_MASK     : slv_31_00 := X"0000707F";
  constant BLTo              : isa_t := "-----------------100-----1100011";

  --  bge
  constant INST_BGE          : slv_31_00 := X"00005063";
  constant INST_BGE_MASK     : slv_31_00 := X"0000707F";
  constant BGEo              : isa_t := "-----------------101-----1100011";

  --  bltu
  constant INST_BLTU         : slv_31_00 := X"00006063";
  constant INST_BLTU_MASK    : slv_31_00 := X"0000707F";
  constant BLTUo             : isa_t := "-----------------110-----1100011";

  --  bgeu
  constant INST_BGEU         : slv_31_00 := X"00007063";
  constant INST_BGEU_MASK    : slv_31_00 := X"0000707F";
  constant BGEUo             : isa_t := "-----------------111-----1100011";



  --  lb (load byte)
  constant INST_LB           : slv_31_00 := X"00000003";
  constant INST_LB_MASK      : slv_31_00 := X"0000707F";
  constant LBo               : isa_t := "-----------------000-----0000011";

  --  lh  (load half word)
  constant INST_LH           : slv_31_00 := X"00001003";
  constant INST_LH_MASK      : slv_31_00 := X"0000707F";
  constant LHo               : isa_t := "-----------------001-----0000011";

  --  lw  (load word)
  constant INST_LW           : slv_31_00 := X"00002003";
  constant INST_LW_MASK      : slv_31_00 := X"0000707F";
  constant LWo               : isa_t := "-----------------010-----0000011";

--constant LDo               : isa_t := "-----------------011-----0000011";

  --  lbu  (load byte, unsigned)
  constant INST_LBU          : slv_31_00 := X"00004003";
  constant INST_LBU_MASK     : slv_31_00 := X"0000707F";
  constant LBUo              : isa_t := "-----------------100-----0000011";

  --  lhu  (load half word, unsigned)
  constant INST_LHU          : slv_31_00 := X"00005003";
  constant INST_LHU_MASK     : slv_31_00 := X"0000707F";
  constant LHUo              : isa_t := "-----------------101-----0000011";

  --  lwu  (load word, unsigned)
  constant INST_LWU          : slv_31_00 := X"00006003";
  constant INST_LWU_MASK     : slv_31_00 := X"0000707F";
  constant LWUo              : isa_t := "-----------------110-----0000011";



  --  sb   (store byte)
  constant INST_SB           : slv_31_00 := X"00000023";
  constant INST_SB_MASK      : slv_31_00 := X"0000707F";
  constant SBo               : isa_t := "-----------------000-----0100011";

  --  sh   (store half word)
  constant INST_SH           : slv_31_00 := X"00001023";
  constant INST_SH_MASK      : slv_31_00 := X"0000707F";
  constant SHo               : isa_t := "-----------------001-----0100011";

  --  sw   (store word)
  constant INST_SW           : slv_31_00 := X"00002023";
  constant INST_SW_MASK      : slv_31_00 := X"0000707F";
  constant SWo               : isa_t := "-----------------010-----0100011";


  --  ecall
  constant INST_ECALL        : slv_31_00 := X"00000073";
  constant INST_ECALL_MASK   : slv_31_00 := X"ffffffff";
  constant ECALLo            : isa_t := "00000000000000000000000001110011";

  --  ebreak
  constant INST_EBREAK       : slv_31_00 := X"00100073";
  constant INST_EBREAK_MASK  : slv_31_00 := X"ffffffff";
  constant EBREAKo           : isa_t := "00000000000100000000000001110011";

  --  eret
  constant INST_ERET         : slv_31_00 := X"00200073";
  constant INST_ERET_MASK    : slv_31_00 := X"cfffffff";
  constant ERETo             : isa_t := "00--0000001000000000000001110011";

--constant URETo             : isa_t := "00000000001000000000000001110011";
--constant MRETo             : isa_t := "00110000001000000000000001110011";
--constant SRETo             : isa_t := "00010000001000000000000001110011";



  --  csrrw
  constant INST_CSRRW        : slv_31_00 := X"00001073";
  constant INST_CSRRW_MASK   : slv_31_00 := X"0000707F";
  constant CSRRWo            : isa_t := "-----------------001-----1110011";

  --  csrrs
  constant INST_CSRRS        : slv_31_00 := X"00002073";
  constant INST_CSRRS_MASK   : slv_31_00 := X"0000707F";
  constant CSRRSo            : isa_t := "-----------------010-----1110011";

  --  csrrc
  constant INST_CSRRC        : slv_31_00 := X"00003073";
  constant INST_CSRRC_MASK   : slv_31_00 := X"0000707F";
  constant CSRRCo            : isa_t := "-----------------011-----1110011";

  --  csrrwi
  constant INST_CSRRWI       : slv_31_00 := X"00005073";
  constant INST_CSRRWI_MASK  : slv_31_00 := X"0000707F";
  constant CSRRWIo           : isa_t := "-----------------101-----1110011";

  --  csrrsi
  constant INST_CSRRSI       : slv_31_00 := X"00006073";
  constant INST_CSRRSI_MASK  : slv_31_00 := X"0000707F";
  constant CSRRSIo           : isa_t := "-----------------110-----1110011";

  --  csrrci
  constant INST_CSRRCI       : slv_31_00 := X"00007073";
  constant INST_CSRRCI_MASK  : slv_31_00 := X"0000707F";
  constant CSRRCIo           : isa_t := "-----------------111-----1110011";



  --  mul
  constant INST_MUL          : slv_31_00 := X"02000033";
  constant INST_MUL_MASK     : slv_31_00 := X"FE00707F";
  constant MULo              : isa_t := "0000001----------000-----0110011";

  --  mulh
  constant INST_MULH         : slv_31_00 := X"02001033";
  constant INST_MULH_MASK    : slv_31_00 := X"FE00707F";
  constant MULHo             : isa_t := "0000001----------001-----0110011";

  --  mulhsu
  constant INST_MULHSU       : slv_31_00 := X"02002033";
  constant INST_MULHSU_MASK  : slv_31_00 := X"FE00707F";
  constant MULHSUo           : isa_t := "0000001----------010-----0110011";

  --  mulhu
  constant INST_MULHU        : slv_31_00 := X"02003033";
  constant INST_MULHU_MASK   : slv_31_00 := X"FE00707F";
  constant MULHUo            : isa_t := "0000001----------011-----0110011";



  --  div
  constant INST_DIV          : slv_31_00 := X"02004033";
  constant INST_DIV_MASK     : slv_31_00 := X"FE00707F";
  constant DIVo              : isa_t := "0000001----------100-----0110011";

  --  divu
  constant INST_DIVU         : slv_31_00 := X"02005033";
  constant INST_DIVU_MASK    : slv_31_00 := X"FE00707F";
  constant DIVUo             : isa_t := "0000001----------101-----0110011";

  --  rem
  constant INST_REM          : slv_31_00 := X"02006033";
  constant INST_REM_MASK     : slv_31_00 := X"FE00707F";
  constant REMo              : isa_t := "0000001----------110-----0110011";

  --  remu
  constant INST_REMU         : slv_31_00 := X"02007033";
  constant INST_REMU_MASK    : slv_31_00 := X"FE00707F";
  constant REMUo             : isa_t := "0000001----------111-----0110011";



  --  wfi (wait for interrupt)
  constant INST_WFI          : slv_31_00 := X"10500073";
  constant INST_WFI_MASK     : slv_31_00 := X"ffff8fff";
  constant WFIo              : isa_t := "00010000010100000---000001110011";


  --  fence
  constant INST_FENCE        : slv_31_00 := X"0000000f";
  constant INST_FENCE_MASK   : slv_31_00 := X"0000707f";
  constant FENCEo            : isa_t := "-----------------000-----0001111";

  --  sfence
  constant INST_SFENCE       : slv_31_00 := X"12000073";
  constant INST_SFENCE_MASK  : slv_31_00 := X"fe007fff";
  constant SFENCE_VMAo       : isa_t := "0001001----------000000001110011";

  --  fence.i
  constant INST_IFENCE       : slv_31_00 := X"0000100f";
  constant INST_IFENCE_MASK  : slv_31_00 := X"0000707f";
  constant IFENCEo           : isa_t := "-----------------001-----0001111";

--constant FENCEIo           : isa_t := "00000000000000000001000000001111";


  -- atomic memory access instructions  -- funct5 : [31:27], opcode : [6:0]
  constant OPCODE_AMO        : slv_t(6 downto 0) := "0101111";  -- 0x2f
  constant INST_AMO_MASK     : slv_31_00 := X"F800707F";

  --  31          25 24      20 19      15 14  12 11       7 6            0
  -- -----------------------------------------------------------------------
  -- |    FUNC7     |   RS2    |   RS1    | FUNC3|   RD     |   OPCODE     |
  -- -----------------------------------------------------------------------
  --           /     \
  --          / aq rl \
  --
  constant INST_LR           : slv_31_00 := X"1000202F";
  constant LRo               : isa_t := "00010--00000-----010-----0101111";

  constant INST_SC           : slv_31_00 := X"1800202F";
  constant SCo               : isa_t := "00011------------010-----0101111";

  constant INST_AMOSWAP      : slv_31_00 := X"0800202F";
  constant AMOSWAPo          : isa_t := "00001------------010-----0101111";

  constant INST_AMOADD       : slv_31_00 := X"0000202F";
  constant AMOADDo           : isa_t := "00000------------010-----0101111";

  constant INST_AMOXOR       : slv_31_00 := X"2000202F";
  constant AMOXORo           : isa_t := "00100------------010-----0101111";

  constant INST_AMOAND       : slv_31_00 := X"6000202F";
  constant AMOANDo           : isa_t := "01100------------010-----0101111";

  constant INST_AMOOR        : slv_31_00 := X"4000202F";
  constant AMOORo            : isa_t := "01000------------010-----0101111";

  constant INST_AMOMIN       : slv_31_00 := X"8000202F";
  constant AMOMINo           : isa_t := "10000------------010-----0101111";

  constant INST_AMOMAX       : slv_31_00 := X"A000202F";
  constant AMOMAXo           : isa_t := "10100------------010-----0101111";

  constant INST_AMOMINU      : slv_31_00 := X"C000202F";
  constant AMOMINUo          : isa_t := "11000------------010-----0101111";

  constant INST_AMOMAXU      : slv_31_00 := X"E000202F";
  constant AMOMAXUo          : isa_t := "11100------------010-----0101111";

  type alu_amo_t is ( AMONULL, AMOSWAP, AMOADD, AMOXOR,  AMOAND,    --  0 -  4
                      AMOOR,   AMOMIN,  AMOMAX, AMOMINU, AMOMAXU ); --  5 -  9



  -- - - - - - - - - - - - - - - - - - - -
  --  Privilege levels
  -- - - - - - - - - - - - - - - - - - - -
  constant PRIV_USER         : slv_01_00 := "00";
  constant PRIV_SUPER        : slv_01_00 := "01";
  constant PRIV_MACHINE      : slv_01_00 := "11";

  -- - - - - - - - - - - - - - - - - - - -
  --  IRQ numbers
  -- - - - - - - - - - - - - - - - - - - -
  constant IRQ_S_SOFT        : natural := 1;
  constant IRQ_M_SOFT        : natural := 3;
  constant IRQ_S_TIMER       : natural := 5;
  constant IRQ_M_TIMER       : natural := 7;
  constant IRQ_S_EXT         : natural := 9;
  constant IRQ_M_EXT         : natural := 11;

  constant IRQ_S_SOFT_B      : slv_31_00 := X"00000001";  -- d1
  constant IRQ_M_SOFT_B      : slv_31_00 := X"00000003";  -- d3
  constant IRQ_S_TIMER_B     : slv_31_00 := X"00000005";  -- d5
  constant IRQ_M_TIMER_B     : slv_31_00 := X"00000007";  -- d7
  constant IRQ_S_EXT_B       : slv_31_00 := X"00000009";  -- d9
  constant IRQ_M_EXT_B       : slv_31_00 := X"0000000B";  -- d11

  constant IRQ_MIN           : natural := IRQ_S_SOFT;
  constant IRQ_MAX           : natural := IRQ_M_EXT + 1;

  constant IRQ_MASK          : slv_31_00 := ( IRQ_M_EXT   => '1',
                                              IRQ_S_EXT   => '1',
                                              IRQ_M_TIMER => '1',
                                              IRQ_S_TIMER => '1',
                                              IRQ_M_SOFT  => '1',
                                              IRQ_S_SOFT  => '1',
                                              others => '0' );

--constant SR_IP_MSIP_R      : natural := IRQ_M_SOFT;     -- 3   (software)
  constant SR_IP_MTIP_R      : natural := IRQ_M_TIMER;    -- 7   (timer)
  constant SR_IP_MEIP_R      : natural := IRQ_M_EXT;      -- 11  (external)
--constant SR_IP_SSIP_R      : natural := IRQ_S_SOFT;     -- 1   (software)
  constant SR_IP_STIP_R      : natural := IRQ_S_TIMER;    -- 5   (timer)
  constant SR_IP_SEIP_R      : natural := IRQ_S_EXT;      -- 9   (external)


  -- - - - - - - - - - - - - - - - - - - -
  --  CSR Registers
  -- MEPC : Machine Exception Program Counter
  -- - - - - - - - - - - - - - - - - - - -
  constant CSR_MSTATUS       : slv_11_00 := X"300";
  constant CSR_MSTATUS_MASK  : slv_31_00 := X"FFFFFFFF";

  constant CSR_MISA          : slv_11_00 := X"301";
  constant CSR_MISA_MASK     : slv_31_00 := X"FFFFFFFF";
  constant MISA_RV32         : slv_31_00 := X"40000000";
  constant MISA_RVU          : slv_31_00 := X"00100000";
  constant MISA_RVS          : slv_31_00 := X"00040000";
  constant MISA_RVM          : slv_31_00 := X"00001000";
  constant MISA_RVI          : slv_31_00 := X"00000100";
  constant MISA_RVF          : slv_31_00 := X"00000020";
  constant MISA_RVE          : slv_31_00 := X"00000010";
  constant MISA_RVD          : slv_31_00 := X"00000008";
  constant MISA_RVC          : slv_31_00 := X"00000004";
  constant MISA_RVB          : slv_31_00 := X"00000002";
  constant MISA_RVA          : slv_31_00 := X"00000001";
  constant   MISA_RV32_B     : natural := 30;
  constant   MISA_RVU_B      : natural := 20;
  constant   MISA_RVS_B      : natural := 18;
  constant   MISA_RVM_B      : natural := 12;
  constant   MISA_RVI_B      : natural := 8;
  constant   MISA_RVF_B      : natural := 5;
  constant   MISA_RVE_B      : natural := 4;
  constant   MISA_RVD_B      : natural := 3;
  constant   MISA_RVC_B      : natural := 2;
  constant   MISA_RVB_B      : natural := 1;
  constant   MISA_RVA_B      : natural := 0;

  constant CSR_MEDELEG       : slv_11_00 := X"302";      -- M exception delegate
  constant CSR_MEDELEG_MASK  : slv_31_00 := X"0000FFFF";
  constant CSR_MIDELEG       : slv_11_00 := X"303";      -- M intr delegate
  constant CSR_MIDELEG_MASK  : slv_31_00 := X"0000FFFF";
  constant CSR_MIE           : slv_11_00 := X"304";
  constant CSR_MIE_MASK      : slv_31_00 :=   IRQ_MASK;
  constant CSR_MTVEC         : slv_11_00 := X"305";
  constant CSR_MTVEC_MASK    : slv_31_00 := X"FFFFFFFF";
  constant CSR_MSCRATCH      : slv_11_00 := X"340";
  constant CSR_MSCRATCH_MASK : slv_31_00 := X"FFFFFFFF";
  constant CSR_MEPC          : slv_11_00 := X"341";
  constant CSR_MEPC_MASK     : slv_31_00 := X"FFFFFFFF";
  constant CSR_MCAUSE        : slv_11_00 := X"342";
  constant CSR_MCAUSE_MASK   : slv_31_00 := X"8000000F";
  constant CSR_MTVAL         : slv_11_00 := X"343";
  constant CSR_MTVAL_MASK    : slv_31_00 := X"FFFFFFFF";
  constant CSR_MIP           : slv_11_00 := X"344";
  constant CSR_MIP_MASK      : slv_31_00 :=   IRQ_MASK;
  constant CSR_MCYCLE        : slv_11_00 := X"C00";
  constant CSR_MCYCLE_MASK   : slv_31_00 := X"FFFFFFFF";
  constant CSR_MTIME         : slv_11_00 := X"C01";
  constant CSR_MTIME_MASK    : slv_31_00 := X"FFFFFFFF";
  constant CSR_MTIMEH        : slv_11_00 := X"C81";
  constant CSR_MTIMEH_MASK   : slv_31_00 := X"FFFFFFFF";
  constant CSR_MARCHID       : slv_11_00 := X"F12";
  constant CSR_MARCHID_MASK  : slv_31_00 := X"FFFFFFFF";
  constant CSR_MIMPID        : slv_11_00 := X"F13";
  constant CSR_MIMPID_MASK   : slv_31_00 := X"FFFFFFFF";
  constant CSR_MHARTID       : slv_11_00 := X"F14";
  constant CSR_MHARTID_MASK  : slv_31_00 := X"FFFFFFFF";

  --  Non-std
  constant CSR_MTIMECMP      : slv_11_00 := X"7C0";
  constant CSR_MTIMECMP_MASK : slv_31_00 := X"FFFFFFFF";

  -- - - - - - - - - - - - - - - - - - - -
  --  Status register
  -- - - - - - - - - - - - - - - - - - - -
  constant SR_UIE            : natural := 2**0;           -- (1 << 0)
  constant SR_UIE_R          : natural := 0;
  constant SR_SIE            : natural := 2**1;           -- (1 << 1)
  constant SR_SIE_R          : natural := 1;
  constant SR_MIE            : natural := 2**3;           -- (1 << 3)
  constant SR_MIE_R          : natural := 3;
  constant SR_UPIE           : natural := 2**4;           -- (1 << 4)
  constant SR_UPIE_R         : natural := 4;
  constant SR_SPIE           : natural := 2**5;           -- (1 << 5)
  constant SR_SPIE_R         : natural := 5;
  constant SR_MPIE           : natural := 2**7;           -- (1 << 7)
  constant SR_MPIE_R         : natural := 7;
  constant SR_SPP            : natural := 2**8;           -- (1 << 8)
  constant SR_SPP_R          : natural := 8;

  constant SR_MPP_SHIFT      : natural   := 11;
  constant SR_MPP_MASK       : slv_01_00 := "11";         -- d3
  constant SR_MPP_R          : slv_12_11 := "00";         -- 12:11 range
  constant SR_MPP_U          : slv_t(PRIV_USER'range)   := PRIV_USER;
  constant SR_MPP_S          : slv_t(PRIV_SUPER'range)  := PRIV_SUPER;
  constant SR_MPP_M          : slv_t(PRIV_MACHINE'range):= PRIV_MACHINE;

  constant SR_SUM_R          : natural := 18;
  constant SR_SUM            : natural := 2**SR_SUM_R;    -- (1 << SR_SUM_R)

  constant SR_MPRV_R         : natural := 17;
  constant SR_MPRV           : natural := 2**SR_MPRV_R;   -- (1 << SR_MPRV_R)

  constant SR_MXR_R          : natural := 19;
  constant SR_MXR            : natural := 2**SR_MXR_R;    -- (1 << SR_MXR_R)

  constant SR_SMODE_MASK     : slv_31_00 := ( SR_UIE_R  => '1',  -- 1
                                              SR_SIE_R  => '1',  -- 2
                                              SR_UPIE_R => '1',  -- 16
                                              SR_SPIE_R => '1',  -- 32
                                              SR_SPP_R  => '1',  -- 128
                                              SR_SUM_R  => '1',  -- 262144
                                              others    => '0' );

  -- - - - - - - - - - - - - - - - - - - -
  --  CSR Registers - Supervisor
  -- - - - - - - - - - - - - - - - - - - -
  constant CSR_SSTATUS         : slv_11_00 := X"100";
  constant CSR_SSTATUS_MASK    : slv_t(SR_SMODE_MASK'range) := SR_SMODE_MASK;
  constant CSR_SIE             : slv_11_00 := X"104";

  constant CSR_SIE_MASK        : slv_31_00 := ( IRQ_S_EXT   => '1',
                                                IRQ_S_TIMER => '1',
                                                IRQ_S_SOFT  => '1',
                                                others      => '0' );
  constant CSR_STVEC           : slv_11_00 := X"105";
  constant CSR_STVEC_MASK      : slv_31_00 := X"FFFFFFFF";
  constant CSR_SSCRATCH        : slv_11_00 := X"140";
  constant CSR_SSCRATCH_MASK   : slv_31_00 := X"FFFFFFFF";
  constant CSR_SEPC            : slv_11_00 := X"141";
  constant CSR_SEPC_MASK       : slv_31_00 := X"FFFFFFFF";
  constant CSR_SCAUSE          : slv_11_00 := X"142";
  constant CSR_SCAUSE_MASK     : slv_31_00 := X"8000000F";
  constant CSR_STVAL           : slv_11_00 := X"143";
  constant CSR_STVAL_MASK      : slv_31_00 := X"FFFFFFFF";
  constant CSR_SIP             : slv_11_00 := X"144";

  constant CSR_SIP_MASK        : slv_31_00 := ( IRQ_S_EXT   => '1',
                                                IRQ_S_TIMER => '1',
                                                IRQ_S_SOFT  => '1',
                                                others      => '0');

  constant CSR_SATP            : slv_11_00 := X"180";
  constant CSR_SATP_MASK       : slv_31_00 := X"FFFFFFFF";

  -- - - - - - - - - - - - - - - - - - - -
  --  CSR Registers - DCACHE control
  -- - - - - - - - - - - - - - - - - - - -
  --  pmpcfg0
  constant CSR_DFLUSH           : slv_11_00 := X"3A0";
  constant CSR_DFLUSH_MASK      : slv_31_00 := X"FFFFFFFF";
  --  pmpcfg1
  constant CSR_DWRITEBACK       : slv_11_00 := X"3A1";
  constant CSR_DWRITEBACK_MASK  : slv_31_00 := X"FFFFFFFF";
  --  pmpcfg2
  constant CSR_DINVALIDATE      : slv_11_00 := X"3A2";
  constant CSR_DINVALIDATE_MASK : slv_31_00 := X"FFFFFFFF";

  -- - - - - - - - - - - - - - - - - - - -
  --  SATP definitions
  -- - - - - - - - - - - - - - - - - - - -
  -- 19:0, range def only
  constant SATP_PPN_R  : slv_19_00 := (others => '0');
  constant SATP_ASID_R : slv_30_22 := (others => '0') ;
  constant SATP_MODE_R : natural := 31;

  -- - - - - - - - - - - - - - - - - - - -
  --  MMU Defs (SV32)
  -- - - - - - - - - - - - - - - - - - - -
  constant MMU_LEVELS     : natural := 2;
  constant MMU_PTIDXBITS  : natural := 10;
  constant MMU_PTESIZE    : natural := 4;
  -- PGSIZE   4096  - bytes mapped by a page
  -- PGSHIFT  12    - log2(PGSIZE)
  constant MMU_PGSHIFT    : natural := (MMU_PTIDXBITS + 2);           -- 12
  constant MMU_PGSIZE     : natural := 2**MMU_PGSHIFT;                -- 4096
  constant MMU_VPN_BITS   : natural := (MMU_PTIDXBITS * MMU_LEVELS);  -- 20
  constant MMU_PPN_BITS   : natural := (32 - MMU_PGSHIFT);            -- 20
  constant MMU_VA_BITS    : natural := (MMU_VPN_BITS + MMU_PGSHIFT);  -- 32

  constant PAGE_PRESENT   : natural := 0;
  constant PAGE_READ      : natural := 1;
  constant PAGE_WRITE     : natural := 2;
  constant PAGE_EXEC      : natural := 3;
  constant PAGE_USER      : natural := 4;
  constant PAGE_GLOBAL    : natural := 5;
  constant PAGE_ACCESSED  : natural := 6;
  constant PAGE_DIRTY     : natural := 7;
  constant PAGE_SOFT      : slv_09_08 := "00";         -- range 9:8

  constant PAGE_FLAGS     : slv_09_00 := "1111111111"; -- 3FF

  constant PAGE_PFN_SHIFT : natural := 10;             -- page frame number, PFN
  constant PAGE_SIZE      : natural := 4096;

  -- - - - - - - - - - - - - - - - - - - -
  --  Exception Causes
  -- - - - - - - - - - - - - - - - - - - -
  constant EXCEPTION_W                   : natural := 6;
  subtype  exception_t is slv_t(EXCEPTION_W-1 downto 0);
  constant EXCEPTION_MISALIGNED_FETCH    : exception_t := "010000";  -- 6'h10
  constant EXCEPTION_FAULT_FETCH         : exception_t := "010001";  -- 6'h11
  constant EXCEPTION_ILLEGAL_INSTRUCTION : exception_t := "010010";  -- 6'h12
  constant EXCEPTION_BREAKPOINT          : exception_t := "010011";  -- 6'h13
  constant EXCEPTION_MISALIGNED_LOAD     : exception_t := "010100";  -- 6'h14
  constant EXCEPTION_FAULT_LOAD          : exception_t := "010101";  -- 6'h15
  constant EXCEPTION_MISALIGNED_STORE    : exception_t := "010110";  -- 6'h16
  constant EXCEPTION_FAULT_STORE         : exception_t := "010111";  -- 6'h17
  constant EXCEPTION_ECALL               : exception_t := "011000";  -- 6'h18
  constant EXCEPTION_ECALL_U             : exception_t := "011000";  -- 6'h18
  constant EXCEPTION_ECALL_S             : exception_t := "011001";  -- 6'h19
  constant EXCEPTION_ECALL_H             : exception_t := "011010";  -- 6'h1a
  constant EXCEPTION_ECALL_M             : exception_t := "011011";  -- 6'h1b
  constant EXCEPTION_PAGE_FAULT_INST     : exception_t := "011100";  -- 6'h1c
  constant EXCEPTION_PAGE_FAULT_LOAD     : exception_t := "011101";  -- 6'h1d
  constant EXCEPTION_PAGE_FAULT_STORE    : exception_t := "011111";  -- 6'h1f
  constant EXCEPTION_EXCEPTION           : exception_t := "010000";  -- 6'h10
  constant EXCEPTION_INTERRUPT           : exception_t := "100000";  -- 6'h20
  constant EXCEPTION_ERET_U              : exception_t := "110000";  -- 6'h30
  constant EXCEPTION_ERET_S              : exception_t := "110001";  -- 6'h31
  constant EXCEPTION_ERET_H              : exception_t := "110010";  -- 6'h32
  constant EXCEPTION_ERET_M              : exception_t := "110011";  -- 6'h33
  constant EXCEPTION_FENCE               : exception_t := "110100";  -- 6'h34
  constant EXCEPTION_TYPE_MASK           : exception_t := "110000";  -- 6'h30
  constant EXCEPTION_SUBTYPE_R           : slv_03_00   := "0000";    --??--  3:0

  constant MCAUSE_INT                    : natural := 31;
--constant MCAUSE_PAGE_FAULT_STORE       : natural := ( 0**MCAUSE_INT + 15);
--
--constant MCAUSE_PAGE_FAULT_LOAD        : natural := ( 0**MCAUSE_INT + 13);
--constant MCAUSE_PAGE_FAULT_INST        : natural := ( 0**MCAUSE_INT + 12);
--constant MCAUSE_ECALL_M                : natural := ( 0**MCAUSE_INT + 11);
--constant MCAUSE_ECALL_H                : natural := ( 0**MCAUSE_INT + 10);
--constant MCAUSE_ECALL_S                : natural := ( 0**MCAUSE_INT + 9);
--constant MCAUSE_ECALL_U                : natural := ( 0**MCAUSE_INT + 8);
--constant MCAUSE_FAULT_STORE            : natural := ( 0**MCAUSE_INT + 7);
--constant MCAUSE_MISALIGNED_STORE       : natural := ( 0**MCAUSE_INT + 6);
--constant MCAUSE_FAULT_LOAD             : natural := ( 0**MCAUSE_INT + 5);
--constant MCAUSE_MISALIGNED_LOAD        : natural := ( 0**MCAUSE_INT + 4);
--constant MCAUSE_BREAKPOINT             : natural := ( 0**MCAUSE_INT + 3);
--constant MCAUSE_ILLEGAL_INSTRUCTION    : natural := ( 0**MCAUSE_INT + 2);
--constant MCAUSE_FAULT_FETCH            : natural := ( 0**MCAUSE_INT + 1);
--constant MCAUSE_MISALIGNED_FETCH       : natural := ( 0**MCAUSE_INT + 0);

  constant MCAUSE_INTERRUPT              : slv_31_00 := (MCAUSE_INT => '1',
                                                         others => '0');

  -- - - - - - - - - - - - - - - - - - - -
  --  Debug
  -- - - - - - - - - - - - - - - - - - - -
  constant RISCV_REGNO_FIRST   : slv_12_00 := (others => '0');
  constant RISCV_REGNO_GPR0    : slv_12_00 := (others => '0');
  constant RISCV_REGNO_GPR31   : slv_12_00 := "0" & X"01F"; -- 31
  constant RISCV_REGNO_PC      : slv_12_00 := "0" & X"020"; -- 32
  constant RISCV_REGNO_CSR0    : slv_12_00 := "0" & X"041"; -- 65
  constant RISCV_REGNO_CSR4095 : slv_12_00 :=  std_logic_vector(
    unsigned(RISCV_REGNO_CSR0) +  4095);     -- RISCV_REGNO_CSR0 + d4095
  constant RISCV_REGNO_PRIV    : slv_12_00 :=  "1" & X"041";   -- d4161


  function log2           (arg1 : natural)              return natural;

  function to_uint        (arg1 : slv_t)                return natural;
  function to_uint        (arg1 : unsigned)             return natural;
  function to_uint        (arg1 : sl_t)                 return natural;


  function eq             (svec1, svec2 : slv_t)        return std_logic;
  function eq             (val1,  val2 : natural)       return std_logic;
  function eq             (svec1, svec2 : slv_t)        return boolean;
  function eq             (svec : slv_t; val : natural) return boolean;
  function ne             (svec1, svec2 : slv_t)        return std_logic;
  function ne             (svec1, svec2 : slv_t)        return boolean;
  function ne             (svec : slv_t; val : natural) return std_logic;
  function ne             (svec : slv_t; val : natural) return boolean;

  function is_zero        (svec1 : slv_t)               return std_logic;
  function not_zero       (svec1 : slv_t)               return std_logic;
  function is_zero        (svec1 : slv_t)               return boolean;
  function not_zero       (svec1 : slv_t)               return boolean;

  function rev_bits       (arg1  : slv_t)               return slv_t;

  -- less than unsigned
  function lt             (svec1, svec2 : slv_t)        return std_logic;
  function lt             (svec1, svec2 : slv_t)        return boolean;
  -- less than or equal unsigned
  function le             (svec1, svec2 : slv_t)        return std_logic;
  function le             (svec : slv_t; val : natural) return std_logic;
  function le             (svec1, svec2 : slv_t)        return boolean;
  -- greater than unsigned
  function gt             (svec1, svec2 : slv_t)        return std_logic;
  function gt             (svec1, svec2 : slv_t)        return boolean;
  -- greater than or equal unsigned
  function ge             (svec1, svec2 : slv_t)        return std_logic;
  function ge             (svec1, svec2 : slv_t)        return boolean;

  function lt_signed      (x, y : slv_t)                return std_logic;
  function gt_signed      (x, y : slv_31_00)            return std_logic;

  -- increment unsigned
  function inc1u ( svec : slv_t )                       return slv_t;
  -- decrement unsigned
  function dec1u ( svec : slv_t )                       return slv_t;

  -- add unsigned
  function addu  ( svec1, svec2 : slv_t )               return slv_t;
  function addu  ( svec  : slv_t; val : natural )       return slv_t;
  -- add signed
  function adds  ( svec1, svec2 : slv_t )               return slv_t;

  function f_clz    ( svec : slv_31_00 )                return slv_31_00;
  function f_popcnt ( svec : slv_31_00 )                return slv_31_00;

  function f_opcode_eq ( iword, opcode : slv_31_00 )    return std_logic;



  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' and func id if "iword" matches to "opcode" for A-extension
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_amo_dec (        enable        : in  std_logic;
                        signal iword         : in  slv_31_00;
                        signal legal         : out std_logic;
                        signal amoop         : out amo_op_t );


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' and func id if "iword" matches to "opcode" for ZB-extension
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_zb_dec (        enable         : in  std_logic;
                       signal iword          : in  slv_31_00;
                       signal legal          : out std_logic;
                       signal zbfunc5        : out slv_04_00 );


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- alu operations for ZB- andZicond instructions
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_alu_zb (        enable         : in  std_logic;
                       signal valid          : in  std_logic;
                       signal zbfunc         : in  slv_04_00;
                       signal imm12          : in  slv_31_00;
                       signal oprs1          : in  slv_31_00;
                       signal oprs2          : in  slv_31_00;
                       signal result         : out slv_31_00 );


  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  --  D flip-flop with synchonous active-low reset
  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_dffr ( signal clk              : in  std_logic;
                     signal xrst             : in  std_logic;
                            d                : in  std_logic;
                     signal q                : out std_logic );


  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  --  D flip-flop with synchonous active-low set
  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_dffs ( signal clk              : in  std_logic;
                     signal xset             : in  std_logic;
                            d                : in  std_logic;
                     signal q                : out std_logic );


  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  --  one hot decoder for register file write enable
  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_one_hot_rf32 ( signal addr     : in  slv_t(4 downto 0);
                                    half_rf  : in  std_logic;
                             signal dout     : out slv_31_00 );

  procedure p_dff32x32 (     signal clk      : in  std_logic;
                             signal xrst     : in  std_logic;
                             signal wr       : in  slv_31_00;
                             signal d        : in  slv_31_00;
                             signal q        : out rfile_t );

  procedure p_rf32x2mux (    signal ra       : in  slv_04_00;
                             signal rb       : in  slv_04_00;
                             signal q        : in  rfile_t;
                             signal ya       : out slv_31_00;
                             signal yb       : out slv_31_00 );

  procedure p_ld_resp   (    signal ack      : in  std_logic;
                             signal err      : in  std_logic;
                             signal ldtype   : in  dmem_ldtyp_t;
                             signal idata    : in  slv_31_00;
                             signal odata    : out slv_31_00 );


  procedure p_btarget (  signal iword           : in  slv_31_00;
                         signal btype           : in  branch_op_t;
                         signal pc              : in  slv_31_00;
                         signal oprs1_dat       : in  slv_31_00;
                         signal oprs2_dat       : in  slv_31_00;
                         signal opcode_valid    : in std_logic;
                         signal branch_pc       : out slv_31_00;
                         signal branch_req      : out std_logic );

end rv_pkg;



-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



package body rv_pkg is

  -- - - - - - - - - - - - - - - - - - - - -
  -- return log2 of argument
  -- - - - - - - - - - - - - - - - - - - - -
  function log2 (arg1 : natural) return natural is
    variable result: natural;
  begin
    result := 0;
    while 2 ** result < arg1 loop
      result := result + 1;
    end loop;
    if (result < 1) then
      result := 1;
    end if;
    return result;
  end function log2;


  -- - - - - - - - - - - - - - - - - - - - - - -
  -- return std_logic_vector as unsigned integer
  -- - - - - - - - - - - - - - - - - - - - - - -
  function to_uint (arg1 : slv_t)
    return natural is            -- POSITIVE integer, i.e. natural
  begin
    return to_integer(unsigned(arg1));
  end function to_uint;
  --
  function to_uint (arg1 : unsigned)
    return natural is            -- POSITIVE integer, i.e. natural
  begin
    return to_integer(arg1);
  end function to_uint;
  --
  function to_uint (arg1 : sl_t)
    return natural is            -- POSITIVE integer, i.e. natural
  begin
    return to_integer(unsigned'('0' & arg1));
  end function to_uint;


  -- - - - - - - - - - - - - - - - - - - - -
  -- return '1' if std_logic_vectors match
  -- - - - - - - - - - - - - - - - - - - - -
  function eq (svec1, svec2 : slv_t) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '0';
    if (svec1 = svec2) then
      result_v := '1';
    end if;
    return result_v;
  end function eq;

  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if integers match
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function eq (val1, val2 : natural) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '0';
    if (val1 = val2) then
      result_v := '1';
    end if;
    return result_v;
  end function eq;

  -- - - - - - - - - - - - - - - - - - - - -
  -- return 'true' if std_logic_vectors match
  -- - - - - - - - - - - - - - - - - - - - -
  function eq (svec1, svec2 : slv_t) return boolean is
  begin
    return (svec1 = svec2);
  end function eq;

  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return 'true' if unsigned std_logic_vector and natural match
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function eq (svec : slv_t; val : natural) return boolean is
  begin
    return (unsigned(svec) = val);
  end function eq;




  -- - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if std_logic_vectors do not match
  -- - - - - - - - - - - - - - - - - - - - - - - -
  function ne (svec1, svec2 : slv_t) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '0';
    if (svec1 /= svec2) then
      result_v := '1';
    end if;
    return result_v;
  end function ne;
  -- - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if std_logic_vector and natural do not match
  -- - - - - - - - - - - - - - - - - - - - - - - -
  function ne (svec : slv_t; val : natural) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '0';
    if (unsigned(svec) /= val) then
      result_v := '1';
    end if;
    return result_v;
  end function ne;

  -- - - - - - - - - - - - - - - - - - - - - - - -
  -- return 'true' if std_logic_vectors do not match
  -- - - - - - - - - - - - - - - - - - - - - - - -
  function ne (svec1, svec2 : slv_t) return boolean is
  begin
    return (svec1 /= svec2);
  end function ne;
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return 'true' if unsigned std_logic_vector  and natural do not match
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function ne (svec : slv_t; val : natural) return boolean is
  begin
    return (unsigned(svec) /= val);
  end function ne;



  -- - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if std_logic_vectors is zero vec
  -- - - - - - - - - - - - - - - - - - - - - - - - -
  function is_zero (svec1 : slv_t) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '0';
    if (unsigned(svec1) = 0) then
      result_v := '1';
    end if;
    return result_v;
  end function is_zero;
  -- - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if std_logic_vectors is not zero vec
  -- - - - - - - - - - - - - - - - - - - - - - - - -
  function not_zero (svec1 : slv_t) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '1';
    if (unsigned(svec1) = 0) then
      result_v := '0';
    end if;
    return result_v;
  end function not_zero;
  -- - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if std_logic_vectors is zero vec
  -- - - - - - - - - - - - - - - - - - - - - - - - -
  function is_zero (svec1 : slv_t) return boolean is
  begin
    return (unsigned(svec1) = 0);
  end function is_zero;
  -- - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if std_logic_vectors is not zero vec
  -- - - - - - - - - - - - - - - - - - - - - - - - -
  function not_zero (svec1 : slv_t) return boolean is
  begin
    return not (unsigned(svec1) = 0);
  end function not_zero;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return vector with reversed order bits
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function rev_bits ( arg1 : slv_t ) return slv_t is
    variable result : slv_t(arg1'length-1 downto 0);
    variable tmpvec : slv_t(arg1'length-1 downto 0);
  begin
    result := (others => '0');
    tmpvec := arg1;
    for i in tmpvec'range loop
      result(tmpvec'length-1-i) := tmpvec(i);   -- 31 .. 24
    end loop;
    return result;
  end function rev_bits;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if unsigned svec1 is less than unsigned svec2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function lt (svec1, svec2 : slv_t)
    return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '0';
    if (unsigned(svec1) < unsigned(svec2)) then
      result_v := '1';
    end if;
    return result_v;
  end function lt;
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return 'true' if unsigned svec1 is less than unsigned svec2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function lt (svec1, svec2 : slv_t) return boolean is
  begin
    return unsigned(svec1) < unsigned(svec2);
  end function lt;

  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if unsigned svec1 is less than or equal to unsigned svec2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function le (svec1, svec2 : slv_t) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '1';
    if (unsigned(svec1) > unsigned(svec2)) then
      result_v := '0';
    end if;
    return result_v;
  end function le;
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if unsigned svec is less than or equal to unsigned val
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function le (svec : slv_t; val : natural) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '1';
    if (unsigned(svec) > val) then
      result_v := '0';
    end if;
    return result_v;
  end function le;
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return 'true' if unsigned svec1 is less than or equal to unsigned svec2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function le (svec1, svec2 : slv_t) return boolean is
  begin
    return not (unsigned(svec1) > unsigned(svec2));
  end function le;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if unsigned svec1 is greater than unsigned svec2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function gt (svec1, svec2 : slv_t) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '0';
    if (unsigned(svec1) > unsigned(svec2)) then
      result_v := '1';
    end if;
    return result_v;
  end function gt;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return true if unsigned svec1 is greater than unsigned svec2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function gt (svec1, svec2 : slv_t) return boolean is
  begin
    return (unsigned(svec1) > unsigned(svec2));
  end function gt;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if unsigned svec1 is greater than or equal to unsigned svec2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function ge (svec1, svec2 : slv_t) return std_logic is
    variable result_v : std_logic;
  begin
    result_v := '1';
    if (unsigned(svec1) < unsigned(svec2)) then
      result_v := '0';
    end if;
    return result_v;
  end function ge;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return true if unsigned svec1 is greater than or equal to unsigned svec2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function ge (svec1, svec2 : slv_t) return boolean is
  begin
    return not (unsigned(svec1) < unsigned(svec2));
  end function ge;


  -- - - - - - - - - - - - - - - - - - - - - - - -
  -- less_than_signed: Less than operator (signed)
  -- inputs: x = left operand, y = right operand
  -- return: (int)x < (int)y
  -- - - - - - - - - - - - - - - - - - - - - - - -
  function lt_signed (x, y : slv_t) return std_logic is
    variable v : signed(x'range);
  begin
    v := (signed(x) - signed(y));
    if (x(x'high) /= y(y'high)) then
      return x(x'high);
    else
      return v(v'high);
    end if;
  end function lt_signed;


  -- - - - - - - - - - - - - - - - - - - - - - - -
  -- gt_signed: Greater than operator (signed)
  -- inputs: x = left operand, y = right operand
  -- return: (int)x > (int)y
  -- - - - - - - - - - - - - - - - - - - - - - - -
  function gt_signed (x, y : slv_31_00) return std_logic is
    variable v : signed(31 downto 0);
  begin
    v := (signed(y) - signed(x));
    if (x(31) /= y(31)) then
      return y(31);
    else
      return v(31);
    end if;
  end function gt_signed;


  -- - - - - - - - - - - - - - - - - - - - - - -
  -- increment unsigned std_logic_vector by one
  -- - - - - - - - - - - - - - - - - - - - - - -
  function inc1u ( svec : slv_t ) return slv_t is
  begin
    return (slv_t(unsigned(svec) + 1));
  end function inc1u;


  -- - - - - - - - - - - - - - - - - - - - - - -
  -- decrement unsigned std_logic_vector by one
  -- - - - - - - - - - - - - - - - - - - - - - -
  function dec1u ( svec : slv_t ) return slv_t is
  begin
    return (slv_t(unsigned(svec) - 1));
  end function dec1u;


  -- - - - - - - - - - - - - - - - - - - - -
  -- add two unsigned vectors
  -- - - - - - - - - - - - - - - - - - - - -
  function addu ( svec1, svec2 : slv_t ) return slv_t is
  begin
    return (slv_t(unsigned(svec1) + unsigned(svec2)));
  end function addu;
  -- - - - - - - - - - - - - - - - - - - - -
  -- add unsigned vector and natural
  -- - - - - - - - - - - - - - - - - - - - -
  function addu ( svec : slv_t; val : natural )
    return slv_t is
  begin
    return (slv_t(unsigned(svec) + val));
  end function addu;



  -- - - - - - - - - - - - - - - - - - - - -
  -- add two signed vectors
  -- - - - - - - - - - - - - - - - - - - - -
  function adds ( svec1, svec2 : slv_t ) return slv_t is
  begin
    return (slv_t(signed(svec1) + signed(svec2)));
  end function adds;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' if "iword" matches to "opcode"
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function f_opcode_eq ( iword, opcode : slv_31_00 ) return std_logic is
    variable result_v : std_logic;
  begin
    --
    result_v := '0';
    --
    case opcode is
      when INST_AUIPC | INST_JAL | INST_LUI =>
        -- mask : X"0000007F"
        result_v := eq(iword(6 downto 0), opcode(6 downto 0));

      when INST_ADDI | INST_ANDI | INST_BEQ | INST_BGEU | INST_BGE |
        INST_BLTU | INST_BLT | INST_BNE | INST_CSRRCI | INST_CSRRC |
        INST_CSRRSI | INST_CSRRS | INST_CSRRWI | INST_CSRRW | INST_FENCE |
        INST_IFENCE | INST_JALR | INST_LBU | INST_LB | INST_LHU | INST_LH |
        INST_LWU | INST_LW | INST_ORI | INST_SB | INST_SH | INST_SLTIU |
        INST_SLTI | INST_SW | INST_XORI =>
        -- mask : X"0000707F"
        result_v := eq(iword(14 downto 12), opcode(14 downto 12)) and
                    eq(iword(6 downto 0),   opcode(6 downto 0));

      when INST_ERET =>
        -- mask : X"CFFFFFFF"
        result_v := eq(iword(31 downto 30), opcode(31 downto 30)) and
                    eq(iword(27 downto 0),  opcode(27 downto 0));

      when INST_SLLI | INST_SRAI | INST_SRLI =>
        -- mask : X"FE00707F"
        result_v := eq(iword(31 downto 25), opcode(31 downto 25)) and
                    eq(iword(14 downto 12), opcode(14 downto 12)) and
                    eq(iword(6 downto 0),   opcode(6 downto 0));

      when INST_ADD | INST_AND | INST_DIVU | INST_DIV |
        INST_MULHSU | INST_MULHU | INST_MULH | INST_MUL |
        INST_OR | INST_REMU | INST_REM | INST_SLL | INST_SLTU |
        INST_SLT | INST_SRA | INST_SRL | INST_SUB | INST_XOR |
        -- Zba - address generation extension
        INST_SH1ADD  |  INST_SH2ADD  |  INST_SH3ADD =>
        -- mask : X"FE00707F"
        result_v := eq(iword(31 downto 25), opcode(31 downto 25)) and
                    eq(iword(14 downto 12), opcode(14 downto 12)) and
                    eq(iword(6 downto 0),   opcode(6 downto 0));

      when INST_SFENCE =>
        -- mask : X"fe007fff"
        result_v := eq(iword(31 downto 25), opcode(31 downto 25)) and
                    eq(iword(14 downto 0),  opcode(14 downto 0));

      when INST_WFI =>
        -- mask : X"ffff8fff"
        result_v := eq(iword(31 downto 15), opcode(31 downto 15)) and
                    eq(iword(11 downto 0),  opcode(11 downto 0));

      when INST_EBREAK | INST_ECALL =>
        -- mask : X"ffffffff"
        result_v := eq(iword(31 downto 0),  opcode(31 downto 0));

      when others =>
        null;
        -- pragma translate_off
        assert (FALSE)
          report "-- ERROR: INVALID OPCODE"
          severity FAILURE;
        -- pragma translate_on
    end case;
    --
    return result_v;
    --
  end function  f_opcode_eq;



  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' and func id if "iword" matches to "opcode" for A-extension
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --
  -- mask : X"F800707F"
  --
  -- INST_AMO_MASK  0xF800707F          /======= MUST BE ZERO
  --                                   /////
  -- INST_LR      0x1000202F   "00010--00000-----010-----0101111"
  -- INST_SC      0x1800202F   "00011------------010-----0101111"
  -- INST_AMOSWAP X"0800202F   "00001------------010-----0101111"
  --
  -- INST_AMOADD  0x0000202F   "00000------------010-----0101111"
  -- INST_AMOXOR  0x2000202F   "00100------------010-----0101111"
  -- INST_AMOOR   0x4000202F   "01000------------010-----0101111"
  -- INST_AMOAND  0x6000202F   "01100------------010-----0101111"
  -- INST_AMOMIN  0x8000202F   "10000------------010-----0101111"
  -- INST_AMOMAX  0xa000202F   "10100------------010-----0101111"
  -- INST_AMOMINU 0xc000202F   "11000------------010-----0101111"
  -- INST_AMOMAXU 0xe000202F   "11100------------010-----0101111"
  --
  procedure p_amo_dec (        enable  : in  std_logic;
                        signal iword   : in  slv_31_00;
                        signal legal   : out std_logic;
                        signal amoop   : out amo_op_t ) is
    variable legal_v                 : std_logic;
    variable amoop_v                 : amo_op_t;
    variable iword_31_27_14_12_6_0_v : slv_14_00;
   begin
    --
    legal_v    := '0';
    amoop_v    := (amofunc => "0000", others => '0');
    --
    amoop_v.i_lr   := eq(iword(31 downto 27), INST_LR(31      downto 27)) and
                      eq(iword(24 downto 20), INST_LR(24      downto 20)) and--!
                      eq(iword(14 downto 12), INST_LR(14      downto 12)) and
                      eq(iword(6  downto 0),  INST_LR(6       downto 0));
    amoop_v.i_sc   := eq(iword(31 downto 27), INST_SC(31      downto 27)) and
                      eq(iword(14 downto 12), INST_SC(14      downto 12)) and
                      eq(iword(6  downto 0),  INST_SC(6       downto 0));
   -- pragma translate_off;
    amoop_v.i_swap := eq(iword(31 downto 27), INST_AMOSWAP(31 downto 27)) and
                      eq(iword(14 downto 12), INST_AMOSWAP(14 downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOSWAP(6  downto 0));
    amoop_v.i_add  := eq(iword(31 downto 27), INST_AMOADD(31  downto 27)) and
                      eq(iword(14 downto 12), INST_AMOADD(14  downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOADD(6   downto 0));
    amoop_v.i_xor  := eq(iword(31 downto 27), INST_AMOXOR(31  downto 27)) and
                      eq(iword(14 downto 12), INST_AMOXOR(14  downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOXOR(6   downto 0));
    amoop_v.i_and  := eq(iword(31 downto 27), INST_AMOAND(31  downto 27)) and
                      eq(iword(14 downto 12), INST_AMOAND(14  downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOAND(6   downto 0));
    amoop_v.i_or   := eq(iword(31 downto 27), INST_AMOOR(31   downto 27)) and
                      eq(iword(14 downto 12), INST_AMOOR(14   downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOOR(6    downto 0));
    amoop_v.i_min  := eq(iword(31 downto 27), INST_AMOMIN(31  downto 27)) and
                      eq(iword(14 downto 12), INST_AMOMIN(14  downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOMIN(6   downto 0));
    amoop_v.i_max  := eq(iword(31 downto 27), INST_AMOMAX(31  downto 27)) and
                      eq(iword(14 downto 12), INST_AMOMAX(14  downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOMAX(6   downto 0));
    amoop_v.i_minu := eq(iword(31 downto 27), INST_AMOMINU(31 downto 27)) and
                      eq(iword(14 downto 12), INST_AMOMINU(14 downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOMINU(6  downto 0));
    amoop_v.i_maxu := eq(iword(31 downto 27), INST_AMOMAXU(31 downto 27)) and
                      eq(iword(14 downto 12), INST_AMOMAXU(14 downto 12)) and
                      eq(iword(6  downto 0),  INST_AMOMAXU(6  downto 0));
    --
    amoop_v.lr_sc  := amoop_v.i_lr or amoop_v.i_sc;
    --
    -- pragma translate_on;
    --
    --
    -- AMONULL, AMOSWAP, AMOADD, AMOXOR,  AMOAND,                      -- 0 -  4
    -- AMOOR,   AMOMIN,  AMOMAX, AMOMINU, AMOMAXU );                   -- 5 -  9
    amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMONULL), 4)); -- 0
    --
    iword_31_27_14_12_6_0_v := iword(31 downto 27) & iword(14 downto 12) &
                               iword(6 downto 0);
    case iword_31_27_14_12_6_0_v is
      when "000010100101111"  =>      -- AMOSWAPo  X"0800202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOSWAP), 4));
      when "000000100101111"  =>      -- AMOADDo   X"0000202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOADD),  4));
      when "001000100101111"  =>      -- AMOXORo   X"2000202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOXOR),  4));
      when "011000100101111"  =>      -- AMOANDo   X"6000202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOAND),  4));
      when "010000100101111"  =>      -- AMOORo    X"4000202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOOR),   4));
      when "100000100101111"  =>      -- AMOMINo   X"8000202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOMIN),  4));
      when "101000100101111"  =>      -- AMOMAXo   X"A000202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOMAX),  4));
      when "110000100101111"  =>      -- AMOMINUo  X"C000202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOMINU), 4));
      when "111000100101111"  =>      -- AMOMAXUo  X"E000202F"
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMOMAXU), 4));
      when others =>
        amoop_v.amofunc :=  slv_t(to_unsigned(alu_amo_t'pos(AMONULL), 4));
    end case;
    --
    legal_v  := amoop_v.i_lr  or amoop_v.i_sc  or  not_zero(amoop_v.amofunc);
    --   amoop_v.i_swap or amoop_v.i_add  or amoop_v.i_xor or
    --   amoop_v.i_and  or amoop_v.i_or   or amoop_v.i_min or
    --   amoop_v.i_max  or amoop_v.i_minu or amoop_v.i_maxu;
    --
    legal    <= legal_v and enable;
    --
    if (enable = '1') then  amoop <= amoop_v;
    else                    amoop <= (amofunc => "0000", others => '0');
    end if;
   end procedure;



  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- return '1' and func id if "iword" matches to "opcode" for ZB-extension
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_zb_dec (        enable   : in  std_logic;
                       signal iword    : in  slv_31_00;
                       signal legal    : out std_logic;
                       signal zbfunc5  : out slv_04_00 ) is
    variable zbfunc5_v                : slv_04_00;
    variable iword_31_20_14_12_6_0_v  : slv_21_00;
    variable iword_31_25_14_12_6_0_v  : slv_16_00;
    variable legal31_v                : std_logic;
  begin
    --
    zbfunc5_v  := "11111";  -- i.e. N/A
    legal31_v  := '0';
    --
    -- mask : X"FFF0707F"
    --   INST_SEXTB  INST_SEXTH   INST_CLZ    ! INST_ZEXTH !
    --   INST_CTZ    INST_CPOP    INST_ORCB   INST_REV8 |
    --   INST_ZIP    INST_UNZIP   INST_REVB
    --
    iword_31_20_14_12_6_0_v := iword(31 downto 20) & iword(14 downto 12) &
                               iword(6 downto 0);
    case iword_31_20_14_12_6_0_v is
      when  "0110000001000010010011"  =>      -- INST_SEXTB         x60401013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbSEXTB), 5));
      when  "0110000001010010010011"  =>      -- INST_SEXTH         0x60501013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbSEXTH), 5));
--    when  "0000100000001000110011"  =>      --  INST_ZEXTH        0x08004033
--      zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbZEXTH), 56));
--      --
--      -- ! ! NOTE: MATCHES ALSO TO : INST_PACK ! !
--      --
      when  "0110000000000010010011"  =>      -- INST_CLZ           0x60001013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbCLZ),   5));
      when  "0110000000010010010011"  =>      -- INST_CTZ           0x60101013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbCTZ),   5));
      when  "0110000000100010010011"  =>      -- INST_CPOP          0x60201013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbCPOP),  5));
      when  "0010100001111010010011"  =>      -- INST_ORCB          0x28705013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbORCB),  5));
      when  "0110100110001010010011"  =>      -- INST_REV8          0x69805013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbREV8),  5));
      when  "0000100011110010010011"  =>      -- INST_ZIP           0x08F01013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbZIP),   5));
      when  "0000100011111010010011"  =>      -- INST_UNZIP         0x08F05013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbUNZIP), 5));
      when  "0110100001111010010011"  =>      -- INST_REVB         0x68705013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbREVB), 5));
      when others =>
        null;
    end case;
    --
    -- mask : X"FE00707F"
    --   INST_MAX   INST_MAXU   INST_MIN    INST_MINU
    --   INST_ANDN  INST_ORN    INST_XORN
    --   INST_ROR   INST_ROL    INST_RORI
    --   --
    --   INST_BCLR  INST_BCLRI  INST_BEXT   INST_BEXTI
    --   INST_BINV  INST_BINVI  INST_BSET   INST_BSETI
    --   --
    --   ! INST_PACK !  INST_PACKH  INST_CZEROEQZ  INST_CZERONEZ
    --
    iword_31_25_14_12_6_0_v := iword(31 downto 25) & iword(14 downto 12) &
                               iword(6 downto 0);
    case iword_31_25_14_12_6_0_v is
      when  "00001011100110011"  =>           -- INST_MAX           0x0a006033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbMAX),   5));
      when  "00001011110110011"  =>           -- INST_MAXU          0x0a007033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbMAXU),  5));
      when  "00001011000110011"  =>           -- INST_MIN           0x0a004033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbMIN),   5));
      when  "00001011010110011"  =>           -- INST_MINU          0x0a005033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbMINU),  5));
      when  "01000001110110011"  =>           -- INST_ANDN          0x40007033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbANDN),  5));
      when  "01000001100110011"  =>           -- INST_ORN           0x40006033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbORN),   5));
      when  "01000001000110011"  =>           -- INST_XORN          0x40004033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbXORN),  5));
      when  "01100000010110011"  =>           -- INST_ROL           0x60001033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbROL),   5));
      when  "01100001010110011"  =>           -- INST_ROR           0x60005033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbROR),   5));
      when  "01100001010010011"  =>           -- INST_RORI          0x60005013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbRORI),  5));
      when  "01001000010110011"  =>          -- INST_BCLR           0x48001033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbBCLR),  5));
      when  "01001000010010011"  =>           -- INST_BCLRI         0x48001013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbBCLRI), 5));
      when  "01001001010110011"  =>           -- INST_BEXT          0x48005033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbBEXT),  5));
      when  "01001001010010011"  =>           -- INST_BEXTI         0x48005013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbBEXTI), 5));
      when  "01101000010110011"  =>           -- INST_BINV          0x68001033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbBINV),  5));
      when  "01101000010010011"  =>           -- INST_BINVI         0x68001013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbBINVI), 5));
      when  "00101000010110011"  =>           -- INST_BSET          0x28001033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbBSET),  5));
      when  "00101000010010011"  =>           -- INST_BSETI         0x28001013
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbBSETI), 5));
--    when  "00001001000110011"  =>           -- INST_PACK          0x08004033
--      --
--      -- ! ! NOTE: MATCHES ALSO TO : INST_ZEXTH ! !
--      --
--      --   "0000100     1000110011" vs.   "0000100 00000 1000110011"
--      if ( not_zero(iword(24 downto 20)) ) then
--        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbPACK),  5));
--      end if;
      when  "00001001000110011"  =>           -- zbZEXTH_PACK       0x08004033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbZEXTH_PACK), 5));
      when  "00001001110110011"  =>           -- INST_PACKH         0x08007033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbPACKH),      5));
      when  "00001111010110011"  =>           -- INST_CZEROEQZ      0x0E005033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbCZEROEQZ),   5));
      when  "00001111110110011"  =>           -- INST_CZERONEZ      0x0E007033
        zbfunc5_v  := slv_t(to_unsigned(alu_zb_t'pos(zbCZERONEZ),   5));
        legal31_v  := '1';
      when others =>
        null;
    end case;
    --
    legal    <= (not_zero(not zbfunc5_v) or legal31_v)  and enable;
    if (enable = '1') then  zbfunc5 <= zbfunc5_v;
    else                    zbfunc5 <= (others => '1');
    end if;
    --
  end procedure;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- count leading zero bits, return 0 if msb == '1', 32 if all are '0'
  -- eg. "001..." returns 2
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function f_clz ( svec : slv_31_00 ) return slv_31_00 is
    variable res_v : integer range 0 to 63;
  begin
    res_v := 0;
    for ii in 31 downto 0 loop
      if ( svec(ii) = '0' ) then
        res_v := 32 - ii;
      else
        exit;
      end if;
    end loop;
    return slv_t(to_unsigned(res_v, svec'length));
  end f_clz;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- count set ('1') bits from vector
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function f_popcnt ( svec : slv_31_00 ) return slv_31_00 is
    variable res_v : integer range 0 to 63;
  begin
    res_v := 0;
    for ii in 31 downto 0 loop
      if ( svec(ii) = '1' ) then
        res_v := res_v + 1;
      end if;
    end loop;
    return slv_t(to_unsigned(res_v, svec'length));
  end f_popcnt;



  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- alu operations for ZB- andZicond instructions
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_alu_zb (        enable  : in  std_logic;
                       signal valid   : in  std_logic;
                       signal zbfunc  : in  slv_04_00;
                       signal imm12   : in  slv_31_00;  -- 31:12 sign bits
                       signal oprs1   : in  slv_31_00;
                       signal oprs2   : in  slv_31_00;
                       signal result  : out slv_31_00 ) is
    variable zbfunc_v      : natural range 0 to 32;
    variable result_v      : slv_31_00;
    variable tmp32_v       : slv_31_00;
    variable tmprot_v      : slv_31_00;
    variable sign_op_v     : std_logic;
    variable cmp_rs1_v     : slv_32_00;
    variable cmp_rs2_v     : slv_32_00;
    variable rs1_less_v    : std_logic;
    variable oprs2_zero_v  : std_logic;
  begin
    zbfunc_v    := to_uint(zbfunc);
    if (valid = '0') then
      zbfunc_v  := 32;
    end if;
    --
    tmp32_v     := X"00000000";
    tmprot_v    := X"00000000";
    result_v    := X"00000000";
    sign_op_v   := eq(zbfunc_v, alu_zb_t'pos(zbMAX)) or
                   eq(zbfunc_v, alu_zb_t'pos(zbMIN));
    cmp_rs1_v   := (oprs1(31) and sign_op_v) & oprs1;
    cmp_rs2_v   := (oprs2(31) and sign_op_v) & oprs2;
    rs1_less_v  := '0';
    if (signed(cmp_rs1_v) < signed(cmp_rs2_v)) then
      rs1_less_v      := '1';
    end if;
    oprs2_zero_v :=  is_zero(oprs2);
    --
    case zbfunc_v is
      when alu_zb_t'pos(zbSEXTB)      =>                      -- INST_SEXTB   0
        --  sign extend byte
        result_v  := slv_t(resize(signed(oprs1(7 downto 0)), 32));
      when alu_zb_t'pos(zbSEXTH)      =>                      -- INST_SEXTH   1
        --  sign extend half word
        result_v  := slv_t(resize(signed(oprs1(15 downto 0)), 32));
  --  when alu_zb_t'pos(zbZEXTH)      =>
      when alu_zb_t'pos(zbZEXTH_PACK) =>                      -- INST_ZEXTH  25
        --   ! ! ! zero extend halfword / pack two half words  ! ! !
        result_v  := oprs2(15 downto 0) & oprs1(15 downto 0); --(oprs2 == 0)
      when alu_zb_t'pos(zbMAX)  |                             -- INST_MAX     2
           alu_zb_t'pos(zbMAXU)       =>                      -- INST_MAXU    3
        result_v  := oprs1;
        if (rs1_less_v = '1') then
          result_v  := oprs2;
        end if;
      when alu_zb_t'pos(zbMIN)  |                             -- INST_MIN     4
           alu_zb_t'pos(zbMINU)       =>                      -- INST_MINU    5
        result_v  := oprs2;
        if (rs1_less_v = '1') then
          result_v  := oprs1;
        end if;
      when alu_zb_t'pos(zbANDN)       =>                      -- INST_ANDN    6
        result_v   := oprs1 and (not oprs2);
      when alu_zb_t'pos(zbORN)        =>                      -- INST_ORN     7
        result_v   := oprs1 or  (not oprs2);
      when alu_zb_t'pos(zbXORN)       =>                      -- INST_XORN    8
        result_v   := not (oprs1 xor oprs2);
  --  when alu_zb_t'pos(zbROL)        =>                      -- INST_ROL     9
  --    --  rotate left        (register)
  --    result_v := slv_t(rotate_left(
  --      unsigned(oprs1), to_uint(oprs2(4 downto 0))));
      when alu_zb_t'pos(zbROL)        |                       -- INST_ROL     9
           alu_zb_t'pos(zbROR)        |                       -- INST_ROR    10
           alu_zb_t'pos(zbRORI)       |                       -- INST_RORI   11
           alu_zb_t'pos(zbBEXT)       |                       -- INST_BEXT   19
           alu_zb_t'pos(zbBEXTI)      =>                      -- INST_BEXTI  20
        --  rotate right       (register / immediate)
        --  single-bit extract (register / immediate)
        tmp32_v   := oprs2;
        tmprot_v  := oprs1;
        if ( zbfunc_v = alu_zb_t'pos(zbROL) ) then
          tmprot_v   := rev_bits(oprs1);
        end if;
        if ( zbfunc_v = alu_zb_t'pos(zbRORI) or
             zbfunc_v = alu_zb_t'pos(zbBEXTI) ) then
          tmp32_v   := imm12;
        end if;
        result_v := slv_t(rotate_right(
          unsigned(tmprot_v), to_uint(tmp32_v(4 downto 0))));
        if ( zbfunc_v = alu_zb_t'pos(zbROL) ) then
          result_v   := rev_bits(result_v);
        end if;
        if ( zbfunc_v = alu_zb_t'pos(zbBEXT) or
             zbfunc_v = alu_zb_t'pos(zbBEXTI) ) then
          result_v(31 downto 1) := (others => '0');
        end if;
      when alu_zb_t'pos(zbCLZ)        |                       -- INST_CLZ    12
           alu_zb_t'pos(zbCTZ)        =>                      -- INST_CTZ    13
        --  count leading / trailing zero bits
        tmp32_v  := oprs1;
        if (zbfunc_v = alu_zb_t'pos(zbCTZ)) then
          tmp32_v  := rev_bits(oprs1);
        end if;
        result_v := f_clz(tmp32_v);
      when alu_zb_t'pos(zbCPOP)       =>                      -- INST_CPOP   14
        --  population count
        result_v := f_popcnt(oprs1);
      when alu_zb_t'pos(zbORCB)       =>                      -- INST_ORCB   15
        --  bitwise OR-Combine, byte granule
        result_v := X"00000000";
        if (not_zero(oprs1(31 downto 24))) then
          result_v(31 downto 24) := X"FF";
        end if;
        if (not_zero(oprs1(23 downto 16))) then
          result_v(23 downto 16) := X"FF";
        end if;
        if (not_zero(oprs1(15 downto 8))) then
          result_v(15 downto  8) := X"FF";
        end if;
        if (not_zero(oprs1(7 downto 0))) then
          result_v(7 downto  0) := X"FF";
        end if;
      when alu_zb_t'pos(zbREV8)       =>                      -- INST_REV8   16
        --  byte-reverse register
        result_v := oprs1(7 downto 0)   & oprs1(15 downto 8) &
                    oprs1(23 downto 16) & oprs1(31 downto 24);
      when alu_zb_t'pos(zbBCLR)       |                       -- INST_BCLR   17
           alu_zb_t'pos(zbBCLRI)      |                       -- INST_BCLRI  18
           alu_zb_t'pos(zbBINV)       |                       -- INST_BINV   21
           alu_zb_t'pos(zbBINVI)      |                       -- INST_BINVI  22
           alu_zb_t'pos(zbBSET)       |                       -- INST_BSET   23
           alu_zb_t'pos(zbBSETI)      =>                      -- INST_BSETI  24
        --  single-bit clear  (register / immediate)
        --  single-bit invert (register / immediate)
        --  single-bit set    (register / immediate)
        tmp32_v     := (others => '1');
        if ( zbfunc_v = alu_zb_t'pos(zbBCLRI) or
             zbfunc_v = alu_zb_t'pos(zbBINVI) or
             zbfunc_v = alu_zb_t'pos(zbBSETI)) then
          tmp32_v(to_uint(imm12(4 downto 0))) := '0';         -- imm
        else
          tmp32_v(to_uint(oprs2(4 downto 0))) := '0';         -- reg
        end if;
        --
        if ( zbfunc_v = alu_zb_t'pos(zbBCLR) or
             zbfunc_v = alu_zb_t'pos(zbBCLRI) ) then
          result_v  :=     oprs1 and tmp32_v;                 -- clear bit
        elsif ( zbfunc_v = alu_zb_t'pos(zbBSET) or
                zbfunc_v = alu_zb_t'pos(zbBSETI)) then
          result_v  :=     oprs1 or  (not tmp32_v);           -- set bit
        else
          result_v  := not(oprs1 xor tmp32_v);                -- invert bit
        end if;
   -- when alu_zb_t'pos(zbPACK)       =>                      -- INST_PACK  !
      when alu_zb_t'pos(zbPACKH)      =>                      -- INST_PACKH  26
        --  pack the low bytes of rs1 and rs2
        result_v  := X"0000" & oprs2(7 downto 0) & oprs1(7 downto 0);
      when alu_zb_t'pos(zbZIP)        =>                      -- INST_ZIP    27
        --  gather odd and even bits into upper/lower halves
        for ii in 0 to 32/2-1 loop                            --   0..15
          result_v(2*ii)    := oprs1(ii);                     --   0,2,4, ..,30
          result_v(2*ii+1)  := oprs1(ii+32/2);                --   1,3,5, ..,31
        end loop;
      when alu_zb_t'pos(zbUNZIP)      =>                      -- INST_UNZIP  28
        --  inverse of the zip instruction
        for ii in 0 to 32/2-1 loop                            --   0..15
          result_v(ii)       := oprs1(2*ii);                  --   0,1,2, ..,15
          result_v(ii+32/2)  := oprs1(2*ii+1);                --   16,17, ..,31
        end loop;
      when alu_zb_t'pos(zbREVB)       =>                      -- INST_REVB   29
        --  reverse the bits in each byte
        result_v := rev_bits(oprs1(31 downto 24)) &
                    rev_bits(oprs1(23 downto 16)) &
                    rev_bits(oprs1(15 downto 8))  &
                    rev_bits(oprs1(7  downto 0)) ;
      --
      --
      when alu_zb_t'pos(zbCZEROEQZ)   =>                     -- INST_CZEROEQZ 30
        -- conditional zero, if condition is equal to zero
        result_v    := oprs1;
        if (oprs2_zero_v = '1') then
          result_v  := X"00000000";
        end if;
      when alu_zb_t'pos(zbCZERONEZ)   =>                     -- INST_CZERONEZ 31
        -- conditional zero, if condition is nonzero
        result_v    := oprs1;
        if (oprs2_zero_v /= '1') then
          result_v  := X"00000000";
        end if;
      --
      --
      when others =>
        null;
    end case;
    --
    if (enable = '1') then
      result <= result_v;
    else
      result <= X"00000000";
    end if;
   end procedure;



  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  --  D flip-flop with synchonous active-low reset
  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_dffr ( signal clk   : in  std_logic;
                     signal xrst  : in  std_logic;
                            d     : in  std_logic;
                     signal q     : out std_logic ) is
  begin
    if rising_edge ( clk ) then
      q <= xrst and d;
    end if;
  end procedure;


  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  --  D flip-flop with synchonous active-low reset
  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_dffs ( signal clk   : in  std_logic;
                     signal xset  : in  std_logic;
                            d     : in  std_logic;
                     signal q     : out std_logic ) is
  begin
    if rising_edge ( clk ) then
      q <= (not xset) or d;
    end if;
  end procedure;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --  one hot decoder for register file write enable
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_one_hot_rf32 ( signal addr     : in  slv_t(4 downto 0);
                                    half_rf  : in  std_logic;
                             signal dout     : out slv_31_00 ) is
  begin
    dout                  <= (others => '0');
    dout(to_uint(addr))   <=  '1';
    if (half_rf = '1') then
      dout(31 downto 16)  <= (others => '0');
    end  if;
  end procedure;


  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --  register file
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_dff32x32 ( signal clk   : in  std_logic;
                         signal xrst  : in  std_logic;
                         signal wr    : in  slv_31_00;
                         signal d     : in  slv_31_00;
                         signal q     : out rfile_t ) is
  begin
    if rising_edge( clk ) then
        if    ( xrst = '0' ) then  q <= (others => X"00000000");
        else
        for ii in 1 to 31 loop
          if ( wr(ii) = '1' )   then  q(ii) <= d; end if;
        end loop;
      end if;
    q(0) <= X"00000000";
    end if;
  end procedure;


  procedure p_rf32x2mux ( signal ra  : in  slv_04_00;
                          signal rb  : in  slv_04_00;
                          signal q   : in  rfile_t;
                          signal ya  : out slv_31_00;
                          signal yb  : out slv_31_00 ) is
  begin
    case ra is
      when "11111" =>  ya <= q(31);
      when "11110" =>  ya <= q(30);
      when "11101" =>  ya <= q(29);
      when "11100" =>  ya <= q(28);
      when "11011" =>  ya <= q(27);
      when "11010" =>  ya <= q(26);
      when "11001" =>  ya <= q(25);
      when "11000" =>  ya <= q(24);
      when "10111" =>  ya <= q(23);
      when "10110" =>  ya <= q(22);
      when "10101" =>  ya <= q(21);
      when "10100" =>  ya <= q(20);
      when "10011" =>  ya <= q(19);
      when "10010" =>  ya <= q(18);
      when "10001" =>  ya <= q(17);
      when "10000" =>  ya <= q(16);
      when "01111" =>  ya <= q(15);
      when "01110" =>  ya <= q(14);
      when "01101" =>  ya <= q(13);
      when "01100" =>  ya <= q(12);
      when "01011" =>  ya <= q(11);
      when "01010" =>  ya <= q(10);
      when "01001" =>  ya <= q(9);
      when "01000" =>  ya <= q(8);
      when "00111" =>  ya <= q(7);
      when "00110" =>  ya <= q(6);
      when "00101" =>  ya <= q(5);
      when "00100" =>  ya <= q(4);
      when "00011" =>  ya <= q(3);
      when "00010" =>  ya <= q(2);
      when "00001" =>  ya <= q(1);
      when others  =>  ya <= X"00000000";
    end case;
    --
    case rb is
      when "11111" =>  yb <= q(31);
      when "11110" =>  yb <= q(30);
      when "11101" =>  yb <= q(29);
      when "11100" =>  yb <= q(28);
      when "11011" =>  yb <= q(27);
      when "11010" =>  yb <= q(26);
      when "11001" =>  yb <= q(25);
      when "11000" =>  yb <= q(24);
      when "10111" =>  yb <= q(23);
      when "10110" =>  yb <= q(22);
      when "10101" =>  yb <= q(21);
      when "10100" =>  yb <= q(20);
      when "10011" =>  yb <= q(19);
      when "10010" =>  yb <= q(18);
      when "10001" =>  yb <= q(17);
      when "10000" =>  yb <= q(16);
      when "01111" =>  yb <= q(15);
      when "01110" =>  yb <= q(14);
      when "01101" =>  yb <= q(13);
      when "01100" =>  yb <= q(12);
      when "01011" =>  yb <= q(11);
      when "01010" =>  yb <= q(10);
      when "01001" =>  yb <= q(9);
      when "01000" =>  yb <= q(8);
      when "00111" =>  yb <= q(7);
      when "00110" =>  yb <= q(6);
      when "00101" =>  yb <= q(5);
      when "00100" =>  yb <= q(4);
      when "00011" =>  yb <= q(3);
      when "00010" =>  yb <= q(2);
      when "00001" =>  yb <= q(1);
      when others  =>  yb <= X"00000000";
    end case;
  end procedure;


  -- - - - - - - - - - - - -
  --  load response
  -- - - - - - - - - - - - -
  --        dmem rdata
  --              |_______________________________________________
  --              |                        |                      |
  --   [31:24][23:16][15:8][7:0]           |               [31:16] [15:00]
  --   ___|______|______|____|___           \          _______|_______|_______
  --   \  11     10     01   00 /-- resp_addr_q(1:0) --\      1-      0-     /
  --    \______________________/            /           \___________________/
  --               |                       |                      |
  -- X"000000" & [7:0]                     |         X"0000" & [15:0]
  -- X"FFFFFF" &   |    (sign extend)      |         X"FFFF" &    |
  --               |                       |                      |
  --               |_________________      |      ________________|
  --                            _____|_____|_____|_____
  --                          --\                     /
  --                             \___________________/
  --                                       |
  --                                   wb value
  --
  procedure p_ld_resp   ( signal ack     : in  std_logic;
                          signal err     : in  std_logic;
                          signal ldtype  : in  dmem_ldtyp_t;
                          signal idata   : in  slv_31_00;
                          signal odata   : out slv_31_00 ) is
    variable ld_flt_v     : std_logic;
    variable ld_dat_v     : std_logic;
    variable tmp_odata_v  : slv_31_00;
  begin
    --  tag associated with load
    --  access fault - pass badaddr on writeback result bus
    ld_flt_v     := (ack and err) or ldtype.ualigned_e2;
    ld_dat_v     := ack and ldtype.ld;
    tmp_odata_v  := idata;                                 -- load word
    --
    if (ldtype.ldb = '1') then                             -- load byte
      case ldtype.exp_addr(1 downto 0) is
        when "11" => tmp_odata_v := X"000000" & idata(31 downto 24);
        when "10" => tmp_odata_v := X"000000" & idata(23 downto 16);
        when "01" => tmp_odata_v := X"000000" & idata(15 downto  8);
        when "00" => tmp_odata_v := X"000000" & idata( 7 downto  0);
        when others => null;
      end case;
      --
      if (ldtype.sint = '1' and tmp_odata_v(7) = '1') then
        tmp_odata_v  := (X"FFFFFF" & tmp_odata_v(7 downto 0));
      end if;
    elsif (ldtype.ldh = '1') then                          -- load half word
      tmp_odata_v    := (X"0000" & idata(15 downto 0));
      if (ldtype.exp_addr(1) = '1') then
        tmp_odata_v  := (X"0000" & idata(31 downto 16));
      end if;
      --
      if (ldtype.sint = '1' and tmp_odata_v(15) = '1') then
        tmp_odata_v  := (X"FFFF" & tmp_odata_v(15 downto 0));
      end if;
    end if;
    --
    odata    <=  X"00000000";
    if (ld_flt_v = '1') then
      odata  <= ldtype.exp_addr;
    elsif (ld_dat_v = '1') then                            -- handle responses
      odata  <= tmp_odata_v;
    end if;
  end procedure;



  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --  branch address calculation
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  procedure p_btarget ( signal iword         : in  slv_31_00;
                        signal btype         : in  branch_op_t;
                        signal pc            : in  slv_31_00;
                        signal oprs1_dat     : in  slv_31_00;
                        signal oprs2_dat     : in  slv_31_00;
                        signal opcode_valid  : in std_logic;
                        signal branch_pc     : out slv_31_00;
                        signal branch_req    : out std_logic ) is
    variable imm12_v      : slv_31_00;
    variable bimm_v       : slv_31_00;
    variable jimm20_v     : slv_31_00;
    variable arg1_v       : slv_t(31+1 downto 0);
    variable arg2_v       : slv_t(31+1 downto 0);
    variable branch_go_v  : std_logic;
  begin
    --
    -- default branch target is relative to current PC
    --      : addu(pc,  bimm_w;
    -- jal  : addu(pc,  jimm20_w)
    -- jalr : adds(dat, imm12_w)
    --
    -- B-immediate
    bimm_v   := ( (18 downto 0 => iword(31)) &
                  iword(31) & iword(7) & iword(30 downto 25) &
                  iword(11 downto 8) & '0' );
    -- J-immediate
    jimm20_v := ( (11 downto 0 => iword(31)) & iword(19 downto 12) &
                  iword(20) & iword(30 downto 25) & iword(24 downto 21) & '0' );
    -- I-immediate
    imm12_v  := ( (19 downto 0 => iword(31)) & iword(31 downto 20) );
    --
    --  default branch target is relative to current PC
    arg1_v := '0' & pc;                         -- UNSIGNED ADD
    arg2_v := '0' & bimm_v;
    if ( btype.i_jal = '1' ) then               -- jal, jump and link
      arg2_v := '0' & jimm20_v;
    end if;
    if ( btype.i_jalr = '1' ) then              -- jalr, jump and link register
      arg1_v := oprs1_dat(oprs1_dat'left)  &  oprs1_dat;    -- SIGNED ADD
      arg2_v := imm12_v(imm12_v'left)      &  imm12_v;
    end if;
    --
    branch_pc    <= adds(arg1_v, arg2_v)(31 downto 1) & '0';
    -- jal, jalr, beq, bne, blt, bge, bltu, bgeu
    branch_go_v  := btype.i_jal or
                    btype.i_jalr or
                    ( btype.i_beq and      eq(oprs1_dat, oprs2_dat) ) or
                    (btype.i_bne and (not (eq(oprs1_dat, oprs2_dat)))) or
                    (btype.i_blt and       lt_signed(oprs1_dat, oprs2_dat)) or
                    (btype.i_bge and
                     (not lt_signed(oprs1_dat, oprs2_dat))) or   -- = GE
                    (btype.i_bltu and      lt(oprs1_dat, oprs2_dat)) or
                    (btype.i_bgeu and not  lt(oprs1_dat, oprs2_dat));
    -- branch signals exec -> issue
    branch_req   <= opcode_valid and branch_go_v;
  end procedure;

end rv_pkg;
