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

package CPU_Decode_C;

// ================================================================
// fv_decode_C() is a function that decodes and expands a 16-bit
// "compressed" RISC-V instruction ('C' extension) into its full
// 32-bit equivalent.

// ================================================================
// Exports

export fv_decode_C;

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

// None

// ----------------
// BSV additional libs

// None

// ================================================================
// Project imports

import ISA_Decls       :: *;
import ISA_Decls_CHERI :: *;

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

function Instr fv_decode_C (MISA misa, Bit #(2) xl, Bool cap_enc, Instr_C instr_C);
   // ----------------
   // Try each possible C instruction
   match { .valid_C_LWSP,               .i_C_LWSP }               = fv_decode_C_LWSP               (misa, xl, cap_enc, instr_C);
   match { .valid_C_SWSP,               .i_C_SWSP }               = fv_decode_C_SWSP               (misa, xl, cap_enc, instr_C);
   match { .valid_C_LW,                 .i_C_LW }                 = fv_decode_C_LW                 (misa, xl, cap_enc, instr_C);
   match { .valid_C_SW,                 .i_C_SW }                 = fv_decode_C_SW                 (misa, xl, cap_enc, instr_C);

   match { .valid_C_J,                  .i_C_J }                  = fv_decode_C_J                  (misa, xl, cap_enc, instr_C);
   match { .valid_C_JAL,                .i_C_JAL }                = fv_decode_C_JAL                (misa, xl, cap_enc, instr_C);
   match { .valid_C_JR,                 .i_C_JR }                 = fv_decode_C_JR                 (misa, xl, cap_enc, instr_C);
   match { .valid_C_JALR,               .i_C_JALR }               = fv_decode_C_JALR               (misa, xl, cap_enc, instr_C);
   match { .valid_C_BEQZ,               .i_C_BEQZ }               = fv_decode_C_BEQZ               (misa, xl, cap_enc, instr_C);
   match { .valid_C_BNEZ,               .i_C_BNEZ }               = fv_decode_C_BNEZ               (misa, xl, cap_enc, instr_C);
   match { .valid_C_LI,                 .i_C_LI }                 = fv_decode_C_LI                 (misa, xl, cap_enc, instr_C);
   match { .valid_C_LUI,                .i_C_LUI }                = fv_decode_C_LUI                (misa, xl, cap_enc, instr_C);
   match { .valid_C_ADDI,               .i_C_ADDI }               = fv_decode_C_ADDI               (misa, xl, cap_enc, instr_C);
   match { .valid_C_NOP,                .i_C_NOP }                = fv_decode_C_NOP                (misa, xl, cap_enc, instr_C);
   match { .valid_C_ADDIW,              .i_C_ADDIW }              = fv_decode_C_ADDIW              (misa, xl, cap_enc, instr_C);
   match { .valid_C_ADDI16SP,           .i_C_ADDI16SP }           = fv_decode_C_ADDI16SP           (misa, xl, cap_enc, instr_C);
   match { .valid_C_CIncOffsetImm16CSP, .i_C_CIncOffsetImm16CSP } = fv_decode_C_CIncOffsetImm16CSP (misa, xl, cap_enc, instr_C);
   match { .valid_C_ADDI4SPN,           .i_C_ADDI4SPN }           = fv_decode_C_ADDI4SPN           (misa, xl, cap_enc, instr_C);
   match { .valid_C_CIncOffsetImm4CSPN, .i_C_CIncOffsetImm4CSPN } = fv_decode_C_CIncOffsetImm4CSPN (misa, xl, cap_enc, instr_C);
   match { .valid_C_SLLI,               .i_C_SLLI }               = fv_decode_C_SLLI               (misa, xl, cap_enc, instr_C);
   match { .valid_C_SRLI,               .i_C_SRLI }               = fv_decode_C_SRLI               (misa, xl, cap_enc, instr_C);
   match { .valid_C_SRAI,               .i_C_SRAI }               = fv_decode_C_SRAI               (misa, xl, cap_enc, instr_C);
   match { .valid_C_ANDI,               .i_C_ANDI }               = fv_decode_C_ANDI               (misa, xl, cap_enc, instr_C);
   match { .valid_C_MV,                 .i_C_MV }                 = fv_decode_C_MV                 (misa, xl, cap_enc, instr_C);
   match { .valid_C_ADD,                .i_C_ADD }                = fv_decode_C_ADD                (misa, xl, cap_enc, instr_C);
   match { .valid_C_AND,                .i_C_AND }                = fv_decode_C_AND                (misa, xl, cap_enc, instr_C);
   match { .valid_C_OR,                 .i_C_OR }                 = fv_decode_C_OR                 (misa, xl, cap_enc, instr_C);
   match { .valid_C_XOR,                .i_C_XOR }                = fv_decode_C_XOR                (misa, xl, cap_enc, instr_C);
   match { .valid_C_SUB,                .i_C_SUB }                = fv_decode_C_SUB                (misa, xl, cap_enc, instr_C);
   match { .valid_C_ADDW,               .i_C_ADDW }               = fv_decode_C_ADDW               (misa, xl, cap_enc, instr_C);
   match { .valid_C_SUBW,               .i_C_SUBW }               = fv_decode_C_SUBW               (misa, xl, cap_enc, instr_C);
   match { .valid_C_EBREAK,             .i_C_EBREAK }             = fv_decode_C_EBREAK             (misa, xl, cap_enc, instr_C);

   match { .valid_C_LDSP,               .i_C_LDSP }               = fv_decode_C_LDSP               (misa, xl, cap_enc, instr_C);
   match { .valid_C_SDSP,               .i_C_SDSP }               = fv_decode_C_SDSP               (misa, xl, cap_enc, instr_C);
   match { .valid_C_LD,                 .i_C_LD }                 = fv_decode_C_LD                 (misa, xl, cap_enc, instr_C);
   match { .valid_C_SD,                 .i_C_SD }                 = fv_decode_C_SD                 (misa, xl, cap_enc, instr_C);

`ifdef RV64
   match { .valid_C_LQSP,               .i_C_LQSP }               = fv_decode_C_LQSP               (misa, xl, cap_enc, instr_C);
   match { .valid_C_SQSP,               .i_C_SQSP }               = fv_decode_C_SQSP               (misa, xl, cap_enc, instr_C);
   match { .valid_C_LQ,                 .i_C_LQ }                 = fv_decode_C_LQ                 (misa, xl, cap_enc, instr_C);
   match { .valid_C_SQ,                 .i_C_SQ }                 = fv_decode_C_SQ                 (misa, xl, cap_enc, instr_C);
`endif

`ifdef ISA_F
   match { .valid_C_FLWSP,              .i_C_FLWSP }              = fv_decode_C_FLWSP              (misa, xl, cap_enc, instr_C);
   match { .valid_C_FSWSP,              .i_C_FSWSP }              = fv_decode_C_FSWSP              (misa, xl, cap_enc, instr_C);
   match { .valid_C_FLW,                .i_C_FLW }                = fv_decode_C_FLW                (misa, xl, cap_enc, instr_C);
   match { .valid_C_FSW,                .i_C_FSW }                = fv_decode_C_FSW                (misa, xl, cap_enc, instr_C);
`endif

`ifdef ISA_D
   match { .valid_C_FLDSP,              .i_C_FLDSP }              = fv_decode_C_FLDSP              (misa, xl, cap_enc, instr_C);
   match { .valid_C_FSDSP,              .i_C_FSDSP }              = fv_decode_C_FSDSP              (misa, xl, cap_enc, instr_C);
   match { .valid_C_FLD,                .i_C_FLD }                = fv_decode_C_FLD                (misa, xl, cap_enc, instr_C);
   match { .valid_C_FSD,                .i_C_FSD }                = fv_decode_C_FSD                (misa, xl, cap_enc, instr_C);
`endif

   // ----------------
   // Pick the one (if any) that decodes

   Instr instr = ?;

   if      (valid_C_LWSP)               instr = i_C_LWSP;
   else if (valid_C_SWSP)               instr = i_C_SWSP;
   else if (valid_C_LW)                 instr = i_C_LW;
   else if (valid_C_SW)                 instr = i_C_SW;

   else if (valid_C_J)                  instr = i_C_J;
   else if (valid_C_JAL)                instr = i_C_JAL;
   else if (valid_C_JR)                 instr = i_C_JR;
   else if (valid_C_JALR)               instr = i_C_JALR;
   else if (valid_C_BEQZ)               instr = i_C_BEQZ;
   else if (valid_C_BNEZ)               instr = i_C_BNEZ;
   else if (valid_C_LI)                 instr = i_C_LI;
   else if (valid_C_LUI)                instr = i_C_LUI;
   else if (valid_C_ADDI)               instr = i_C_ADDI;
   else if (valid_C_NOP)                instr = i_C_NOP;
   else if (valid_C_ADDIW)              instr = i_C_ADDIW;
   else if (valid_C_ADDI16SP)           instr = i_C_ADDI16SP;
   else if (valid_C_CIncOffsetImm16CSP) instr = i_C_CIncOffsetImm16CSP;
   else if (valid_C_ADDI4SPN)           instr = i_C_ADDI4SPN;
   else if (valid_C_CIncOffsetImm4CSPN) instr = i_C_CIncOffsetImm4CSPN;
   else if (valid_C_SLLI)               instr = i_C_SLLI;
   else if (valid_C_SRLI)               instr = i_C_SRLI;
   else if (valid_C_SRAI)               instr = i_C_SRAI;
   else if (valid_C_ANDI)               instr = i_C_ANDI;
   else if (valid_C_MV)                 instr = i_C_MV;
   else if (valid_C_ADD)                instr = i_C_ADD;
   else if (valid_C_AND)                instr = i_C_AND;
   else if (valid_C_OR)                 instr = i_C_OR;
   else if (valid_C_XOR)                instr = i_C_XOR;
   else if (valid_C_SUB)                instr = i_C_SUB;
   else if (valid_C_ADDW)               instr = i_C_ADDW;
   else if (valid_C_SUBW)               instr = i_C_SUBW;
   else if (valid_C_EBREAK)             instr = i_C_EBREAK;

   else if (valid_C_LDSP)               instr = i_C_LDSP;
   else if (valid_C_SDSP)               instr = i_C_SDSP;
   else if (valid_C_LD)                 instr = i_C_LD;
   else if (valid_C_SD)                 instr = i_C_SD;

`ifdef RV64
   else if (valid_C_LQSP)               instr = i_C_LQSP;
   else if (valid_C_SQSP)               instr = i_C_SQSP;
   else if (valid_C_LQ)                 instr = i_C_LQ;
   else if (valid_C_SQ)                 instr = i_C_SQ;
`endif

`ifdef ISA_F
   else if (valid_C_FLWSP)              instr = i_C_FLWSP;
   else if (valid_C_FSWSP)              instr = i_C_FSWSP;
   else if (valid_C_FLW)                instr = i_C_FLW;
   else if (valid_C_FSW)                instr = i_C_FSW;
`endif

`ifdef ISA_D
   else if (valid_C_FLDSP)              instr = i_C_FLDSP;
   else if (valid_C_FSDSP)              instr = i_C_FSDSP;
   else if (valid_C_FLD)                instr = i_C_FLD;
   else if (valid_C_FSD)                instr = i_C_FSD;
`endif

   else
      instr = illegal_instr;

   return instr;
endfunction

// ================================================================
// 'C' Extension Stack-Pointer-Based Loads

// LWSP: expands into LW
function Tuple2 #(Bool, Instr) fv_decode_C_LWSP (MISA  misa, Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: I-type
      match { .funct3, .imm_at_12, .rd, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(8) offset = { imm_at_6_2 [1:0], imm_at_12, imm_at_6_2 [4:2], 2'b0};

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (rd != 0)
                       && (funct3 == funct3_C_LWSP));

      RegName rs1   = reg_sp;
      let     instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_LW,  rd,  op_LOAD);

      return tuple2 (is_legal, instr);
   end
endfunction

// LDSP: expands into LD
function Tuple2 #(Bool, Instr) fv_decode_C_LDSP (MISA  misa, Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: I-type
      match { .funct3, .imm_at_12, .rd, .imm_at_6_2, .op } = fv_ifields_CI_type  (instr_C);
      Bit #(9) offset = { imm_at_6_2 [2:0], imm_at_12, imm_at_6_2 [4:3], 3'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (rd != 0)
                       && (funct3 == funct3_C_LDSP)
                       && (   (xl == misa_mxl_64)
                           || (cap_enc)));

      RegName rs1   = reg_sp;
      let     instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_LD,  rd,  op_LOAD);

      return tuple2 (is_legal, instr);
   end
endfunction

`ifdef RV64
// LQSP: expands into LQ
function Tuple2 #(Bool, Instr) fv_decode_C_LQSP (MISA  misa, Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: I-type
      match { .funct3, .imm_at_12, .rd, .imm_at_6_2, .op } = fv_ifields_CI_type  (instr_C);
      Bit #(10) offset = { imm_at_6_2 [3:0], imm_at_12, imm_at_6_2 [4], 4'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (rd != 0)
                       && (funct3 == funct3_C_LQSP)
                       && (xl == misa_mxl_64)
                       && (cap_enc));

      RegName rs1   = reg_sp;
      let     instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_LQ,  rd,  op_MISC_MEM);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

`ifdef ISA_F
// FLWSP: expands into FLW
function Tuple2 #(Bool, Instr) fv_decode_C_FLWSP (MISA  misa, Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: I-type
      match { .funct3, .imm_at_12, .rd, .imm_at_6_2, .op } = fv_ifields_CI_type  (instr_C);
      Bit #(8) offset = { imm_at_6_2 [1:0], imm_at_12, imm_at_6_2 [4:2], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
		       && (op == opcode_C2)
		       && (funct3 == funct3_C_FLWSP)
		       && (misa.f == 1'b1)
                       && (! cap_enc));

      RegName rs1   = reg_sp;
      let     instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_FLW,  rd,  op_LOAD_FP);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

`ifdef ISA_D
// FLDSP: expands into FLD
function Tuple2 #(Bool, Instr) fv_decode_C_FLDSP (MISA  misa,  Bit #(2) xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: I-type
      match { .funct3, .imm_at_12, .rd, .imm_at_6_2, .op } = fv_ifields_CI_type  (instr_C);
      Bit #(9) offset = { imm_at_6_2 [2:0], imm_at_12, imm_at_6_2 [4:3], 3'b0 };

      Bool is_legal = ((misa.c == 1'b1)
		       && (op == opcode_C2)
		       && (funct3 == funct3_C_FLDSP)
		       && (misa.d == 1'b1)
                       && (   (xl == misa_mxl_32)
                           || (! cap_enc)));

      RegName rs1   = reg_sp;
      let     instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_FLD,  rd,  op_LOAD_FP);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

// ================================================================
// 'C' Extension Stack-Pointer-Based Stores

// SWSP: expands to SW
function Tuple2 #(Bool, Instr) fv_decode_C_SWSP (MISA  misa,  Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: CSS-type
      match { .funct3, .imm_at_12_7, .rs2, .op } = fv_ifields_CSS_type (instr_C);
      Bit #(8) offset = { imm_at_12_7 [1:0], imm_at_12_7 [5:2], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct3 == funct3_C_SWSP));

      RegName   rs1   = reg_sp;
      let       instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_SW, op_STORE);

      return tuple2 (is_legal, instr);
   end
endfunction

// SDSP: expands to SD
function Tuple2 #(Bool, Instr) fv_decode_C_SDSP (MISA  misa,  Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: CSS-type
      match { .funct3, .imm_at_12_7, .rs2, .op } = fv_ifields_CSS_type (instr_C);
      Bit #(9) offset = { imm_at_12_7 [2:0], imm_at_12_7 [5:3], 3'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct3 == funct3_C_SDSP)
                       && (   (xl == misa_mxl_64)
                           || (cap_enc)));

      RegName   rs1   = reg_sp;
      let       instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_SD, op_STORE);

      return tuple2 (is_legal, instr);
   end
endfunction

`ifdef RV64
// SQSP: expands to SQ
function Tuple2 #(Bool, Instr) fv_decode_C_SQSP (MISA  misa,  Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: CSS-type
      match { .funct3, .imm_at_12_7, .rs2, .op } = fv_ifields_CSS_type (instr_C);
      Bit #(10) offset = { imm_at_12_7 [3:0], imm_at_12_7 [5:4], 4'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct3 == funct3_C_SQSP)
                       && (xl == misa_mxl_64)
                       && (cap_enc));

      RegName   rs1   = reg_sp;
      let       instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_SQ, op_STORE);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

`ifdef ISA_F
// FSWSP: expands to FSW
function Tuple2 #(Bool, Instr) fv_decode_C_FSWSP (MISA  misa,  Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: CSS-type
      match { .funct3, .imm_at_12_7, .rs2, .op } = fv_ifields_CSS_type (instr_C);
      Bit #(8) offset = { imm_at_12_7 [1:0], imm_at_12_7 [5:2], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct3 == funct3_C_FSWSP)
                       && (xl == misa_mxl_32)
                       && (! cap_enc));

      RegName   rs1   = reg_sp;
      let       instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_FSW, op_STORE_FP);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

`ifdef ISA_D
// FSDSP: expands to FSD
function Tuple2 #(Bool, Instr) fv_decode_C_FSDSP (MISA  misa,  Bit #(2)  xl, Bool cap_enc, Instr_C  instr_C);
   begin
      // Instr fields: CSS-type
      match { .funct3, .imm_at_12_7, .rs2, .op } = fv_ifields_CSS_type (instr_C);
      Bit #(9) offset = { imm_at_12_7 [2:0], imm_at_12_7 [5:3], 3'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct3 == funct3_C_FSDSP)
                       && (   (xl == misa_mxl_32)
                           || (! cap_enc)));

      RegName   rs1   = reg_sp;
      let       instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_FSD, op_STORE_FP);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

// ================================================================
// 'C' Extension Register-Based Loads

// C_LW: expands to LW
function Tuple2 #(Bool, Instr) fv_decode_C_LW (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CL-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rd, .op } = fv_ifields_CL_type (instr_C);
      Bit #(7) offset = { imm_at_6_5 [0], imm_at_12_10, imm_at_6_5 [1], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_LW));

      let instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_LW,  rd,  op_LOAD);

      return tuple2 (is_legal, instr);
   end
endfunction

// C_LD: expands to LD
function Tuple2 #(Bool, Instr) fv_decode_C_LD (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CL-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rd, .op } = fv_ifields_CL_type (instr_C);
      Bit #(8) offset = { imm_at_6_5, imm_at_12_10, 3'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_LD)
                       && (   (xl == misa_mxl_64)
                           || (cap_enc)));

      let instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_LD,  rd,  op_LOAD);

      return tuple2 (is_legal, instr);
   end
endfunction

`ifdef RV64
// C_LQ: expands to LQ
function Tuple2 #(Bool, Instr) fv_decode_C_LQ (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CL-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rd, .op } = fv_ifields_CL_type (instr_C);
      Bit #(9) offset = { imm_at_12_10 [0], imm_at_6_5, imm_at_12_10 [2], imm_at_12_10 [1], 4'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_LQ)
                       && (xl == misa_mxl_64)
                       && (cap_enc));

      let     instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_LQ,  rd,  op_MISC_MEM);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

`ifdef ISA_F
// C_FLW: expands to FLW
function Tuple2 #(Bool, Instr) fv_decode_C_FLW (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CL-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rd, .op } = fv_ifields_CL_type (instr_C);
      Bit #(7) offset = { imm_at_6_5 [0], imm_at_12_10, imm_at_6_5 [1], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_FLW)
                       && (xl == misa_mxl_32)
                       && (! cap_enc));

      let     instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_FLW,  rd,  op_LOAD_FP);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

`ifdef ISA_D
// C_FLD: expands to FLD
function Tuple2 #(Bool, Instr) fv_decode_C_FLD (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CL-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rd, .op } = fv_ifields_CL_type (instr_C);
      Bit #(8) offset = { imm_at_6_5, imm_at_12_10, 3'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_FLD)
                       && (   (xl == misa_mxl_32)
                           || (! cap_enc)));

      let     instr = mkInstr_I_type (zeroExtend (offset),  rs1,  f3_FLD,  rd,  op_LOAD_FP);

      return tuple2 (is_legal, instr);
   end
endfunction
`endif

// ================================================================
// 'C' Extension Register-Based Stores

// C_SW: expands to SW
function Tuple2 #(Bool, Instr) fv_decode_C_SW (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CS-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rs2, .op } = fv_ifields_CS_type (instr_C);
      Bit #(7) offset = { imm_at_6_5 [0], imm_at_12_10, imm_at_6_5 [1], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_SW));

      let instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_SW, op_STORE);
      
      return tuple2 (is_legal, instr);
   end
endfunction

// C_SD: expands to SD
function Tuple2 #(Bool, Instr) fv_decode_C_SD (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CS-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rs2, .op } = fv_ifields_CS_type (instr_C);
      Bit #(8) offset = { imm_at_6_5, imm_at_12_10, 3'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_SD)
                       && (   (xl == misa_mxl_64)
                           || (cap_enc)));

      let instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_SD, op_STORE);
      
      return tuple2 (is_legal, instr);
   end
endfunction

`ifdef RV64
// C_SQ: expands to SQ
function Tuple2 #(Bool, Instr) fv_decode_C_SQ (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CS-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rs2, .op } = fv_ifields_CS_type (instr_C);
      Bit #(9) offset = { imm_at_12_10 [0], imm_at_6_5, imm_at_12_10 [2], imm_at_12_10 [1], 4'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_SQ)
                       && (xl == misa_mxl_64)
                       && (cap_enc));

      let instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_SQ, op_STORE);
      
      return tuple2 (is_legal, instr);
   end
endfunction
`endif

`ifdef ISA_F
// C_FSW: expands to FSW
function Tuple2 #(Bool, Instr) fv_decode_C_FSW (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CS-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rs2, .op } = fv_ifields_CS_type (instr_C);
      Bit #(7) offset = { imm_at_6_5 [0], imm_at_12_10, imm_at_6_5 [1], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_FSW)
                       && (xl == misa_mxl_32)
                       && (! cap_enc));

      let instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_FSW, op_STORE_FP);
      
      return tuple2 (is_legal, instr);
   end
endfunction
`endif

`ifdef ISA_D
// C_FSD: expands to FSD
function Tuple2 #(Bool, Instr) fv_decode_C_FSD (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CS-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_5, .rs2, .op } = fv_ifields_CS_type (instr_C);
      Bit #(8) offset = { imm_at_6_5, imm_at_12_10, 3'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_FSD)
                       && (   (xl == misa_mxl_32)
                           || (! cap_enc)));

      let instr = mkInstr_S_type (zeroExtend (offset), rs2, rs1, f3_FSD, op_STORE_FP);
      
      return tuple2 (is_legal, instr);
   end
endfunction
`endif

// ================================================================
// 'C' Extension Control Transfer
// C.J, C.JAL, C.JR, C.JALR, C.BEQZ, C.BNEZ

// C.J: expands to JAL
function Tuple2 #(Bool, Instr) fv_decode_C_J (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CJ-type
      match { .funct3, .imm_at_12_2, .op } = fv_ifields_CJ_type (instr_C);
      Bit #(12) offset = {imm_at_12_2 [10],
                          imm_at_12_2 [6],
                          imm_at_12_2 [8:7],
                          imm_at_12_2 [4],
                          imm_at_12_2 [5],
                          imm_at_12_2 [0],
                          imm_at_12_2 [9],
                          imm_at_12_2 [3:1],
                          1'b0};

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_J));

      RegName   rd    = reg_zero;
      Bit #(21) imm21 = signExtend (offset);
      let       instr = mkInstr_J_type (imm21, rd, op_JAL);
      
      return tuple2 (is_legal, instr);
   end
endfunction

// C.JAL: expands to JAL
function Tuple2 #(Bool, Instr) fv_decode_C_JAL (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CJ-type
      match { .funct3, .imm_at_12_2, .op } = fv_ifields_CJ_type (instr_C);
      Bit #(12) offset = {imm_at_12_2 [10],
                          imm_at_12_2 [6],
                          imm_at_12_2 [8:7],
                          imm_at_12_2 [4],
                          imm_at_12_2 [5],
                          imm_at_12_2 [0],
                          imm_at_12_2 [9],
                          imm_at_12_2 [3:1],
                          1'b0};

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_JAL)
                       && (xl == misa_mxl_32));

      RegName   rd    = reg_ra;
      Bit #(21) imm21 = signExtend (offset);
      let       instr = mkInstr_J_type  (imm21,  rd,  op_JAL);
      
      return tuple2 (is_legal, instr);
   end
endfunction

// C.JR: expands to JALR
function Tuple2 #(Bool, Instr) fv_decode_C_JR (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CR-type
      match { .funct4, .rs1, .rs2, .op } = fv_ifields_CR_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct4 == funct4_C_JR)
                       && (rs1 != 0)
                       && (rs2 == 0));

      RegName   rd    = reg_zero;
      Bit #(12) imm12 = 0;
      let       instr = mkInstr_I_type (imm12, rs1, funct3_JALR, rd, op_JALR);
      return tuple2 (is_legal, instr);
   end
endfunction

// C.JALR: expands to JALR
function Tuple2 #(Bool, Instr) fv_decode_C_JALR (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CR-type
      match { .funct4, .rs1, .rs2, .op } = fv_ifields_CR_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct4 == funct4_C_JALR)
                       && (rs1 != 0)
                       && (rs2 == 0));

      RegName   rd    = reg_ra;
      Bit #(12) imm12 = 0;
      let       instr = mkInstr_I_type (imm12, rs1, funct3_JALR, rd, op_JALR);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.BEQZ: expands to BEQ
function Tuple2 #(Bool, Instr) fv_decode_C_BEQZ (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CB-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_2, .op } = fv_ifields_CB_type (instr_C);
      Bit #(9) offset = { imm_at_12_10 [2], imm_at_6_2 [4:3], imm_at_6_2 [0], imm_at_12_10 [1:0], imm_at_6_2 [2:1], 1'b0 };
      
      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_BEQZ));

      RegName   rs2   = reg_zero;
      Bit #(13) imm13 = signExtend (offset);
      let       instr = mkInstr_B_type (imm13, rs2, rs1, f3_BEQ, op_BRANCH);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.BNEZ: expands to BNE
function Tuple2 #(Bool, Instr) fv_decode_C_BNEZ (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CB-type
      match { .funct3, .imm_at_12_10, .rs1, .imm_at_6_2, .op } = fv_ifields_CB_type (instr_C);
      Bit #(9) offset = { imm_at_12_10 [2], imm_at_6_2 [4:3], imm_at_6_2 [0], imm_at_12_10 [1:0], imm_at_6_2 [2:1], 1'b0 };
      
      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_BNEZ));

      RegName   rs2   = reg_zero;
      Bit #(13) imm13 = signExtend (offset);
      let       instr = mkInstr_B_type (imm13, rs2, rs1, f3_BNE, op_BRANCH);

      return tuple2 (is_legal, instr);
   end
endfunction

// ================================================================
// 'C' Extension Integer Constant-Generation

// C.LI: expands to ADDI
function Tuple2 #(Bool, Instr) fv_decode_C_LI (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CI-type
      match { .funct3, .imm_at_12, .rd, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(6) imm6 = { imm_at_12, imm_at_6_2 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_LI)
                       && (rd != 0));

      RegName   rs1   = reg_zero;
      Bit #(12) imm12 = signExtend (imm6);
      let       instr = mkInstr_I_type (imm12, rs1, f3_ADDI, rd, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.LUI: expands to LUI
function Tuple2 #(Bool, Instr) fv_decode_C_LUI (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CI-type
      match { .funct3, .imm_at_12, .rd, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(6) nzimm6 = { imm_at_12, imm_at_6_2 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_LUI)
                       && (rd != 0)
                       && (rd != 2)
                       && (nzimm6 != 0));

      Bit #(20) imm20 = signExtend (nzimm6);
      let       instr = mkInstr_U_type (imm20, rd, op_LUI);

      return tuple2 (is_legal, instr);
   end
endfunction

// ================================================================
// 'C' Extension Integer Register-Immediate Operations

// C.ADDI: expands to ADDI
function Tuple2 #(Bool, Instr) fv_decode_C_ADDI (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CI-type
      match { .funct3, .imm_at_12, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(6) nzimm6 = { imm_at_12, imm_at_6_2 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_ADDI)
                       && (rd_rs1 != 0)
                       && (nzimm6 != 0));

      Bit #(12) imm12 = signExtend (nzimm6);
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_ADDI, rd_rs1, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.NOP: expands to ADDI
function Tuple2 #(Bool, Instr) fv_decode_C_NOP (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CI-type
      match { .funct3, .imm_at_12, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(6) nzimm6 = { imm_at_12, imm_at_6_2 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_NOP)
                       && (rd_rs1 == 0)
                       && (nzimm6 == 0));

      Bit #(12) imm12 = signExtend (nzimm6);
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_ADDI, rd_rs1, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.ADDIW: expands to ADDIW
function Tuple2 #(Bool, Instr) fv_decode_C_ADDIW (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CI-type
      match { .funct3, .imm_at_12, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(6) imm6 = { imm_at_12, imm_at_6_2 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_ADDIW)
                       && (rd_rs1 != 0)
                       && (xl == misa_mxl_64));

      Bit #(12) imm12 = signExtend (imm6);
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_ADDIW, rd_rs1, op_OP_IMM_32);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.ADDI16SP: expands to ADDI
function Tuple2 #(Bool, Instr) fv_decode_C_ADDI16SP (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CI-type
      match { .funct3, .imm_at_12, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(10) nzimm10 = { imm_at_12, imm_at_6_2 [2:1], imm_at_6_2 [3], imm_at_6_2 [0], imm_at_6_2 [4], 4'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_ADDI16SP)
                       && (rd_rs1 == reg_sp)
                       && (nzimm10 != 0)
                       && (! cap_enc));

      Bit #(12) imm12 = signExtend (nzimm10);
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_ADDI, rd_rs1, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.CIncOffsetImm16CSP: expands to CIncOffsetImm
function Tuple2 #(Bool, Instr) fv_decode_C_CIncOffsetImm16CSP (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CI-type
      match { .funct3, .imm_at_12, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(10) nzimm10 = { imm_at_12, imm_at_6_2 [2:1], imm_at_6_2 [3], imm_at_6_2 [0], imm_at_6_2 [4], 4'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_CIncOffsetImm16CSP)
                       && (rd_rs1 == reg_sp)
                       && (nzimm10 != 0)
                       && (cap_enc));

      Bit #(12) imm12 = signExtend (nzimm10);
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_cap_CIncOffsetImmediate, rd_rs1, op_cap_Manip);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.ADDI4SPN: expands to ADDI
function Tuple2 #(Bool, Instr) fv_decode_C_ADDI4SPN (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CIW-type
      match { .funct3, .imm_at_12_5, .rd, .op } = fv_ifields_CIW_type (instr_C);
      Bit #(10) nzimm10 = { imm_at_12_5 [5:2], imm_at_12_5 [7:6], imm_at_12_5 [0], imm_at_12_5 [1], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_ADDI4SPN)
                       && (nzimm10 != 0)
                       && (! cap_enc));

      RegName   rs1   = reg_sp;
      Bit #(12) imm12 = zeroExtend (nzimm10);
      let       instr = mkInstr_I_type (imm12, rs1, f3_ADDI, rd, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.CIncOffsetImm4CSPN: expands to CIncOffsetImm
function Tuple2 #(Bool, Instr) fv_decode_C_CIncOffsetImm4CSPN (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CIW-type
      match { .funct3, .imm_at_12_5, .rd, .op } = fv_ifields_CIW_type (instr_C);
      Bit #(10) nzimm10 = { imm_at_12_5 [5:2], imm_at_12_5 [7:6], imm_at_12_5 [0], imm_at_12_5 [1], 2'b0 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C0)
                       && (funct3 == funct3_C_CIncOffsetImm4CSPN)
                       && (nzimm10 != 0)
                       && (cap_enc));

      RegName   rs1   = reg_sp;
      Bit #(12) imm12 = zeroExtend (nzimm10);
      let       instr = mkInstr_I_type (imm12, rs1, f3_cap_CIncOffsetImmediate, rd, op_cap_Manip);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.SLLI: expands to SLLI
function Tuple2 #(Bool, Instr) fv_decode_C_SLLI (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CI-type
      match { .funct3, .imm_at_12, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CI_type (instr_C);
      Bit #(6) shamt6 = { imm_at_12, imm_at_6_2 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct3 == funct3_C_SLLI)
                       && (rd_rs1 != 0)
                       && (shamt6 != 0)
                       && ((xl == misa_mxl_32) ? (imm_at_12 == 0) : True));

      Bit #(12) imm12 = (  (xl == misa_mxl_32)
                         ? { msbs7_SLLI, imm_at_6_2 }
                         : { msbs6_SLLI, shamt6 } );
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_SLLI, rd_rs1, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.SRLI: expands to SRLI
function Tuple2 #(Bool, Instr) fv_decode_C_SRLI (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CB-type
      match { .funct3, .imm_at_12_10, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CB_type (instr_C);
      Bit #(1) shamt6_5 = imm_at_12_10 [2];
      Bit #(2) funct2   = imm_at_12_10 [1:0];
      Bit #(6) shamt6   = { shamt6_5, imm_at_6_2 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_SRLI)
                       && (funct2 == funct2_C_SRLI)
                       && (rd_rs1 != 0)
                       && (shamt6 != 0)
                       && ((xl == misa_mxl_32) ? (shamt6_5 == 0) : True));

      Bit #(12) imm12 = (  (xl == misa_mxl_32)
                         ? { msbs7_SRLI, imm_at_6_2 }
                         : { msbs6_SRLI, shamt6 } );
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_SRLI, rd_rs1, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.SRAI: expands to SRAI
function Tuple2 #(Bool, Instr) fv_decode_C_SRAI (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CB-type
      match { .funct3, .imm_at_12_10, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CB_type (instr_C);
      Bit #(1) shamt6_5 = imm_at_12_10 [2];
      Bit #(2) funct2   = imm_at_12_10 [1:0];
      Bit #(6) shamt6   = { shamt6_5, imm_at_6_2 };

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_SRAI)
                       && (funct2 == funct2_C_SRAI)
                       && (rd_rs1 != 0)
                       && (shamt6 != 0)
                       && ((xl == misa_mxl_32) ? (shamt6_5 == 0) : True));

      Bit #(12) imm12 = (  (xl == misa_mxl_32)
                         ? { msbs7_SRAI, imm_at_6_2 }
                         : { msbs6_SRAI, shamt6 } );
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_SRAI, rd_rs1, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.ANDI: expands to ANDI
function Tuple2 #(Bool, Instr) fv_decode_C_ANDI (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CB-type
      match { .funct3, .imm_at_12_10, .rd_rs1, .imm_at_6_2, .op } = fv_ifields_CB_type (instr_C);
      Bit #(1) imm6_5 = imm_at_12_10 [2];
      Bit #(6) imm6   = { imm6_5, imm_at_6_2 };
      Bit #(2) funct2 = imm_at_12_10 [1:0];

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct3 == funct3_C_ANDI)
                       && (funct2 == funct2_C_ANDI));

      Bit #(12) imm12 = signExtend (imm6);
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_ANDI, rd_rs1, op_OP_IMM);

      return tuple2 (is_legal, instr);
   end
endfunction

// ================================================================
// 'C' Extension Integer Register-Register Operations

// C.MV: expands to ADD
function Tuple2 #(Bool, Instr) fv_decode_C_MV (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      match { .funct4, .rd_rs1, .rs2, .op } = fv_ifields_CR_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct4 == funct4_C_MV)
                       && (rd_rs1 != 0)
                       && (rs2 != 0));

      RegName rs1   = reg_zero;
      let     instr = mkInstr_R_type (funct7_ADD, rs2, rs1, funct3_ADD, rd_rs1, op_OP);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.ADD: expands to ADD
function Tuple2 #(Bool, Instr) fv_decode_C_ADD (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      match { .funct4, .rd_rs1, .rs2, .op } = fv_ifields_CR_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct4 == funct4_C_ADD)
                       && (rd_rs1 != 0)
                       && (rs2 != 0));

      let     instr = mkInstr_R_type (funct7_ADD, rs2, rd_rs1, funct3_ADD, rd_rs1, op_OP);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.AND: expands to AND
function Tuple2 #(Bool, Instr) fv_decode_C_AND (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CA-type
      match { .funct6, .rd_rs1, .funct2, .rs2, .op } = fv_ifields_CA_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct6 == funct6_C_AND)
                       && (funct2 == funct2_C_AND));

      let instr = mkInstr_R_type (funct7_AND, rs2, rd_rs1, funct3_AND, rd_rs1, op_OP);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.OR: expands to OR
function Tuple2 #(Bool, Instr) fv_decode_C_OR (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CA-type
      match { .funct6, .rd_rs1, .funct2, .rs2, .op } = fv_ifields_CA_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct6 == funct6_C_OR)
                       && (funct2 == funct2_C_OR));

      let instr = mkInstr_R_type (funct7_OR, rs2, rd_rs1, funct3_OR, rd_rs1, op_OP);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.XOR: expands to XOR
function Tuple2 #(Bool, Instr) fv_decode_C_XOR (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CA-type
      match { .funct6, .rd_rs1, .funct2, .rs2, .op } = fv_ifields_CA_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct6 == funct6_C_XOR)
                       && (funct2 == funct2_C_XOR));

      let instr = mkInstr_R_type (funct7_XOR, rs2, rd_rs1, funct3_XOR, rd_rs1, op_OP);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.SUB: expands to SUB
function Tuple2 #(Bool, Instr) fv_decode_C_SUB (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CA-type
      match { .funct6, .rd_rs1, .funct2, .rs2, .op } = fv_ifields_CA_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct6 == funct6_C_SUB)
                       && (funct2 == funct2_C_SUB));

      let instr = mkInstr_R_type (funct7_SUB, rs2, rd_rs1, funct3_SUB, rd_rs1, op_OP);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.ADDW: expands to ADDW
function Tuple2 #(Bool, Instr) fv_decode_C_ADDW (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CA-type
      match { .funct6, .rd_rs1, .funct2, .rs2, .op } = fv_ifields_CA_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct6 == funct6_C_ADDW)
                       && (funct2 == funct2_C_ADDW)
                       && (xl == misa_mxl_64));

      let instr = mkInstr_R_type (funct7_ADDW, rs2, rd_rs1, funct3_ADDW, rd_rs1, op_OP_32);

      return tuple2 (is_legal, instr);
   end
endfunction

// C.SUBW: expands to SUBW
function Tuple2 #(Bool, Instr) fv_decode_C_SUBW (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CA-type
      match { .funct6, .rd_rs1, .funct2, .rs2, .op } = fv_ifields_CA_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C1)
                       && (funct6 == funct6_C_SUBW)
                       && (funct2 == funct2_C_SUBW)
                       && ((xl == misa_mxl_64)));

      let instr = mkInstr_R_type (funct7_SUBW, rs2, rd_rs1, funct3_SUBW, rd_rs1, op_OP_32);

      return tuple2 (is_legal, instr);
   end
endfunction

// ================================================================
// 'C' Extension EBREAK

// C.EBREAK: expands to EBREAK
function Tuple2 #(Bool, Instr) fv_decode_C_EBREAK (MISA  misa,  Bit #(2)  xl, Bool cap_enc,  Instr_C  instr_C);
   begin
      // Instr fields: CR-type
      match { .funct4, .rd_rs1, .rs2, .op } = fv_ifields_CR_type (instr_C);

      Bool is_legal = ((misa.c == 1'b1)
                       && (op == opcode_C2)
                       && (funct4 == funct4_C_EBREAK)
                       && (rd_rs1 == 0)
                       && (rs2 == 0));

      Bit #(12) imm12 = f12_EBREAK;
      let       instr = mkInstr_I_type (imm12, rd_rs1, f3_PRIV,  rd_rs1, op_SYSTEM);

      return tuple2 (is_legal, instr);
   end
endfunction

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

endpackage
