7.8. Scripting

CCS Scripting (a.k.a "Scripting") is a synchronous scripting interface available from the debugger. This allows for users to automate debugger actions from a script. JavaScript is the default scripting language supported by CCS Scripting.

Note

CCS Scripting is a new scripting solution independent of DSS. DSS (also referred to as Legacy Scripting) is still available with CCS Theia. Most of the syntax for the CCS Scripting APIs remains unchanged from DSS. The full API documentation can be found in <CCS THEIA INSTALL DIR>/ccs/scripting/docs.

Warning

Note that the CCS Scripting interface from CCS Theia versions 1.2 and earlier were asynchronous in behavior and required the use of the await operator to wait for the completion of operations. This is no longer the case with CCS Theia 1.3, which is synchronous in behavior. Hence the await operator is no longer needed.

7.8.1. Scripting API

The full API documentation can be found in <CCS THEIA INSTALL DIR>/ccs/scripting/docs.

7.8.2. Getting Started

To launch the debugger, initScripting must be called. This returns the main scripting interface.

// usual usage
const ds = initScripting();

// providing a optional object with additional configuration options
const ds = initScripting({ inProcess: true });

Note

To support 64-bit addressable targets and 64-bit values in general, the scripting environment will accept and return BigInts. This is a JavaScript type that can represent arbitrary-sized integers. Regular JavaScript numbers and strings will also be accepted, but using BigInts is recommended.

7.8.2.1. Configuring and Launching a Debug Session

To debug a given target, the debugger must be configured using configure. The Target Configuration File needs to be specified to the scripting environment before attempting to start a debug session. This is done by passing in a target configuration (.ccxml) file to the configure API.

let { cores, nonDebugCores } = ds.configure("my_configuration.ccxml");

To open a debug session to a target, openSession must be called with the core name. You can open multiple debug sessions for multi-core debugging.

// Specifying an exact name
let session = ds.openSession("Texas Instruments XDS110 USB Debug Probe_0/Cortex_M4_0")

// The first core with CortexM or CortexR anywhere in the name
let session = ds.openSession("Cortex(M|R)")
// equivalently
let session = ds.openSession(/Cortex(M|R)/)

// The first core
let session = ds.openSession()

Tip

A list of the currently configured cores can be found by:

let { cores, nonDebugCores } = ds.listCores();

7.8.2.2. Connecting and Disconnecting a Target

A target can be connected or disconnected using target.connect and target.disconnect.

// connects and disconnects from the target
session.target.connect()
session.target.disconnect()

7.8.2.3. Loading and Running a Program

First, a program file must be loaded to the target.

session.memory.loadProgram("C:/my_project/Debug/example.out");

The loaded program can then be run.

session.target.run()

7.8.2.4. Shutting Down the Debug Session

To deconfigure the debugger and close the connection to the debugger, call shutdown. This should be called at the end of a script.

ds.shutdown();

7.8.2.5. Breakpoints

Breakpoints can be added or removed using session.breakpoints.add() and session.breakpoints.remove()

// Adds a breakpoint at 0x2400
let id = session.breakpoints.add(0x2400n);

// Adds a breakpoint at symbol 'main'
let id = session.breakpoints.add("main");

// Adds a breakpoint at an offset from symbol 'main'
let id = session.breakpoints.add("main + 0x8");

// Adds a breakpoint at line 24 of main.c
let id = session.breakpoints.add("C:/my_project/source/main.c", 24);

// Removes the breakpoint at line 24 of main.c
session.breakpoints.remove(id);

7.8.3. Example Script

Below is a full example script. It is strongly encouraged for new users to slowly walk through this example to get an understanding of how CCS Scripting works. The example is well commented and should give you an understanding of the basic steps needed to create the scripting environment and start a debug session for a specified target, in addition to highlighting some of the APIs available. It is also useful to use this script as a baseline for creating your own scripts (by referencing it or simply copying and then "gutting" it to use as a template).

This example script also ships with CCS Theia and can be found in <CCS THEIA INSTALL DIR>/ccs/scripting/examples/debugger.

// Since we don't know where CCS will be installed, we must find files relative
// to this script. To this end, we will need access to node.js's path.join function.
const { join } = require ("path");


// Initialize scripting and obtain the main debugger scripting interface
const ds = initScripting();

// Configure a 10 second timeout on all operations (by default there is no timeout)
ds.setScriptingTimeout(10000);

// Configure the debugger and open a debug session to the cortex M core
ds.configure(join(__dirname, "mspm0g3507/mspm0g3507.ccxml"));
const session = ds.openSession(/cortex/i);

session.target.connect();

// Load program
// The path provided must be an absolute path. Here we use the current script's location
// to resolve the location.
session.memory.loadProgram(join(__dirname, "mspm0g3507/modem.out"));

// Set a breakpoint at ReadNextData
const bp1 = session.breakpoints.add("ReadNextData");

// Set a second breakpoint using the address of the function ShapingFilter
const bp2Addr = session.expressions.evaluate("ShapingFilter");
const bp2 = session.breakpoints.add(bp2Addr);

// Let's define a function to run the target and check if it halts at the correct symbol
function expectRunToHaltAt(symbol) {
    // Run the target and wait for it to halt
    session.target.run();

    const symbolAddr = session.expressions.evaluate(symbol);
    const pc = session.registers.read("PC")
    if (pc === symbolAddr) {
        console.log(`Success: target is halted at ${symbol} as expected.`);
    } else {
        console.error(
            `Failure: Expected target to be halted at 0x${symbolAddr.toString(16)}, `
            `but is actually halted at 0x${pc.toString(16)}.`
        );
        process.exit(1);
    }
}

// If we run, we should hit our first breakpoint at ReadNextData
expectRunToHaltAt("ReadNextData");

// If we run a second time, we expect to halt at our second breakpoint
expectRunToHaltAt("ShapingFilter");

// Remove our first breakpoint
session.breakpoints.remove(bp1);

// The program runs in an infinite loop, so running a third time should, once again, halt at the second breakpoint
expectRunToHaltAt("ShapingFilter");

// If we remove our second breakpoint as well, we expect to run in a loop until we time out
session.breakpoints.remove(bp2);

// We expect the next run to timeout, let's reduce the timeout duration so we don't have to wait as long
ds.setScriptingTimeout(2000);

try {
    console.log("Expecting target to not halt for 2 seconds");
    session.target.run();
    console.error("Failure: Halted unexpectedly after removing both breakpoints.");
} catch (err) {
    // Check if we actually timed out, or if some other error occurred
    if (err instanceof ScriptingTimeoutError) {
        console.log("Success: we timed out while waiting for the target to halt");
        session.target.halt();
    } else {
        console.error(`Failure: unexpected error while running ${err}`);
    }
}

// shutdown the debugger
ds.shutdown();

Tip

You can send messages from your script to the console by using console.log(). For more information, visit https://developer.mozilla.org/en-US/docs/Web/API/console.

7.8.4. Running a Script

Navigate to <CCS THEIA INSTALL DIR>/ccs/scripting and use the run.bat and run.sh scripts with your script path as an argument:

run.bat examples/debugger/breakpoints.js

7.8.5. Using GEL with Scripting

You can call GEL expressions from Scripting by using expressions.evaluate().

// Adds 8 to the address of the main symbol, returns a BigInt
let result = ession.expressions.evaluate("main + 8");

// Returns a (floating point) number
let result = session.expressions.evaluate("1.0 / 2");

// Call a GEL function (assuming that this function is either defined in a loaded GEL script or a build-in GEL function)
session.expressions.evaluate("userDefinedGelFunction()");