diff options
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | src/machine/board_pico.go | 43 | ||||
-rw-r--r-- | src/machine/machine_rp2040.go | 44 | ||||
-rw-r--r-- | src/machine/machine_rp2040_clocks.go | 251 | ||||
-rw-r--r-- | src/machine/machine_rp2040_gpio.go | 131 | ||||
-rw-r--r-- | src/machine/machine_rp2040_pll.go | 100 | ||||
-rw-r--r-- | src/machine/machine_rp2040_resets.go | 43 | ||||
-rw-r--r-- | src/machine/machine_rp2040_timer.go | 51 | ||||
-rw-r--r-- | src/machine/machine_rp2040_watchdog.go | 27 | ||||
-rw-r--r-- | src/machine/machine_rp2040_xosc.go | 42 | ||||
-rw-r--r-- | src/runtime/runtime_rp2040.go | 58 | ||||
-rw-r--r-- | targets/pico.json | 10 | ||||
-rw-r--r-- | targets/pico.ld | 31 | ||||
-rw-r--r-- | targets/pico_boot_stage2.S | 23 | ||||
-rw-r--r-- | targets/rp2040.json | 12 | ||||
-rw-r--r-- | targets/rp2040.ld | 9 |
16 files changed, 882 insertions, 2 deletions
@@ -36,7 +36,7 @@ else LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF' endif -.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr +.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine frontendopenmp instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target @@ -107,7 +107,7 @@ fmt-check: @unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1 -gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp +gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp ifneq ($(STM32), 0) gen-device: gen-device-stm32 endif @@ -150,6 +150,9 @@ gen-device-stm32: build/gen-device-svd ./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd lib/stm32-svd/svd src/device/stm32/ GO111MODULE=off $(GO) fmt ./src/device/stm32 +gen-device-rp: build/gen-device-svd + ./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/RaspberryPi lib/cmsis-svd/data/RaspberryPi/ src/device/rp/ + GO111MODULE=off $(GO) fmt ./src/device/rp # Get LLVM sources. $(LLVM_PROJECTDIR)/llvm: @@ -351,6 +354,8 @@ smoketest: @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=pico examples/blinky1 + @$(MD5SUM) test.hex # test pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm @$(MD5SUM) test.hex diff --git a/src/machine/board_pico.go b/src/machine/board_pico.go new file mode 100644 index 000000000..b1be4b913 --- /dev/null +++ b/src/machine/board_pico.go @@ -0,0 +1,43 @@ +// +build pico + +package machine + +// GPIO pins +const ( + GP0 Pin = 0 + GP1 Pin = 1 + GP2 Pin = 2 + GP3 Pin = 3 + GP4 Pin = 4 + GP5 Pin = 5 + GP6 Pin = 6 + GP7 Pin = 7 + GP8 Pin = 8 + GP9 Pin = 9 + GP10 Pin = 10 + GP11 Pin = 11 + GP12 Pin = 12 + GP13 Pin = 13 + GP14 Pin = 14 + GP15 Pin = 15 + GP16 Pin = 16 + GP17 Pin = 17 + GP18 Pin = 18 + GP19 Pin = 19 + GP20 Pin = 20 + GP21 Pin = 21 + GP22 Pin = 22 + GP23 Pin = 23 + GP24 Pin = 24 + GP25 Pin = 25 + GP26 Pin = 26 + GP27 Pin = 27 + GP28 Pin = 28 + GP29 Pin = 29 + + // Onboard LED + LED Pin = GP25 + + // Onboard crystal oscillator frequency, in MHz. + xoscFreq = 12 // MHz +) diff --git a/src/machine/machine_rp2040.go b/src/machine/machine_rp2040.go new file mode 100644 index 000000000..ecab6688d --- /dev/null +++ b/src/machine/machine_rp2040.go @@ -0,0 +1,44 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + _ "unsafe" +) + +//go:linkname machineInit runtime.machineInit +func machineInit() { + // Reset all peripherals to put system into a known state, + // except for QSPI pads and the XIP IO bank, as this is fatal if running from flash + // and the PLLs, as this is fatal if clock muxing has not been reset on this boot + // and USB, syscfg, as this disturbs USB-to-SWD on core 1 + bits := ^uint32(rp.RESETS_RESET_IO_QSPI | + rp.RESETS_RESET_PADS_QSPI | + rp.RESETS_RESET_PLL_USB | + rp.RESETS_RESET_USBCTRL | + rp.RESETS_RESET_SYSCFG | + rp.RESETS_RESET_PLL_SYS) + resetBlock(bits) + + // Remove reset from peripherals which are clocked only by clkSys and + // clkRef. Other peripherals stay in reset until we've configured clocks. + bits = ^uint32(rp.RESETS_RESET_ADC | + rp.RESETS_RESET_RTC | + rp.RESETS_RESET_SPI0 | + rp.RESETS_RESET_SPI1 | + rp.RESETS_RESET_UART0 | + rp.RESETS_RESET_UART1 | + rp.RESETS_RESET_USBCTRL) + unresetBlockWait(bits) + + clocks.init() + + // Peripheral clocks should now all be running + unresetBlockWait(RESETS_RESET_Msk) +} + +//go:linkname ticks runtime.machineTicks +func ticks() uint64 { + return timer.timeElapsed() +} diff --git a/src/machine/machine_rp2040_clocks.go b/src/machine/machine_rp2040_clocks.go new file mode 100644 index 000000000..9edaa2ede --- /dev/null +++ b/src/machine/machine_rp2040_clocks.go @@ -0,0 +1,251 @@ +// +build rp2040 + +package machine + +import ( + "device/arm" + "device/rp" + "runtime/volatile" + "unsafe" +) + +const ( + KHz = 1000 + MHz = 1000000 +) + +// clockIndex identifies a hardware clock +type clockIndex uint8 + +const ( + clkGPOUT0 clockIndex = iota // GPIO Muxing 0 + clkGPOUT1 // GPIO Muxing 1 + clkGPOUT2 // GPIO Muxing 2 + clkGPOUT3 // GPIO Muxing 3 + clkRef // Watchdog and timers reference clock + clkSys // Processors, bus fabric, memory, memory mapped registers + clkPeri // Peripheral clock for UART and SPI + clkUSB // USB clock + clkADC // ADC clock + clkRTC // Real time clock + numClocks +) + +type clockType struct { + ctrl volatile.Register32 + div volatile.Register32 + selected volatile.Register32 +} + +type fc struct { + refKHz volatile.Register32 + minKHz volatile.Register32 + maxKHz volatile.Register32 + delay volatile.Register32 + interval volatile.Register32 + src volatile.Register32 + status volatile.Register32 + result volatile.Register32 +} + +type clocksType struct { + clk [numClocks]clockType + resus struct { + ctrl volatile.Register32 + status volatile.Register32 + } + fc0 fc + wakeEN0 volatile.Register32 + wakeEN1 volatile.Register32 + sleepEN0 volatile.Register32 + sleepEN1 volatile.Register32 + enabled0 volatile.Register32 + enabled1 volatile.Register32 + intR volatile.Register32 + intE volatile.Register32 + intF volatile.Register32 + intS volatile.Register32 +} + +var clocks = (*clocksType)(unsafe.Pointer(rp.CLOCKS)) + +var configuredFreq [numClocks]uint32 + +type clock struct { + *clockType + cix clockIndex +} + +// clock returns the clock identified by cix. +func (clks *clocksType) clock(cix clockIndex) *clock { + return &clock{ + &clks.clk[cix], + cix, + } +} + +// hasGlitchlessMux returns true if clock contains a glitchless multiplexer. +// +// Clock muxing consists of two components: +// +// A glitchless mux, which can be switched freely, but whose inputs must be +// free-running. +// +// An auxiliary (glitchy) mux, whose output glitches when switched, but has +// no constraints on its inputs. +// +// Not all clocks have both types of mux. +func (clk *clock) hasGlitchlessMux() bool { + return clk.cix == clkSys || clk.cix == clkRef +} + +// configure configures the clock by selecting the main clock source src +// and the auxiliary clock source auxsrc +// and finally setting the clock frequency to freq +// given the input clock source frequency srcFreq. +func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) { + if freq > srcFreq { + panic("clock frequency cannot be greater than source frequency") + } + + // Div register is 24.8 int.frac divider so multiply by 2^8 (left shift by 8) + div := uint32((uint64(srcFreq) << 8) / uint64(freq)) + + // If increasing divisor, set divisor before source. Otherwise set source + // before divisor. This avoids a momentary overspeed when e.g. switching + // to a faster source and increasing divisor to compensate. + if div > clk.div.Get() { + clk.div.Set(div) + } + + // If switching a glitchless slice (ref or sys) to an aux source, switch + // away from aux *first* to avoid passing glitches when changing aux mux. + // Assume (!!!) glitchless source 0 is no faster than the aux source. + if clk.hasGlitchlessMux() && src == rp.CLOCKS_CLK_SYS_CTRL_SRC_CLKSRC_CLK_SYS_AUX { + clk.ctrl.ClearBits(rp.CLOCKS_CLK_REF_CTRL_SRC_Msk) + for !clk.selected.HasBits(1) { + } + } else + // If no glitchless mux, cleanly stop the clock to avoid glitches + // propagating when changing aux mux. Note it would be a really bad idea + // to do this on one of the glitchless clocks (clkSys, clkRef). + { + // Disable clock. On clkRef and clkSys this does nothing, + // all other clocks have the ENABLE bit in the same position. + clk.ctrl.ClearBits(rp.CLOCKS_CLK_GPOUT0_CTRL_ENABLE_Msk) + if configuredFreq[clk.cix] > 0 { + // Delay for 3 cycles of the target clock, for ENABLE propagation. + // Note XOSC_COUNT is not helpful here because XOSC is not + // necessarily running, nor is timer... so, 3 cycles per loop: + delayCyc := configuredFreq[clkSys]/configuredFreq[clk.cix] + 1 + arm.AsmFull( + ` + ldr r0, {cyc} + 1: + subs r0, #1 + bne 1b + `, + map[string]interface{}{ + "cyc": &delayCyc, + }) + } + } + + // Set aux mux first, and then glitchless mux if this clock has one. + clk.ctrl.ReplaceBits(auxsrc<<rp.CLOCKS_CLK_SYS_CTRL_AUXSRC_Pos, + rp.CLOCKS_CLK_SYS_CTRL_AUXSRC_Msk, 0) + + if clk.hasGlitchlessMux() { + clk.ctrl.ReplaceBits(src<<rp.CLOCKS_CLK_REF_CTRL_SRC_Pos, + rp.CLOCKS_CLK_REF_CTRL_SRC_Msk, 0) + for !clk.selected.HasBits(1 << src) { + } + } + + // Enable clock. On clkRef and clkSys this does nothing, + // all other clocks have the ENABLE bit in the same position. + clk.ctrl.SetBits(rp.CLOCKS_CLK_GPOUT0_CTRL_ENABLE) + + // Now that the source is configured, we can trust that the user-supplied + // divisor is a safe value. + clk.div.Set(div) + + // Store the configured frequency + configuredFreq[clk.cix] = freq + +} + +// init initializes the clock hardware. +// +// Must be called before any other clock function. +func (clks *clocksType) init() { + // Start the watchdog tick + watchdog.startTick(xoscFreq) + + // Disable resus that may be enabled from previous software + clks.resus.ctrl.Set(0) + + // Enable the xosc + xosc.init() + + // Before we touch PLLs, switch sys and ref cleanly away from their aux sources. + clks.clk[clkSys].ctrl.ClearBits(rp.CLOCKS_CLK_SYS_CTRL_SRC_Msk) + for !clks.clk[clkSys].selected.HasBits(0x1) { + } + + clks.clk[clkRef].ctrl.ClearBits(rp.CLOCKS_CLK_REF_CTRL_SRC_Msk) + for !clks.clk[clkRef].selected.HasBits(0x1) { + } + + // Configure PLLs + // REF FBDIV VCO POSTDIV + // pllSys: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz + // pllUSB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz + pllSys.init(1, 1500*MHz, 6, 2) + pllUSB.init(1, 480*MHz, 5, 2) + + // Configure clocks + // clkRef = xosc (12MHz) / 1 = 12MHz + clkref := clks.clock(clkRef) + clkref.configure(rp.CLOCKS_CLK_REF_CTRL_SRC_XOSC_CLKSRC, + 0, // No aux mux + 12*MHz, + 12*MHz) + + // clkSys = pllSys (125MHz) / 1 = 125MHz + clksys := clks.clock(clkSys) + clksys.configure(rp.CLOCKS_CLK_SYS_CTRL_SRC_CLKSRC_CLK_SYS_AUX, + rp.CLOCKS_CLK_SYS_CTRL_AUXSRC_CLKSRC_PLL_SYS, + 125*MHz, + 125*MHz) + + // clkUSB = pllUSB (48MHz) / 1 = 48MHz + clkusb := clks.clock(clkUSB) + clkusb.configure(0, // No GLMUX + rp.CLOCKS_CLK_USB_CTRL_AUXSRC_CLKSRC_PLL_USB, + 48*MHz, + 48*MHz) + + // clkADC = pllUSB (48MHZ) / 1 = 48MHz + clkadc := clks.clock(clkADC) + clkadc.configure(0, // No GLMUX + rp.CLOCKS_CLK_ADC_CTRL_AUXSRC_CLKSRC_PLL_USB, + 48*MHz, + 48*MHz) + + // clkRTC = pllUSB (48MHz) / 1024 = 46875Hz + clkrtc := clks.clock(clkRTC) + clkrtc.configure(0, // No GLMUX + rp.CLOCKS_CLK_RTC_CTRL_AUXSRC_CLKSRC_PLL_USB, + 48*MHz, + 46875) + + // clkPeri = clkSys. Used as reference clock for Peripherals. + // No dividers so just select and enable. + // Normally choose clkSys or clkUSB. + clkperi := clks.clock(clkPeri) + clkperi.configure(0, + rp.CLOCKS_CLK_PERI_CTRL_AUXSRC_CLK_SYS, + 125*MHz, + 125*MHz) +} diff --git a/src/machine/machine_rp2040_gpio.go b/src/machine/machine_rp2040_gpio.go new file mode 100644 index 000000000..1d35254c9 --- /dev/null +++ b/src/machine/machine_rp2040_gpio.go @@ -0,0 +1,131 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + "runtime/volatile" + "unsafe" +) + +type io struct { + status volatile.Register32 + ctrl volatile.Register32 +} + +type irqCtrl struct { + intE [4]volatile.Register32 + intS [4]volatile.Register32 + intF [4]volatile.Register32 +} + +type ioBank0Type struct { + io [30]io + intR [4]volatile.Register32 + proc0IRQctrl irqCtrl + proc1IRQctrl irqCtrl + dormantWakeIRQctrl irqCtrl +} + +var ioBank0 = (*ioBank0Type)(unsafe.Pointer(rp.IO_BANK0)) + +type padsBank0Type struct { + voltageSelect volatile.Register32 + io [30]volatile.Register32 +} + +var padsBank0 = (*padsBank0Type)(unsafe.Pointer(rp.PADS_BANK0)) + +// pinFunc represents a GPIO function. +// +// Each GPIO can have one function selected at a time. +// Likewise, each peripheral input (e.g. UART0 RX) should only be selected +// on one GPIO at a time. If the same peripheral input is connected to multiple GPIOs, +// the peripheral sees the logical OR of these GPIO inputs. +type pinFunc uint8 + +// GPIO function selectors +const ( + fnJTAG pinFunc = 0 + fnSPI pinFunc = 1 + fnUART pinFunc = 2 + fnI2C pinFunc = 3 + fnPWM pinFunc = 4 + fnSIO pinFunc = 5 + fnPIO0 pinFunc = 6 + fnPIO1 pinFunc = 7 + fnGPCK pinFunc = 8 + fnUSB pinFunc = 9 + fnNULL pinFunc = 0x1f + + fnXIP pinFunc = 0 +) + +const ( + PinOutput PinMode = iota +) + +// set drives the pin high +func (p Pin) set() { + mask := uint32(1) << p + rp.SIO.GPIO_OUT_SET.Set(mask) +} + +// clr drives the pin low +func (p Pin) clr() { + mask := uint32(1) << p + rp.SIO.GPIO_OUT_CLR.Set(mask) +} + +// xor toggles the pin +func (p Pin) xor() { + mask := uint32(1) << p + rp.SIO.GPIO_OUT_XOR.Set(mask) +} + +func (p Pin) ioCtrl() *volatile.Register32 { + return &ioBank0.io[p].ctrl +} + +func (p Pin) padCtrl() *volatile.Register32 { + return &padsBank0.io[p] +} + +// setFunc will set pin function to fn. +func (p Pin) setFunc(fn pinFunc) { + // Set input enable, Clear output disable + p.padCtrl().ReplaceBits(rp.PADS_BANK0_GPIO0_IE, + rp.PADS_BANK0_GPIO0_IE_Msk|rp.PADS_BANK0_GPIO0_OD_Msk, 0) + + // Zero all fields apart from fsel; we want this IO to do what the peripheral tells it. + // This doesn't affect e.g. pullup/pulldown, as these are in pad controls. + p.ioCtrl().Set(uint32(fn) << rp.IO_BANK0_GPIO0_CTRL_FUNCSEL_Pos) +} + +// init initializes the gpio pin +func (p Pin) init() { + mask := uint32(1) << p + rp.SIO.GPIO_OE_CLR.Set(mask) + p.clr() + p.setFunc(fnSIO) + +} + +// Configure configures the gpio pin as per mode. +func (p Pin) Configure(config PinConfig) { + p.init() + mask := uint32(1) << p + switch config.Mode { + case PinOutput: + rp.SIO.GPIO_OE_SET.Set(mask) + } +} + +// Set drives the pin high if value is true else drives it low. +func (p Pin) Set(value bool) { + if value { + p.set() + } else { + p.clr() + } +} diff --git a/src/machine/machine_rp2040_pll.go b/src/machine/machine_rp2040_pll.go new file mode 100644 index 000000000..275aa25f1 --- /dev/null +++ b/src/machine/machine_rp2040_pll.go @@ -0,0 +1,100 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + "runtime/volatile" + "unsafe" +) + +type pll struct { + cs volatile.Register32 + pwr volatile.Register32 + fbDivInt volatile.Register32 + prim volatile.Register32 +} + +var ( + pllSys = (*pll)(unsafe.Pointer(rp.PLL_SYS)) + pllUSB = (*pll)(unsafe.Pointer(rp.PLL_USB)) +) + +// init initializes pll (Sys or USB) given the following parameters. +// +// Input clock divider, refdiv. +// +// Requested output frequency from the VCO (voltage controlled oscillator), vcoFreq. +// +// Post Divider 1, postDiv1 with range 1-7 and be >= postDiv2. +// +// Post Divider 2, postDiv2 with range 1-7. +func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) { + refFreq := xoscFreq / refdiv + + // What are we multiplying the reference clock by to get the vco freq + // (The regs are called div, because you divide the vco output and compare it to the refclk) + fbdiv := vcoFreq / (refFreq * MHz) + + // Check fbdiv range + if !(fbdiv >= 16 && fbdiv <= 320) { + panic("fbdiv should be in the range [16,320]") + } + + // Check divider ranges + if !((postDiv1 >= 1 && postDiv1 <= 7) && (postDiv2 >= 1 && postDiv2 <= 7)) { + panic("postdiv1, postdiv1 should be in the range [1,7]") + } + + // postDiv1 should be >= postDiv2 + // from appnote page 11 + // postdiv1 is designed to operate with a higher input frequency + // than postdiv2 + if postDiv1 < postDiv2 { + panic("postdiv1 should be greater than or equal to postdiv2") + } + + // Check that reference frequency is no greater than vco / 16 + if refFreq > vcoFreq/16 { + panic("reference frequency should not be greater than vco frequency divided by 16") + } + + // div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10 + pdiv := postDiv1<<rp.PLL_SYS_PRIM_POSTDIV1_Pos | postDiv2<<rp.PLL_SYS_PRIM_POSTDIV2_Pos + + if pll.cs.HasBits(rp.PLL_SYS_CS_LOCK) && + refdiv == pll.cs.Get()&rp.PLL_SYS_CS_REFDIV_Msk && + fbdiv == pll.fbDivInt.Get()&rp.PLL_SYS_FBDIV_INT_FBDIV_INT_Msk && + pdiv == pll.prim.Get()&(rp.PLL_SYS_PRIM_POSTDIV1_Msk&rp.PLL_SYS_PRIM_POSTDIV2_Msk) { + // do not disrupt PLL that is already correctly configured and operating + return + } + + var pllRst uint32 + if pll == pllSys { + pllRst = rp.RESETS_RESET_PLL_SYS + } else { + pllRst = rp.RESETS_RESET_PLL_USB + } + resetBlock(pllRst) + unresetBlockWait(pllRst) + + // Load VCO-related dividers before starting VCO + pll.cs.Set(refdiv) + pll.fbDivInt.Set(fbdiv) + + // Turn on PLL + pwr := uint32(rp.PLL_SYS_PWR_PD | rp.PLL_SYS_PWR_VCOPD) + pll.pwr.ClearBits(pwr) + + // Wait for PLL to lock + for !(pll.cs.HasBits(rp.PLL_SYS_CS_LOCK)) { + } + + // Set up post dividers + pll.prim.Set(pdiv) + + // Turn on post divider + pll.pwr.ClearBits(rp.PLL_SYS_PWR_POSTDIVPD) + +} diff --git a/src/machine/machine_rp2040_resets.go b/src/machine/machine_rp2040_resets.go new file mode 100644 index 000000000..39c834505 --- /dev/null +++ b/src/machine/machine_rp2040_resets.go @@ -0,0 +1,43 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + "runtime/volatile" + "unsafe" +) + +// RESETS_RESET_Msk is bitmask to reset all peripherals +// +// TODO: This field is not available in the device file. +const RESETS_RESET_Msk = 0x01ffffff + +type resetsType struct { + reset volatile.Register32 + wdSel volatile.Register32 + resetDone volatile.Register32 +} + +var resets = (*resetsType)(unsafe.Pointer(rp.RESETS)) + +// resetBlock resets hardware blocks specified +// by the bit pattern in bits. +func resetBlock(bits uint32) { + resets.reset.SetBits(bits) +} + +// unresetBlock brings hardware blocks specified by the +// bit pattern in bits out of reset. +func unresetBlock(bits uint32) { + resets.reset.ClearBits(bits) +} + +// unresetBlockWait brings specified hardware blocks +// specified by the bit pattern in bits +// out of reset and wait for completion. +func unresetBlockWait(bits uint32) { + unresetBlock(bits) + for !resets.resetDone.HasBits(bits) { + } +} diff --git a/src/machine/machine_rp2040_timer.go b/src/machine/machine_rp2040_timer.go new file mode 100644 index 000000000..e714a1915 --- /dev/null +++ b/src/machine/machine_rp2040_timer.go @@ -0,0 +1,51 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + "runtime/volatile" + "unsafe" +) + +const numTimers = 4 + +type timerType struct { + timeHW volatile.Register32 + timeLW volatile.Register32 + timeHR volatile.Register32 + timeLR volatile.Register32 + alarm [numTimers]volatile.Register32 + armed volatile.Register32 + timeRawH volatile.Register32 + timeRawL volatile.Register32 + dbgPause volatile.Register32 + pause volatile.Register32 + intR volatile.Register32 + intE volatile.Register32 + intF volatile.Register32 + intS volatile.Register32 +} + +var timer = (*timerType)(unsafe.Pointer(rp.TIMER)) + +// TimeElapsed returns time elapsed since power up, in microseconds. +func (tmr *timerType) timeElapsed() (us uint64) { + // Need to make sure that the upper 32 bits of the timer + // don't change, so read that first + hi := tmr.timeRawH.Get() + var lo, nextHi uint32 + for { + // Read the lower 32 bits + lo = tmr.timeRawL.Get() + // Now read the upper 32 bits again and + // check that it hasn't incremented. If it has, loop around + // and read the lower 32 bits again to get an accurate value + nextHi = tmr.timeRawH.Get() + if hi == nextHi { + break + } + hi = nextHi + } + return uint64(hi)<<32 | uint64(lo) +} diff --git a/src/machine/machine_rp2040_watchdog.go b/src/machine/machine_rp2040_watchdog.go new file mode 100644 index 000000000..b55bbc671 --- /dev/null +++ b/src/machine/machine_rp2040_watchdog.go @@ -0,0 +1,27 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + "runtime/volatile" + "unsafe" +) + +type watchdogType struct { + ctrl volatile.Register32 + load volatile.Register32 + reason volatile.Register32 + scratch [8]volatile.Register32 + tick volatile.Register32 +} + +var watchdog = (*watchdogType)(unsafe.Pointer(rp.WATCHDOG)) + +// startTick starts the watchdog tick. +// cycles needs to be a divider that when applied to the xosc input, +// produces a 1MHz clock. So if the xosc frequency is 12MHz, +// this will need to be 12. +func (wd *watchdogType) startTick(cycles uint32) { + wd.tick.Set(cycles | rp.WATCHDOG_TICK_ENABLE) +} diff --git a/src/machine/machine_rp2040_xosc.go b/src/machine/machine_rp2040_xosc.go new file mode 100644 index 000000000..357853948 --- /dev/null +++ b/src/machine/machine_rp2040_xosc.go @@ -0,0 +1,42 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + "runtime/volatile" + "unsafe" +) + +type xoscType struct { + ctrl volatile.Register32 + status volatile.Register32 + dormant volatile.Register32 + startup volatile.Register32 + reserved [3]volatile.Register32 + count volatile.Register32 +} + +var xosc = (*xoscType)(unsafe.Pointer(rp.XOSC)) + +// init initializes the crystal oscillator system. +// +// This function will block until the crystal oscillator has stabilised. +func (osc *xoscType) init() { + // Assumes 1-15 MHz input + if xoscFreq > 15 { + panic("xosc frequency cannot be greater than 15MHz") + } + osc.ctrl.Set(rp.XOSC_CTRL_FREQ_RANGE_1_15MHZ) + + // Set xosc startup delay + delay := (((xoscFreq * MHz) / 1000) + 128) / 256 + osc.startup.Set(uint32(delay)) + + // Set the enable bit now that we have set freq range and startup delay + osc.ctrl.SetBits(rp.XOSC_CTRL_ENABLE_ENABLE << rp.XOSC_CTRL_ENABLE_Pos) + + // Wait for xosc to be stable + for !osc.status.HasBits(rp.XOSC_STATUS_STABLE) { + } +} diff --git a/src/runtime/runtime_rp2040.go b/src/runtime/runtime_rp2040.go new file mode 100644 index 000000000..162f18f1e --- /dev/null +++ b/src/runtime/runtime_rp2040.go @@ -0,0 +1,58 @@ +// +build rp2040 + +package runtime + +import ( + "device/arm" +) + +// machineTicks is provided by package machine. +func machineTicks() uint64 + +type timeUnit uint64 + +// ticks returns the number of ticks (microseconds) elapsed since power up. +func ticks() timeUnit { + t := machineTicks() + return timeUnit(t) +} + +func ticksToNanoseconds(ticks timeUnit) int64 { + return int64(ticks) * 1000 +} + +func nanosecondsToTicks(ns int64) timeUnit { + return timeUnit(ns / 1000) +} + +func sleepTicks(d timeUnit) { + if d == 0 { + return + } + sleepUntil := ticks() + d + for ticks() < sleepUntil { + } +} + +func waitForEvents() { + arm.Asm("wfe") +} + +func putchar(c byte) { +} + +// machineInit is provided by package machine. +func machineInit() + +func init() { + machineInit() +} + +func postinit() {} + +//export Reset_Handler +func main() { + preinit() + run() + abort() +} diff --git a/targets/pico.json b/targets/pico.json new file mode 100644 index 000000000..e8e5a06b1 --- /dev/null +++ b/targets/pico.json @@ -0,0 +1,10 @@ +{ + "inherits": [ + "rp2040" + ], + "build-tags": ["pico"], + "linkerscript": "targets/pico.ld", + "extra-files": [ + "targets/pico_boot_stage2.S" + ] +} diff --git a/targets/pico.ld b/targets/pico.ld new file mode 100644 index 000000000..267fbc4c9 --- /dev/null +++ b/targets/pico.ld @@ -0,0 +1,31 @@ + +MEMORY +{ + FLASH_TEXT (rx) : ORIGIN = 0x10000000, LENGTH = 2048k +} + +SECTIONS +{ + /* Second stage bootloader is prepended to the image. It must be 256 bytes big + and checksummed. It is usually built by the boot_stage2 target + in the Raspberry Pi Pico SDK + */ + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH_TEXT + + ASSERT(__boot2_end__ - __boot2_start__ == 256, + "ERROR: Pico second stage bootloader must be 256 bytes in size") + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ +} + +INCLUDE "targets/rp2040.ld" diff --git a/targets/pico_boot_stage2.S b/targets/pico_boot_stage2.S new file mode 100644 index 000000000..4902d3bbb --- /dev/null +++ b/targets/pico_boot_stage2.S @@ -0,0 +1,23 @@ +// Padded and checksummed version of: /home/rkanchan/src/pico-sdk/build/src/rp2_common/boot_stage2/bs2_default.bin + +.cpu cortex-m0plus +.thumb + +.section .boot2, "ax" + +.byte 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60 +.byte 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61 +.byte 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20 +.byte 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0 +.byte 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0 +.byte 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21 +.byte 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60 +.byte 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21 +.byte 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60 +.byte 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49 +.byte 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20 +.byte 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66 +.byte 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40 +.byte 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00 +.byte 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0 +.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a diff --git a/targets/rp2040.json b/targets/rp2040.json new file mode 100644 index 000000000..ce49ba356 --- /dev/null +++ b/targets/rp2040.json @@ -0,0 +1,12 @@ +{ + "inherits": ["cortex-m0plus"], + "build-tags": ["rp2040", "rp"], + "flash-method": "msd", + "msd-volume-name": "RPI-RP2", + "msd-firmware-name": "firmware.uf2", + "binary-format": "uf2", + "uf2-family-id": "0xe48bff56", + "extra-files": [ + "src/device/rp/rp2040.s" + ] +} diff --git a/targets/rp2040.ld b/targets/rp2040.ld new file mode 100644 index 000000000..186db68f6 --- /dev/null +++ b/targets/rp2040.ld @@ -0,0 +1,9 @@ + +MEMORY +{ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k +} + +_stack_size = 2K; + +INCLUDE "targets/arm.ld" |