1
\$\begingroup\$

I'm coming from this problem where I had the issue that one GPIO write would influence the level of another pin. (GPIO19 would influence GPIO13).

When changing the set/clear operation from |= to = the behaviour is suddenly correct:

// clear GPIO19
*(GPCLEAR0) |= (1u << 19); // does not work
*(GPCLEAR0) = (1u << 19); // works

I'm aware that there are registers that should only be written to (like clearing interrupt flag).

However, the datasheet of BCM2835 does not mention to not read GPCLEAR0.

In fact, the datasheet claims that this register is read+write.

So why is it working with = and not with the more "logical" |=?

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Why would you do a read/modify/write on a register that is provided to make bit clearing atomic? I gather there’s a corresponding set register which operates similarly. If you were accessing the port register, then the r/m/w would be the choice but consider if you have multiple tasks accessing the same port register - you would encounter atomicity issues. With the set and clear registers, only one indivisible write is involved. \$\endgroup\$
    – Kartman
    Commented Oct 30, 2022 at 18:50
  • \$\begingroup\$ @Kartman Ah.. gotcha. I only have worked with port (AVR) registers so far, so that's why I used r/m/w. Thanks! \$\endgroup\$
    – Marco
    Commented Oct 30, 2022 at 19:06
  • \$\begingroup\$ It does not mention you should not read from it. However, when you read it, what do you expect to read back from a write-only register? There is no mention what you get back if you read it. If zeroes, your code should work. if ones, then your code should fail. \$\endgroup\$
    – Justme
    Commented Oct 30, 2022 at 19:26
  • 1
    \$\begingroup\$ @marco-a AVR has I/o bit operations which the compiler translates the apparent r/m/w to one of these. In comparison many PIC and 8051 compilers used a non standard bit syntax to implicitly do the same thing. With the ARMs, the i/o is not tightly coupled to the processor like the PIC, AVR,8051 etc as the gpio is a peripheral just like the uarts etc, so many manufacturers add the set and clear registers to give the same flexibility in setting/clearing bits quickly and atomically. Doing actual r/m/w operations are slow and need special attention in multitasking environments. \$\endgroup\$
    – Kartman
    Commented Oct 30, 2022 at 23:12

1 Answer 1

1
\$\begingroup\$

This behavior is rather typical for set-and-clear register pairs. When you read it, you get the actual state of the register back (or an undefined value if the register doesn't support reading). When you write to the clear register, any 1 bits in the written value will cause the corresponding register bit to be cleared.

Let's assume that GPIO13 is currently 1. You want to clear GPIO19.

In the |= case, your code first reads GPCLEAR0. This returns the actual register state, so bit 13 is 1.

Then you OR bit 19 onto that. Now bit 13 and bit 19 are both 1.

Then you finally write that value back into GPCLEAR0. Since bit 13 and bit 19 are both set, this will clear these two bits, clearing GPIO13 accidentally (in addition to GPIO19). Oops!

The correct way to clear a bit is to write only that bit to the GPCLEAR register. All other bits have to be 0. The same goes for GPSET. That's why you have to use =, not |=.

\$\endgroup\$
15
  • \$\begingroup\$ Thanks for the quick and elaborate answer! \$\endgroup\$
    – Marco
    Commented Oct 30, 2022 at 19:04
  • \$\begingroup\$ How do you know what value is read back when reading a write-only register? Why would it read back current state of IO pin? \$\endgroup\$
    – Justme
    Commented Oct 30, 2022 at 19:28
  • \$\begingroup\$ @Justme You're right about that - I just used the behavior that set/clear register pairs generally have on ARM MCUs. It doesn't invalidate the reasoning in the answer, though. \$\endgroup\$ Commented Oct 30, 2022 at 19:34
  • \$\begingroup\$ I guess it is possible to read back what has previously been written to it, but the information is contradictory. Table 6-1 says write only, tables 6-8 and 6-9 say read/write. But yes, the registers should be written with the bit you want to change, because it specifically exists for having atomic access to GPIO bits so there is no need to read-modify-write any registers. \$\endgroup\$
    – Justme
    Commented Oct 30, 2022 at 19:51
  • \$\begingroup\$ The thing is, most ARM MCUs do return the underlying register's contents on read, not just what's been previously written to the atomic set/clear registers. The fact that there doesn't seem to be an extra read-only register to get the GPIO state also makes me think that this broadcom chip works the same way, even if the datasheet is a little weird and contradictory. \$\endgroup\$ Commented Oct 30, 2022 at 19:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.