github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_pll.go (about)

     1  //go:build rp2040
     2  
     3  package machine
     4  
     5  import (
     6  	"device/rp"
     7  	"runtime/volatile"
     8  	"unsafe"
     9  )
    10  
    11  type pll struct {
    12  	cs       volatile.Register32
    13  	pwr      volatile.Register32
    14  	fbDivInt volatile.Register32
    15  	prim     volatile.Register32
    16  }
    17  
    18  var (
    19  	pllSys = (*pll)(unsafe.Pointer(rp.PLL_SYS))
    20  	pllUSB = (*pll)(unsafe.Pointer(rp.PLL_USB))
    21  )
    22  
    23  // init initializes pll (Sys or USB) given the following parameters.
    24  //
    25  // Input clock divider, refdiv.
    26  //
    27  // Requested output frequency from the VCO (voltage controlled oscillator), vcoFreq.
    28  //
    29  // Post Divider 1, postDiv1 with range 1-7 and be >= postDiv2.
    30  //
    31  // Post Divider 2, postDiv2 with range 1-7.
    32  func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
    33  	refFreq := xoscFreq / refdiv
    34  
    35  	// What are we multiplying the reference clock by to get the vco freq
    36  	// (The regs are called div, because you divide the vco output and compare it to the refclk)
    37  	fbdiv := vcoFreq / (refFreq * MHz)
    38  
    39  	// Check fbdiv range
    40  	if !(fbdiv >= 16 && fbdiv <= 320) {
    41  		panic("fbdiv should be in the range [16,320]")
    42  	}
    43  
    44  	// Check divider ranges
    45  	if !((postDiv1 >= 1 && postDiv1 <= 7) && (postDiv2 >= 1 && postDiv2 <= 7)) {
    46  		panic("postdiv1, postdiv1 should be in the range [1,7]")
    47  	}
    48  
    49  	// postDiv1 should be >= postDiv2
    50  	// from appnote page 11
    51  	// postdiv1 is designed to operate with a higher input frequency
    52  	// than postdiv2
    53  	if postDiv1 < postDiv2 {
    54  		panic("postdiv1 should be greater than or equal to postdiv2")
    55  	}
    56  
    57  	// Check that reference frequency is no greater than vco / 16
    58  	if refFreq > vcoFreq/16 {
    59  		panic("reference frequency should not be greater than vco frequency divided by 16")
    60  	}
    61  
    62  	// div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
    63  	pdiv := postDiv1<<rp.PLL_SYS_PRIM_POSTDIV1_Pos | postDiv2<<rp.PLL_SYS_PRIM_POSTDIV2_Pos
    64  
    65  	if pll.cs.HasBits(rp.PLL_SYS_CS_LOCK) &&
    66  		refdiv == pll.cs.Get()&rp.PLL_SYS_CS_REFDIV_Msk &&
    67  		fbdiv == pll.fbDivInt.Get()&rp.PLL_SYS_FBDIV_INT_FBDIV_INT_Msk &&
    68  		pdiv == pll.prim.Get()&(rp.PLL_SYS_PRIM_POSTDIV1_Msk&rp.PLL_SYS_PRIM_POSTDIV2_Msk) {
    69  		// do not disrupt PLL that is already correctly configured and operating
    70  		return
    71  	}
    72  
    73  	var pllRst uint32
    74  	if pll == pllSys {
    75  		pllRst = rp.RESETS_RESET_PLL_SYS
    76  	} else {
    77  		pllRst = rp.RESETS_RESET_PLL_USB
    78  	}
    79  	resetBlock(pllRst)
    80  	unresetBlockWait(pllRst)
    81  
    82  	// Load VCO-related dividers before starting VCO
    83  	pll.cs.Set(refdiv)
    84  	pll.fbDivInt.Set(fbdiv)
    85  
    86  	// Turn on PLL
    87  	pwr := uint32(rp.PLL_SYS_PWR_PD | rp.PLL_SYS_PWR_VCOPD)
    88  	pll.pwr.ClearBits(pwr)
    89  
    90  	// Wait for PLL to lock
    91  	for !(pll.cs.HasBits(rp.PLL_SYS_CS_LOCK)) {
    92  	}
    93  
    94  	// Set up post dividers
    95  	pll.prim.Set(pdiv)
    96  
    97  	// Turn on post divider
    98  	pll.pwr.ClearBits(rp.PLL_SYS_PWR_POSTDIVPD)
    99  
   100  }