// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.

package DM_Common;

// ================================================================
// This package has non-implementation-specific definitions, i.e.,
// just encodes things in the spec.

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

// None

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

// None

// ================================================================
// Debug Module Interface (DMI) addresses and data.
// Note: data is always 32b, whether the connected CPU is RV32, RV64 or RV128.

typedef Bit #(7)  DM_Addr;

DM_Addr max_DM_Addr = 'h5F;

typedef Bit #(32) DM_Word;

// ================================================================
// Debug Module address map

// ----------------
// Run Control

DM_Addr dm_addr_dmcontrol    = 'h10;
DM_Addr dm_addr_dmstatus     = 'h11;
DM_Addr dm_addr_hartinfo     = 'h12;
DM_Addr dm_addr_haltsum      = 'h13;
DM_Addr dm_addr_hawindowsel  = 'h14;
DM_Addr dm_addr_hawindow     = 'h15;
DM_Addr dm_addr_devtreeaddr0 = 'h19;
DM_Addr dm_addr_authdata     = 'h30;
DM_Addr dm_addr_haltregion0  = 'h40;
DM_Addr dm_addr_haltregion31 = 'h5F;

DM_Addr dm_addr_verbosity    = 'h60;    // Non-standard (not in spec)

// ----------------
// Abstract commands (read/write RISC-V registers and RISC-V CSRs)

DM_Addr dm_addr_abstractcs   = 'h16;
DM_Addr dm_addr_command      = 'h17;

DM_Addr dm_addr_data0        = 'h04;
DM_Addr dm_addr_data1        = 'h05;
DM_Addr dm_addr_data2        = 'h06;
DM_Addr dm_addr_data3        = 'h07;
DM_Addr dm_addr_data4        = 'h08;
DM_Addr dm_addr_data5        = 'h09;
DM_Addr dm_addr_data6        = 'h0a;
DM_Addr dm_addr_data7        = 'h0b;
DM_Addr dm_addr_data8        = 'h0c;
DM_Addr dm_addr_data9        = 'h0d;
DM_Addr dm_addr_data10       = 'h0d;
DM_Addr dm_addr_data11       = 'h0f;

DM_Addr dm_addr_abstractauto = 'h18;
DM_Addr dm_addr_progbuf0     = 'h20;

// ----------------
// System Bus access (read/write RISC-V memory/devices)

DM_Addr dm_addr_sbcs         = 'h38;
DM_Addr dm_addr_sbaddress0   = 'h39;
DM_Addr dm_addr_sbaddress1   = 'h3a;
DM_Addr dm_addr_sbaddress2   = 'h3b;
DM_Addr dm_addr_sbdata0      = 'h3c;
DM_Addr dm_addr_sbdata1      = 'h3d;
DM_Addr dm_addr_sbdata2      = 'h3e;
DM_Addr dm_addr_sbdata3      = 'h3f;

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

function Fmt fshow_dm_addr (DM_Addr dm_addr);
   return case (dm_addr)
	     // Run Control
	     dm_addr_dmcontrol:    $format ("dm_addr_dmcontrol");
	     dm_addr_dmstatus:     $format ("dm_addr_dmstatus");
	     dm_addr_hartinfo:     $format ("dm_addr_hartinfo");
	     dm_addr_haltsum:      $format ("dm_addr_haltsum");
	     dm_addr_hawindowsel:  $format ("dm_addr_hawindowsel");
	     dm_addr_hawindow:     $format ("dm_addr_hawindow");
	     dm_addr_devtreeaddr0: $format ("dm_addr_devtreeaddr0");
	     dm_addr_authdata:     $format ("dm_addr_authdata");
	     dm_addr_haltregion0:  $format ("dm_addr_haltregion0");
	     dm_addr_haltregion31: $format ("dm_addr_haltregion31");
             dm_addr_verbosity:    $format ("dm_addr_verbosity");

	     // Abstract Commands
	     dm_addr_abstractcs:   $format ("dm_addr_abstractcs");
	     dm_addr_command:      $format ("dm_addr_command");
	     dm_addr_data0:        $format ("dm_addr_data0");
	     dm_addr_data1:        $format ("dm_addr_data1");
	     dm_addr_data2:        $format ("dm_addr_data2");
	     dm_addr_data3:        $format ("dm_addr_data3");
	     dm_addr_data4:        $format ("dm_addr_data4");
	     dm_addr_data5:        $format ("dm_addr_data5");
	     dm_addr_data6:        $format ("dm_addr_data6");
	     dm_addr_data7:        $format ("dm_addr_data7");
	     dm_addr_data8:        $format ("dm_addr_data8");
	     dm_addr_data9:        $format ("dm_addr_data9");
	     dm_addr_data10:       $format ("dm_addr_data10");
	     dm_addr_data11:       $format ("dm_addr_data11");
	     dm_addr_abstractauto: $format ("dm_addr_abstractauto");
	     dm_addr_progbuf0:     $format ("dm_addr_progbuf0");

	     // System Bus
	     dm_addr_sbcs:         $format ("dm_addr_sbcs");
	     dm_addr_sbaddress0:   $format ("dm_addr_sbaddress0");
	     dm_addr_sbaddress1:   $format ("dm_addr_sbaddress1");
	     dm_addr_sbaddress2:   $format ("dm_addr_sbaddress2");
	     dm_addr_sbdata0:      $format ("dm_addr_sbdata0");
	     dm_addr_sbdata1:      $format ("dm_addr_sbdata1");
	     dm_addr_sbdata2:      $format ("dm_addr_sbdata2");
	     dm_addr_sbdata3:      $format ("dm_addr_sbdata3");

	     default:              $format ("<Unknown dm_abstract_command dm_addr 0x%0h>", dm_addr);
	  endcase;
endfunction

// ================================================================
// Run Control DM register fields

// ----------------------------------------------------------------
// 'dmcontrol' register

function DM_Word fn_mk_dmcontrol (Bool       haltreq,
				  Bool       resumereq,
				  Bool       hartreset,
				  Bool       hasel,
				  Bit #(10)  hartsel,
				  Bool       ndmreset,
				  Bool       dmactive);
   return {pack (haltreq),
	   pack (resumereq),
	   pack (hartreset),
	   2'b0,
	   pack (hasel),
	   hartsel,
	   14'b0,
	   pack (ndmreset),
	   pack (dmactive)};
endfunction

function Bool fn_dmcontrol_haltreq (DM_Word dm_word);
   return unpack (dm_word [31]);
endfunction

function Bool fn_dmcontrol_resumereq (DM_Word dm_word);
   return unpack (dm_word [30]);
endfunction

function Bool fn_dmcontrol_hartreset (DM_Word dm_word);
   return unpack (dm_word [29]);
endfunction

function Bool fn_dmcontrol_hasel (DM_Word dm_word);
   return unpack (dm_word [26]);
endfunction

function Bit #(10) fn_dmcontrol_hartsel (DM_Word dm_word);
   return dm_word [25:16];
endfunction

function Bool fn_dmcontrol_ndmreset (DM_Word dm_word);
   return unpack (dm_word [1]);
endfunction

function Bool fn_dmcontrol_dmactive (DM_Word dm_word);
   return unpack (dm_word [0]);
endfunction

// ----------------------------------------------------------------
// 'dmstatus' register

function Bool fn_dmstatus_allresumeack (DM_Word x);
   return unpack (x [17]);
endfunction

function Bool fn_dmstatus_anyresumeack (DM_Word x);
   return unpack (x [16]);
endfunction

function Bool fn_dmstatus_allnonexistent (DM_Word x);
   return unpack (x [15]);
endfunction

function Bool fn_dmstatus_anynonexistent (DM_Word x);
   return unpack (x [14]);
endfunction

function Bool fn_dmstatus_allunavail (DM_Word x);
   return unpack (x [13]);
endfunction

function Bool fn_dmstatus_anyunavail (DM_Word x);
   return unpack (x [12]);
endfunction

function Bool fn_dmstatus_allrunning (DM_Word x);
   return unpack (x [11]);
endfunction

function Bool fn_dmstatus_anyrunning (DM_Word x);
   return unpack (x [10]);
endfunction

function Bool fn_dmstatus_allhalted (DM_Word x);
   return unpack (x [9]);
endfunction

function Bool fn_dmstatus_anyhalted (DM_Word x);
   return unpack (x [8]);
endfunction

function Bool fn_dmstatus_authenticated (DM_Word x);
   return unpack (x [7]);
endfunction

function Bool fn_dmstatus_authbusy (DM_Word x);
   return unpack (x [6]);
endfunction

function Bool fn_dmstatus_devtreevalid (DM_Word x);
   return unpack (x [4]);
endfunction

function Bit #(4) fn_dmstatus_version (DM_Word x);
   return unpack (x [3:0]);
endfunction

function Fmt fshow_dmstatus (DM_Word x);
   Fmt fmt_version = (  (x[3:0] == 0)
		      ? $format ("v.none")
		      : (  (x[3:0] == 1)
			 ? $format ("v0.11")
			 : (  (x[3:0] == 2)
			    ? $format ("v0.13")
			    : $format ("v??"))));

   return (  $format ("(all/any) ")
	   + $format ("resumeack %0d/%0d ",   x[17], x[16])
	   + $format ("nonexistent %0d/%0d ", x[15], x[14])
	   + $format ("unavail %0d/%0d ",     x[13], x[12])
	   + $format ("running %0d/%0d ",     x[11], x[10])
	   + $format ("halted %0d/%0d ",      x[9],  x[8])
	   + $format ("authenticated %0d ",   x[7])
	   + $format ("authbusy %0d ",        x[6])
	   + $format ("devtreevalid %0d ",    x[4])
	   + fmt_version);
endfunction

// ================================================================
// Abstract Command register fields

// ----------------------------------------------------------------
// 'dm_abstractcs' register

typedef enum {DM_ABSTRACTCS_CMDERR_NONE,             // 0
	      DM_ABSTRACTCS_CMDERR_BUSY,             // 1
	      DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED,    // 2
	      DM_ABSTRACTCS_CMDERR_EXCEPTION,        // 3
	      DM_ABSTRACTCS_CMDERR_HALT_RESUME,      // 4
	      DM_ABSTRACTCS_CMDERR_UNDEF5,           // 5
	      DM_ABSTRACTCS_CMDERR_UNDEF6,           // 6
	      DM_ABSTRACTCS_CMDERR_OTHER             // 7
   } DM_abstractcs_cmderr
deriving (Bits, Eq, FShow);

// The following is used in writes, to clear cmderr
DM_abstractcs_cmderr dm_cmderr_w1c = DM_ABSTRACTCS_CMDERR_OTHER;

function DM_Word fn_mk_abstractcs (DM_abstractcs_cmderr cmderr);
   return { 0, pack (cmderr), 8'h0 };
endfunction

function Bit #(5) fn_abstractcs_progsize (DM_Word dm_word);
   return unpack (dm_word [28:24]);
endfunction

function Bool fn_abstractcs_busy (DM_Word dm_word);
   return unpack (dm_word [12]);
endfunction

function DM_abstractcs_cmderr fn_abstractcs_cmderr (DM_Word dm_word);
   return unpack (dm_word [10:8]);
endfunction

function Bit #(5) fn_abstractcs_datacount (DM_Word dm_word);
   return unpack (dm_word [4:0]);
endfunction

// ----------------------------------------------------------------
// 'command' register

typedef enum {DM_COMMAND_CMDTYPE_ACCESS_REG,
	      DM_COMMAND_CMDTYPE_QUICK_ACCESS
   } DM_command_cmdtype
deriving (Bits, Eq, FShow);

typedef enum {DM_COMMAND_ACCESS_REG_SIZE_UNDEF0,     // 0
	      DM_COMMAND_ACCESS_REG_SIZE_UNDEF1,     // 1
	      DM_COMMAND_ACCESS_REG_SIZE_LOWER32,    // 2
	      DM_COMMAND_ACCESS_REG_SIZE_LOWER64,    // 3
	      DM_COMMAND_ACCESS_REG_SIZE_LOWER128,   // 4
	      DM_COMMAND_ACCESS_REG_SIZE_UNDEF5,     // 5
	      DM_COMMAND_ACCESS_REG_SIZE_UNDEF6,     // 6
	      DM_COMMAND_ACCESS_REG_SIZE_UNDEF7      // 7
   } DM_command_access_reg_size
deriving (Bits, Eq, FShow);

Integer dm_command_access_reg_regno_csr_0   = 'h0000;
Integer dm_command_access_reg_regno_csr_FFF = 'h0FFF;
Integer dm_command_access_reg_regno_gpr_0   = 'h1000;
Integer dm_command_access_reg_regno_gpr_1F  = 'h101F;
Integer dm_command_access_reg_regno_fpr_0   = 'h1020;
Integer dm_command_access_reg_regno_fpr_1F  = 'h103F;

function DM_Word fn_mk_command_access_reg (DM_command_access_reg_size  size,
					   Bool                        postexec,
					   Bool                        transfer,
					   Bool                        write,
					   Bit #(16)                   regno);
   Bit #(8)  b8_cmdtype = zeroExtend (pack (DM_COMMAND_CMDTYPE_ACCESS_REG));
   Bit #(3)  b3_size    = pack (size);
   return {b8_cmdtype,
	   1'b0,
	   b3_size,
	   1'b0,
	   pack (postexec),
	   pack (transfer),
	   pack (write),
	   regno};
endfunction

function DM_command_cmdtype fn_command_cmdtype (DM_Word dm_word);
   return unpack (truncate (dm_word [31:24]));
endfunction

function DM_command_access_reg_size fn_command_access_reg_size (DM_Word dm_word);
   return unpack (dm_word [22:20]);
endfunction

function Bool fn_command_access_reg_postexec (DM_Word dm_word);
   return unpack (dm_word [18]);
endfunction

function Bool fn_command_access_reg_transfer (DM_Word dm_word);
   return unpack (dm_word [17]);
endfunction

function Bool fn_command_access_reg_write (DM_Word dm_word);
   return unpack (dm_word [16]);
endfunction

function Bit #(16) fn_command_access_reg_regno (DM_Word dm_word);
   return dm_word [15:0];
endfunction

// ================================================================
// System Bus Access DM register fields

// ----------------------------------------------------------------
// 'dm_sbcs' register

typedef enum {DM_SBACCESS_8_BIT,
	      DM_SBACCESS_16_BIT,
	      DM_SBACCESS_32_BIT,
	      DM_SBACCESS_64_BIT,
	      DM_SBACCESS_128_BIT
   } DM_sbaccess
deriving (Bits, Eq, FShow);

function Integer fn_sbaccess_to_addr_incr (DM_sbaccess sbaccess);
   case (sbaccess)
      DM_SBACCESS_8_BIT:   return 1;
      DM_SBACCESS_16_BIT:  return 2;
      DM_SBACCESS_32_BIT:  return 4;
      DM_SBACCESS_64_BIT:  return 8;
      DM_SBACCESS_128_BIT: return 16;
   endcase
endfunction

typedef enum {DM_SBERROR_NONE,          // 0
	      DM_SBERROR_TIMEOUT,       // 1
	      DM_SBERROR_BADADDR,       // 2
	      DM_SBERROR_OTHER,         // 3
	      DM_SBERROR_BUSY_STALE,    // 4
	      DM_SBERROR_UNDEF5,        // 5
	      DM_SBERROR_UNDEF6,        // 6
	      DM_SBERROR_UNDEF7_W1C     // 7, used in writes, to clear sberror
   } DM_sberror
deriving (Bits, Eq, FShow);

// Constructor

function DM_Word fn_mk_sbcs_val (Bit #(3)     sbversion,
				 Bool         sbbusyerror,
				 Bool         sbbusy,
				 Bool         sbreadonaddr,
				 DM_sbaccess  sbaccess,
				 Bool         sbautoincrement,
				 Bool         sbreadondata,
				 DM_sberror   sberror,
				 Bit #(7)     sbasize,
				 Bit #(1)     sbaccess128,
				 Bit #(1)     sbaccess64,
				 Bit #(1)     sbaccess32,
				 Bit #(1)     sbaccess16,
				 Bit #(1)     sbaccess8);
   return {sbversion,
	   6'b0,
	   pack (sbbusyerror),
	   pack (sbbusy),
	   pack (sbreadonaddr),
	   pack (sbaccess),
	   pack (sbautoincrement),
	   pack (sbreadondata),
	   pack (sberror),
	   sbasize,
	   sbaccess128,
	   sbaccess64,
	   sbaccess32,
	   sbaccess16,
	   sbaccess8};
endfunction

// Selectors

function Bit #(3)    fn_sbcs_sbversion       (DM_Word dm_word);  return unpack (dm_word [31:29]);  endfunction
function Bool        fn_sbcs_sbbusyerror     (DM_Word dm_word);  return unpack (dm_word [22]);     endfunction
function Bool        fn_sbcs_sbbusy          (DM_Word dm_word);  return unpack (dm_word [21]);     endfunction
function Bool        fn_sbcs_sbreadonaddr    (DM_Word dm_word);  return unpack (dm_word [20]);     endfunction
function DM_sbaccess fn_sbcs_sbaccess        (DM_Word dm_word);  return unpack (dm_word [19:17]);  endfunction
function Bool        fn_sbcs_sbautoincrement (DM_Word dm_word);  return unpack (dm_word [16]);     endfunction
function Bool        fn_sbcs_sbreadondata    (DM_Word dm_word);  return unpack (dm_word [15]);     endfunction
function DM_sberror  fn_sbcs_sberror         (DM_Word dm_word);  return unpack (dm_word [14:12]);  endfunction
function Bit #(7)    fn_sbcs_sbasize         (DM_Word dm_word);  return         dm_word [11:5];    endfunction
function Bool        fn_sbcs_sbaccess128     (DM_Word dm_word);  return unpack (dm_word [4]);      endfunction
function Bool        fn_sbcs_sbaccess64      (DM_Word dm_word);  return unpack (dm_word [3]);      endfunction
function Bool        fn_sbcs_sbaccess32      (DM_Word dm_word);  return unpack (dm_word [2]);      endfunction
function Bool        fn_sbcs_sbaccess16      (DM_Word dm_word);  return unpack (dm_word [1]);      endfunction
function Bool        fn_sbcs_sbaccess8       (DM_Word dm_word);  return unpack (dm_word [0]);      endfunction

// Debugging

function Fmt fshow_sbcs (DM_Word dm_word);
   return (  $format ("SBCS{")
	   + $format ("sbversion %0d",      fn_sbcs_sbversion (dm_word))
	   + $format (" sbbusyerror %0d",   fn_sbcs_sbbusyerror (dm_word))
	   + $format (" sbbusy %0d",        fn_sbcs_sbbusy (dm_word))
	   + $format (" sbreadonaddr ")     + fshow (fn_sbcs_sbreadonaddr (dm_word))
	   + $format (" sbaccess ")         + fshow (fn_sbcs_sbaccess (dm_word))
	   + $format (" sbautoincrement ")  + fshow (fn_sbcs_sbautoincrement (dm_word))
	   + $format (" sbreadondata ")     + fshow (fn_sbcs_sbreadondata (dm_word))
	   + $format (" sberror ")          + fshow (fn_sbcs_sberror (dm_word))
	   + $format (" sbasize %0d",       fn_sbcs_sbasize (dm_word))
	   + $format (" sbaccess")
	   + ((fn_sbcs_sbaccess128 (dm_word)) ? $format ("_128") : $format ("x"))
	   + ((fn_sbcs_sbaccess64  (dm_word)) ? $format ("_64")  : $format ("x"))
	   + ((fn_sbcs_sbaccess32  (dm_word)) ? $format ("_32")  : $format ("x"))
	   + ((fn_sbcs_sbaccess16  (dm_word)) ? $format ("_16")  : $format ("x"))
	   + ((fn_sbcs_sbaccess8   (dm_word)) ? $format ("_8")   : $format ("x"))
	   + $format ("}"));
endfunction

// ================================================================
// DCSR 'cause' field values

typedef enum {DCSR_CAUSE_RESERVED0,
	      DCSR_CAUSE_EBREAK,
	      DCSR_CAUSE_TRIGGER,
	      DCSR_CAUSE_HALTREQ,
	      DCSR_CAUSE_STEP,
	      DCSR_CAUSE_RESERVED5,
	      DCSR_CAUSE_RESERVED6,
	      DCSR_CAUSE_RESERVED7
   } DCSR_Cause
deriving (Bits, Eq, FShow);

// ================================================================
// Sub-interface of the Debug Module facing the remote debugger (e.g. GDB)

interface DMI;
   method Action                 read_addr  (DM_Addr dm_addr);
   method ActionValue #(DM_Word) read_data;
   method Action                 write    (DM_Addr dm_addr, DM_Word dm_word);
endinterface

// A dummy interface to tie off DMI if it is not used.

DMI dummy_DMI_ifc = interface DMI;
		       method Action                 read_addr (DM_Addr dm_addr) = noAction;
		       method ActionValue #(DM_Word) read_data = actionvalue
								    return 0;
								 endactionvalue;
		       method Action                 write    (DM_Addr dm_addr, DM_Word dm_word) = noAction;
		    endinterface;

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

endpackage
