How to use SystemView Multicore
With SystemView V3.60 multicore support was introduced. This article describes how to set up a multicore recording and how to configure RTT on the target device.
How it works
Even in a multicore recording, the J-Link connects to one core only. This core, to which the J-Link is connected is called main core in the following.
To get the events of all cores, multiple RTT channels (one for each core) are used. This means each core stores its events in its own RTT buffer.
Since the J-Link is only connected to the main core, this core must be able to access the RTT buffers of all other cores.
To organize the different RTT buffers, there is one RTT Control Block. This RTT Control Block contains the Buffer Descriptors for all cores.
Make sure that the RTT Control Block is only initalized once (in the main core).
Please refer to https://kb.segger.com/RTT for further information how RTT buffers work generally.
Each core starts its own recording which is completely independed of the other cores recording. Each core can run a different RTOS. Each core can run at a different speed. Thanks to the different RTT buffers, no locking mechanisms are required to write the events.
Sample configuration
The example target system is an LPC4367, including one Cortex-M4 and two Cortex-M0 cores, dubbed CM4, CM0APP, CM0SUB. J-Link connects to M4MAIN. All three cores execute some application and should be recorded via SystemView.
The device has the following memories:
RAM1, RAM2, RAM3 are connected via I-bus and D-bus to CM4 to CM0APP via bridge to CM0SUB to all bus masters AHB_RAM1 is connected via System bus to CM4 to CM0APP via bridge to CM0SUB to all bus masters AHB_RAM2, ETB_RAM1 are connected via System bus to CM4 to CM0APP to USB0 bus master M0SUB_RAM1, M0SUB_RAM1 are connected via I-bus and D-bus via bridge to CM4 via bridge to CM0APP to CM0SUB via bridge to all bus masters BANKA_FLASH1, BANKA_FLASH2, BANKA_FLASH3 are connected via I-bus and D-bus to CM4 to CM0APP via bridge to CM0SUB to all bus masters BANKB_FLASH1, BANKB_FLASH2, BANKB_FLASH3 are connected via I-bus and D-bus to CM4 to CM0APP to DMA, ETHERNET, USB0 bus masters
For the example system the memories are used by the different cores like this:
CM4 Code: BANKA_FLASH1, BANKA_FLASH2 Data: RAM1 CM0APP: Code: BANKB_FLASH1 (mapped to 0x00000000) Data: RAM2 CM0SUB: Code: BANKA_FLASH3 (mapped to 0x00000000), M0SUB_RAM2 (initialized by CM4) Data: M0SUB_RAM1 Shared: Code: BANKA_FLASH3 Data: RAM3
RTT placement
The important parts for SystemView multicore recording are the Shared Data memory and memory accessible by CM4 and one core respectively.
The RTT Control Block needs to be in memory, which is accessible by all cores and is initialized by the CM4 application only.
The SystemView RTT Buffers need to be in memory, which is accessible by the core recording itself, by CM4, and by J-Link through CM4. The chosen memory should favor fast bus access by the recording core rather than by CM4.
The following table shows which core needs to access which buffers. The RTT Control Block needs to be accessible for all cores.
Accessed by CM4 | Accessed by CM0APP | Accessed by CM0SUB | Chosen region | |
---|---|---|---|---|
RTT Control Block | yes | yes | yes | Shared Data (RAM3) |
SystemView RTT Buffer CM4 | yes | no | no | CM4 Data (RAM1) |
SystemView RTT Buffer CM0APP | yes | yes | no | CM0APP Data (RAM2) |
SystemView RTT Buffer CM0SUB | yes | no | yes | CM0SUB Data (M0SUB_RAM1) |
Linker placement
With the SEGGER Linker, the RTT configuration can be achieved as such:
#if defined(CORE_M4) initialize by copy with packing=auto { section .shared.data, section .shared.data.* }; #else do not initialize { section .shared.data, section .shared.data.* }; #endif place in SHARED_DATA with fixed order { section .shared.data, section .shared.data.* };
With the GNU Linker and other toolchains the linker script and runtime initialization is similar.
RTT compile-time configuration
The following configuration options for RTT are important for SystemView multicore recording. They can be done in SEGGER_RTT_Conf.h
.
SEGGER_RTT_MAX_NUM_UP_BUFFERS
- Number of up and down buffers- Minimum: Number of cores + 1
SEGGER_RTT_SECTION
- Placement of RTT Control Block- Section name:
".shared.data._SEGGER_RTT"
- Section name:
SEGGER_RTT_BUFFER_SECTION
- Placement of Terminal RTT Buffer- Section name:
".noinit.rtt_buffer"
- Section name:
#define SEGGER_RTT_MAX_NUM_UP_BUFFERS (NUM_CORES + 1) // Max. number of up-buffers (T->H) available on this target (Default: 3) #define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (NUM_CORES + 1) // Max. number of down-buffers (H->T) available on this target (Default: 3) #if defined(CORE_M4) #define BUFFER_SIZE_UP (1024) // Size of the buffer for terminal output of target, up to host (Default: 1k) #define BUFFER_SIZE_DOWN (16) // Size of the buffer for terminal input to target from host (Usually keyboard input) (Default: 16) #else #define BUFFER_SIZE_UP (4) // Unused. Set minimum on other cores. #define BUFFER_SIZE_DOWN (4) // Unused. Set minimum on other cores. #endif #define SEGGER_RTT_SECTION ".shared.data._SEGGER_RTT" #define SEGGER_RTT_BUFFER_SECTION ".noinit.rtt_buffer"
SystemView compile-time configuration
The following configuration options for SystemView need to be done in SEGGER_SYSVIEW_Conf.h
SEGGER_SYSVIEW_RTT_CHANNEL
- RTT Channel number- Different per core, not 0.
#if defined(CORE_M4) #define SEGGER_SYSVIEW_APP_NAME "LPC4367 Multicore Demo - M4" #define SEGGER_SYSVIEW_DEVICE_NAME "LPC4367_M4" #define SEGGER_SYSVIEW_ID_BASE 0x10000000 #define SEGGER_SYSVIEW_TIMESTAMP_FREQ SystemCoreClock #define SEGGER_SYSVIEW_CPU_FREQ SystemCoreClock #define SEGGER_SYSVIEW_SYSDESC0 "I#29=TIMER1,I#17=M0APP,I#66=M0SUB" #define SEGGER_SYSVIEW_CORE_NAME "M4MAIN" #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() ((*(U32*)(0xE000ED04)) & 0x1FF) #define SEGGER_SYSVIEW_GET_TIMESTAMP() (*(U32 *)(0x400C000C)) // RIT Counter #define SEGGER_SYSVIEW_RTT_CHANNEL 1 #elif defined(CORE_M0) #define SEGGER_SYSVIEW_APP_NAME "LPC4367 Multicore Demo - M0APP" #define SEGGER_SYSVIEW_DEVICE_NAME "LPC4367_M0APP" #define SEGGER_SYSVIEW_ID_BASE 0x10000000 #define SEGGER_SYSVIEW_TIMESTAMP_FREQ SystemCoreClock #define SEGGER_SYSVIEW_CPU_FREQ SystemCoreClock #define SEGGER_SYSVIEW_SYSDESC0 "I#28=TIMER0,I#17=M4CORE,I#47=M0SUB" #define SEGGER_SYSVIEW_CORE_NAME "M0APP" #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() ((*(U32*)(0xE000ED04)) & 0x3F) #define SEGGER_SYSVIEW_GET_TIMESTAMP() (*(U32 *)(0x400C000C)) // RIT Counter #define SEGGER_SYSVIEW_RTT_CHANNEL 2 #elif defined(CORE_M0SUB) #define SEGGER_SYSVIEW_APP_NAME "LPC4367 Multicore Demo - M0SUB" #define SEGGER_SYSVIEW_DEVICE_NAME "LPC4367_M0SUB" #define SEGGER_SYSVIEW_ID_BASE 0x10000000 #define SEGGER_SYSVIEW_TIMESTAMP_FREQ SystemCoreClock #define SEGGER_SYSVIEW_CPU_FREQ SystemCoreClock #define SEGGER_SYSVIEW_SYSDESC0 "I#30=TIMER2,I#17=M4CORE,I#47=M0APP" #define SEGGER_SYSVIEW_CORE_NAME "M0SUB" #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() ((*(U32*)(0xE000ED04)) & 0x3F) #define SEGGER_SYSVIEW_GET_TIMESTAMP() (*(U32 *)(0x400C000C)) // RIT Counter #define SEGGER_SYSVIEW_RTT_CHANNEL 3 #endif
Runtime configuration
The use of SystemView multicore recording is almost the same as single-core recording. It is only necessary to make sure to call SEGGER_SYSVIEW_Conf() or SEGGER_RTT_Init() on main core, before other cores call any SystemView or RTT API.
void CM4_AppInit(void) { CM0APP_ResetAndStop(); CM0SUB_ResetAndStop(); ... SEGGER_SYSVIEW_Conf(); ... CM0APP_Start(); CM0SUB_Start(); ... }
SystemView configuration
To start a SystemView multicore recording just follow the following steps:
- Open the Recorder configuration (Target → Recorder Configuration).
- Select J-Link.
- Setup your J-Link and Target connection as usual.
- Set Number of cores either to 0 (then all RTT channels are detected automatically) or the number of cores in your application.
- If the RTT Control Block cannot be found automatically, add the address of the RTT Control Block.
- Save the settings by clicking OK and start a recording (F5).