Raspberry Pi 2 Bare Metal Multicore JTAG Debugging
To get going with multicore development and JTAG debugging, these code snippets and pieces of information should be useful.
1. JTAG debugging
To enable JTAG on Raspberry Pi 2, some of the GPIO pins
needs to be configured to their “alternate” function.
Create a small application that calls the function below.
Build the application, and place the kernel.img
file
on the SD-card. Now, when the Raspberry Pi 2 boots, JTAG
pins will be enabled.
/*--------------------------------------------------*/
static void jtag_enable(void)
{
// http://sysprogs.com/VisualKernel/tutorials/raspberry/jtagsetup/
/* -------- JTAG SETUP --------
JTAG pin JTAG sig GPIO Header pin
1 VREF N/A 1
3 nTRST GPIO22 15
4 GND N/A 9
5 TDI GPIO4 7
7 TMS GPIO27 13
9 TCK GPIO25 22
11 RTCK GPIO23 16
13 TDO GPIO24 18
-------------------------------*/
GPFSEL2_bit.FSEL22 = GPFSEL_ALT4; // nTRST
GPFSEL0_bit.FSEL4 = GPFSEL_ALT5; // TDI
GPFSEL2_bit.FSEL27 = GPFSEL_ALT4; // TMS
GPFSEL2_bit.FSEL25 = GPFSEL_ALT4; // TCK
GPFSEL2_bit.FSEL23 = GPFSEL_ALT4; // RTCK
GPFSEL2_bit.FSEL24 = GPFSEL_ALT4; // TDO
}
2. Multicore JTAG debugging
To get in contact with all 4 CPU cores, write the following in a bcm2836.ProbeConfig
file:
"A7_0" "Cortex-A7:0@0x80010000"
+"A7_1" "Cortex-A7:0@0x80012000"
+"A7_2" "Cortex-A7:0@0x80014000"
+"A7_3" "Cortex-A7:0@0x80016000"
Specify the ProbeConfig
file on:
Project > Options > I-jet > JTAG/SWD > Probe configuration file.
Enable Multicore with: Project > Options > Debugger > Multicore > Number of cores : 4.
The Debug Log should say:
Connecting on DAP APB-AP port 0 to core Cortex-A7 r0p5 at 0x80010000.
Connecting on DAP APB-AP port 0 to core Cortex-A7 r0p5 at 0x80012000.
Connecting on DAP APB-AP port 0 to core Cortex-A7 r0p5 at 0x80014000.
Connecting on DAP APB-AP port 0 to core Cortex-A7 r0p5 at 0x80016000.
3. Multicore coding
To enable a specific core, use the function below:
#define CORE0_MBOX3_SET 0x4000008C
static void core_enable(uint32_t core, uint32_t addr)
{
// http://www.raspberrypi.org/forums/viewtopic.php?f=72&t=98904&start=25
volatile uint32_t *p;
p = (uint32_t*)(CORE0_MBOX3_SET + 0x10 * core);
*p = addr;
}
For example, to enable all CPU cores and make them start at address 0x8000:
core_enable(1, 0x8000);
core_enable(2, 0x8000);
core_enable(3, 0x8000);
Another nice function to have is get_core_id:
uint32_t get_core_id(void)
{
uint32_t core_id;
asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (core_id));
return core_id & 0x3;
}
With this function, the running code can detect on which CPU core it is executing. This is useful for a switch-case, like this for example:
volatile uint32_t core_id = get_core_id();
switch (core_id) {
case 0:
// Code for Core 0
core_enable(1, 0x8000);
core_enable(2, 0x8000);
core_enable(3, 0x8000);
break;
case 1:
// Code for Core 1
break;
case 2:
// Code for Core 2
break;
case 3:
// Code for Core 3
break;
default:
while (1) {}
break;
}