Let’s take a basic example without any tests. We’ll use a blank project that sets up the board correctly but has an empty main()
function. We don’t have many requirements, but we need RTT installed on the target. More details on RTT will be in a future article.
This is the command I’ll be running:
JRun --device STM32H563ZI --if SWD --USB xxxx --wait TestProgram.elf
I’ve added the SEGGER software suite to my system path so that I can access the command-line tools directly. Let’s look at the parameters.
--device
will tell J-Run which target is going to be used.--if SWD
specifies the interface to use. For this example, we’ll be using Serial Wire Debug.--USB
followed by the serial number tells J-Run that it will be looking for a USB device with the given serial number. There are options for Ethernet. Usually, J-Run will detect the JLink, but on my Mac, it won’t. I’m not too sure why. If you can’t detect your adapter, then use the serial number printed underneath your J-Link.--wait
tells J-Run to wait for a keypress before exiting. You’ll want this option activated if you run J-Run through a script. Otherwise, the window might close before you see the result.
Finally, TestProgram.elf
is the binary to be flashed onto your device and executed.
Running the command
Let’s run the command and see what happens. I have created a very basic application, one that basically does nothing. It doesn’t perform any tests; we’ll get into that later. This is the output:
❯ JRun --device STM32H563ZI --if SWD --RTT -USB xxxx TestProgram.elf
SEGGER J-Run V8.28 Command Line Version
Open application...OK
Connecting to J-Link...OK
Connected to J-Link Ultra V6 compiled Apr 1 2025 10:03:24 (S/N xxxx).
JLinkARM.dll V8.28 (DLL compiled Apr 23 2025 12:33:44)
Set target device to STM32H563ZI...OK
Select SWD interface...OK
Set interface speed to 4000 kHz...OK
Reset target...OK
Downloading file TestProgram.elf...OK
Set RTT control block at 0x20000000...OK
Start RTT...OK
Determine application addresses (SP 0x200A0000, PC 0x 8002021)...OK
Start target application... OK
Reading output from target until exit command.
==============================================
We get a list of the different actions performed. The application opened. It connected to a JLink and returned the JLink version along with some software details. Then, it connected to the target via SWD, as requested. The firmware was uploaded, some RTT information was displayed, and then the application was run.
Then, nothing. JRun executes forever, waiting for something that will never arrive. We have successfully run the binary, but never tell J-Run that the test has ended. To do this, we must send a message: *STOP*
. Let’s change the code, adding one line at the end:
SEGGER_RTT_printf(0, "*STOP*\n");
Now let’s execute J-Run again. Here is the new output:
❯ JRun --device STM32H563ZI --if SWD --RTT -USB xxxx --wait TestProgram.elf
SEGGER J-Run V8.28 Command Line Version
Open application...OK
Connecting to J-Link...OK
Connected to J-Link Ultra V6 compiled Apr 1 2025 10:03:24 (S/N xxxx).
JLinkARM.dll V8.28 (DLL compiled Apr 23 2025 12:33:44)
Set target device to STM32H563ZI...OK
Select SWD interface...OK
Set interface speed to 4000 kHz...OK
Reset target...OK
Downloading file TestProgram.elf...OK
Set RTT control block at 0x20000000...OK
Start RTT...OK
Determine application addresses (SP 0x200A0000, PC 0x 80029AF)...OK
Start target application... OK
Reading output from target until exit command.
==============================================
*STOP*
==============================================
Target RTT wildcard exit value 0.
J-Run finished. Press any key to exit.
That’s better. J-Run now detects the end of the test procedure and returns a value of 0. We printed out *STOP*
to the output RTT buffer, and it is displayed.
By default, J-Run will wait for a *STOP*
statement before terminating. This can be overridden by the --wexit
parameter, but this is fine for our example, so we will leave it as is.
More output
Having a single line of text doesn’t help our use case; we don’t want just to run the commands, we want to know the results. Since we can see the RTT stop string, let’s add a little more text. First, we’ll add a function that simulates everything performing well. It will display some text:
static void testRoutine01(void);
void testRoutine01(void)
{
SEGGER_RTT_printf(0, "testRoutine01: PASS\n");
}
Then we call it inside our main
function:
SEGGER_RTT_printf(0, “Starting tests\n”);
testRoutine01();
SEGGER_RTT_printf(0, “Tests completed!\n”);
SEGGER_RTT_printf(0, “STOP\n”);
return 0;
And here is our output:
==============================================
Starting tests
testRoutine01: PASS
Tests completed!
*STOP*
==============================================
And if we add a function that fails?
static void testRoutine02(void);
void testRoutine02(void)
{
SEGGER_RTT_printf(0, "testRoutine01: FAIL\n");
}
And we add it to our main
function:
SEGGER_RTT_printf(0, "Starting tests\n");
testRoutine01();
testRoutine02();
SEGGER_RTT_printf(0, "Tests completed!\n");
SEGGER_RTT_printf(0, "*STOP*\n");
return 0;
And the result:
==============================================
Starting tests
testRoutine01: PASS
testRoutine01: FAIL
Tests completed!
*STOP*
==============================================
It is better since we know what has been run and the status, but we still have to parse the output to see what happened. There is a solution for this, and we will look into it in the following article.