You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix race condition causing ADCs to read stale values
This was a *fun* bug to track down. Initially, it appeared as an out of
order read of multiple ADC channels (the second would read the first
channel, the third from the second, and so on). This only happened with
a prescaler value of `8`, for other values, the corect behaviour was
observed.
`git bisect` traced the issue back to a version bump of the `pac` crate
which shouldn't have affected things. Further git bisection hinted at
the commit bumping version number in the PAC was the issue. This is of
course nonsensical, and the key change that caused the **symptoms** was
a bump of the svd2rust crate, from 0.14 to 0.15.
Dumping the memory in the ADC and RCC peripherals showed barely any
difference, apart from the newer `pac` version randomly having the
`swstart` and `eoc` bits set after the second line of the convert
function. After further single stepping, those bits were set after a
write of `0001e0001` to `cr2`. This was the value that was already in
the register, so why would that start a conversion?
Well, someone decided that it would be a good idea to make one way of
starting the ADC conversion to write a `1` to the `adon` bit if the
`adon` bit is already one. But that might trigger a read when you want
to modify other parts of the register, so this behaviour only happens if
no other bits are modified. Thus, `modify` on the `cr2` register will
start a conversion unless at least one bit is *actually* modified.
This was accidentally done as the first part of the `convert` function,
which started a conversion which would be ready roughly by the time the
CPU starts checking for `eoc` bits.
The race condition occurs if the CPU gets to the `EOC` check, before the
ADC has time to reset the bit as a result of the `swstart` trigger. This
relies on the CPU being faster than the ADC clock which explains the
behaviour at high prescalers.
Finally, it seems like the line
```rust
while self.rb.cr2.read().swstart().bit_is_set() {}
```
*should* have prevented this as the ADC should have taken care of the
`swstart` and reset the `EOC` before that line. For whatever reason,
that did not happen
Co-authored-by: Adam Greig <adam@adamgreig.com>
0 commit comments