address #3787 reviews: bsp fixes, script hardening, board-note accuracy

Bot findings (Copilot/Codex): no-op board_trace_pinmux stubs for
lpcxpresso18s37/43s67 (TRACE_ETM otherwise broke their build), SAME70
ID_PIOD clock enable, capture-script duplicate BeforeTargetConnect on the
RA references, profile-script support for --no-timestamps itraces.

Deep review (whole branch): same70_xplained board row + caveat restored,
stale pico2 72 MHz claim corrected to the shipped 48, explicit
SetTracePortWidth(4) in the three references that relied on Ozone's
default, coverage-cell guard, median-based SysTick calibration, dead
session flag removed, stale RA8M1 divider comment fixed (0x02 = /4 is the
validated chip max) and the debugger guard indented.

EVKB bench findings: only R1884/D3 remains open (D1/D2 meter-verified);
RT1176 trace width is 1 or 4 only - J-Link arms the CSSYS TPIU and its
own sampler at 4-bit for any width>=2 request; a powered MCU-Link USB
breaks the external probe even with JP4 shorted.
This commit is contained in:
hathach
2026-07-24 18:38:40 +07:00
parent d3eaeb06e0
commit e65368ea16
10 changed files with 100 additions and 35 deletions

View File

@ -23,11 +23,12 @@ reference.
| ea4088_quickstart | 120 MHz | 120 MHz | 4 | 0 (unset) | J7 (fully wired) | — |
| nrf52840dk | 64 MHz | 16 MHz (hw cap) | 4 | 0 (unset) | solder P25, SW7 → Alt | — |
| nrf5340dk (M33) | 64 MHz | 16 MHz (TAD, forced) | 4 | +3 ns | mount P25; cut SB27/SB28 | — |
| mimxrt1170_evkb | 996 MHz | 50 MHz (root/2) | 1 | 0 | weld 0 Ω R1881-R1886; JP4 shorted; J58 (populated) | reflow R1882-R1884 (D1-D3 open) → width 4 |
| mimxrt1170_evkb | 996 MHz | 50 MHz (root/2) | 1 | 0 | weld 0 Ω R1881-R1886; JP4 shorted; J58 (populated) | re-weld R1884 (D3 open; D1/D2 meter-verified good) → width 4 |
| ra6m5_ek (M33) | 200 MHz | 25 MHz (TRCLK/4 /2) | 4 | 0 (unset) | J9 closed; native J20 trace | — |
| ra8m1_ek (M85) | 480 MHz | 60 MHz (TRCLK/4 /2) | 4 | 0 (unset) | J9 closed + Table 7 jumpers | — |
| raspberry_pi_pico2 (RP2350 M33) | 48 MHz | 24 MHz (clk_sys/2) | 4 | 0 (unset) | fly-wire GPIO1-5 → MIPI20 (map in jdebug) | 72-80 MHz per seating (re-qualify); >80 needs V3 probe + trace board |
| same54_xplained (E54 M4F) | 120 MHz | 60 MHz (CPU/2) | 4 | 0 (unset) | none — populated 20-pin ETM header | — |
| same70_xplained (E70 M7) | 300 MHz | 37.5 MHz (PCK3/2) | 1 | 0 (unset) | solder 20-pin header on J403 (bottom) | width 4 blocked: D1 (J403.16) dead at speed — probe-channel crosscheck pending |
| SEGGER H7/F407 ref | demo defaults | demo | 4 | demo | probe-powered: add `--power` | — |
Board caveats (beyond the table):
@ -35,7 +36,7 @@ Board caveats (beyond the table):
- **stm32h743eval**: startup-burst overflow at 400 MHz is normal (reduce
PLLN in board.h for overflow-free capture); timestamp ref 200 MHz.
- **stm32n657nucleo** (M55, flashless): **JP2 (BOOT1) must be 1** — the app
is a RAM image the debugger loads (Development boot); in flash boot theb
is a RAM image the debugger loads (Development boot); in flash boot the
bootROM parks the chip un-attachable ("Can not attach to CPU"). SEGGER's
KB says BOOT0/BOOT1 = 0/0 for their example — that is flash boot and it
does NOT attach; the board manual's Table 11 is right. 600 MHz core kills
@ -76,7 +77,12 @@ Board caveats (beyond the table):
and enables the CM7 platform trace-funnel port, which J-Link doesn't
program: without it everything reads register-perfect yet zero data
arrives. JP4 must be shorted (disables MCU-Link SWD) for the external
J-Trace on J58. FlexSPI apps: ROM bootloader must set SP/PC the
J-Trace on J58; a powered MCU-Link USB breaks the external probe's connect
even with JP4 shorted - power the board from another port. **Width is 1 or
4 only**: a width-2 request arms the CSSYS TPIU (E004_6000) and the probe
sampler at 4-bit anyway (CSPSR reads 0x8 after a width-2 session; a live
CSPSR=2 poke with LAR unlock + Trace.Clear still captures nothing because
the probe keeps sampling 4-bit). FlexSPI apps: ROM bootloader must set SP/PC the
committed reset/download hooks handle this. Startup-burst overflow at
996 MHz is normal. No Ethernet (100M) while tracing.
- **ra6m5_ek**: TRCKCR div-2 (100 MHz TRCLK = 50 MHz pin, the chip max) is
@ -118,7 +124,7 @@ Board caveats (beyond the table):
trace component map (funnel/TPIU/ETM are not in the ROM table → "Required
trace components for pin trace not found", 0 fetches) and re-arms the whole
chip-side path via `OnTraceStart` at every resume. Firmware therefore does
no trace setup; TRACE_ETM builds only (a) pin clk_sys to 72 MHz from crt0
no trace setup; TRACE_ETM builds only (a) pin clk_sys to 48 MHz from crt0
(board.cmake) — the fly-wire ceiling: 96/150 MHz kill the stream in the
startup burst at any sample timing (and at 150 MHz the saturated probe
stops answering halts, "CPU could not be halted"); any post-arm clock
@ -136,5 +142,15 @@ Board caveats (beyond the table):
`trace_etm_init` feeds it GCLK0. Pins PC24-28 mux to function H. The
populated 20-pin header runs chip-max 60 MHz TRACECLK width 4 with no
timing adjustment - the connector-vs-flywire contrast board.
- **same70_xplained**: J403 is a bottom-side bare footprint — solder the
header. Trace pins PD4-7 double as the KSZ8081 PHY's RMII receive outputs:
TRACE_ETM builds hold it in reset (PHY_RESET=PC10) or it drives against
the stream. TPIU clock = PCK3 (datasheet 16.7.4), run at MCK/2; TPIU
programming while PCK3 is stopped is silently LOST — the reference starts
PCK3 in the post-reset/download hooks (a reset wipes the PMC, so
AfterTargetConnect is too early). Width 1 validated at the stock 300 MHz
core; width 2/4 blocked on a dead D1 line at J403.16 (clean at DC by
meter, dead at speed — probe-channel crosscheck on a known-good width-4
board pending). No Ethernet while tracing.
- **SEGGER ref boards**: run their own demo (ladder step 3 flags); `--isr`
degrades gracefully without a live SysTick.

View File

@ -126,6 +126,12 @@ def resolve_board(board):
name, block = m.group(1), m.group(0)
if name == "OnProjectLoad":
continue
elif name == "BeforeTargetConnect":
# the generated project synthesizes its own BeforeTargetConnect
# (JLINK_SCRIPT_HOOK) from the SetJLinkScript regex below;
# inheriting the reference's copy too would emit a duplicate
# function definition
continue
elif name == "AfterTargetReset":
cfg["reset_hook"] = block
elif name == "AfterTargetDownload":
@ -472,7 +478,6 @@ def main():
start_new_session=True)
profile_out = os.path.join(outdir, "code_profile.txt")
itrace_out = os.path.join(outdir, "itrace.csv")
ok = False
try:
ses.connect(20)
ses.drain(3) # version banner
@ -513,7 +518,6 @@ def main():
ses.wait_echo(f'Export.PowerGraphs ("{outdir}/power.csv")', 60)
ses.send("Debug.Stop", 5)
ses.send("File.Exit", 2)
ok = True
finally:
for _ in range(15):
if proc.poll() is not None:
@ -551,9 +555,6 @@ def main():
sys.exit("error: session completed but NO trace data was collected "
"(profile totals are zero) - trace signal not reaching the "
"probe: check wiring/connector, trace pinmux, sample timing.")
if not ok:
sys.exit("error: session did not complete cleanly (see session.log)")
print(f"\ncapture OK: {outdir}")
print(f" code_profile.txt ({os.path.getsize(profile_out)} bytes)")
if args.trace_csv:

View File

@ -66,7 +66,7 @@ def parse_profile(path):
for module, name, cells in rows(lines[cov_start:prof_start]):
m_src = cov_pat.match(cells[0]) if cells else None
m_inst = cov_pat.match(cells[1]) if len(cells) > 1 else None
if name == "Total" and m_inst:
if name == "Total" and m_inst and m_src:
totals["src_cov"] = num(m_src.group(1)), num(m_src.group(2))
totals["inst_cov"] = num(m_inst.group(1)), num(m_inst.group(2))
elif name in funcs and m_inst:
@ -120,14 +120,29 @@ def iter_itrace(path):
caller reads unit separately with itrace_unit()."""
with open(path, newline="", errors="replace") as f:
rd = csv.reader(f)
next(rd, None)
hdr = next(rd, None) or []
# --no-timestamps captures drop the Timestamp column entirely: locate
# the Address column from the header and yield t=None for such rows
# (consumers count instructions but skip time math)
has_ts = any("Timestamp" in c for c in hdr)
try:
addr_i = next(i for i, c in enumerate(hdr) if "Address" in c)
except StopIteration:
addr_i = 1 if has_ts else 0
for row in rd:
if not row or row[0] == "PC" or len(row) < 2:
if not row or len(row) <= addr_i:
continue
try:
yield float(row[0]), int(row[1], 16)
addr = int(row[addr_i], 16)
except ValueError:
continue
if has_ts and row[0] != "PC":
try:
yield float(row[0]), addr
continue
except ValueError:
pass
yield None, addr
def itrace_unit(path):
@ -144,6 +159,8 @@ def time_by_func(path, syms):
sample = []
t_prev = None
for t, _ in iter_itrace(path):
if t is None:
continue
if t_prev is not None and t_prev - t > 0:
sample.append(t_prev - t)
if len(sample) >= 200000:
@ -158,6 +175,8 @@ def time_by_func(path, syms):
fn = addr_to_func(syms, a)
if fn:
cf[fn] = cf.get(fn, 0) + 1
if t is None:
continue
if t_prev is not None:
d = t_prev - t
if 0 < d < cap and fn:
@ -180,6 +199,8 @@ def isr_report(itrace, elf, isr_arg, top):
usb_rows, tick_rows, tmin, tmax = [], [], None, None
for t, a in iter_itrace(itrace):
if t is None:
continue # --no-timestamps capture: the <20-rows message below applies
tmin = t if tmin is None else min(tmin, t)
tmax = t if tmax is None else max(tmax, t)
if any(lo <= a < hi for lo, hi in body):
@ -195,10 +216,12 @@ def isr_report(itrace, elf, isr_arg, top):
f"({len(tick_rows)} rows) - capture with timestamps enabled")
return
# rough raw-units-per-1ms from the large mode of consecutive tick deltas
# rough raw-units-per-1ms from the large mode of consecutive tick deltas;
# threshold from the median, not the max - one trace-overflow gap would
# otherwise inflate the cut and leave only outliers in the sample
deltas = [b[0] - a[0] for a, b in zip(tick_rows, tick_rows[1:])]
big = [d for d in deltas if d > max(deltas) / 10]
raw_ms = statistics.median(big)
big = [d for d in deltas if d > 10 * statistics.median(deltas)]
raw_ms = statistics.median(big) if big else statistics.median(deltas)
gap = 0.03 * raw_ms # 30 us in raw units
edge = 0.05 * raw_ms
@ -390,11 +413,18 @@ def main():
cshare = {k: v / tc for k, v in cf.items()}
unit = itrace_unit(itrace)
print(f"\n## Instruction history (itrace.csv, unit '{unit}')\n")
print(f"- {n:,} instructions; top {args.top} by TIME share "
f"(vs instruction share):")
for name, ts in sorted(tshare.items(), key=lambda kv: -kv[1])[:args.top]:
print(f" - `{short(name)}`: {100 * ts:.1f}% time, "
f"{100 * cshare.get(name, 0):.1f}% instructions")
if not tf:
print(f"- {n:,} instructions, NO timestamps (--no-timestamps "
f"capture): time shares unavailable, top {args.top} by "
f"instruction share:")
for name, cs in sorted(cshare.items(), key=lambda kv: -kv[1])[:args.top]:
print(f" - `{short(name)}`: {100 * cs:.1f}% instructions")
else:
print(f"- {n:,} instructions; top {args.top} by TIME share "
f"(vs instruction share):")
for name, ts in sorted(tshare.items(), key=lambda kv: -kv[1])[:args.top]:
print(f" - `{short(name)}`: {100 * ts:.1f}% time, "
f"{100 * cshare.get(name, 0):.1f}% instructions")
lines_csv = os.path.join(args.capture_dir, "profile_lines.csv")
if os.path.isfile(lines_csv):

View File

@ -10,6 +10,7 @@
*/
void OnProjectLoad (void) {
Project.SetTraceSource ("Trace Pins");
Project.SetTracePortWidth (4);
Project.SetTraceTiming (50, 50, 50, 50);
Project.SetDevice ("MIMXRT1011xxx4A");
Project.SetHostIF ("USB", "");

View File

@ -76,6 +76,12 @@ static inline void board_lpc18_pinmux(void)
Chip_SCU_SetPinMuxing(pinmuxing, sizeof(pinmuxing) / sizeof(PINMUX_GRP_T));
}
// TRACE_ETM builds: no trace header is wired out on the LPCXpresso18S37 -
// provide the no-op the family init expects (see mcb1800/ea4357 for a
// board that routes the trace pins)
static inline void board_trace_pinmux(void) {}
#ifdef __cplusplus
}
#endif

View File

@ -71,6 +71,12 @@ static const PINMUX_GRP_T pinmuxing[] = {
{0x2, 5, SCU_MODE_INBUFF_EN | SCU_MODE_PULLUP | SCU_MODE_FUNC4 },
};
// TRACE_ETM builds: no trace header is wired out on the LPCXpresso43S67 -
// provide the no-op the family init expects (see mcb1800/ea4357 for a
// board that routes the trace pins)
static inline void board_trace_pinmux(void) {}
#ifdef __cplusplus
}
#endif

View File

@ -10,6 +10,7 @@
*/
void OnProjectLoad (void) {
Project.SetTraceSource ("Trace Pins");
Project.SetTracePortWidth (4);
Project.SetDevice ("R7FA8M1AH");
Project.SetHostIF ("USB", "");
Project.SetTargetIF ("SWD");

View File

@ -103,25 +103,27 @@ void board_init(void) {
// standalone boot must skip trace init or the write wedges the chip into
// an un-attachable crash loop (recover: power-cycle + immediate erase)
if (DCB->DHCSR & DCB_DHCSR_C_DEBUGEN_Msk) {
// TRCKCR is protected by PRCR bit0 register
R_SYSTEM->PRCR = (uint16_t) (BSP_PRV_PRCR_KEY | 0x01);
// TRCKCR is protected by PRCR bit0 register
R_SYSTEM->PRCR = (uint16_t) (BSP_PRV_PRCR_KEY | 0x01);
// TCLK pin = TRCLK/2; set the divider with TRCKEN=0 first (HUM procedure).
// Values are the empirical per-board ceilings: one step below the divider
// at which the stream dies mid-run.
// TCLK pin = TRCLK/2; set the divider with TRCKEN=0 first (HUM procedure).
// Values are the empirical per-board ceilings.
#if defined(BSP_MCU_GROUP_RA8M1)
// 480 MHz CPU: /8 -> 60 MHz TRCLK, 30 MHz pin (/4 = 60 MHz pin dies)
R_SYSTEM->TRCKCR = 0x02;
R_SYSTEM->TRCKCR = R_SYSTEM_TRCKCR_TRCKEN_Msk | 0x02;
// 480 MHz CPU: /4 -> 120 MHz TRCLK, 60 MHz pin - chip max, clean on
// EK-RA8M1 with the committed empty-OnTraceStart JLinkScript (which
// defers the trace clock to firmware; without it the FSP MOCO->PLL
// switch steps the clock mid-stream and any divider fails)
R_SYSTEM->TRCKCR = 0x02;
R_SYSTEM->TRCKCR = R_SYSTEM_TRCKCR_TRCKEN_Msk | 0x02;
#else
// RA6M5 200 MHz CPU: /4 -> 50 MHz TRCLK, 25 MHz pin. /2 (50 MHz pin) is
// silent on the EK-RA6M5 in every combination - board path ceiling,
// reconfirmed with J9 closed and the OnTraceStart override
R_SYSTEM->TRCKCR = 0x02;
R_SYSTEM->TRCKCR = R_SYSTEM_TRCKCR_TRCKEN_Msk | 0x02;
// RA6M5 200 MHz CPU: /4 -> 50 MHz TRCLK, 25 MHz pin. /2 (50 MHz pin) is
// silent on the EK-RA6M5 in every combination - board path ceiling,
// reconfirmed with J9 closed and the OnTraceStart override
R_SYSTEM->TRCKCR = 0x02;
R_SYSTEM->TRCKCR = R_SYSTEM_TRCKCR_TRCKEN_Msk | 0x02;
#endif
R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_KEY;
R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_KEY;
}
#endif

View File

@ -82,6 +82,7 @@ void board_init(void) {
PMC->PMC_SCER = PMC_SCER_PCK3;
while (!(PMC->PMC_SR & PMC_SR_PCKRDY3)) {}
}
_pmc_enable_periph_clock(ID_PIOD);
uint32_t const clk_pin = PIO_PD8D_TPIU_TRACECLK;
uint32_t const dat_pin = PIO_PD4C_TPIU_TRACED0 | PIO_PD5C_TPIU_TRACED1 |
PIO_PD6C_TPIU_TRACED2 | PIO_PD7C_TPIU_TRACED3;

View File

@ -10,6 +10,7 @@
*/
void OnProjectLoad (void) {
Project.SetTraceSource ("Trace Pins");
Project.SetTracePortWidth (4);
Project.SetTraceTiming (100, 100, 100, 100);
Project.SetSWO (0);
Edit.SysVar (VAR_TRACE_CORE_CLOCK, 200000000);