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 }