When I turned all the templates into inline procs, then it no longer read from the register a second time. I also read the gcc's docs about the asm statement in greater detail and fixed one register modifier and discovered how to convert Nim static args to an assembly immediate operand. With these two details in place, I finally got this working as intended. The near-final code will look like this: proc setField[T](regVal: T, fieldVal: RegisterVal, bitOffset: static int, bitWidth: static int): T {.inline.} = {.emit: ["asm (\"BFI %0, %1, %2, %3\"\n\t: \"+r\" (", regVal, ")\n\t: \"r\" (", fieldVal, "), \"n\" (", bitOffset, "), \"n\" (", bitWidth, "));\n"].} regVal # reg read proc AHB1ENR*(base: static RCC_Base): RCC_AHB1ENR_Val {.inline.} = volatileLoad(RCC_AHB1ENR) # chained field modify proc GPIOAEN*(regVal: RCC_AHB1ENR_Val, fieldVal: uint32): RCC_AHB1ENR_Val {.inline.} = setField[RCC_AHB1ENR_Val](regVal, fieldVal, 0, 1) # write the modified fields to the reg proc write*(regVal: RCC_AHB1ENR_Val) {.inline.} = volatileStore(RCC_AHB1ENR, regVal) proc main() = RCC.AHB1ENR # reg read .GPIOAEN(1'u32) # field modify .write() # reg write Run
I verified that the procs are inlined when the `--passC="-Ox"` is greater than 1. This will soon be integrated into <https://github.com/dwhall/minisvd2nim>