Beetwise

The read, change and write race

The idiom reg |= mask is three operations: a read, a change and a write.

An interrupt between the read and the write loses whatever it wrote.

A write-one-to-set register removes the read, so it loses nothing.

Try the read and write race in the bit editor

Bit 2 cleared, and the other seven bits kept. Edit the expression or select a bit to flip it.

7
6
5
4
3
2
1
0
0xB2
hex · 8 bits
binary
0b10110010
value
178
Shift

The read and write race step by step

Follow a lost change through the three operations:

  1. The main loop reads the register, which holds 0x01.
  2. An interrupt fires and sets bit 3, so the register holds 0x09.
  3. The main loop changes its own copy, 0x01, to 0x03.
  4. The main loop writes 0x03, and bit 3 is gone.

Ways to change a register, and what each costs

Ways to change a register, and what each costs
ApproachReads firstSafe against an interrupt
reg |= maskYesNo
reg &= ~maskYesNo
W1TS registerNoYes
W1TC registerNoYes
Bit-band aliasNoYes on Cortex-M3 and M4
Disable interrupts around itYesYes, at the cost of latency

Where the read and write race is used

An ESP32 and many ARM parts hold a pair of set and clear registers for this reason.

A Cortex-M3 maps each bit of some regions to its own address, which the name bit-band covers.

A driver that runs in an interrupt and in the main loop needs one of these approaches.

The read and write race: points to note

  • The problem is rare and depends on timing, so it survives every test and appears in the field.
  • A volatile pointer keeps the read in the code. It does not make the sequence atomic.
  • A write of 0 to a W1TS register does nothing at all. Use the W1TC register of the pair to clear.
  • Disabled interrupts around a register write add latency, so keep the window short.

The full bit editor

The full editor holds many values, evaluates expressions across them, and reads live values from a serial port.

Open this value in the full editor

Questions about the read and write race

Why did my interrupt lose a pin change?
The main loop read the register before the interrupt and wrote its old copy back afterwards.
Does volatile fix the race?
No. It keeps the read in the code. The read, the change and the write are still three steps.
What is a W1TS register?
A register where a 1 sets that bit and a 0 changes nothing. It needs no read, so it cannot lose one.