Skip to content

Commit 01d28b4

Browse files
committed
scripts: add ETB grabber script
This patch adds a script to grab ETB data from a nRF91 device using pyocd. Signed-off-by: Maximilian Deubel <[email protected]>
1 parent 71afed5 commit 01d28b4

File tree

1 file changed

+293
-0
lines changed

1 file changed

+293
-0
lines changed

scripts/nrf91_etb_grabber.py

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) 2024 Nordic Semiconductor ASA
4+
#
5+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
6+
7+
from pyocd.core.helpers import ConnectHelper
8+
from pyocd.flash.file_programmer import FileProgrammer
9+
from pyocd.core.target import Target
10+
from pyocd.target.family.target_nRF91 import ModemUpdater
11+
from pyocd.core.exceptions import TargetError
12+
from pyocd.debug.elf.symbols import ELFSymbolProvider
13+
from intelhex import IntelHex
14+
from tempfile import TemporaryDirectory
15+
import os
16+
from timeit import default_timer as timer
17+
import argparse
18+
19+
import logging
20+
21+
logging.basicConfig(level=logging.INFO)
22+
23+
parser = argparse.ArgumentParser(
24+
description="Wrapper around pyOCD to fump ETB on fatal error"
25+
)
26+
parser.add_argument("-e", "--elf-file", help="perform mass_erase", required=True)
27+
parser.add_argument("-o", "--output", help="output binary file", required=True)
28+
parser.add_argument("-u", "--uid", help="probe uid")
29+
30+
args = parser.parse_args()
31+
32+
options = {
33+
"target_override": "nrf91",
34+
"cmsis_dap.limit_packets": False,
35+
"frequency": 8000000,
36+
"logging": {"loggers": {"pyocd.coresight": {"level": logging.INFO}}},
37+
}
38+
39+
40+
def BIT(x):
41+
return 1 << x
42+
43+
44+
def CS_UNLOCK(target, base):
45+
target.write32(base + 0xFB0, 0xC5ACCE55)
46+
47+
48+
def CS_LOCK(target, base):
49+
target.write32(base + 0xFB0, 0)
50+
51+
52+
# Embedded Trace Buffer registers
53+
ETB_BASE_ADDR = 0xE0051000
54+
ETB_RDP = ETB_BASE_ADDR + 0x004
55+
ETB_STS = ETB_BASE_ADDR + 0x00C
56+
ETB_RRD = ETB_BASE_ADDR + 0x010
57+
ETB_RRP = ETB_BASE_ADDR + 0x014
58+
ETB_RWP = ETB_BASE_ADDR + 0x018
59+
ETB_TRG = ETB_BASE_ADDR + 0x01C
60+
ETB_CTL = ETB_BASE_ADDR + 0x020
61+
ETB_RWD = ETB_BASE_ADDR + 0x024
62+
ETB_FFSR = ETB_BASE_ADDR + 0x300
63+
ETB_FFCR = ETB_BASE_ADDR + 0x304
64+
65+
ETB_CTL_TRACECAPTEN = BIT(0)
66+
67+
ETB_FFSR_FLINPROG = BIT(0)
68+
ETB_FFSR_FTSTOPPED = BIT(1)
69+
70+
ETB_FFCR_ENFTC = BIT(0)
71+
ETB_FFCR_ENFCONT = BIT(1)
72+
73+
# Embedded Trace Macrocell registers
74+
ETM_BASE_ADDR = 0xE0041000
75+
ETM_TRCPRGCTLR = ETM_BASE_ADDR + 0x004 # Programming Control Register
76+
ETM_TRCSTATR = ETM_BASE_ADDR + 0x00C # Status Register
77+
ETM_TRCCONFIGR = ETM_BASE_ADDR + 0x010 # Trace Configuration Register
78+
ETM_TRCCCCTLR = ETM_BASE_ADDR + 0x038 # Cycle Count Control Register
79+
ETM_TRCSTALLCTLR = ETM_BASE_ADDR + 0x02C # Stall Control Register
80+
ETM_TRCTSCTLR = ETM_BASE_ADDR + 0x030 # Timestamp Control Register
81+
ETM_TRCTRACEIDR = ETM_BASE_ADDR + 0x040 # Trace ID Register
82+
ETM_TRCVICTLR = ETM_BASE_ADDR + 0x080 # ViewInst Main Control Register
83+
ETM_TRCEVENTCTL0R = ETM_BASE_ADDR + 0x020 # Event Control 0 Register
84+
ETM_TRCEVENTCTL1R = ETM_BASE_ADDR + 0x024 # Event Control 1 Register
85+
ETM_TRCPDSR = ETM_BASE_ADDR + 0x314 # Power down status register
86+
87+
ETM_TRCSTATR_IDLE = BIT(0)
88+
ETM_TRCSTATR_PMSTABLE = BIT(1)
89+
90+
ETM_TRCVICTLR_TRCERR = BIT(11)
91+
ETM_TRCVICTLR_TRCRESET = BIT(10)
92+
ETM_TRCVICTLR_SSSTATUS = BIT(9)
93+
ETM_TRCVICTLR_EVENT0 = BIT(0)
94+
95+
ETM_TRCPRGCTLR_ENABLE = BIT(0)
96+
97+
# Advanced Trace Bus 1 (ATB) registers
98+
ATB_1_BASE_ADDR = 0xE005A000
99+
ATB_1_CTL = ATB_1_BASE_ADDR + 0x000
100+
ATB_1_PRIO = ATB_1_BASE_ADDR + 0x004
101+
102+
# Advanced Trace Bus 2 (ATB) registers
103+
ATB_2_BASE_ADDR = 0xE005B000
104+
ATB_2_CTL = ATB_2_BASE_ADDR + 0x000
105+
ATB_2_PRIO = ATB_2_BASE_ADDR + 0x004
106+
107+
ATB_REPLICATOR_BASE_ADDR = 0xE0058000
108+
ATB_REPLICATOR_IDFILTER0 = ATB_REPLICATOR_BASE_ADDR + 0x000
109+
ATB_REPLICATOR_IDFILTER1 = ATB_REPLICATOR_BASE_ADDR + 0x004
110+
111+
def word_to_bytes(wrd):
112+
result = []
113+
for i in range(4):
114+
result.append((wrd >> (8*i)) & 0xFF)
115+
return bytes(result)
116+
117+
def etm_init(target):
118+
# Disable ETM to allow configuration
119+
target.write32(ETM_TRCPRGCTLR, 0x0)
120+
121+
# Wait until ETM is idle and programmer's model is stable
122+
required_flags = ETM_TRCSTATR_PMSTABLE | ETM_TRCSTATR_IDLE
123+
while target.read32(ETM_TRCSTATR) & required_flags != required_flags:
124+
pass
125+
126+
# Configure ETM
127+
target.write32(ETM_TRCCONFIGR, BIT(3))
128+
target.write32(ETM_TRCSTALLCTLR, 0)
129+
target.write32(ETM_TRCTSCTLR, 0)
130+
target.write32(ETM_TRCTRACEIDR, BIT(2))
131+
target.write32(
132+
ETM_TRCVICTLR,
133+
ETM_TRCVICTLR_TRCERR
134+
| ETM_TRCVICTLR_TRCRESET
135+
| ETM_TRCVICTLR_SSSTATUS
136+
| ETM_TRCVICTLR_EVENT0,
137+
)
138+
target.write32(ETM_TRCEVENTCTL0R, 0)
139+
target.write32(ETM_TRCEVENTCTL1R, 0)
140+
141+
# Enable ETM
142+
target.write32(ETM_TRCPRGCTLR, ETM_TRCPRGCTLR_ENABLE)
143+
144+
145+
def etm_stop(target):
146+
target.write32(ETM_TRCPRGCTLR, 0x0)
147+
148+
149+
def atb_init(target):
150+
# ATB replicator
151+
CS_UNLOCK(target, ATB_REPLICATOR_BASE_ADDR)
152+
153+
# ID filter for master port 0
154+
target.write32(ATB_REPLICATOR_IDFILTER0, 0xFFFFFFFF)
155+
# ID filter for master port 1, allowing ETM traces from CM33 to ETB
156+
target.write32(ATB_REPLICATOR_IDFILTER1, 0xFFFFFFFD)
157+
158+
CS_LOCK(target, ATB_REPLICATOR_BASE_ADDR)
159+
160+
# ATB funnel 1
161+
CS_UNLOCK(target, ATB_1_BASE_ADDR)
162+
163+
# Set priority 1 for ports 0 and 1
164+
target.write32(ATB_1_PRIO, 0x00000009)
165+
166+
# Enable port 0 and 1, and set hold time to 4 transactions
167+
target.write32(ATB_1_CTL, 0x00000303)
168+
169+
CS_LOCK(target, ATB_1_BASE_ADDR)
170+
171+
# ATB funnel 2
172+
CS_UNLOCK(target, ATB_2_BASE_ADDR)
173+
174+
# Set priority 3 for port 3
175+
target.write32(ATB_2_PRIO, 0x00003000)
176+
177+
# Enable ETM traces on port 3, and set hold time to 4 transactions
178+
target.write32(ATB_2_CTL, 0x00000308)
179+
180+
CS_LOCK(target, ATB_2_BASE_ADDR)
181+
182+
183+
def etb_init(target):
184+
CS_UNLOCK(target, ETB_BASE_ADDR)
185+
186+
# Disable ETB
187+
target.write32(ETB_CTL, 0)
188+
189+
# Wait for ETB formatter to stop
190+
while not target.read32(ETB_FFSR) & ETB_FFSR_FTSTOPPED:
191+
pass
192+
193+
# Set ETB formatter to continuous mode
194+
target.write32(ETB_FFCR, ETB_FFCR_ENFCONT | ETB_FFCR_ENFTC)
195+
196+
# Enable ETB
197+
target.write32(ETB_CTL, ETB_CTL_TRACECAPTEN)
198+
199+
# Wait until the ETB Formatter has started
200+
while target.read32(ETB_FFSR) & ETB_FFSR_FTSTOPPED:
201+
pass
202+
203+
CS_LOCK(target, ETB_BASE_ADDR)
204+
205+
206+
def etb_stop(target):
207+
CS_UNLOCK(target, ETB_BASE_ADDR)
208+
209+
# Disable ETB
210+
target.write32(ETB_CTL, 0)
211+
212+
# Wait for ETB formatter to flush
213+
while target.read32(ETB_FFSR) & ETB_FFSR_FLINPROG:
214+
pass
215+
216+
CS_LOCK(target, ETB_BASE_ADDR)
217+
218+
219+
def etb_trace_start(target):
220+
# start debug clock
221+
target.write32(0xE0080000, 1)
222+
223+
atb_init(target)
224+
etb_init(target)
225+
etm_init(target)
226+
227+
228+
def etb_trace_stop(target):
229+
etb_stop(target)
230+
etm_stop(target)
231+
232+
def etb_data_get(target):
233+
result = []
234+
CS_UNLOCK(target, ETB_BASE_ADDR)
235+
236+
last_write_pointer = target.read32(ETB_RWP)
237+
238+
target.write32(ETB_RRP, last_write_pointer)
239+
240+
# read out 2kb ETB buffer
241+
for i in range(2048//4):
242+
result += word_to_bytes(target.read32(ETB_RRD))
243+
244+
CS_LOCK(target, ETB_BASE_ADDR)
245+
246+
return result
247+
248+
249+
with ConnectHelper.session_with_chosen_probe(
250+
unique_id=args.uid, options=options
251+
) as session:
252+
target = session.target
253+
254+
target.reset_and_halt()
255+
256+
# set breakpoint at fault handler
257+
logging.info(f"Using ELF file: {args.elf_file}")
258+
target.elf = args.elf_file
259+
provider = ELFSymbolProvider(target.elf)
260+
k_sys_fatal_error_handler_addr = provider.get_symbol_value(
261+
"k_sys_fatal_error_handler"
262+
)
263+
if k_sys_fatal_error_handler_addr is None:
264+
logging.error("Failed to find k_sys_fatal_error_handler symbol")
265+
else:
266+
logging.info(f"Setting breakpoint at k_sys_fatal_error_handler: {hex(k_sys_fatal_error_handler_addr)}")
267+
target.set_breakpoint(k_sys_fatal_error_handler_addr)
268+
269+
# start ETB trace
270+
logging.info("Starting ETB trace")
271+
etb_trace_start(target)
272+
273+
# reset target
274+
target.resume()
275+
276+
# run until breakpoint
277+
while not target.is_halted():
278+
pass
279+
280+
logging.info(f"target has halted with halt reason: {target.get_halt_reason()}")
281+
logging.info(f"target PC: {hex(target.read_core_register('pc'))}")
282+
283+
target.halt()
284+
285+
# stop ETB trace
286+
etb_trace_stop(target)
287+
288+
# get ETB data
289+
data = bytes(etb_data_get(target))
290+
291+
with open(args.output, "wb") as f:
292+
f.write(data)
293+
print(f"ETB data saved to {args.output}")

0 commit comments

Comments
 (0)