rp2040: RP2350/pico2 ETM trace over fly-wired MIPI-20

J-Link's built-in RP2350 script owns the whole chip-side path (component
map is not ROM-table-discoverable; a custom JLinkScript replaces the
built-in one and kills pin trace), re-arming at every resume - firmware
does no trace setup. TRACE_ETM builds pin clk_sys to 48 MHz from crt0
(fly-wire seating-proof; the port is DDR at clk_sys/2 and the J-Trace PRO
V2 cliff sits just above 40 MHz TRACECLK - SEGGER requires V3.0+ for this
chip), clear TIMER0/1 DBGPAUSE (default freezes the us-timer while any
core is debug-halted and sleep_ms spins forever), and run the UART console
TX-only (GPIO1 = default UART0 RX = TRACECLK).
This commit is contained in:
hathach
2026-07-24 15:28:53 +07:00
parent 4d9e4c9e38
commit e90d232b77
3 changed files with 107 additions and 1 deletions

View File

@ -1,3 +1,17 @@
set(PICO_PLATFORM rp2350-arm-s)
set(PICO_BOARD pico2)
#set(OPENOCD_SERIAL E6614103E77C5A24)
if (TRACE_ETM STREQUAL "1")
# TRACECLK is clk_sys/2 and must stay constant once trace is armed (a step
# desyncs the decoder), so the trace clock is pinned from crt0 onwards.
# 48 MHz (24 MHz TRACECLK) holds full-width trace on a typical fly-wire
# seating; a fresh, tight seating supports up to 72-80 MHz (re-qualify per
# the etm-trace skill), and >80 MHz needs a V3 probe + real trace board.
add_compile_definitions(
SYS_CLK_KHZ=48000
PLL_SYS_VCO_FREQ_HZ=1440000000
PLL_SYS_POSTDIV1=6
PLL_SYS_POSTDIV2=5
)
endif ()

View File

@ -0,0 +1,70 @@
/*********************************************************************
*
* OnProjectLoad
*
* Function description
* Project load routine. Required.
*
* Notes
* Pico 2 has no trace connector - fly-wire GPIO1-5 to the MIPI20:
* TRACECLK=GPIO1->12, D0=GPIO2->14, D1=GPIO3->16, D2=GPIO4->18,
* D3=GPIO5->20 (SEGGER validates this board the same way). Firmware must
* be built with TRACE_ETM=1: it pins clk_sys to 48 MHz (board.cmake) so
* the 4-bit port never saturates and the clock never steps mid-stream,
* and keeps the us-timer free of TIMER DBGPAUSE (family.c). The whole
* chip-side trace path (ETM/funnel/TPIU/pin mux) is armed by J-Link's
* built-in RP2350 script at every resume - do NOT set a custom
* JLinkScript here: it would replace that script and J-Link then fails
* with "Required trace components for pin trace not found".
* GPIO1 is the default UART0 RX: console TX still works, RX is lost.
*
**********************************************************************
*/
void OnProjectLoad (void) {
Project.SetTraceSource ("Trace Pins");
Project.SetTracePortWidth (4);
Project.SetSWO (0);
Edit.SysVar (VAR_TRACE_CORE_CLOCK, 48000000);
Project.AddSvdFile ("$(InstallDir)/Config/CPU/Cortex-M33F.svd");
Project.SetDevice ("RP2350_M33_0");
Project.SetHostIF ("USB", "");
Project.SetTargetIF ("SWD");
Project.SetTIFSpeed ("25 MHz");
File.Open ("../../../../../../examples/cmake-build-raspberry_pi_pico2/device/cdc_msc/cdc_msc.elf");
}
/*********************************************************************
*
* AfterTargetReset
*
* Function description
* Event handler routine.
* - Sets the PC register to program reset value.
* - Sets the SP register to program reset value on Cortex-M.
*
**********************************************************************
*/
void AfterTargetReset (void) {
// intentionally empty: the RP2350 bootrom must run to validate the
// IMAGE_DEF and hand over to the app - setting SP/PC from the vector
// table bypasses it and the pico-sdk runtime never comes up
}
/*********************************************************************
*
* AfterTargetDownload
*
* Function description
* Event handler routine.
* - Sets the PC register to program reset value.
* - Sets the SP register to program reset value on Cortex-M.
*
**********************************************************************
*/
void AfterTargetDownload (void) {
// intentionally empty: the RP2350 bootrom must run to validate the
// IMAGE_DEF and hand over to the app - setting SP/PC from the vector
// table bypasses it and the pico-sdk runtime never comes up
}

View File

@ -161,6 +161,20 @@ static void stdio_rtt_init(void) {
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
#if defined(TRACE_ETM) && defined(PICO_RP2350) && PICO_RP2350 == 1
// J-Link's built-in RP2350 device script re-arms the whole chip-side trace
// path (ETM/funnel/TPIU/pins) via OnTraceStart at every resume, so firmware
// must NOT touch it - it only keeps the us-timer running while cores sit
// debug-halted (default TIMER DBGPAUSE freezes it, and sleep_ms() then spins
// forever after any debugger session).
static void trace_etm_init(void) {
*(volatile uint32_t*) 0x400B002Cu = 0; // TIMER0 DBGPAUSE
*(volatile uint32_t*) 0x400B802Cu = 0; // TIMER1 DBGPAUSE
}
#else
#define trace_etm_init()
#endif
void board_init(void)
{
#if (CFG_TUH_ENABLED && CFG_TUH_RPI_PIO_USB) || (CFG_TUD_ENABLED && CFG_TUD_RPI_PIO_USB)
@ -199,10 +213,18 @@ void board_init(void)
#endif
#ifdef UART_DEV
bi_decl(bi_2pins_with_func(UART_TX_PIN, UART_RX_PIN, GPIO_FUNC_UART));
uart_inst = uart_get_instance(UART_DEV);
#if defined(TRACE_ETM) && defined(PICO_RP2350) && PICO_RP2350 == 1
// GPIO1 (default UART RX) is TRACECLK: TX-only console, and never touch
// GPIO1 - even a brief re-mux gaps the trace clock and desyncs the probe
bi_decl(bi_1pin_with_name(UART_TX_PIN, "UART TX"));
stdio_uart_init_full(uart_inst, CFG_BOARD_UART_BAUDRATE, UART_TX_PIN, -1);
#else
bi_decl(bi_2pins_with_func(UART_TX_PIN, UART_RX_PIN, GPIO_FUNC_UART));
stdio_uart_init_full(uart_inst, CFG_BOARD_UART_BAUDRATE, UART_TX_PIN, UART_RX_PIN);
#endif
#endif
trace_etm_init();
#if defined(LOGGER_RTT)
stdio_rtt_init();