/*********************************************************************
*               SEGGER MICROCONTROLLER GmbH                          *
*       Solutions for real time microcontroller applications         *
**********************************************************************
*                                                                    *
*       (c) 1995 - 2024  SEGGER Microcontroller GmbH                 *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
*                                                                    *
*       Please note:                                                 *
*                                                                    *
*       Knowledge of this file may under no circumstances            *
*       be used to write a similar product                           *
*                                                                    *
*       Thank you for your fairness !                                *
*                                                                    *
**********************************************************************
*                                                                    *
*       Plug-in version: 1                                           *
*       Tested OS Version: 6.1.12                                    *
*                                                                    *
**********************************************************************

----------------------------------------------------------------------
File    : ThreadXPlugin.js
Purpose : Script for RTOS window for ThreadX and 
          SEGGER Embedded Studio/Ozone
---------------------------END-OF-HEADER------------------------------
*/

/**** ARM Register indices, constant ********************************/
var ARM_R0  =  0;
var ARM_R1  =  1;
var ARM_R2  =  2;
var ARM_R3  =  3;
var ARM_R4  =  4;
var ARM_R5  =  5;
var ARM_R6  =  6;
var ARM_R7  =  7;
var ARM_R8  =  8;
var ARM_R9  =  9;
var ARM_R10 = 10;
var ARM_R11 = 11;
var ARM_R12 = 12;
var ARM_SP  = 13;
var ARM_LR  = 14;
var ARM_PC  = 15;
var ARM_PSR = 16;

/*********************************************************************
*
*       Local functions
*
**********************************************************************
*/
/*********************************************************************
*
*       _MapThreadState()
*
*  Functions description:
*    Maps given numercial state to a character string.
*/
function _MapThreadState(State) {
  switch (State) {
  case 0x00:
    return "Ready";
  case 0x01:
    return "Completed";
  case 0x02:
    return "Terminated";
  case 0x03:
    return "Suspended";
  case 0x04:
    return "Sleep";
  case 0x05:
    return "Queue susp";
  case 0x06:
    return "Semaphore susp";
  case 0x07:
    return "Event flag";
  case 0x08:
    return "Block memory";
  case 0x09:
    return "Mutex susp";
  case 0x0A:
    return "IO driver";
  default:
    return "Invalid";
  }
}

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/
/*********************************************************************
*
*       init()
*
*  Functions description:
*    This function describes the look of the threads window.
*/
function init() {
  Threads.clear();
  Threads.setColumns("Name", "Run Count", "Priority", "State", "Stack Info", "Preemption Threshold");
  Threads.setSortByNumber("Run Count");
  Threads.setSortByNumber("Priority");
  Threads.setSortByNumber("Preemption Threshold");
  if (Threads.setColor) {
    Threads.setColor("State", "0", "Executing", "4");
  }
}

/*********************************************************************
*
*       update()
*
*  Functions description:
*    This function is called to update the threads window and its
*    entries upon debug stop.
*/
function update() {
  var NextThreadPtr;
  var NumThreads;
  var CurrentThread;
  
  Threads.clear();

  NumThreads    = Debug.evaluate("_tx_thread_created_count");
  CurrentThread = Debug.evaluate("_tx_thread_current_ptr");
  NextThreadPtr = Debug.evaluate("_tx_thread_created_ptr->tx_thread_created_next");

  for (var i = 0; i < NumThreads; i++) {
    var ThreadData  = Debug.evaluate("*(TX_THREAD*)" + NextThreadPtr);
    var Name        = Debug.evaluate("(char*)((TX_THREAD*)" + NextThreadPtr + ")->tx_thread_name");
    var StackInfo;
    var State;
    //
    // Calculate stack usage.
    //
    if (ThreadData.tx_thread_stack_highest_ptr >= ThreadData.tx_thread_stack_start) {
      var StackUsed = ThreadData.tx_thread_stack_end - ThreadData.tx_thread_stack_highest_ptr;
      StackInfo = StackUsed + " / " + ThreadData.tx_thread_stack_size + " @ 0x" + ThreadData.tx_thread_stack_start.toString(16).toUpperCase();
    } else {
      StackInfo = "Enable TX_ENABLE_STACK_CHECKING";
    }
    
    if (CurrentThread == NextThreadPtr) {
      //
      // This thread is currently active.
      // ThreadX does not use a dedicated state for the active thread (active thread also has state 'ready').
      //
      State = "Executing";
    } else {
      State = _MapThreadState(ThreadData.tx_thread_state);
    }
    Threads.add(Name, ThreadData.tx_thread_run_count, ThreadData.tx_thread_priority, State, StackInfo, ThreadData.tx_thread_preempt_threshold, (NextThreadPtr == CurrentThread) ? undefined : NextThreadPtr);
    NextThreadPtr = Debug.evaluate("((TX_THREAD*)" + NextThreadPtr + ")->tx_thread_created_next");
  }
  //
  // Add idle to queue.
  //
  Threads.add("Idle", "", "", ((CurrentThread == 0) && (NumThreads > 0)) ? "Executing" : "", "", "", undefined);
}

/*********************************************************************
*
*       getOSName()
*
*  Functions description:
*    Returns the name of the RTOS this script supplies support for.
*/
function getOSName() {
  return "ThreadX";
}

/*********************************************************************
*
*       getContextSwitchAddrs()
*
*  Functions description:
*    Returns an unsigned integer array containing the base addresses
*    of all functions that complete a task switch when executed.
*/
function getContextSwitchAddrs() {
  var aAddrs = new Array(1);
  
  aAddrs[0] = Debug.evaluate("&PendSV_Handler");
  if (aAddrs[0] == undefined) {
    aAddrs[0] = Debug.evaluate("&_tx_thread_schedule");
  }
  if (aAddrs[0] == undefined) {
    return [];
  }

  return aAddrs;
}

/*********************************************************************
*
*       getregs()
*
* Function description
* Returns the register set of a task.
* For ARM cores, this function is expected to return the values
* of registers R0 to R15 and PSR.
*
* Parameters
* hTask: integer number identifying the task.
* Identical to the last parameter supplied to method Threads.add.
* For convenience, this should be the address of the TCB.
*
* Return Values
* An array of unsigned integers containing the task's register values.
* The array must be sorted according to the logical indexes of the regs.
* The logical register indexing scheme is defined by the ELF-DWARF ABI.
*/
function getregs(pTask) {
  var aRegs   = new Array(17);
  var FPUUsed = false;
  
  aRegs[ARM_SP] = Debug.evaluate("((TX_THREAD*)" + pTask + ")->tx_thread_stack_ptr");
  //
  // Registers saved by ThreadX.
  //
  aRegs[ARM_LR]    = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" LR
  if ((aRegs[ARM_LR] & 0x10) != 0x10 ) {  // check if FPU is used.  
    FPUUsed = true;
    aRegs[ARM_SP] += 16 * 4;  // Skip S16 - S31
  }
  aRegs[ARM_R4]    = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" R4
  aRegs[ARM_R5]    = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" R5
  aRegs[ARM_R6]    = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" R6
  aRegs[ARM_R7]    = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" R7
  aRegs[ARM_R8]    = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" R8
  aRegs[ARM_R9]    = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" R9
  aRegs[ARM_r10]   = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" R10
  aRegs[ARM_R11]   = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]   += 4;  // "pop" R11
  //
  // Registers saved by hardware.
  //
  aRegs[ARM_R0]   = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]  += 4;  // "pop" R0
  aRegs[ARM_R1]   = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]  += 4;  // "pop" R1
  aRegs[ARM_R2]   = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]  += 4;  // "pop" R2
  aRegs[ARM_R3]   = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]  += 4;  // "pop" R3
  aRegs[ARM_R12]  = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]  += 4;  // "pop" R12
  aRegs[ARM_SP]  += 4;  // Skip LR
  aRegs[ARM_PC]   = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]  += 4;  // "pop" PC
  aRegs[ARM_PSR]  = TargetInterface.peekWord(aRegs[ARM_SP]);
  aRegs[ARM_SP]  += 4;  // "pop" PSR

  if (FPUUsed == true) {
    aRegs[ARM_SP]  += (4 * 17);   // Skip S0 - S15 + FPSCR
  }
  
  return aRegs;
}