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  }