From 2fa24ef752c6a5aacc96dd7f8e317e49cf0302f1 Mon Sep 17 00:00:00 2001 From: sago35 Date: Wed, 6 Jul 2022 07:58:42 +0900 Subject: [PATCH] samd21,samd51,nrf52840: refactor usb initialization --- src/machine/machine_atsamd21_usb.go | 35 ++----- src/machine/machine_atsamd51_usb.go | 30 +----- src/machine/machine_nrf52840_usb.go | 44 +++----- src/machine/serial-usb.go | 1 - src/machine/usb.go | 24 +++++ src/runtime/runtime_atsamd21.go | 5 +- src/runtime/runtime_atsamd51.go | 5 +- src/runtime/runtime_nrf.go | 4 +- src/runtime/runtime_nrf52840.go | 149 ++++++++++++++++++++++++++++ 9 files changed, 200 insertions(+), 97 deletions(-) create mode 100644 src/runtime/runtime_nrf52840.go diff --git a/src/machine/machine_atsamd21_usb.go b/src/machine/machine_atsamd21_usb.go index e63f6124..51f276c0 100644 --- a/src/machine/machine_atsamd21_usb.go +++ b/src/machine/machine_atsamd21_usb.go @@ -22,7 +22,6 @@ type USBCDC struct { waitTxc bool waitTxcRetryCount uint8 sent bool - configured bool } var ( @@ -149,26 +148,8 @@ const ( usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF ) -var ( - usbEndpointDescriptors [8]usbDeviceDescriptor - - udd_ep_in_cache_buffer [7][128]uint8 - udd_ep_out_cache_buffer [7][128]uint8 - - isEndpointHalt = false - isRemoteWakeUpEnabled = false - endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL, - (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn), - (usb_ENDPOINT_TYPE_BULK | usbEndpointOut), - (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)} - - usbConfiguration uint8 - usbSetInterface uint8 - usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} -) - -// Configure the USB CDC interface. The config is here for compatibility with the UART interface. -func (usbcdc *USBCDC) Configure(config UARTConfig) { +// Configure the USB peripheral. The config is here for compatibility with the UART interface. +func (dev *USBDevice) Configure(config UARTConfig) { // reset USB interface sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST) for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) || @@ -203,15 +184,11 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) { sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE) // enable IRQ - intr := interrupt.New(sam.IRQ_USB, handleUSB) - intr.Enable() - - usbcdc.configured = true + interrupt.New(sam.IRQ_USB, handleUSBIRQ).Enable() } -// Configured returns whether usbcdc is configured or not. -func (usbcdc *USBCDC) Configured() bool { - return usbcdc.configured +func (usbcdc *USBCDC) Configure(config UARTConfig) { + // dummy } func handlePadCalibration() { @@ -257,7 +234,7 @@ func handlePadCalibration() { sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos) } -func handleUSB(intr interrupt.Interrupt) { +func handleUSBIRQ(intr interrupt.Interrupt) { // reset all interrupt flags flags := sam.USB_DEVICE.INTFLAG.Get() sam.USB_DEVICE.INTFLAG.Set(flags) diff --git a/src/machine/machine_atsamd51_usb.go b/src/machine/machine_atsamd51_usb.go index 36880287..f0f7e114 100644 --- a/src/machine/machine_atsamd51_usb.go +++ b/src/machine/machine_atsamd51_usb.go @@ -22,7 +22,6 @@ type USBCDC struct { waitTxc bool waitTxcRetryCount uint8 sent bool - configured bool } var ( @@ -150,26 +149,8 @@ const ( usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF ) -var ( - usbEndpointDescriptors [8]usbDeviceDescriptor - - udd_ep_in_cache_buffer [7][128]uint8 - udd_ep_out_cache_buffer [7][128]uint8 - - isEndpointHalt = false - isRemoteWakeUpEnabled = false - endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL, - (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn), - (usb_ENDPOINT_TYPE_BULK | usbEndpointOut), - (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)} - - usbConfiguration uint8 - usbSetInterface uint8 - usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} -) - -// Configure the USB CDC interface. The config is here for compatibility with the UART interface. -func (usbcdc *USBCDC) Configure(config UARTConfig) { +// Configure the USB peripheral. The config is here for compatibility with the UART interface. +func (dev *USBDevice) Configure(config UARTConfig) { // reset USB interface sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST) for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) || @@ -208,13 +189,10 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) { interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable() interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable() interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable() - - usbcdc.configured = true } -// Configured returns whether usbcdc is configured or not. -func (usbcdc *USBCDC) Configured() bool { - return usbcdc.configured +func (usbcdc *USBCDC) Configure(config UARTConfig) { + // dummy } func handlePadCalibration() { diff --git a/src/machine/machine_nrf52840_usb.go b/src/machine/machine_nrf52840_usb.go index 80c8653a..663f9e22 100644 --- a/src/machine/machine_nrf52840_usb.go +++ b/src/machine/machine_nrf52840_usb.go @@ -14,8 +14,6 @@ import ( // USBCDC is the USB CDC aka serial over USB interface on the nRF52840 type USBCDC struct { Buffer *RingBuffer - interrupt interrupt.Interrupt - initcomplete bool TxIdx volatile.Register8 waitTxc bool waitTxcRetryCount uint8 @@ -125,25 +123,11 @@ var ( _USB = USBCDC{Buffer: NewRingBuffer()} waitHidTxc bool - usbEndpointDescriptors [8]usbDeviceDescriptor - - udd_ep_in_cache_buffer [7][128]uint8 - udd_ep_out_cache_buffer [7][128]uint8 - sendOnEP0DATADONE struct { ptr *byte count int } - isEndpointHalt = false - isRemoteWakeUpEnabled = false - endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL, - (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn), - (usb_ENDPOINT_TYPE_BULK | usbEndpointOut), - (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)} - usbConfiguration uint8 - usbSetInterface uint8 - usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} epinen uint32 epouten uint32 easyDMABusy volatile.Register8 @@ -167,19 +151,15 @@ func exitCriticalSection() { easyDMABusy.ClearBits(1) } -// Configure the USB CDC interface. The config is here for compatibility with the UART interface. -func (usbcdc *USBCDC) Configure(config UARTConfig) { - if usbcdc.initcomplete { - return - } - +// Configure the USB peripheral. The config is here for compatibility with the UART interface. +func (dev *USBDevice) Configure(config UARTConfig) { // Enable IRQ. Make sure this is higher than the SWI2 interrupt handler so // that it is possible to print to the console from a BLE interrupt. You // shouldn't generally do that but it is useful for debugging and panic // logging. - usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, _USB.handleInterrupt) - usbcdc.interrupt.SetPriority(0x40) // interrupt priority 2 (lower number means more important) - usbcdc.interrupt.Enable() + intr := interrupt.New(nrf.IRQ_USBD, handleUSBIRQ) + intr.SetPriority(0x40) // interrupt priority 2 (lower number means more important) + intr.Enable() // enable USB nrf.USBD.ENABLE.Set(1) @@ -194,14 +174,16 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) { ) nrf.USBD.USBPULLUP.Set(0) - - usbcdc.initcomplete = true } -func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { +func (usbcdc *USBCDC) Configure(config UARTConfig) { + // dummy +} + +func handleUSBIRQ(interrupt.Interrupt) { if nrf.USBD.EVENTS_SOF.Get() == 1 { nrf.USBD.EVENTS_SOF.Set(0) - usbcdc.Flush() + USB.Flush() if hidCallback != nil && !waitHidTxc { hidCallback() } @@ -301,7 +283,7 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { } case usb_CDC_ENDPOINT_IN: //, usb_CDC_ENDPOINT_ACM: if inDataDone { - usbcdc.waitTxc = false + USB.waitTxc = false exitCriticalSection() } case usb_HID_ENDPOINT_IN: @@ -327,7 +309,7 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { nrf.USBD.TASKS_EP0STATUS.Set(1) } if i == usb_CDC_ENDPOINT_OUT { - usbcdc.handleEndpoint(uint32(i)) + USB.handleEndpoint(uint32(i)) } exitCriticalSection() } diff --git a/src/machine/serial-usb.go b/src/machine/serial-usb.go index eb4f83e8..0bec8e29 100644 --- a/src/machine/serial-usb.go +++ b/src/machine/serial-usb.go @@ -7,5 +7,4 @@ package machine var Serial = USB func InitSerial() { - Serial.Configure(UARTConfig{}) } diff --git a/src/machine/usb.go b/src/machine/usb.go index 354bc1b1..cc7e4534 100644 --- a/src/machine/usb.go +++ b/src/machine/usb.go @@ -8,6 +8,12 @@ import ( "runtime/volatile" ) +type USBDevice struct { +} + +var ( + USBDev = &USBDevice{} +) var usbDescriptor = descriptorCDC var ( @@ -47,6 +53,24 @@ var ( usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English ) +var ( + usbEndpointDescriptors [8]usbDeviceDescriptor + + udd_ep_in_cache_buffer [7][128]uint8 + udd_ep_out_cache_buffer [7][128]uint8 + + isEndpointHalt = false + isRemoteWakeUpEnabled = false + endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL, + (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn), + (usb_ENDPOINT_TYPE_BULK | usbEndpointOut), + (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)} + + usbConfiguration uint8 + usbSetInterface uint8 + usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} +) + const ( usb_IMANUFACTURER = 1 usb_IPRODUCT = 2 diff --git a/src/runtime/runtime_atsamd21.go b/src/runtime/runtime_atsamd21.go index 9b3d8439..df0e8a49 100644 --- a/src/runtime/runtime_atsamd21.go +++ b/src/runtime/runtime_atsamd21.go @@ -28,11 +28,8 @@ func init() { initUSBClock() initADCClock() - // connect to USB CDC interface + machine.USBDev.Configure(machine.UARTConfig{}) machine.InitSerial() - if !machine.USB.Configured() { - machine.USB.Configure(machine.UARTConfig{}) - } } func putchar(c byte) { diff --git a/src/runtime/runtime_atsamd51.go b/src/runtime/runtime_atsamd51.go index d4502624..cb29d965 100644 --- a/src/runtime/runtime_atsamd51.go +++ b/src/runtime/runtime_atsamd51.go @@ -28,11 +28,8 @@ func init() { initUSBClock() initADCClock() - // connect to USB CDC interface + machine.USBDev.Configure(machine.UARTConfig{}) machine.InitSerial() - if !machine.USB.Configured() { - machine.USB.Configure(machine.UARTConfig{}) - } } func putchar(c byte) { diff --git a/src/runtime/runtime_nrf.go b/src/runtime/runtime_nrf.go index 4e8c0288..26a48d2a 100644 --- a/src/runtime/runtime_nrf.go +++ b/src/runtime/runtime_nrf.go @@ -1,5 +1,5 @@ -//go:build nrf -// +build nrf +//go:build nrf && !nrf52840 +// +build nrf,!nrf52840 package runtime diff --git a/src/runtime/runtime_nrf52840.go b/src/runtime/runtime_nrf52840.go new file mode 100644 index 00000000..a4907b92 --- /dev/null +++ b/src/runtime/runtime_nrf52840.go @@ -0,0 +1,149 @@ +//go:build nrf && nrf52840 +// +build nrf,nrf52840 + +package runtime + +import ( + "device/arm" + "device/nrf" + "machine" + "runtime/interrupt" + "runtime/volatile" +) + +type timeUnit int64 + +//go:linkname systemInit SystemInit +func systemInit() + +//export Reset_Handler +func main() { + if nrf.FPUPresent { + arm.SCB.CPACR.Set(0) // disable FPU if it is enabled + } + systemInit() + preinit() + run() + exit(0) +} + +func init() { + machine.USBDev.Configure(machine.UARTConfig{}) + machine.InitSerial() + initLFCLK() + initRTC() +} + +func initLFCLK() { + if machine.HasLowFrequencyCrystal { + nrf.CLOCK.LFCLKSRC.Set(nrf.CLOCK_LFCLKSTAT_SRC_Xtal) + } + nrf.CLOCK.TASKS_LFCLKSTART.Set(1) + for nrf.CLOCK.EVENTS_LFCLKSTARTED.Get() == 0 { + } + nrf.CLOCK.EVENTS_LFCLKSTARTED.Set(0) +} + +func initRTC() { + nrf.RTC1.TASKS_START.Set(1) + intr := interrupt.New(nrf.IRQ_RTC1, func(intr interrupt.Interrupt) { + if nrf.RTC1.EVENTS_COMPARE[0].Get() != 0 { + nrf.RTC1.EVENTS_COMPARE[0].Set(0) + nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0) + nrf.RTC1.EVENTS_COMPARE[0].Set(0) + rtc_wakeup.Set(1) + } + if nrf.RTC1.EVENTS_OVRFLW.Get() != 0 { + nrf.RTC1.EVENTS_OVRFLW.Set(0) + rtcOverflows.Set(rtcOverflows.Get() + 1) + } + }) + nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_OVRFLW) + intr.SetPriority(0xc0) // low priority + intr.Enable() +} + +func putchar(c byte) { + machine.Serial.WriteByte(c) +} + +func getchar() byte { + for machine.Serial.Buffered() == 0 { + Gosched() + } + v, _ := machine.Serial.ReadByte() + return v +} + +func buffered() int { + return machine.Serial.Buffered() +} + +func sleepTicks(d timeUnit) { + for d != 0 { + ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side) + rtc_sleep(ticks) + d -= timeUnit(ticks) + } +} + +var rtcOverflows volatile.Register32 // number of times the RTC wrapped around + +// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds. +func ticksToNanoseconds(ticks timeUnit) int64 { + // The following calculation is actually the following, but with both sides + // reduced to reduce the risk of overflow: + // ticks * 1e9 / 32768 + return int64(ticks) * 1953125 / 64 +} + +// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz). +func nanosecondsToTicks(ns int64) timeUnit { + // The following calculation is actually the following, but with both sides + // reduced to reduce the risk of overflow: + // ns * 32768 / 1e9 + return timeUnit(ns * 64 / 1953125) +} + +// Monotonically increasing numer of ticks since start. +func ticks() timeUnit { + // For some ways of capturing the time atomically, see this thread: + // https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617 + // Here, instead of re-reading the counter register if an overflow has been + // detected, we simply try again because that results in (slightly) smaller + // code and is perhaps easier to prove correct. + for { + mask := interrupt.Disable() + counter := uint32(nrf.RTC1.COUNTER.Get()) + overflows := rtcOverflows.Get() + hasOverflow := nrf.RTC1.EVENTS_OVRFLW.Get() != 0 + interrupt.Restore(mask) + + if hasOverflow { + // There was an overflow. Try again. + continue + } + + // The counter is 24 bits in size, so the number of overflows form the + // upper 32 bits (together 56 bits, which covers 71493 years at + // 32768kHz: I'd argue good enough for most purposes). + return timeUnit(overflows)<<24 + timeUnit(counter) + } +} + +var rtc_wakeup volatile.Register8 + +func rtc_sleep(ticks uint32) { + nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_COMPARE0) + rtc_wakeup.Set(0) + if ticks == 1 { + // Race condition (even in hardware) at ticks == 1. + // TODO: fix this in a better way by detecting it, like the manual + // describes. + ticks = 2 + } + nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff) + for rtc_wakeup.Get() == 0 { + waitForEvents() + } +}