Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

no LDRD in v6-M #3041

Merged
merged 1 commit into from
Feb 17, 2025
Merged

no LDRD in v6-M #3041

merged 1 commit into from
Feb 17, 2025

Conversation

scaprile
Copy link
Collaborator

@scaprile scaprile commented Feb 17, 2025

Our usual implementation of mg_millis() in our HALs, just reads an uint64_t that is incremented on interrupts.
In some platforms, like ARM v6-M (Cortex-M0, M0+), there is no way to atomically read a 64-bit quantity, so an interrupt could occur while reading its 32-bit components. This gets worse with 16- and 8-bit architectures; the effects are described in this article
The probablity of this happening is very low on 32-bit systems, but it is not zero, so it can cause glitches on always-on systems.; the 32-bit low word rolls over every ~50 days, a 16-bit one every ~1 minute and so on, though the errors introduced are less significative (the 8-bit LSB rolls over every 256ms but an error here is of the same magnitude), but nevertheless can introduce jumps back in time.

For ARMv7-M and newer:

		08000348 <mg_millis>:
		8000348:	4b01      	ldr	r3, [pc, #4]	; (8000350 <mg_millis+0x8>)
	OK -->	800034a:	e9d3 0100 	ldrd	r0, r1, [r3]
		800034e:	4770      	bx	lr
		8000350:	20002048 	.word	0x20002048

LDRD is interruptable, but restarts after the interrupt, so the correct value is always read. NO NEED FOR ACTION

For ARMv6-M:

		08000464 <mg_millis>:
		8000464:	4b01      	ldr	r3, [pc, #4]	; (800046c <mg_millis+0x8>)
	! -->	8000466:	6818      	ldr	r0, [r3, #0]
	! -->	8000468:	6859      	ldr	r1, [r3, #4]
		800046a:	4770      	bx	lr
		800046c:	200001e0 	.word	0x200001e0

That non-atomic read is problematic. This patch addresses the existing example.

The traditional strategy to deal with this, avoiding disabling interrupts (mg_millis() is called every loop so that would introduce a lot of jitter, at least), is to do a second reading of the problematic quantity; if both readings yield the same value, we're fine, but if they're different, then an interrupt took place in the middle of one of the readings, but we don't know which one, so we do a third reading:

  uint64_t ticks = s_ticks;            // first read
  if (ticks == s_ticks) return ticks;  // second read, same value => OK
  return s_ticks;                      // changed while reading, read again

@scaprile scaprile requested review from cpq and robertc2000 February 17, 2025 13:44
Copy link
Collaborator

@robertc2000 robertc2000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice article, BTW! Enjoyed the read!

@scaprile scaprile merged commit 49ce255 into master Feb 17, 2025
80 checks passed
@scaprile scaprile deleted the atomillis branch February 17, 2025 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants