From 17b5b6ec5b895e327399b7e0b7553f3dc45c3504 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 5 Sep 2018 11:29:15 +0200 Subject: [PATCH] all: use less magic in memory-mapped IO Don't store addresses in the values of registers, this leads to problems with char arrays (among others). Instead, do it like it's done in C with raw addresses cast to struct pointers. This commit also splits gen-device.py, as AVR and ARM have very different ideas of what a register is. It's easier to just keep them separate. --- Makefile | 4 +- compiler.go | 23 +-- gen-device-avr.py | 217 +++++++++++++++++++++++++++++ gen-device.py => gen-device-svd.py | 204 ++++++--------------------- src/device/arm/arm.go | 21 ++- src/machine/machine_avr.go | 16 +-- src/runtime/runtime_avr.go | 20 +-- 7 files changed, 296 insertions(+), 209 deletions(-) create mode 100755 gen-device-avr.py rename gen-device.py => gen-device-svd.py (52%) diff --git a/Makefile b/Makefile index f7d56679..83be0143 100644 --- a/Makefile +++ b/Makefile @@ -80,11 +80,11 @@ fmt: gen-device: gen-device-nrf gen-device-nrf: - ./gen-device.py lib/nrfx/mdk/ src/device/nrf/ + ./gen-device-svd.py lib/nrfx/mdk/ src/device/nrf/ go fmt ./src/device/nrf gen-device-avr: - ./gen-device.py lib/avr/packs/atmega src/device/avr/ + ./gen-device-avr.py lib/avr/packs/atmega src/device/avr/ go fmt ./src/device/avr diff --git a/compiler.go b/compiler.go index cf10efee..eeaf40e9 100644 --- a/compiler.go +++ b/compiler.go @@ -1421,15 +1421,12 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { if err != nil { return err } + store := c.builder.CreateStore(llvmVal, llvmAddr) valType := instr.Addr.Type().(*types.Pointer).Elem() if valType, ok := valType.(*types.Named); ok && valType.Obj().Name() == "__reg" { - // Magic type name to transform this store to a register store. - registerAddr := c.builder.CreateLoad(llvmAddr, "") - ptr := c.builder.CreateIntToPtr(registerAddr, llvmAddr.Type(), "") - store := c.builder.CreateStore(llvmVal, ptr) + // Magic type name to make this store volatile, for memory-mapped + // registers. store.SetVolatile(true) - } else { - c.builder.CreateStore(llvmVal, llvmAddr) } return nil default: @@ -2739,19 +2736,13 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { } case token.MUL: // *x, dereference pointer valType := unop.X.Type().(*types.Pointer).Elem() + load := c.builder.CreateLoad(x, "") if valType, ok := valType.(*types.Named); ok && valType.Obj().Name() == "__reg" { - // Magic type name: treat the value as a register pointer. - register := unop.X.(*ssa.FieldAddr) - global := register.X.(*ssa.Global) - llvmGlobal := c.ir.GetGlobal(global).llvmGlobal - llvmAddr := c.builder.CreateExtractValue(llvmGlobal.Initializer(), register.Field, "") - ptr := llvm.ConstIntToPtr(llvmAddr, x.Type()) - load := c.builder.CreateLoad(ptr, "") + // Magic type name to make this load volatile, for memory-mapped + // registers. load.SetVolatile(true) - return load, nil - } else { - return c.builder.CreateLoad(x, ""), nil } + return load, nil case token.XOR: // ^x, toggle all bits in integer return c.builder.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil default: diff --git a/gen-device-avr.py b/gen-device-avr.py new file mode 100755 index 00000000..93463286 --- /dev/null +++ b/gen-device-avr.py @@ -0,0 +1,217 @@ +#!/usr/bin/python3 + +import sys +import os +from xml.dom import minidom +from glob import glob +from collections import OrderedDict +import re + +class Device: + # dummy + pass + +def getText(element): + strings = [] + for node in element.childNodes: + if node.nodeType == node.TEXT_NODE: + strings.append(node.data) + return ''.join(strings) + +def formatText(text): + text = re.sub('[ \t\n]+', ' ', text) # Collapse whitespace (like in HTML) + text = text.replace('\\n ', '\n') + text = text.strip() + return text + +def readATDF(path): + # Read Atmel device descriptor files. + # See: http://packs.download.atmel.com + + device = Device() + + xml = minidom.parse(path) + device = xml.getElementsByTagName('device')[0] + deviceName = device.getAttribute('name') + arch = device.getAttribute('architecture') + family = device.getAttribute('family') + + memorySizes = {} + for el in device.getElementsByTagName('address-space'): + addressSpace = { + 'size': int(el.getAttribute('size'), 0), + 'segments': {}, + } + memorySizes[el.getAttribute('name')] = addressSpace + for segmentEl in el.getElementsByTagName('memory-segment'): + addressSpace['segments'][segmentEl.getAttribute('name')] = int(segmentEl.getAttribute('size'), 0) + + device.interrupts = [] + for el in device.getElementsByTagName('interrupts')[0].getElementsByTagName('interrupt'): + device.interrupts.append({ + 'index': int(el.getAttribute('index')), + 'name': el.getAttribute('name'), + 'description': el.getAttribute('caption'), + }) + + allRegisters = {} + commonRegisters = {} + + device.peripherals = [] + for el in xml.getElementsByTagName('modules')[0].getElementsByTagName('module'): + peripheral = { + 'name': el.getAttribute('name'), + 'description': el.getAttribute('caption'), + 'registers': [], + } + device.peripherals.append(peripheral) + for regElGroup in el.getElementsByTagName('register-group'): + for regEl in regElGroup.getElementsByTagName('register'): + size = int(regEl.getAttribute('size')) + regName = regEl.getAttribute('name') + regOffset = int(regEl.getAttribute('offset'), 0) + reg = { + 'description': regEl.getAttribute('caption'), + 'bitfields': [], + 'array': None, + } + if size == 1: + reg['variants'] = [{ + 'name': regName, + 'address': regOffset, + }] + elif size == 2: + reg['variants'] = [{ + 'name': regName + 'L', + 'address': regOffset, + }, { + 'name': regName + 'H', + 'address': regOffset + 1, + }] + else: + # TODO + continue + + for bitfieldEl in regEl.getElementsByTagName('bitfield'): + reg['bitfields'].append({ + 'name': regName + '_' + bitfieldEl.getAttribute('name'), + 'description': bitfieldEl.getAttribute('caption'), + 'value': int(bitfieldEl.getAttribute('mask'), 0), + }) + + if regName in allRegisters: + firstReg = allRegisters[regName] + if firstReg['register'] in firstReg['peripheral']['registers']: + firstReg['peripheral']['registers'].remove(firstReg['register']) + if firstReg['address'] != regOffset: + continue # TODO + commonRegisters = allRegisters[regName]['register'] + continue + else: + allRegisters[regName] = {'address': regOffset, 'register': reg, 'peripheral': peripheral} + + peripheral['registers'].append(reg) + + device.metadata = { + 'file': os.path.basename(path), + 'descriptorSource': 'http://packs.download.atmel.com/', + 'name': deviceName, + 'nameLower': deviceName.lower(), + 'description': 'Device information for the {}.'.format(deviceName), + 'arch': arch, + 'family': family, + 'flashSize': memorySizes['prog']['size'], + 'ramSize': memorySizes['data']['segments'].get('IRAM', memorySizes['data']['segments'].get('INTERNAL_SRAM')), + 'numInterrupts': len(device.interrupts), + } + + return device + +def writeGo(outdir, device): + # The Go module for this device. + out = open(outdir + '/' + device.metadata['nameLower'] + '.go', 'w') + pkgName = os.path.basename(outdir.rstrip('/')) + out.write('''\ +// Automatically generated file. DO NOT EDIT. +// Generated by gen-device-avr.py from {file}, see {descriptorSource} + +// +build {pkgName},{nameLower} + +// {description} +package {pkgName} + +import "unsafe" + +// Magic type name for the compiler. +type __reg uint8 + +// Export this magic type name. +type RegValue = __reg + +// Some information about this device. +const ( + DEVICE = "{name}" + ARCH = "{arch}" + FAMILY = "{family}" +) +'''.format(pkgName=pkgName, **device.metadata)) + + out.write('\n// Interrupts\nconst (\n') + for intr in device.interrupts: + out.write('\tIRQ_{name} = {index} // {description}\n'.format(**intr)) + intrMax = max(map(lambda intr: intr['index'], device.interrupts)) + out.write('\tIRQ_max = {} // Highest interrupt number on this device.\n'.format(intrMax)) + out.write(')\n') + + out.write('\n// Peripherals.\nvar (') + first = True + for peripheral in device.peripherals: + out.write('\n\t// {description}\n'.format(**peripheral)) + for register in peripheral['registers']: + for variant in register['variants']: + out.write('\t{name} = (*__reg)(unsafe.Pointer(uintptr(0x{address:x})))\n'.format(**variant)) + out.write(')\n') + + for peripheral in device.peripherals: + if not sum(map(lambda r: len(r['bitfields']), peripheral['registers'])): continue + out.write('\n// Bitfields for {name}: {description}\nconst('.format(**peripheral)) + for register in peripheral['registers']: + if not register['bitfields']: continue + for variant in register['variants']: + out.write('\n\t// {name}'.format(**variant)) + if register['description']: + out.write(': {description}'.format(**register)) + out.write('\n') + for bitfield in register['bitfields']: + out.write('\t{name} = 0x{value:x}'.format(**bitfield)) + if bitfield['description']: + out.write(' // {description}'.format(**bitfield)) + out.write('\n') + out.write(')\n') + +def writeLD(outdir, device): + # Variables for the linker script. + out = open(outdir + '/' + device.metadata['nameLower'] + '.ld', 'w') + out.write('''\ +/* Automatically generated file. DO NOT EDIT. */ +/* Generated by gen-device-avr.py from {file}, see {descriptorSource} */ + +__flash_size = 0x{flashSize:x}; +__ram_size = 0x{ramSize:x}; +__num_isrs = {numInterrupts}; +'''.format(**device.metadata)) + out.close() + + +def generate(indir, outdir): + for filepath in sorted(glob(indir + '/*.atdf')): + print(filepath) + device = readATDF(filepath) + writeGo(outdir, device) + writeLD(outdir, device) + + +if __name__ == '__main__': + indir = sys.argv[1] # directory with register descriptor files (*.atdf) + outdir = sys.argv[2] # output directory + generate(indir, outdir) diff --git a/gen-device.py b/gen-device-svd.py similarity index 52% rename from gen-device.py rename to gen-device-svd.py index 179c400a..a79c68c6 100755 --- a/gen-device.py +++ b/gen-device-svd.py @@ -52,6 +52,7 @@ def readSVD(path): peripheral = { 'name': name, 'description': description, + 'baseAddress': baseAddress, 'registers': [], } device.peripherals.append(peripheral) @@ -94,7 +95,6 @@ def readSVD(path): 'nameLower': deviceName.lower(), 'description': deviceDescription, 'licenseBlock': '\n// ' + licenseText.replace('\n', '\n// '), - 'regType': 'uint32', 'arch': ARM_ARCHS[cpuName], 'family': getText(root.getElementsByTagName('series')[0]), } @@ -134,119 +134,13 @@ def parseSVDRegister(peripheralName, regEl, baseAddress, namePrefix=''): }) return { - 'variants': [{ - 'name': namePrefix + regName, - 'address': address, - }], + 'name': namePrefix + regName, + 'address': address, 'description': regDescription.replace('\n', ' '), 'bitfields': fields, 'array': array, } -def readATDF(path): - # Read Atmel device descriptor files. - # See: http://packs.download.atmel.com - - device = Device() - - xml = minidom.parse(path) - device = xml.getElementsByTagName('device')[0] - deviceName = device.getAttribute('name') - arch = device.getAttribute('architecture') - family = device.getAttribute('family') - - memorySizes = {} - for el in device.getElementsByTagName('address-space'): - addressSpace = { - 'size': int(el.getAttribute('size'), 0), - 'segments': {}, - } - memorySizes[el.getAttribute('name')] = addressSpace - for segmentEl in el.getElementsByTagName('memory-segment'): - addressSpace['segments'][segmentEl.getAttribute('name')] = int(segmentEl.getAttribute('size'), 0) - - device.interrupts = [] - for el in device.getElementsByTagName('interrupts')[0].getElementsByTagName('interrupt'): - device.interrupts.append({ - 'index': int(el.getAttribute('index')), - 'name': el.getAttribute('name'), - 'description': el.getAttribute('caption'), - }) - - allRegisters = {} - commonRegisters = {} - - device.peripherals = [] - for el in xml.getElementsByTagName('modules')[0].getElementsByTagName('module'): - peripheral = { - 'name': el.getAttribute('name'), - 'description': el.getAttribute('caption'), - 'registers': [], - } - device.peripherals.append(peripheral) - for regElGroup in el.getElementsByTagName('register-group'): - for regEl in regElGroup.getElementsByTagName('register'): - size = int(regEl.getAttribute('size')) - regName = regEl.getAttribute('name') - regOffset = int(regEl.getAttribute('offset'), 0) - reg = { - 'description': regEl.getAttribute('caption'), - 'bitfields': [], - 'array': None, - } - if size == 1: - reg['variants'] = [{ - 'name': regName, - 'address': regOffset, - }] - elif size == 2: - reg['variants'] = [{ - 'name': regName + 'L', - 'address': regOffset, - }, { - 'name': regName + 'H', - 'address': regOffset, - }] - else: - reg['variants'] = [] # TODO - - for bitfieldEl in regEl.getElementsByTagName('bitfield'): - reg['bitfields'].append({ - 'name': regName + '_' + bitfieldEl.getAttribute('name'), - 'description': bitfieldEl.getAttribute('caption'), - 'value': int(bitfieldEl.getAttribute('mask'), 0), - }) - - if regName in allRegisters: - firstReg = allRegisters[regName] - if firstReg['register'] in firstReg['peripheral']['registers']: - firstReg['peripheral']['registers'].remove(firstReg['register']) - if firstReg['address'] != regOffset: - continue # TODO - commonRegisters = allRegisters[regName]['register'] - continue - else: - allRegisters[regName] = {'address': regOffset, 'register': reg, 'peripheral': peripheral} - - peripheral['registers'].append(reg) - - device.metadata = { - 'file': os.path.basename(path), - 'descriptorSource': 'http://packs.download.atmel.com/', - 'name': deviceName, - 'nameLower': deviceName.lower(), - 'description': 'Device information for the {}.'.format(deviceName), - 'licenseBlock': '', - 'regType': 'uint8', - 'arch': arch, - 'family': family, - 'flashSize': memorySizes['prog']['size'], - 'ramSize': memorySizes['data']['segments'].get('IRAM', memorySizes['data']['segments'].get('INTERNAL_SRAM')), - 'numInterrupts': len(device.interrupts), - } - - return device - def writeGo(outdir, device): # The Go module for this device. out = open(outdir + '/' + device.metadata['nameLower'] + '.go', 'w') @@ -261,8 +155,10 @@ def writeGo(outdir, device): // {licenseBlock} package {pkgName} +import "unsafe" + // Magic type name for the compiler. -type __reg {regType} +type __reg uint32 // Export this magic type name. type RegValue = __reg @@ -282,33 +178,41 @@ const ( out.write('\tIRQ_max = {} // Highest interrupt number on this device.\n'.format(intrMax)) out.write(')\n') - out.write('\n// Peripherals\nvar (') for peripheral in device.peripherals: - out.write('\n\t// {description}\n\t{name} = struct {{\n'.format(**peripheral)) + out.write('\n// {description}\ntype {name}_Type struct {{\n'.format(**peripheral)) + address = peripheral['baseAddress'] + padNumber = 0 for register in peripheral['registers']: - for variant in register['variants']: - regType = '__reg' - if register['array'] is not None: - regType = '[{}]__reg'.format(register['array']) - out.write('\t\t{name} {regType}\n'.format(**variant, regType=regType)) - out.write('\t}{\n') - for register in peripheral['registers']: - for variant in register['variants']: - out.write('\t\t{name}: '.format(**variant)) - if register['array'] is not None: - out.write('[{num}]__reg{{'.format(num=register['array'])) - if register['description']: - out.write(' // {description}'.format(**register)) - out.write('\n') - for i in range(register['array']): - out.write('\t\t\t0x{:x},\n'.format(variant['address'] + i * 4)) # TODO: pointer width - out.write('\t\t},') + if address > register['address']: + # In Nordic SVD files, these registers are deprecated or + # duplicates, so can be ignored. + #print('skip: %s.%s' % (peripheral['name'], register['name'])) + continue + + # insert padding, if needed + if address < register['address']: + numSkip = (register['address'] - address) // 4 + if numSkip == 1: + out.write('\t_padding{padNumber} __reg\n'.format(padNumber=padNumber)) else: - out.write('0x{address:x},'.format(**variant)) - if register['description']: - out.write(' // {description}'.format(**register)) - out.write('\n') - out.write('\t}\n') + out.write('\t_padding{padNumber} [{num}]__reg\n'.format(padNumber=padNumber, num=numSkip)) + padNumber += 1 + + regType = '__reg' + if register['array'] is not None: + regType = '[{}]__reg'.format(register['array']) + out.write('\t{name} {regType}\n'.format(**register, regType=regType)) + + # next address + if register['array'] is not None and 1: + address = register['address'] + 4 * register['array'] + else: + address = register['address'] + 4 + out.write('}\n') + + out.write('\n// Peripherals.\nvar (\n') + for peripheral in device.peripherals: + out.write('\t{name} = (*{name}_Type)(unsafe.Pointer(uintptr(0x{baseAddress:x}))) // {description}\n'.format(**peripheral)) out.write(')\n') for peripheral in device.peripherals: @@ -316,44 +220,24 @@ const ( out.write('\n// Bitfields for {name}: {description}\nconst('.format(**peripheral)) for register in peripheral['registers']: if not register['bitfields']: continue - for variant in register['variants']: - out.write('\n\t// {name}'.format(**variant)) - if register['description']: - out.write(': {description}'.format(**register)) - out.write('\n') + out.write('\n\t// {name}'.format(**register)) + if register['description']: + out.write(': {description}'.format(**register)) + out.write('\n') for bitfield in register['bitfields']: out.write('\t{name} = 0x{value:x}'.format(**bitfield)) if bitfield['description']: - out.write('// {description}'.format(**bitfield)) + out.write(' // {description}'.format(**bitfield)) out.write('\n') out.write(')\n') -def writeLD(outdir, device): - # Variables for the linker script. - out = open(outdir + '/' + device.metadata['nameLower'] + '.ld', 'w') - out.write('''\ -/* Automatically generated file. DO NOT EDIT. */ -/* Generated by gen-device.py from {file}, see {descriptorSource} */ - -__flash_size = 0x{flashSize:x}; -__ram_size = 0x{ramSize:x}; -__num_isrs = {numInterrupts}; -'''.format(**device.metadata)) - out.close() - def generate(indir, outdir): - for filepath in glob(indir + '/*.svd'): + for filepath in sorted(glob(indir + '/*.svd')): print(filepath) device = readSVD(filepath) writeGo(outdir, device) - for filepath in glob(indir + '/*.atdf'): - print(filepath) - device = readATDF(filepath) - writeGo(outdir, device) - writeLD(outdir, device) - if __name__ == '__main__': indir = sys.argv[1] # directory with register descriptor files (*.svd, *.atdf) diff --git a/src/device/arm/arm.go b/src/device/arm/arm.go index 5a64c357..71ce280d 100644 --- a/src/device/arm/arm.go +++ b/src/device/arm/arm.go @@ -29,6 +29,10 @@ // POSSIBILITY OF SUCH DAMAGE. package arm +import ( + "unsafe" +) + type __reg uint32 type RegValue = __reg @@ -42,22 +46,13 @@ const ( ) // Nested Vectored Interrupt Controller (NVIC). -var NVIC = struct { +type NVIC_Type struct { ISER [8]__reg -}{ - ISER: [8]__reg{ - NVIC_BASE + 0x000, - NVIC_BASE + 0x004, - NVIC_BASE + 0x008, - NVIC_BASE + 0x00C, - NVIC_BASE + 0x010, - NVIC_BASE + 0x014, - NVIC_BASE + 0x018, - NVIC_BASE + 0x01C, - }, } +var NVIC = (*NVIC_Type)(unsafe.Pointer(uintptr(NVIC_BASE))) + // Enable the given interrupt number. func EnableIRQ(irq uint32) { - NVIC.ISER[irq >> 5] = 1 << (irq & 0x1F) + NVIC.ISER[irq>>5] = 1 << (irq & 0x1F) } diff --git a/src/machine/machine_avr.go b/src/machine/machine_avr.go index 23222ca3..3446f7ed 100644 --- a/src/machine/machine_avr.go +++ b/src/machine/machine_avr.go @@ -19,15 +19,15 @@ const LED = 13 func (p GPIO) Configure(config GPIOConfig) { if config.Mode == GPIO_OUTPUT { // set output bit if p.Pin < 8 { - avr.PORT.DDRD |= 1 << p.Pin + *avr.DDRD |= 1 << p.Pin } else { - avr.PORT.DDRB |= 1 << (p.Pin - 8) + *avr.DDRB |= 1 << (p.Pin - 8) } } else { // configure input: clear output bit if p.Pin < 8 { - avr.PORT.DDRD &^= 1 << p.Pin + *avr.DDRD &^= 1 << p.Pin } else { - avr.PORT.DDRB &^= 1 << (p.Pin - 8) + *avr.DDRB &^= 1 << (p.Pin - 8) } } } @@ -35,15 +35,15 @@ func (p GPIO) Configure(config GPIOConfig) { func (p GPIO) Set(value bool) { if value { // set bits if p.Pin < 8 { - avr.PORT.PORTD |= 1 << p.Pin + *avr.PORTD |= 1 << p.Pin } else { - avr.PORT.PORTB |= 1 << (p.Pin - 8) + *avr.PORTB |= 1 << (p.Pin - 8) } } else { // clear bits if p.Pin < 8 { - avr.PORT.PORTB &^= 1 << p.Pin + *avr.PORTB &^= 1 << p.Pin } else { - avr.PORT.PORTB &^= 1 << (p.Pin - 8) + *avr.PORTB &^= 1 << (p.Pin - 8) } } } diff --git a/src/runtime/runtime_avr.go b/src/runtime/runtime_avr.go index 91e517cb..48e433c8 100644 --- a/src/runtime/runtime_avr.go +++ b/src/runtime/runtime_avr.go @@ -36,17 +36,17 @@ func init() { func initUART() { // Initialize UART at 115200 baud when running at 16MHz. - avr.USART.UBRR0H = 0 - avr.USART.UBRR0L = 8 - avr.USART.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 // enable RX and TX - avr.USART.UCSR0C = avr.UCSR0C_UCSZ0 // 8-bits data + *avr.UBRR0H = 0 + *avr.UBRR0L = 8 + *avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 // enable RX and TX + *avr.UCSR0C = avr.UCSR0C_UCSZ0 // 8-bits data } func putchar(c byte) { - for (avr.USART.UCSR0A & avr.UCSR0A_UDRE0) == 0 { + for (*avr.UCSR0A & avr.UCSR0A_UDRE0) == 0 { // Wait until previous char has been sent. } - avr.USART.UDR0 = avr.RegValue(c) // send char + *avr.UDR0 = avr.RegValue(c) // send char } // Sleep by the given amount. @@ -73,21 +73,21 @@ func sleepWDT(period uint8) { avr.Asm("cli") avr.Asm("wdr") // Start timed sequence. - avr.WDT.WDTCSR |= avr.WDTCSR_WDCE | avr.WDTCSR_WDE + *avr.WDTCSR |= avr.WDTCSR_WDCE | avr.WDTCSR_WDE // Enable WDT and set new timeout (0.5s) - avr.WDT.WDTCSR = avr.WDTCSR_WDIE | avr.RegValue(period) + *avr.WDTCSR = avr.WDTCSR_WDIE | avr.RegValue(period) avr.Asm("sei") // Set sleep mode to idle and enable sleep mode. // Note: when using something other than idle, the UART won't work // correctly. This needs to be fixed, though, so we can truly sleep. - avr.CPU.SMCR = (0 << 1) | avr.SMCR_SE + *avr.SMCR = (0 << 1) | avr.SMCR_SE // go to sleep avr.Asm("sleep") // disable sleep - avr.CPU.SMCR = 0 + *avr.SMCR = 0 } func monotime() uint64 {