aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile9
-rw-r--r--src/machine/board_pico.go43
-rw-r--r--src/machine/machine_rp2040.go44
-rw-r--r--src/machine/machine_rp2040_clocks.go251
-rw-r--r--src/machine/machine_rp2040_gpio.go131
-rw-r--r--src/machine/machine_rp2040_pll.go100
-rw-r--r--src/machine/machine_rp2040_resets.go43
-rw-r--r--src/machine/machine_rp2040_timer.go51
-rw-r--r--src/machine/machine_rp2040_watchdog.go27
-rw-r--r--src/machine/machine_rp2040_xosc.go42
-rw-r--r--src/runtime/runtime_rp2040.go58
-rw-r--r--targets/pico.json10
-rw-r--r--targets/pico.ld31
-rw-r--r--targets/pico_boot_stage2.S23
-rw-r--r--targets/rp2040.json12
-rw-r--r--targets/rp2040.ld9
16 files changed, 882 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index af61eb40c..da699cc3c 100644
--- a/Makefile
+++ b/Makefile
@@ -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"