Skip to content

Commit a396354

Browse files
authored
load: improve programming of multiple files (#1831)
- fix bug where smart flash was not used for pack targets - change loading reset mechanism: selected reset and halt before programming and reset using nSRST at the end - flash algorithm extra symbols are added only if they exist in the binary - builder: do init and uninit only once for all pages in a sector - load: handle reset sequence for multi-core devices
1 parent 1d4aa28 commit a396354

File tree

12 files changed

+65
-28
lines changed

12 files changed

+65
-28
lines changed

pyocd/flash/builder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -845,20 +845,20 @@ def _sector_erase_program(self, progress_cb=_stub_progress):
845845

846846
# The sector was erased, so we must program all pages in the sector
847847
# regardless of whether they were the same or not.
848+
self.flash.init(self.flash.Operation.PROGRAM)
848849
for page in sector.page_list:
849850

850851
progress += page.get_program_weight()
851852

852-
self.flash.init(self.flash.Operation.PROGRAM)
853853
self.flash.program_page(page.addr, page.data)
854-
self.flash.uninit()
855854

856855
actual_sector_erase_count += 1
857856
actual_sector_erase_weight += page.get_program_weight()
858857

859858
# Update progress
860859
if self.sector_erase_weight > 0:
861860
progress_cb(float(progress) / float(self.sector_erase_weight))
861+
self.flash.uninit()
862862

863863
progress_cb(1.0)
864864

pyocd/flash/flash.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def region(self, flashRegion):
199199
assert flashRegion.is_flash
200200
self._region = flashRegion
201201

202-
def init(self, operation, address=None, clock=0, reset=True):
202+
def init(self, operation, address=None, clock=0, reset=False):
203203
"""@brief Prepare the flash algorithm for performing operations.
204204
205205
First, the target is prepared to execute flash algo operations, including loading the algo

pyocd/subcommands/load_cmd.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from .base import SubcommandBase
2424
from ..core.helpers import ConnectHelper
25+
from ..core.target import Target
2526
from ..flash.file_programmer import FileProgrammer
2627
from ..utility.cmdline import (
2728
convert_session_options,
@@ -106,7 +107,21 @@ def invoke(self) -> int:
106107
programmer = FileProgrammer(session,
107108
chip_erase=self._args.erase,
108109
trust_crc=self._args.trust_crc,
109-
no_reset=self._args.no_reset)
110+
no_reset=True) # We handle this reset below
111+
112+
# Get a list of all secondary cores.
113+
secondary_cores = [c for c in session.target.cores.values() if c != session.target.primary_core]
114+
try:
115+
# Set reset catch for all secondary cores.
116+
for core in secondary_cores:
117+
core.set_reset_catch()
118+
# Reset and halt the primary core.
119+
session.target.reset_and_halt()
120+
finally:
121+
# Clear reset catch for all secondary cores.
122+
for core in secondary_cores:
123+
core.clear_reset_catch()
124+
110125
if not self._args.file and self._args.cbuild_run:
111126
# Populate file list from cbuild-run output if not provided explicitly
112127
cbuild_files = session.target.get_output()
@@ -148,4 +163,8 @@ def invoke(self) -> int:
148163
skip=self._args.skip,
149164
file_format=file_format)
150165

166+
# Reset the target after programming unless --no-reset was specified.
167+
if not self._args.no_reset:
168+
session.target.reset(Target.ResetType.NSRST)
169+
151170
return 0

pyocd/target/pack/flash_algo.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,8 @@ def __init__(self, data: Union[str, IO[bytes]]) -> None:
103103
self.sector_sizes = self.flash_info.sector_info_list
104104

105105
symbols: Dict[str, int] = {}
106-
x = self._extract_symbols(self.REQUIRED_SYMBOLS)
107-
symbols.update(x)
108-
symbols.update(self._extract_symbols(self.EXTRA_SYMBOLS, default=0xFFFFFFFF))
106+
symbols.update(self._extract_symbols(self.REQUIRED_SYMBOLS, required=True))
107+
symbols.update(self._extract_symbols(self.EXTRA_SYMBOLS))
109108
self.symbols = symbols
110109

111110
ro_rw_zi = self._find_sections(self.SECTIONS_TO_FIND)
@@ -235,15 +234,21 @@ def get_pyocd_flash_algo(self, blocksize: int, ram_region: "RamRegion") -> Dict[
235234
)
236235
# TODO - analyzer support
237236

237+
# Start of actual code (after the flash blob header) when loaded into RAM.
238238
code_start = addr_load + self._FLASH_BLOB_HEADER_SIZE
239+
240+
# Helper to get a symbol address relative to code_start. Returns 0xFFFFFFFF when symbol is not present.
241+
def _sym(sym: str) -> int:
242+
return (code_start + self.symbols[sym]) if (sym in self.symbols) else 0xFFFFFFFF
243+
239244
flash_algo = {
240245
"load_address": addr_load,
241246
"instructions": instructions,
242-
"pc_init": code_start + self.symbols["Init"],
243-
"pc_unInit": code_start + self.symbols["UnInit"],
244-
"pc_eraseAll": code_start + self.symbols["EraseChip"],
245-
"pc_erase_sector": code_start + self.symbols["EraseSector"],
246-
"pc_program_page": code_start + self.symbols["ProgramPage"],
247+
"pc_init": _sym("Init"),
248+
"pc_unInit": _sym("UnInit"),
249+
"pc_eraseAll": _sym("EraseChip"),
250+
"pc_erase_sector": _sym("EraseSector"),
251+
"pc_program_page": _sym("ProgramPage"),
247252
"page_buffers": page_buffers,
248253
"begin_data": page_buffers[0],
249254
"begin_stack": addr_stack,
@@ -254,16 +259,15 @@ def get_pyocd_flash_algo(self, blocksize: int, ram_region: "RamRegion") -> Dict[
254259
}
255260
return flash_algo
256261

257-
def _extract_symbols(self, symbols: Set[str], default: Optional[int] = None) -> Dict[str, int]:
258-
"""@brief Fill 'symbols' field with required flash algo symbols"""
262+
def _extract_symbols(self, symbols: Set[str], required: bool = False) -> Dict[str, int]:
263+
"""@brief Fill 'symbols' field with flash algo symbols"""
259264
to_ret: Dict[str, int] = {}
260265
for symbol in symbols:
261266
symbolInfo = self.elf.symbol_decoder.get_symbol_for_name(symbol)
262267
if symbolInfo is None:
263-
if default is not None:
264-
to_ret[symbol] = default
265-
continue
266-
raise FlashAlgoException("Missing symbol %s" % symbol)
268+
if required:
269+
raise FlashAlgoException("Missing symbol %s" % symbol)
270+
continue
267271
to_ret[symbol] = symbolInfo.address
268272
return to_ret
269273

test/basic_test.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pyOCD debugger
2-
# Copyright (c) 2006-2020 Arm Limited
2+
# Copyright (c) 2006-2020,2025 Arm Limited
33
# SPDX-License-Identifier: Apache-2.0
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -248,6 +248,7 @@ def basic_test(board_id, file):
248248
sectors_to_test = min(rom_region.length // sector_size, 3)
249249
addr_flash = rom_region.start + rom_region.length - sector_size * sectors_to_test
250250
fill = [0x55] * page_size
251+
target.reset_and_halt()
251252
for i in range(0, sectors_to_test):
252253
address = addr_flash + sector_size * i
253254
# Test only supports a location with 3 aligned
@@ -304,6 +305,7 @@ def basic_test(board_id, file):
304305
print("TEST FAILED")
305306

306307
print("\n\n----- FLASH NEW BINARY -----")
308+
target.reset_and_halt()
307309
FileProgrammer(session).program(binary_file, base_address=addr_bin)
308310

309311
target.reset()

test/blank_test.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
# pyOCD debugger
3-
# Copyright (c) 2006-2020 Arm Limited
3+
# Copyright (c) 2006-2020,2025 Arm Limited
44
# SPDX-License-Identifier: Apache-2.0
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,6 +36,7 @@
3636
board = session.board
3737
flash = session.target.memory_map.get_boot_memory().flash
3838
# Erase and then reset - This locks Kinetis devices
39+
board.target.reset_and_halt()
3940
flash.init(flash.Operation.ERASE)
4041
flash.erase_all()
4142
flash.cleanup()
@@ -54,6 +55,7 @@
5455
with ConnectHelper.session_with_chosen_probe(**get_session_options()) as session:
5556
board = session.board
5657
binary_file = get_test_binary_path(board.test_binary)
58+
board.target.reset_and_halt()
5759
FileProgrammer(session).program(binary_file)
5860

5961
print("\n\n------ Testing Attaching to regular board ------")

test/connect_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pyOCD debugger
2-
# Copyright (c) 2017-2020 Arm Limited
2+
# Copyright (c) 2017-2020,2025 Arm Limited
33
# SPDX-License-Identifier: Apache-2.0
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -134,6 +134,7 @@ def test_connect(connect_mode, expected_state, resume):
134134

135135
print("\n\n----- TESTING CONNECT/DISCONNECT -----")
136136
print("Flashing new binary")
137+
live_board.target.reset_and_halt()
137138
FileProgrammer(live_session).program(binary_file, base_address=rom_start)
138139
live_board.target.reset()
139140
test_count += 1

test/cortex_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ def cortex_test(board_id):
133133
gdbFacade = GDBDebugContextFacade(debugContext)
134134

135135
print("\n\n----- FLASH NEW BINARY BEFORE TEST -----")
136+
target.reset_and_halt()
136137
FileProgrammer(session).program(binary_file, base_address=addr_bin)
137138
# Let the target run for a bit so it
138139
# can initialize the watchdog if it needs to

test/debug_context_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pyOCD debugger
2-
# Copyright (c) 2019-2020 Arm Limited
2+
# Copyright (c) 2019-2020,2025 Arm Limited
33
# Copyright (c) 2021 Chris Reed
44
# SPDX-License-Identifier: Apache-2.0
55
#
@@ -151,6 +151,7 @@ def debug_context_test(board_id):
151151

152152
# Program the test binary.
153153
print("Programming test binary to boot memory")
154+
target.reset_and_halt()
154155
FileProgrammer(session).program(binary_file, base_address=boot_region.start)
155156

156157
with mock.patch.object(target.selected_core, 'read_memory_block32') as read_block32_mock:
@@ -200,4 +201,3 @@ def debug_context_test(board_id):
200201
session = ConnectHelper.session_with_chosen_probe(**get_session_options())
201202
test = DebugContextTest()
202203
result = [test.run(session.board)]
203-

test/flash_loader_test.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pyOCD debugger
2-
# Copyright (c) 2018-2020 Arm Limited
2+
# Copyright (c) 2018-2020,2025 Arm Limited
33
# SPDX-License-Identifier: Apache-2.0
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -98,6 +98,7 @@ def flash_loader_test(board_id):
9898

9999
print("\n------ Test Basic Load ------")
100100
loader = FlashLoader(session, chip_erase="sector")
101+
target.reset_and_halt()
101102
loader.add_data(boot_start_addr, data)
102103
loader.commit()
103104
verify_data = target.read_memory_block8(boot_start_addr, data_length)
@@ -117,6 +118,7 @@ def flash_loader_test(board_id):
117118
orig_data_length = data_length
118119

119120
loader = FlashLoader(session, chip_erase="sector")
121+
target.reset_and_halt()
120122
loader.add_data(addr, test_data)
121123
loader.add_data(addr + boot_blocksize, test_data)
122124
loader.commit()
@@ -133,6 +135,7 @@ def flash_loader_test(board_id):
133135
print("\n------ Test Basic Sector Erase ------")
134136
addr = (boot_end_addr + 1) - (boot_blocksize * num_test_sectors)
135137
eraser = FlashEraser(session, FlashEraser.Mode.SECTOR)
138+
target.reset_and_halt()
136139
eraser.erase(["0x%x+0x%x" % (addr, boot_blocksize)])
137140
verify_data = target.read_memory_block8(addr, boot_blocksize)
138141
if target.memory_map.get_region_for_address(addr).is_data_erased(verify_data):
@@ -144,6 +147,7 @@ def flash_loader_test(board_id):
144147

145148
print("\n------ Test Load Chip Erase ------")
146149
loader = FlashLoader(session, chip_erase="chip")
150+
target.reset_and_halt()
147151
loader.add_data(boot_start_addr, data)
148152
loader.commit()
149153
verify_data = target.read_memory_block8(boot_start_addr, data_length)
@@ -156,6 +160,7 @@ def flash_loader_test(board_id):
156160

157161
print("\n------ Test Binary File Load ------")
158162
programmer = FileProgrammer(session)
163+
target.reset_and_halt()
159164
programmer.program(binary_file, file_format='bin', base_address=boot_start_addr)
160165
verify_data = target.read_memory_block8(boot_start_addr, data_length)
161166
if same(verify_data, data):
@@ -167,6 +172,7 @@ def flash_loader_test(board_id):
167172

168173
print("\n------ Test Intel Hex File Load ------")
169174
programmer = FileProgrammer(session)
175+
target.reset_and_halt()
170176
programmer.program(temp_test_hex_name, file_format='hex')
171177
verify_data = target.read_memory_block8(boot_start_addr, data_length)
172178
if same(verify_data, data):
@@ -178,6 +184,7 @@ def flash_loader_test(board_id):
178184

179185
print("\n------ Test ELF File Load ------")
180186
programmer = FileProgrammer(session)
187+
target.reset_and_halt()
181188
programmer.program(temp_test_elf_name, file_format='elf')
182189
verify_data = target.read_memory_block8(boot_start_addr, data_length)
183190
if same(verify_data, data):
@@ -211,4 +218,3 @@ def flash_loader_test(board_id):
211218
session = ConnectHelper.session_with_chosen_probe(**get_session_options())
212219
test = FlashLoaderTest()
213220
result = [test.run(session.board)]
214-

0 commit comments

Comments
 (0)