/***********************************************************************
*                    SEGGER Microcontroller GmbH                       *
*                        The Embedded Experts                          *
************************************************************************
*                                                                      *
*                  (c) SEGGER Microcontroller GmbH                     *
*                        All rights reserved                           *
*                          www.segger.com                              *
*                                                                      *
************************************************************************
*                                                                      *
************************************************************************
*                                                                      *
*                                                                      *
*  Licensing terms                                                     *
*                                                                      *
* This software may be distributed to your customers free of charge.   *
* This grant of redistribution does not entitle YOU or enduser to      *
* receive from SEGGER hard-copy documentation, technical support,      *
* phone assistance, or enhancements or updates to the Software unless  *
* a specific agreement clearly states otherwise.                       *
*                                                                      *
*                                                                      *
* THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY        *
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE    *
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR   *
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE        *
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,     *
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,             *
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR   *
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY  *
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH     *
* DAMAGE.                                                              *
*                                                                      *
************************************************************************

-------------------------- END-OF-HEADER -----------------------------

Purpose
  Connects to a RISC-V core sitting behind a JTAG-DTM (standard
  JTAG-only connection, i.e. NOT behind a CoreSight DAP).
Scope / limitations
  This script ALWAYS assumes RISC-V behind a JTAG-DTM. If your
  target's RISC-V core sits behind a CoreSight DAP, this script
  does not apply.
  Debug spec. version support: only V0.13 and V1.0 are implemented
  (identical connect sequence for both). V0.11 is explicitly
  rejected with an error - see _ConnectRV() below. V0.11 relies on
  a completely different, non-DMI register-access mechanism with
  no script-level equivalent, so no attempt is made to support it
  here.
Literature:
  [1]  J-Link User Guide
  [2]  Official RISC-V specifications:
         v0.13.2: https://riscv.org/wp-content/uploads/2024/12/riscv-debug-release.pdf
         v1.0:    https://docs.riscv.org/reference/debug/v1.0/_attachments/riscv-debug-specification.pdf
*/

/*********************************************************************
*
*       Constants, configurable
*
**********************************************************************
*/

//
// hartsel configuration
// When adjusting this, keep in mind to adjust JLINK_ExecCommand("RISCV_SetHartSel = 0"); in ConfigTargetSettings() as well.
//
__constant U32 HARTSEL                     = 0x0;  // hartsel value

/*********************************************************************
*
*       Constants, fixed (RISC-V debug spec)
*
**********************************************************************
*/
__constant U32 RISCV_JTAG_CMD_DTMCS = 0x10;   // 10000b - IR value selecting DTMCS as the addressed DR

//
// Debug Module register addresses (DMI space), ascending by value.
//
__constant U32 RISCV_DM_REG_ABSTRACT_DATA0 = 0x04;  // data0
__constant U32 RISCV_DM_REG_CONTROL        = 0x10;  // dmcontrol
__constant U32 RISCV_DM_REG_STATUS         = 0x11;  // dmstatus
__constant U32 RISCV_DM_REG_ABSTRACT_CS    = 0x16;  // abstractcs
__constant U32 RISCV_DM_REG_ABSTRACT_CMD   = 0x17;  // command
__constant U32 RISCV_DM_REG_ABSTRACT_AUTO  = 0x18;  // abstractauto
__constant U32 RISCV_DM_REG_SBCS           = 0x38;  // sbcs
__constant U32 RISCV_DM_REG_SBADDR0        = 0x39;  // sbaddress0

//
// Abstract command "Access Register" encoding and GPR index magic
// value.
//
__constant U32 ABS_CMD_COPY_GPR_TO_ABSDATA = ((0 << 20) | (0 << 18) | (1 << 17) | (0 << 16) | (0x000 << 0)); // Caller merges in <RegIndexHW> and <AccWidth>
__constant U32 _INDEX_HW_X1                = 0x1001; // GPR x1
__constant U32 ABS_CMD_ACC_WIDTH_64B       = 3;       // aarsize field value for 64-bit access
__constant U32 ABS_CMD_ACC_WIDTH_128B      = 4;       // aarsize field value for 128-bit access

//
// Test pattern for sbaddress0 implemented-check.
//
__constant U32 _SBA_MAGIC_VALUE = 0xCEDF5678;

//
// Debug spec. version constants.
//
__constant U32 RISCV_DM_VER_NONE = 0;
__constant U32 RISCV_DM_VER_0_11 = 1;
__constant U32 RISCV_DM_VER_0_13 = 2;
__constant U32 RISCV_DM_VER_1_0  = 3;

//
// DMI access timeout (ms) passed to JLINK_RISCV_DMI_SetDMISettings().
//
__constant U32 RISCV_DMI_ACC_TIMEOUT = 100;

//
// Timeout (ms) waiting for dmstatus.allresumeack / allhalted after a
// resume/halt request.
//
__constant U32 RISCV_TIMEOUT_RESUME_REQ = 500;

//
// Timeout (ms) waiting for dmstatus.allhalted after a connect-under-
// reset halt request. Some targets need up to ~1 second after reset
// release before the debug module becomes fully available.
//
__constant U32 RISCV_CPU_HALT_REQ_TIMEOUT_MS = 1500;

//
// Timeout (ms) waiting for dmcontrol read
//
__constant U32 RISCV_TIMEOUT_DMCONTROL_READ = 500;

//
// Access width test value
//
__constant U32 ACC_WIDTH_TEST_VALUE = 0xCDCDCDCD;

//
// HartSel calculations
//
__constant U32 HARTSEL_LO                  = (HARTSEL >>  0) & 0x3FF;  // Extract 10-bit LOW part of HartSel
__constant U32 HARTSEL_HI                  = (HARTSEL >> 10) & 0x3FF;  // Extract 10-bit HIGH part of HartSel
__constant U32 HARTSEL_VALUE               = (HARTSEL_LO << 10) | (HARTSEL_HI << 0);  // Prepared / Swapped value for HartSel as <dmcontrol> expects it. (Note: this does NOT include the shift to move the hartsel field at its final position inside dmcontrol)

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/

/*********************************************************************
*
*       _ConnectRVBehindJTAG_DTM()
*
*  Function description
*    Reads DTMCS to determine DTM version, AddrBits, DataBits,
*    IdleClocks, then configures the DMI engine with those settings so
*    subsequent JLINK_RISCV_DMI_ReadReg()/WriteReg() calls succeed.
*
*  Return value
*    >= 0:  O.K.
*     < 0:  Error
*/
static int _ConnectRVBehindJTAG_DTM(void) {
  U32 v;
  U32 DTMVersion;
  U32 AddrBits;
  U32 DataBits;
  U32 IdleClocks;
  int BitPos;
  int r;
  //
  // Read DTM control/status (DTMCS) to get basic debug architecture
  // info: DTM version, address/data width, idle-cycle requirement.
  //
  JLINK_JTAG_Store(0x1F, 0, 6);             // RESET -> IDLE
  JLINK_JTAG_StoreIR(RISCV_JTAG_CMD_DTMCS);
  BitPos = JLINK_JTAG_StoreDR(0, 32);
  v      = JLINK_JTAG_GetU32(BitPos);
  DTMVersion = v & 0xF;
  if (DTMVersion > 0) {
    DataBits   = 32;
    AddrBits   = (v >>  4) & 0x3F;
    IdleClocks = (v >> 12) & 0x7;
  } else {
    DataBits   = 34;
    AddrBits   = ((v >>  4) & 0xF) | ((v >> 9) & 0x30);
    IdleClocks = (v >> 10) & 0x7;
  }
  JLINK_SYS_Report("DTM architecture:");
  if (DTMVersion) {
    JLINK_SYS_Report("  RISC-V JTAG-DTM version: 0.13");
  } else {
    JLINK_SYS_Report("  RISC-V JTAG-DTM version: 0.11");
  }
  JLINK_SYS_Report1("  AddrBits: ", AddrBits);
  JLINK_SYS_Report1("  DataBits: ", DataBits);
  JLINK_SYS_Report1("  IdleClks: ", IdleClocks);
  //
  // Configure the DMI engine with the settings just derived from
  // DTMCS. Without this, the first JLINK_RISCV_DMI_ReadReg()/
  // WriteReg() call fails due to invalid/default DMI settings.
  //
  r = JLINK_RISCV_DMI_SetDMISettings(AddrBits, IdleClocks, RISCV_DMI_ACC_TIMEOUT);
  if (r < 0) {
    JLINK_SYS_Report("** ERROR: JLINK_RISCV_DMI_SetDMISettings() failed.");
    return -1;
  }
  return 0;
}

/*********************************************************************
*
*       _ConnectUnderReset()
*
*  Function description
*    Performs a connect under reset: requests a halt while releasing
*    the core reset (dmcontrol.haltreq + ndmreset, then clearing
*    ndmreset while keeping haltreq set), waits for the halt request
*    to be confirmed (dmstatus.allhalted), then clears haltreq again
*    (leaving the core halted, without restarting it).
*
*  Return value
*    >= 0:  O.K.
*     < 0:  Error / timeout
*
*  Notes
*    Some targets need up to ~1 second after reset release before the
*    debug module becomes fully available - RISCV_CPU_HALT_REQ_TIMEOUT_MS
*    accounts for that.
*/
static int _ConnectUnderReset(void) {
  U32 v;
  int r;
  int t;

  v = (1 << 31) | (HARTSEL_VALUE << 6) | (1 << 1) | (1 << 0);   // haltreq, hartsel, ndmreset, dmactive
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, v);
  JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_CONTROL, &v);            // Ensure write has completed before continuing
  v = (1 << 31) | (HARTSEL_VALUE << 6) | (0 << 1) | (1 << 0);   // haltreq, hartsel, ndmreset cleared, dmactive
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, v);
  JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_CONTROL, &v);            // Ensure write has completed before continuing
  //
  // Wait for halt request to be confirmed (dmstatus.allhalted). A
  // read return value of -2 (timeout on the DMI operation itself, not
  // on this wait loop) is treated as "not ready yet" and retried;
  // other negative return values abort immediately.
  //
  r = 0;
  t = JLINK_GetTime() + RISCV_CPU_HALT_REQ_TIMEOUT_MS;
  do {
    r = JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_STATUS, &v);
    if (r >= 0) {
      if ((v >> 9) & 1) {
        break;
      }
    } else {
      if (r != -2) {
        JLINK_SYS_Report("** ERROR: Reading <dmstatus> debug register failed.");
        break;
      }
    }
    if ((t - JLINK_GetTime()) <= 0) {
      JLINK_SYS_Report("** ERROR: Timeout while waiting for core to halt after reset and halt request.");
      r = -1;
      break;
    }
  } while (1);
  if (r >= 0) {
    v = (HARTSEL_VALUE << 6) | (1 << 0);                 // Clear haltreq (removes halt request but does not restart core); dmactive remains set
    JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, v);
    JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_CONTROL, &v);   // Ensure write has completed before continuing
  }
  return r;
}

/*********************************************************************
*
*       _HaltCore()
*
*  Function description
*    Halts the core: sets dmcontrol.haltreq (and dmactive), polls
*    dmstatus.allhalted until the core accepts the halt request, then
*    clears haltreq again while leaving dmactive set.
*
*  Return value
*    >= 0:  O.K.
*     < 0:  Error (timeout waiting for allhalted)
*/
static int _HaltCore(void) {
  U32 v;
  int t;
  int Result;

  Result = 0;
  v = (1 << 31) | (HARTSEL_VALUE << 6) | (1 << 0);   // haltreq: 1 == request halt; hartsel; dmactive: 1 == debug module enabled
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, v);
  t = JLINK_GetTime() + RISCV_TIMEOUT_RESUME_REQ;
  do {
    JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_STATUS, &v);
    if ((v >> 9) & 1) {   // allhalted: 1 == all selected cores are halted
      break;
    }
    if ((t - JLINK_GetTime()) <= 0) {
      Result = -1;
      goto Done;
    }
  } while (1);
Done:
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, (HARTSEL_VALUE << 6) | (1 << 0));   // hartsel, dmactive: 1 == debug module enabled (haltreq cleared)
  JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_STATUS, &v);                                  // Ensure write has completed before returning
  return Result;
}

/*********************************************************************
*
*       _RestartCore()
*
*  Function description
*    Resumes the core: sets dmcontrol.resumereq, polls dmstatus.
*    allresumeack until the core accepts the resume request, then
*    clears resumereq again (required so a subsequent single-step
*    command isn't ignored - resumereq only takes effect on a 0->1
*    transition).
*
*  Return value
*    >= 0:  O.K.
*     < 0:  Error (timeout waiting for allresumeack)
*/
static int _RestartCore(void) {
  int Result;
  int t;
  U32 v;

  Result = -1;
  //
  // Make sure haltreq/resumereq both start at 0 (resumereq is
  // write-only per newer debug spec revisions - no read value relied
  // upon), then set resumereq (bit 30) to request resume.
  //
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, (HARTSEL_VALUE << 6) | (1 << 0));
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, (HARTSEL_VALUE << 6) | (1 << 30) | (1 << 0));
  //
  // Wait until core has accepted the resume request
  // (dmstatus.allresumeack, bit 17)
  //
  t = JLINK_GetTime() + RISCV_TIMEOUT_RESUME_REQ;
  do {
    JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_STATUS, &v);
    if ((v >> 17) & 1) {
      break;
    }
    if ((t - JLINK_GetTime()) <= 0) {
      JLINK_SYS_Report("** ERROR: Timeout waiting for dmstatus.allresumeack.");
      goto Done;
    }
  } while (1);
  Result = 0;
Done:
  //
  // Clear resumereq (required so a subsequent single-step isn't
  // ignored, since resumereq only acts on a 0->1 transition)
  //
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, (HARTSEL_VALUE << 6) | (1 << 0));
  JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_STATUS, &v);   // Ensure write has completed before returning
  return Result;
}

/*********************************************************************
*
*       _ConnectToDM_V013_V10()
*
*  Function description
*    Connect handling for RISC-V debug spec V0.13 and V1.0 (identical
*    sequence for both). Determines memory access capabilities
*    (system bus / ProgBuf / abstract command), then determines RV32
*    vs RV64 via an abstract-command GPR read.
*
*  Return value
*    >= 0:  O.K.
*     < 0:  Error / unsupported core
*
*  Notes
*    (1) RV128 detection is reproduced but reported as unsupported.
*    (2) The access-width report (system bus AccWidth) is reported as a
*        raw bitmask instead of a formatted "8/16/32-bit" string, since
*        there is no string formatting available.
*/
static int _ConnectToDM_V013_V10(void) {
  U32 v;
  U32 Data;
  U32 Tmp;
  U32 AbsCS;
  U32 CmdErr;
  U32 NumProgBuf;
  U32 NumDataBuf;
  U32 IsRV64;
  U32 KeepCoreHalted;
  int RetryCnt;
  int Result;
  int r;
  int i;

  RetryCnt = 0;
  IsRV64   = 0;
TryAgain:
  JLINK_SYS_Report("Memory access:");
  Result = 0;
  if (RetryCnt) {
    r = _ConnectUnderReset();
    if (r < 0) {
      JLINK_SYS_Report("** ERROR: Connect under reset failed.");
      goto Done;
    }
  }
  //
  // Make sure DMI/debug module is enabled (hart selection was already
  // applied via ConfigTargetSettings() before this function runs).
  //
  r = JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, (HARTSEL_VALUE << 6) | (1 << 0));   // dmactive = 1
  //
  // Check if core is already halted here. We need to halt the core for a short period to perform feature detection.
  // Preserve previous CPU state: If core was already halted, keep it halted. Otherwise restart it, if the core was running previously.
  //
  r |= JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_STATUS, &v);
  if (r < 0) {
    JLINK_SYS_Report("** ERROR: Cannot read DMI registers.");
    goto Done;
  }
  KeepCoreHalted = (v >> 9) & 1;   // <allhalted>: 1 == All selected cores are halted
  //
  // System bus access info (sbcs.sbasize)
  //
  r = JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_SBCS, &Data);
  if (r < 0) {
    v = 0;
  } else {
    v = (Data >> 5) & 0x7F;   // sbasize: 0 means no system bus access
  }
  //
  // Some implementations report sbcs but don't actually implement it;
  // verify sbaddress0 is writable/readable as a sanity check.
  //
  if (v) {
    Tmp = 0;
    r  = JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_SBADDR0, _SBA_MAGIC_VALUE);
    r |= JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_SBADDR0, &Tmp);
    if ((r < 0) || (Tmp != _SBA_MAGIC_VALUE)) {
      v = 0;
    }
  }
  if (v) {  // Access via system bus? => Output information
    Tmp = 0;
    i = 4;
    while (i >= 0) {
      if ((Data >> i) & 1) {
        Tmp = 8 << i;
        break;
      }
      i = i - 1;
    }
    JLINK_SYS_Report("  Via system bus: Yes");
    JLINK_SYS_Report1("    Maximum access bit width supported: ", Tmp);
  } else {
    JLINK_SYS_Report("  Via system bus: No");
  }
  //
  // ProgBuf info (abstractcs.progbufsize)
  //
  JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_ABSTRACT_CS, &AbsCS);
  v = (AbsCS >> 24) & 0x1F;
  NumProgBuf = v;
  if (v) {
    JLINK_SYS_Report1("  Via ProgBuf: Yes, entries: ", v);
  } else {
    JLINK_SYS_Report("  Via ProgBuf: No (0 ProgBuf entries)");
  }
  //
  // Abstract command access (AAM) - always assumed available as last
  // resort (cannot be verified without a trial memory access).
  //
  JLINK_SYS_Report("  Via abstract command (AAM): May be tried as last resort");
  //
  // DataBuf entry count + autoexec[0] probe
  //
  NumDataBuf = AbsCS & 0xF;
  JLINK_SYS_Report1("  DataBuf entries: ", NumDataBuf);
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_AUTO, 1);
  JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_ABSTRACT_AUTO, &Data);
  if (Data) {
    JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_AUTO, 0);   // Restore
  }
  if (Data) {
    JLINK_SYS_Report("  autoexec[0] implemented: Yes");
  } else {
    JLINK_SYS_Report("  autoexec[0] implemented: No");
  }
  //
  // Halt core to access CSRs / issue abstract commands reliably.
  //
  if (KeepCoreHalted == 0) {
    r = _HaltCore();
    if (r < 0) {
      JLINK_SYS_Report("** ERROR: Failed to temporarily halt core during identification.");
      Result = -1;
      goto Done;
    }
  }
  //
  // Determine RV32 vs RV64 (vs unsupported RV128) via abstract-
  // command GPR read of x1 at increasing access widths.
  //
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_CS, (0x7 << 8));   // Clear cmderr
  v = ABS_CMD_COPY_GPR_TO_ABSDATA | _INDEX_HW_X1 | (ABS_CMD_ACC_WIDTH_64B << 20);
  JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_CMD, v);
  JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_ABSTRACT_CS, &v);
  CmdErr = (v >> 8) & 0x7;
  if (CmdErr) {
    //
    // 64-bit access failed => RV32
    //
    JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_CS, (0x7 << 8));   // Clear cmderr
    IsRV64 = 0;
  } else {
    //
    // 64-bit access succeeded => probably RV64, verify not RV128
    //
    if (NumDataBuf >= 4) {
      JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_DATA0 + 0, ACC_WIDTH_TEST_VALUE);
      JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_DATA0 + 1, ACC_WIDTH_TEST_VALUE);
      JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_DATA0 + 2, ACC_WIDTH_TEST_VALUE);
      JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_DATA0 + 3, ACC_WIDTH_TEST_VALUE);
      v = ABS_CMD_COPY_GPR_TO_ABSDATA | _INDEX_HW_X1 | (ABS_CMD_ACC_WIDTH_128B << 20);
      JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_CMD, v);
      JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_ABSTRACT_CS, &v);
      CmdErr = (v >> 8) & 0x7;
      if (CmdErr == 0) {
        //
        // 128-bit access succeeded - check if data2 stayed the
        // pattern (only data0-1 updated) => RV64, else RV128
        //
        v = 0;
        JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_ABSTRACT_DATA0 + 2, &v);
        if (v == ACC_WIDTH_TEST_VALUE) {
          IsRV64 = 1;
        } else {
          JLINK_SYS_Report("** ERROR: Detected RV128 - not supported.");
          Result = -1;
        }
      } else {
        //
        // 128-bit access failed => RV64
        //
        JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_ABSTRACT_CS, (0x7 << 8));
        IsRV64 = 1;
      }
    } else {
      IsRV64 = 1;
    }
  }
  if (Result < 0) {
    goto Done;
  }
  //
  // Resume core if it was previously running.
  //
  if (KeepCoreHalted == 0) {
    r = _RestartCore();
    if (r < 0) {
      JLINK_SYS_Report("** ERROR: Failed to restart core after identification.");
      Result = -1;
    }
  }
Done:
  if (IsRV64) {
    JLINK_SYS_Report("Detected: RV64 core");
  } else {
    JLINK_SYS_Report("Detected: RV32 core");
  }
  if (Result < 0) {
    if (RetryCnt == 0) {
      JLINK_SYS_Report("Attaching to core failed. Trying again with connect under reset.");
      RetryCnt = 1;
      goto TryAgain;
    }
    JLINK_SYS_Report("** ERROR: Error while connecting to core. Detected information may not be correct.");
  }
  return Result;
}

/*********************************************************************
*
*       _EnableDM()
*
*  Function description
*    Activates the debug module (dmcontrol.dmactive) if not already
*    active, reads dmstatus to determine the debug spec. version, and
*    dispatches to version-specific connect handling.
*
*  Return value
*    >= 0:  O.K.
*     < 0:  Error
*
*  Notes
*    (1) Requires JLINK_RISCV_DMI_SetDMISettings() to have already been
*        called (see _ConnectRVBehindJTAG_DTM() above) - otherwise the
*        first DMI access below fails.
*    (2) V0.11 is explicitly rejected: it relies on a completely
*        different, non-DMI register-access mechanism with no script-
*        level equivalent, so no attempt is made to support it.
*/
static int _EnableDM(void) {
  U32 v;
  U32 DMVersion;
  int t;
  int r;
  //
  // Start up Debug Module if not already active (dmcontrol.dmactive)
  //
  r = JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_CONTROL, &v);
  if (r < 0) {
    JLINK_SYS_Report("** ERROR: Reading <dmcontrol> debug register failed.");
    return -1;
  }
  if ((v & 0x1) == 0) {
    r = JLINK_RISCV_DMI_WriteReg(RISCV_DM_REG_CONTROL, (HARTSEL_VALUE << 6) | (1 << 0));   // Set dmactive
    if (r < 0) {
      JLINK_SYS_Report("** ERROR: Writing <dmcontrol> debug register failed.");
      return -1;
    }
    t = JLINK_GetTime() + RISCV_TIMEOUT_DMCONTROL_READ;
    do {
      r = JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_CONTROL, &v);
      if (r < 0) {
        JLINK_SYS_Report("** ERROR: Reading <dmcontrol> debug register failed.");
        return -1;
      }
      if (v & 0x1) {
        break;
      }
      if ((t - JLINK_GetTime()) <= 0) {
        JLINK_SYS_Report("** ERROR: Timeout while waiting for debug module to become active.");
        return -1;
      }
    } while (1);
  }
  //
  // Read Debug Module version from dmstatus
  //
  r = JLINK_RISCV_DMI_ReadReg(RISCV_DM_REG_STATUS, &v);
  if (r < 0) {
    JLINK_SYS_Report("** ERROR: Reading <dmstatus> debug register failed.");
    return -1;
  }
  DMVersion = v & 0xF;
  if (DMVersion == RISCV_DM_VER_1_0) {
    JLINK_SYS_Report("RISC-V Debug Spec. Version: 1.0");
    r = _ConnectToDM_V013_V10();
  } else if (DMVersion == RISCV_DM_VER_0_13) {
    JLINK_SYS_Report("RISC-V Debug Spec. Version: 0.13");
    r = _ConnectToDM_V013_V10();
  } else if (DMVersion == RISCV_DM_VER_0_11) {
    JLINK_SYS_Report("** ERROR: RISC-V Debug Spec. Version 0.11 detected - not supported by this script.");
    JLINK_SYS_Report("** WARNING: V0.11 relies on a non-DMI register-access mechanism with no script-level equivalent.");
    r = -1;
  } else {
    JLINK_SYS_Report1("** ERROR: unsupported or invalid DM version: ", DMVersion);
    r = -1;
  }
  return r;
}

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/

/*********************************************************************
*
*       ConfigTargetSettings()
*
*  Function description
*    Called before InitTarget(). Selects the hart to work with.
*
*  Return value
*    >= 0: O.K.
*     < 0: Error
*/
int ConfigTargetSettings(void) {
  JLINK_ExecCommand("RISCV_SetHartSel = 0");
  return 0;
}

/*********************************************************************
*
*       InitTarget()
*
*  Function description
*    Replaces the generic RISC-V connect for the RISC-V-behind-JTAG-
*    DTM case.
*
*  Return value
*    >= 0: O.K.
*     < 0: Error
*
*  Notes
*    (1) Must not use high-level API functions (JLINK_MEM_* etc.).
*/
int InitTarget(void) {
  int r;

  r = _ConnectRVBehindJTAG_DTM();
  if (r < 0) {
    return -1;
  }
  r = _EnableDM();
  if (r < 0) {
    return -1;
  }
  return 0;
}