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

Access to COMP_CSR using MMIO16 writes same data to both half of the 32 bit register on STM32F0 #1289

Open
kajusK opened this issue Dec 30, 2020 · 6 comments · May be fixed by #1463
Open

Access to COMP_CSR using MMIO16 writes same data to both half of the 32 bit register on STM32F0 #1289

kajusK opened this issue Dec 30, 2020 · 6 comments · May be fixed by #1463
Labels

Comments

@kajusK
Copy link

kajusK commented Dec 30, 2020

I've noticed a strange bug in the comparator library for STM32F0, the COMP_CSR register (one 32 bit register common for comparator 1 and 2) is accessed in the library using the following approach
#define COMP_CSR(i) MMIO16(SYSCFG_COMP_BASE + 0x1c + (i)*2)

Whenever I try to write to a half of the register, the data are written to both halves. E.g. the CSR register is empty, after executing COMP_CSR1 |= COMP_CSR_SPEED_MED it contains 0x40004 instead of just 0x4.

The dissasembled code is:

  ldr     r3, [pc, #176]  ; (0x80008ac <Compd_Init+228>)
  ldrh    r3, [r3, #0]
  uxth    r3, r3
  ldr     r2, [pc, #172]  ; (0x80008ac <Compd_Init+228>)
  movs    r1, #4
  orrs    r3, r1
  uxth    r3, r3
  strh    r3, [r2, #0]

I'm using gcc version 10.2.0.

@karlp
Copy link
Member

karlp commented Dec 31, 2020

so.. first off, does it happen with an older gcc? 10.2 is the brand new one right?

@yumkam
Copy link

yumkam commented Mar 3, 2021

FTR, I don't see anything wrong in above assembler code (it reads halfword at 0x80008ac, ors with 4, writes resulting halfword at 0x80008ac), so I doubt this is a compiler issue.
And in RM0091 COMP_CSR is described as 32-bit register, so probably accessing it with MMIO16 is not supported or misbehaves.

@karlp
Copy link
Member

karlp commented Mar 3, 2021

The reference manual would normally make explicit mention if it wasn't allowed, fwiw.

@neoxic
Copy link
Contributor

neoxic commented Jan 12, 2022

I can see the same problem with GCC 11.2:

COMP_CSR1 = COMP_CSR_EN | COMP_CSR_OUTSEL_TIM2_IC4;
=> 0x08001400 <main+112>:	3e 4b	ldr	r3, [pc, #248]	; (0x80014fc <main+364>)
(gdb) si
=> 0x08001402 <main+114>:	3f 4a	ldr	r2, [pc, #252]	; (0x8001500 <main+368>)
   0x08001404 <main+116>:	1a 80	strh	r2, [r3, #0]
(gdb) si
   0x08001402 <main+114>:	3f 4a	ldr	r2, [pc, #252]	; (0x8001500 <main+368>)
=> 0x08001404 <main+116>:	1a 80	strh	r2, [r3, #0]
(gdb) info reg r2 r3
r2             0x401               1025
r3             0x4001001c          1073807388
(gdb) x 0x4001001c
0x4001001c:	0x00000000
(gdb) si
(gdb) x 0x4001001c
0x4001001c:	0x04014401

But when done via MMIO32, all goes as expected:

147		MMIO32(SYSCFG_COMP_BASE + 0x1c) = COMP_CSR_EN | COMP_CSR_OUTSEL_TIM2_IC4;
...
(gdb) si
=> 0x08001406 <main+114>:	3f 4a	ldr	r2, [pc, #252]	; (0x8001504 <main+368>)
   0x08001408 <main+116>:	1a 60	str	r2, [r3, #0]
(gdb) si
   0x08001406 <main+114>:	3f 4a	ldr	r2, [pc, #252]	; (0x8001504 <main+368>)
=> 0x08001408 <main+116>:	1a 60	str	r2, [r3, #0]
(gdb) x 0x4001001c
0x4001001c:	0x00000000
(gdb) si
=> 0x0800140a <main+118>:	3f 4b	ldr	r3, [pc, #252]	; (0x8001508 <main+372>)
(gdb) x 0x4001001c
0x4001001c:	0x00004401

Since there is the above issue in regards to the current "split" API, I would personally vote for following the reference and having a single COMP_CSR register instead of two because it is now impossible to modify COMP1INSEL and COMP2INSEL atomically, i.e. in one write operation. This is needed, for example, in windowed mode when both comparators are used together in tandem.

There is also a typo in COMP_CSR_WINDWEN. It should probably be COMP_CSR_WNDWEN to match the reference.

@karlp
Copy link
Member

karlp commented Jan 12, 2022

There's a note in RM0091, section "System and memory overview" that says,

Note:
44/1004
When a 16- or 8-bit access is performed on an APB register, the access is transformed into
a 32-bit access: the bridge duplicates the 16- or 8-bit data to feed the 32-bit vector.

So I'd say that's what's happening, and we just need to drop the MMIO16. I'm not sure whether that's an M0/M0+ issue, or not, because I don't recall that being an issue on some other targets, either way we'll need to drop it.

@karlp karlp added bug and removed needs info labels Jan 12, 2022
@karlp karlp linked a pull request Feb 2, 2023 that will close this issue
karlp added a commit to karlp/libopencm3 that referenced this issue Feb 2, 2023
Fixes: libopencm3#1289

While it had seemed a clever trick to access COMP_CSR as two 16bit
registers, apparently in the real world, this didn't behave as expected.

Switch the register definitions to 32bit and adjust all the functions to
auto select the correct portion of the register.

NEEDSTEST: this has been coded only, I personally have no code using
this, but it seems like the correct approach, and the current code is
obviously broken.

Signed-off-by: Karl Palsson <karlp@tweak.au>
@karlp
Copy link
Member

karlp commented Feb 2, 2023

I've worked up some changes that should fix this, but I don't have any test code for this, or test hardware. IF either of you could try this out and let me know, I can merge it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants