Skip to content

Commit 0d0ce18

Browse files
committed
Verify flash contents after programming
Add an option, on by default, to read out flash contents after programming and compare it to the programmed data. Without it, bugs in flash loader algos and other problems will silently leave flash corrupted.
1 parent c684bdf commit 0d0ce18

File tree

5 files changed

+47
-18
lines changed

5 files changed

+47
-18
lines changed

pyocd/core/options.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ class OptionInfo(NamedTuple):
146146
"Name of test firmware binary."),
147147
OptionInfo('user_script', str, None,
148148
"Path of the user script file."),
149+
OptionInfo('verify', bool, True,
150+
"Controls whether a verify pass should be performed after flash programming has finished. Default is True"),
149151
OptionInfo('warning.cortex_m_default', bool, True,
150152
"Whether to show the warning about use of the cortex_m target type. Default is True."),
151153

pyocd/flash/builder.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ def mark_all_pages_not_same(self):
132132
for page in self.page_list:
133133
page.same = False
134134

135+
def mark_all_pages_unknown(self):
136+
"""@brief Sets the same flag to False for all pages in this sector."""
137+
for page in self.page_list:
138+
page.same = None
139+
135140
def __repr__(self):
136141
return "<_FlashSector@%x addr=%x size=%x wgt=%g pages=%s, subsectors=%d>" % (
137142
id(self), self.addr, self.size, self.erase_weight, self.page_list, self.n_subsectors)
@@ -417,7 +422,7 @@ def add_page_with_existing_data():
417422
page = add_page_with_existing_data()
418423
sector_page_addr += page.size
419424

420-
def program(self, chip_erase=None, progress_cb=None, smart_flash=True, fast_verify=False, keep_unwritten=True, no_reset=False):
425+
def program(self, chip_erase=None, progress_cb=None, smart_flash=True, fast_verify=False, keep_unwritten=True, no_reset=False, verify=True):
421426
"""@brief Determine fastest method of flashing and then run flash programming.
422427
423428
Data must have already been added with add_data().
@@ -444,6 +449,8 @@ def program(self, chip_erase=None, progress_cb=None, smart_flash=True, fast_veri
444449
be read from memory and restored while programming.
445450
@param no_reset Boolean indicating whether if the device should not be reset after the
446451
programming process has finished.
452+
@param verify Boolean indicating whether a verify pass should be performed after the
453+
programming process has finished.
447454
"""
448455

449456
# Send notification that we're about to program flash.
@@ -535,12 +542,6 @@ def program(self, chip_erase=None, progress_cb=None, smart_flash=True, fast_veri
535542
else:
536543
flash_operation = self._sector_erase_program(progress_cb)
537544

538-
# Cleanup flash algo and reset target after programming.
539-
self.flash.cleanup()
540-
541-
if no_reset is not True:
542-
self.flash.target.reset_and_halt()
543-
544545
program_finish = time()
545546
self.perf.program_time = program_finish - program_start
546547
self.perf.program_type = flash_operation
@@ -571,6 +572,23 @@ def program(self, chip_erase=None, progress_cb=None, smart_flash=True, fast_veri
571572
self.perf.skipped_byte_count = skipped_byte_count
572573
self.perf.skipped_page_count = skipped_page_count
573574

575+
if verify:
576+
LOG.info("Verifying")
577+
# Reset same flag for all pages to enable rescanning
578+
for sector in self.sector_list:
579+
sector.mark_all_pages_unknown()
580+
self._scan_pages_for_same(progress_cb)
581+
failed_pages = [page for page in self.page_list if page.same is False]
582+
if failed_pages:
583+
LOG.debug("Verify failed for pages {}".format(failed_pages))
584+
raise FlashProgramFailure('flash verify failure', address=failed_pages[0].addr)
585+
586+
# Cleanup flash algo and reset target after programming.
587+
self.flash.cleanup()
588+
589+
if no_reset is not True:
590+
self.flash.target.reset_and_halt()
591+
574592
if self.log_performance:
575593
if chip_erase:
576594
LOG.info("Erased chip, programmed %d bytes (%s), skipped %d bytes (%s) at %.02f kB/s",
@@ -899,12 +917,6 @@ def _scan_pages_for_same(self, progress_cb=_stub_progress):
899917
if self.sector_erase_weight > 0:
900918
progress_cb(float(progress) / float(self.sector_erase_weight))
901919

902-
# If we have to program any pages of a sector, then mark all pages of that sector
903-
# as needing to be programmed, since the sector will be erased.
904-
for sector in self.sector_list:
905-
if sector.are_any_pages_not_same():
906-
sector.mark_all_pages_not_same()
907-
908920
return progress
909921

910922
def _next_nonsame_page(self, i):
@@ -937,6 +949,8 @@ def _sector_erase_program_double_buffer(self, progress_cb=_stub_progress):
937949
self.flash.init(self.flash.Operation.ERASE)
938950
for sector in self.sector_list:
939951
if sector.are_any_pages_not_same():
952+
# If the sector is erased, all its pages must be programmed
953+
sector.mark_all_pages_not_same()
940954
# Erase the sector
941955
for addr in sector.addrs:
942956
self.flash.erase_sector(addr)

pyocd/flash/file_programmer.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ def __init__(self,
6565
smart_flash: Optional[bool] = None,
6666
trust_crc: Optional[bool] = None,
6767
keep_unwritten: Optional[bool] = None,
68-
no_reset: Optional[bool] = None
68+
no_reset: Optional[bool] = None,
69+
verify: Optional[bool] = None
6970
):
7071
"""@brief Constructor.
7172
@@ -88,13 +89,16 @@ def __init__(self,
8889
be read from memory and restored while programming.
8990
@param no_reset Boolean indicating whether if the device should not be reset after the
9091
programming process has finished.
92+
@param verify Boolean indicating whether a verify pass should be performed after the
93+
programming process has finished.
9194
"""
9295
self._session = session
9396
self._chip_erase = chip_erase
9497
self._smart_flash = smart_flash
9598
self._trust_crc = trust_crc
9699
self._keep_unwritten = keep_unwritten
97100
self._no_reset = no_reset
101+
self._verify = verify
98102
self._progress = progress
99103
self._loader = None
100104

@@ -154,7 +158,8 @@ def program(self, file_or_path: Union[str, IO[bytes]], file_format: Optional[str
154158
smart_flash=self._smart_flash,
155159
trust_crc=self._trust_crc,
156160
keep_unwritten=self._keep_unwritten,
157-
no_reset=self._no_reset)
161+
no_reset=self._no_reset,
162+
verify=self._verify)
158163

159164
# file_obj = None
160165
# Open the file if a path was provided.

pyocd/flash/loader.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class MemoryLoader:
146146
_trust_crc: Optional[bool]
147147
_keep_unwritten: Optional[bool]
148148
_no_reset: Optional[bool]
149+
_verify: Optional[bool]
149150

150151
def __init__(self,
151152
session: "Session",
@@ -154,7 +155,8 @@ def __init__(self,
154155
smart_flash: Optional[bool] = None,
155156
trust_crc: Optional[bool] = None,
156157
keep_unwritten: Optional[bool] = None,
157-
no_reset: Optional[bool] = None
158+
no_reset: Optional[bool] = None,
159+
verify: Optional[bool] = None
158160
):
159161
"""@brief Constructor.
160162
@@ -177,6 +179,8 @@ def __init__(self,
177179
be read from memory and restored while programming.
178180
@param no_reset Boolean indicating whether if the device should not be reset after the
179181
programming process has finished.
182+
@param verify Boolean indicating whether a verify pass should be performed after the
183+
programming process has finished.
180184
"""
181185
self._session = session
182186
assert session.board
@@ -201,6 +205,8 @@ def __init__(self,
201205
else self._session.options.get('keep_unwritten')
202206
self._no_reset = no_reset if (no_reset is not None) \
203207
else self._session.options.get('no_reset')
208+
self._verify = verify if (verify is not None) \
209+
else self._session.options.get('verify')
204210

205211
self._reset_state()
206212

@@ -297,7 +303,8 @@ def commit(self):
297303
smart_flash=self._smart_flash,
298304
fast_verify=self._trust_crc,
299305
keep_unwritten=self._keep_unwritten,
300-
no_reset=self._no_reset)
306+
no_reset=self._no_reset,
307+
verify=self._verify)
301308
perfList.append(perf)
302309
didChipErase = True
303310

pyocd/subcommands/load_cmd.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ def invoke(self) -> int:
101101
programmer = FileProgrammer(session,
102102
chip_erase=self._args.erase,
103103
trust_crc=self._args.trust_crc,
104-
no_reset=self._args.no_reset)
104+
no_reset=self._args.no_reset,
105+
verify=self._args.verify)
105106
for filename in self._args.file:
106107
# Get an initial path with the argument as-is.
107108
file_path = Path(filename).expanduser()

0 commit comments

Comments
 (0)