github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_clocks.go (about) 1 //go:build rp2040 2 3 package machine 4 5 import ( 6 "device/arm" 7 "device/rp" 8 "runtime/volatile" 9 "unsafe" 10 ) 11 12 func CPUFrequency() uint32 { 13 return 125 * MHz 14 } 15 16 // Returns the period of a clock cycle for the raspberry pi pico in nanoseconds. 17 // Used in PWM API. 18 func cpuPeriod() uint32 { 19 return 1e9 / CPUFrequency() 20 } 21 22 // clockIndex identifies a hardware clock 23 type clockIndex uint8 24 25 const ( 26 clkGPOUT0 clockIndex = iota // GPIO Muxing 0 27 clkGPOUT1 // GPIO Muxing 1 28 clkGPOUT2 // GPIO Muxing 2 29 clkGPOUT3 // GPIO Muxing 3 30 clkRef // Watchdog and timers reference clock 31 clkSys // Processors, bus fabric, memory, memory mapped registers 32 clkPeri // Peripheral clock for UART and SPI 33 clkUSB // USB clock 34 clkADC // ADC clock 35 clkRTC // Real time clock 36 numClocks 37 ) 38 39 type clockType struct { 40 ctrl volatile.Register32 41 div volatile.Register32 42 selected volatile.Register32 43 } 44 45 type fc struct { 46 refKHz volatile.Register32 47 minKHz volatile.Register32 48 maxKHz volatile.Register32 49 delay volatile.Register32 50 interval volatile.Register32 51 src volatile.Register32 52 status volatile.Register32 53 result volatile.Register32 54 } 55 56 type clocksType struct { 57 clk [numClocks]clockType 58 resus struct { 59 ctrl volatile.Register32 60 status volatile.Register32 61 } 62 fc0 fc 63 wakeEN0 volatile.Register32 64 wakeEN1 volatile.Register32 65 sleepEN0 volatile.Register32 66 sleepEN1 volatile.Register32 67 enabled0 volatile.Register32 68 enabled1 volatile.Register32 69 intR volatile.Register32 70 intE volatile.Register32 71 intF volatile.Register32 72 intS volatile.Register32 73 } 74 75 var clocks = (*clocksType)(unsafe.Pointer(rp.CLOCKS)) 76 77 var configuredFreq [numClocks]uint32 78 79 type clock struct { 80 *clockType 81 cix clockIndex 82 } 83 84 // clock returns the clock identified by cix. 85 func (clks *clocksType) clock(cix clockIndex) clock { 86 return clock{ 87 &clks.clk[cix], 88 cix, 89 } 90 } 91 92 // hasGlitchlessMux returns true if clock contains a glitchless multiplexer. 93 // 94 // Clock muxing consists of two components: 95 // 96 // A glitchless mux, which can be switched freely, but whose inputs must be 97 // free-running. 98 // 99 // An auxiliary (glitchy) mux, whose output glitches when switched, but has 100 // no constraints on its inputs. 101 // 102 // Not all clocks have both types of mux. 103 func (clk *clock) hasGlitchlessMux() bool { 104 return clk.cix == clkSys || clk.cix == clkRef 105 } 106 107 // configure configures the clock by selecting the main clock source src 108 // and the auxiliary clock source auxsrc 109 // and finally setting the clock frequency to freq 110 // given the input clock source frequency srcFreq. 111 func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) { 112 if freq > srcFreq { 113 panic("clock frequency cannot be greater than source frequency") 114 } 115 116 // Div register is 24.8 int.frac divider so multiply by 2^8 (left shift by 8) 117 div := uint32((uint64(srcFreq) << 8) / uint64(freq)) 118 119 // If increasing divisor, set divisor before source. Otherwise set source 120 // before divisor. This avoids a momentary overspeed when e.g. switching 121 // to a faster source and increasing divisor to compensate. 122 if div > clk.div.Get() { 123 clk.div.Set(div) 124 } 125 126 // If switching a glitchless slice (ref or sys) to an aux source, switch 127 // away from aux *first* to avoid passing glitches when changing aux mux. 128 // Assume (!!!) glitchless source 0 is no faster than the aux source. 129 if clk.hasGlitchlessMux() && src == rp.CLOCKS_CLK_SYS_CTRL_SRC_CLKSRC_CLK_SYS_AUX { 130 clk.ctrl.ClearBits(rp.CLOCKS_CLK_REF_CTRL_SRC_Msk) 131 for !clk.selected.HasBits(1) { 132 } 133 } else 134 // If no glitchless mux, cleanly stop the clock to avoid glitches 135 // propagating when changing aux mux. Note it would be a really bad idea 136 // to do this on one of the glitchless clocks (clkSys, clkRef). 137 { 138 // Disable clock. On clkRef and clkSys this does nothing, 139 // all other clocks have the ENABLE bit in the same position. 140 clk.ctrl.ClearBits(rp.CLOCKS_CLK_GPOUT0_CTRL_ENABLE_Msk) 141 if configuredFreq[clk.cix] > 0 { 142 // Delay for 3 cycles of the target clock, for ENABLE propagation. 143 // Note XOSC_COUNT is not helpful here because XOSC is not 144 // necessarily running, nor is timer... so, 3 cycles per loop: 145 delayCyc := configuredFreq[clkSys]/configuredFreq[clk.cix] + 1 146 for delayCyc != 0 { 147 // This could be done more efficiently but TinyGo inline 148 // assembly is not yet capable enough to express that. In the 149 // meantime, this forces at least 3 cycles per loop. 150 delayCyc-- 151 arm.Asm("nop\nnop\nnop") 152 } 153 } 154 } 155 156 // Set aux mux first, and then glitchless mux if this clock has one. 157 clk.ctrl.ReplaceBits(auxsrc<<rp.CLOCKS_CLK_SYS_CTRL_AUXSRC_Pos, 158 rp.CLOCKS_CLK_SYS_CTRL_AUXSRC_Msk, 0) 159 160 if clk.hasGlitchlessMux() { 161 clk.ctrl.ReplaceBits(src<<rp.CLOCKS_CLK_REF_CTRL_SRC_Pos, 162 rp.CLOCKS_CLK_REF_CTRL_SRC_Msk, 0) 163 for !clk.selected.HasBits(1 << src) { 164 } 165 } 166 167 // Enable clock. On clkRef and clkSys this does nothing, 168 // all other clocks have the ENABLE bit in the same position. 169 clk.ctrl.SetBits(rp.CLOCKS_CLK_GPOUT0_CTRL_ENABLE) 170 171 // Now that the source is configured, we can trust that the user-supplied 172 // divisor is a safe value. 173 clk.div.Set(div) 174 175 // Store the configured frequency 176 configuredFreq[clk.cix] = freq 177 178 } 179 180 // init initializes the clock hardware. 181 // 182 // Must be called before any other clock function. 183 func (clks *clocksType) init() { 184 // Start the watchdog tick 185 Watchdog.startTick(xoscFreq) 186 187 // Disable resus that may be enabled from previous software 188 clks.resus.ctrl.Set(0) 189 190 // Enable the xosc 191 xosc.init() 192 193 // Before we touch PLLs, switch sys and ref cleanly away from their aux sources. 194 clks.clk[clkSys].ctrl.ClearBits(rp.CLOCKS_CLK_SYS_CTRL_SRC_Msk) 195 for !clks.clk[clkSys].selected.HasBits(0x1) { 196 } 197 198 clks.clk[clkRef].ctrl.ClearBits(rp.CLOCKS_CLK_REF_CTRL_SRC_Msk) 199 for !clks.clk[clkRef].selected.HasBits(0x1) { 200 } 201 202 // Configure PLLs 203 // REF FBDIV VCO POSTDIV 204 // pllSys: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz 205 // pllUSB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz 206 pllSys.init(1, 1500*MHz, 6, 2) 207 pllUSB.init(1, 480*MHz, 5, 2) 208 209 // Configure clocks 210 // clkRef = xosc (12MHz) / 1 = 12MHz 211 clkref := clks.clock(clkRef) 212 clkref.configure(rp.CLOCKS_CLK_REF_CTRL_SRC_XOSC_CLKSRC, 213 0, // No aux mux 214 12*MHz, 215 12*MHz) 216 217 // clkSys = pllSys (125MHz) / 1 = 125MHz 218 clksys := clks.clock(clkSys) 219 clksys.configure(rp.CLOCKS_CLK_SYS_CTRL_SRC_CLKSRC_CLK_SYS_AUX, 220 rp.CLOCKS_CLK_SYS_CTRL_AUXSRC_CLKSRC_PLL_SYS, 221 125*MHz, 222 125*MHz) 223 224 // clkUSB = pllUSB (48MHz) / 1 = 48MHz 225 clkusb := clks.clock(clkUSB) 226 clkusb.configure(0, // No GLMUX 227 rp.CLOCKS_CLK_USB_CTRL_AUXSRC_CLKSRC_PLL_USB, 228 48*MHz, 229 48*MHz) 230 231 // clkADC = pllUSB (48MHZ) / 1 = 48MHz 232 clkadc := clks.clock(clkADC) 233 clkadc.configure(0, // No GLMUX 234 rp.CLOCKS_CLK_ADC_CTRL_AUXSRC_CLKSRC_PLL_USB, 235 48*MHz, 236 48*MHz) 237 238 // clkRTC = pllUSB (48MHz) / 1024 = 46875Hz 239 clkrtc := clks.clock(clkRTC) 240 clkrtc.configure(0, // No GLMUX 241 rp.CLOCKS_CLK_RTC_CTRL_AUXSRC_CLKSRC_PLL_USB, 242 48*MHz, 243 46875) 244 245 // clkPeri = clkSys. Used as reference clock for Peripherals. 246 // No dividers so just select and enable. 247 // Normally choose clkSys or clkUSB. 248 clkperi := clks.clock(clkPeri) 249 clkperi.configure(0, 250 rp.CLOCKS_CLK_PERI_CTRL_AUXSRC_CLK_SYS, 251 125*MHz, 252 125*MHz) 253 }