I wrote a small LED blink for a STM32 microcontroller a while back
# code trimmed to the most relevant lines
proc write*(reg: static GPIOA_BSRR_Type, val: GPIOA_BSRR_Fields) {.inline.} =
volatileStore(cast[ptr GPIOA_BSRR_Fields](reg.loc), val)
proc main() =
modifyIt(RCC.AHB1ENR): it.GPIOAEN = true # PortA clk enable
modifyIt(GPIOA.MODER): it.MODER5 = 1 # PA5 pin is GP Output
while true: # GPIOA.BSRR = 0x40020018
GPIOA.BSRR.write((1 shl 5).GPIOA_BSRR_Fields) # Bit-Set PA5
GPIOA.BSRR.write((1 shl 21).GPIOA_BSRR_Fields) # Bit-Reset PA5
I was able to get the LED to blink at 3.2 Mhz back then. Today, I rebuilt the project and it only blinks at 177 KHz. I inspected the assembly and see it calls something named _ZL10nimZeroMemPvl within the while loop:
800030c: 4c0a ldr r4, [pc, #40] ; (8000338 <_Z14main__main_348v+0xc8>)
800030e: 2520 movs r5, #32
8000310: 2104 movs r1, #4
8000312: eb0d 0001 add.w r0, sp, r1
8000316: f7ff ffa7 bl 8000268 <_ZL10nimZeroMemPvl>
800031a: 2104 movs r1, #4
800031c: 6025 str r5, [r4, #0]
800031e: eb0d 0001 add.w r0, sp, r1
8000322: f7ff ffa1 bl 8000268 <_ZL10nimZeroMemPvl>
8000326: f44f 1300 mov.w r3, #2097152 ; 0x200000
800032a: 6023 str r3, [r4, #0]
800032c: e7f0 b.n 8000310 <_Z14main__main_348v+0xa0>
800032e: bf00 nop
8000330: 40023830 andmi r3, r2, r0, lsr r8
8000334: 40020000 andmi r0, r2, r0
8000338: 40020018 andmi r0, r2, r8, lsl r0
I believe this is the cause for the slowdown and I'd like to know how I can get rid of the calls to _ZL10nimZeroMemPvl. My guess is that my build config is different than it used to be. I don't have the old one; here's today's:
nimcache:"src/nimcache"
os:standalone
mm:arc
opt:size
checks:off
compileOnly:on
panics:on
threads:off
profiler:off
assertions:off
stackTrace:off
lineTrace:off
exceptions:goto
define:nimAllocPagesViaMalloc
define:nimMemAlignTiny
define:nimPage512
define:noSignalHandler
Thank you @Araq, but I don't know where to apply it. Both arguments to write() are constants. I applied {.noinit.} to main() to no effect. The first argument to _ZL10nimZeroMemPvl() is an address four bytes after the SP (a ptr to a local variable?), but I don't believe I've declared a local. Here's an explicit re-write that gives me the same problem:
const GPIOA* = GPIOA_Type(
MODER: GPIOA_MODER_Type(loc: 0x40020000'u),
BSRR: GPIOA_BSRR_Type(loc: 0x40020018'u),
)
proc main() {.noinit.} =
modifyIt(RCC.AHB1ENR): it.GPIOAEN = true # PortA clk enable
modifyIt(GPIOA.MODER): it.MODER5 = 1 # PA5 pin is GP Output
const pa5_set = (1 shl 5).GPIOA_BSRR_Fields
const pa5_clr = (1 shl 21).GPIOA_BSRR_Fields
while true: # GPIOA.BSRR = 0x40020018
write(GPIOA.BSRR, pa5_set) # Bit-Set PA5
write(GPIOA.BSRR, pa5_clr) # Bit-Reset PA5
when isMainModule:
main()
you need to cast RCC_AHB1ENR_reg back to Register[uint32, uint32] in order to access its fields:
proc read*(reg: static RCC_AHB1ENR_reg): RCC_AHB1ENR_val {.inline.} =
volatileLoad(cast[ptr RCC_AHB1ENR_val](Register[uint32, uint32](reg).address))
proc write*(reg: RCC_AHB1ENR_reg, val: RCC_AHB1ENR_val) {.inline.} =
volatileStore(cast[ptr RCC_AHB1ENR_val](Register[uint32, uint32](reg).address), val)
proc write*(reg: static RCC_AHB1ENR_reg, val: RCC_AHB1ENR_val) {.inline.} =
volatileStore(cast[ptr RCC_AHB1ENR_val](Register[uint32, uint32](reg).address), val)
The reason why its working with RegisterType = uint32 | uint16 | uint8 could be that there is a compiler bug caused by RCC_AHB1ENR_val* = distinct RegisterType because making a distinct version of a typeclass doenst realy make any sense, and the compiler doesnt complain. (just a wild guess, dont know the compiler internals)