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
}
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.
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;
}