f_ changed the topic of ##raspberrypi-internals to: The inner workings of the Raspberry Pi (Low level VPU/HW) -- for general queries please visit #raspberrypi -- open firmware: https://librerpi.github.io/ -- VC4 VPU Programmers Manual: https://github.com/hermanhermitage/videocoreiv/wiki -- chat logs: https://libera.irclog.whitequark.org/~h~raspberrypi-internals -- bridged to matrix and discord
jcea has quit [Ping timeout: 246 seconds]
ungeskriptet has quit [Quit: Contact links: https://david-w.eu]
Stromeko has quit [Quit: Going… gone.]
Stromeko has joined ##raspberrypi-internals
ungeskriptet has joined ##raspberrypi-internals
jcea has joined ##raspberrypi-internals
tortoise has quit [Ping timeout: 255 seconds]
mithro has quit [Ping timeout: 255 seconds]
strages has quit [Ping timeout: 272 seconds]
mithro has joined ##raspberrypi-internals
tortoise has joined ##raspberrypi-internals
strages has joined ##raspberrypi-internals
angerisagift has quit [Read error: Connection reset by peer]
dolphinana has joined ##raspberrypi-internals
dolphinana_ has joined ##raspberrypi-internals
dolphinana has quit [Ping timeout: 268 seconds]
angerisagift has joined ##raspberrypi-internals
dolphinana_ has quit [Ping timeout: 252 seconds]
Stromeko has quit [Quit: Going… gone.]
Stromeko has joined ##raspberrypi-internals
<f_ridge> <x​2x6_/D> Hi, all
<f_ridge> <x​2x6_/D> So, today is PLL day.
<f_ridge> <x​2x6_/D> I am going to parse this doc
<f_ridge> <x​2x6_/D> In the past I already tried to approach this picture, but did not manage it
<f_ridge> <x​2x6_/D> Each PLL is a fractional N frequency synthesizer that can generate N/M times the crystal oscillator frequency (XOSC). The integer part of N is controlled by the NDIV field of the A2W_PLLx_CTRL register; the fractional part is stored in A2W_PLLx_FRAC.
<f_ridge> <x​2x6_/D> But actually looks pretty basic algebra
<f_ridge> <x​2x6_/D> So, in raspberry we have 19.2MHz clock and a bunch of PLLs, and what they do looks like they just output frequency which is 19.2Mhz * N/D and those N and D are values in registers. ?
<f_ridge> <x​2x6_/D> 6 static void bcm2835_pll_report_one(char pll_symbol, ioreg32_t ctrl_reg,
<f_ridge> <x​2x6_/D> 7 ioreg32_t frac_reg)
<f_ridge> <x​2x6_/D> 8 {
<f_ridge> <x​2x6_/D> 9 uint32_t pll_ctrl, ndiv, pdiv, ndiv_frac;
<f_ridge> <x​2x6_/D> 10 pll_ctrl = ioreg32_read(ctrl_reg);
<f_ridge> <x​2x6_/D> 11 ndiv = pll_ctrl & 0x3ff;
<f_ridge> <x​2x6_/D> 12 pdiv = (pll_ctrl >> 12) & 7;
<f_ridge> <x​2x6_/D> 13 ndiv_frac = ioreg32_read(frac_reg) & 0xfffff;
<f_ridge> <x​2x6_/D> 14 printf("PLL%c: N=%d.%d, PDIV=%d\n", pll_symbol, ndiv, ndiv_frac, pdiv);
<f_ridge> <x​2x6_/D> 15 }
<f_ridge> <x​2x6_/D> I have this output
<f_ridge> <x​2x6_/D> PLLA: N=52.87382, PDIV=1
<f_ridge> <x​2x6_/D> So on raspbery pi that would mean 19.2Mhz * 52.87382 / 1 = 1015.104Mhz ?
<f_ridge> <x​2x6_/D> 17 void bcm2835_pll_report(void)
<f_ridge> <x​2x6_/D> 18 {
<f_ridge> <x​2x6_/D> 19 bcm2835_pll_report_one('A', A2W_PLLA_CTRL, A2W_PLLA_FRAC);
<f_ridge> <x​2x6_/D> 20 }
<f_ridge> <c​lever___/D> yeah, pretty much
<f_ridge> <c​lever___/D> you need to consider the `ndiv_frac` as a fraction out of something, not just a bare number
<f_ridge> <c​lever___/D> ```
<f_ridge> <c​lever___/D> > 19.2 * (52 + (87382/0x100000))
<f_ridge> <c​lever___/D> 1000.0000122070312
<f_ridge> <c​lever___/D> ```
<f_ridge> <c​lever___/D> this looks right to me
<f_ridge> <x​2x6_/D> how you came up with 0x1000000?
<f_ridge> <c​lever___/D> brute force, i forget the right answer, but i know it should be close to 1000mhz, not 1015mhz
<f_ridge> <G​itHub Lines/D> ```cpp
<f_ridge> <G​itHub Lines/D> float divisor = (float)ndiv + ((float)frac / (1<<20));
<f_ridge> <G​itHub Lines/D> ```
<f_ridge> <c​lever___/D> i also just remembered, i have this util
<f_ridge> <c​lever___/D> this runs under linux, reads everything from `/dev/mem`, and tells you what it all means
<f_ridge> <c​lever___/D> ```
<f_ridge> <c​lever___/D> > 19.2 * (52 + (87382/(1<<20)))
<f_ridge> <c​lever___/D> 1000.0000122070312
<f_ridge> <c​lever___/D> ```
<f_ridge> <c​lever___/D> that gets you this
<f_ridge> <x​2x6_/D> There is also a prescaler in the feedback path, which is controlled by a bit in the A2W_PLLx_ANA1
<f_ridge> <x​2x6_/D> how do I use that
<f_ridge> <x​2x6_/D> Now that I print out all PLLS I want to print clocks that depend on them
<f_ridge> <c​lever___/D> thats just an extra `*2` in the math
<f_ridge> <c​lever___/D> basically, the VCO is generating a somewhat random clock in the area of 1-2ghz
<f_ridge> <c​lever___/D> the `NDIV & FRAC` part, then counts to 52 or 53 (in a ratio that meets the fraction)
<f_ridge> <c​lever___/D> and on every 52nd or 53rd clock pulse, it will invert its output
<f_ridge> <c​lever___/D> the `phase detector` will compare that generated signal, to the 19.2mhz clock
<f_ridge> <c​lever___/D> and adjust the speed of the `VCO`
<f_ridge> <c​lever___/D> if the VCO is running too fast, then the `NDIV&FRAC` clock will be generating something over 19.2mhz, and the `phase detector` will slow things down
<f_ridge> <c​lever___/D> the problem, is that the `NDIV&FRAC` part, cant count at 2ghz
<f_ridge> <c​lever___/D> so you slap an extra `/2` infront of it, turning that 2ghz into 1ghz, and now it can count pulses
<f_ridge> <c​lever___/D> so the formula becomes either `19.2 * (52 + (87382/(1<<20)))` or `19.2 * (52 + (87382/(1<<20))) * 2`, depending on if the prescaler is enabled
<f_ridge> <G​itHub Lines/D> ```cpp
<f_ridge> <G​itHub Lines/D> void print_pll_subdivider(volatile uint8_t *base, const char *name, uint32_t offset) {
<f_ridge> <G​itHub Lines/D> uint32_t control = *reinterpret_cast<volatile uint32_t*>(base + offset);
<f_ridge> <G​itHub Lines/D> uint8_t div = control & 0xff;
<f_ridge> <G​itHub Lines/D> bool channel_enable = control & 0x00000100;
<f_ridge> <G​itHub Lines/D> bool bypass_enable = control & 0x00000200;
<f_ridge> <G​itHub Lines/D> printf("%7s: divisor:%d enable:%c bypass:%c\n", name, div, channel_enable?'1':'0', bypass_enable?'1':'0');
<f_ridge> <G​itHub Lines/D> }
<f_ridge> <G​itHub Lines/D> ```
<f_ridge> <G​itHub Lines/D> ```cpp
<f_ridge> <G​itHub Lines/D> print_pll_subdivider(pll_base, "C_CORE2", 0x320);
<f_ridge> <G​itHub Lines/D> print_pll_subdivider(pll_base, "C_CORE1", 0x420);
<f_ridge> <G​itHub Lines/D> print_pll_subdivider(pll_base, "C_PER", 0x520);
<f_ridge> <G​itHub Lines/D> print_pll_subdivider(pll_base, "C_CORE0", 0x620);
<f_ridge> <G​itHub Lines/D> ```
<f_ridge> <c​lever___/D> each PLL has 1 to 4 taps on it, all in the blue box on undocumented, those all divide the PLL clock down by some amount, and the code i just linked prints some of it
<f_ridge> <x​2x6_/D> PLLA: CTRL:00021034,FRAC:00015556,PRE:00144000
<f_ridge> <x​2x6_/D> PLLA: n=52, fra:87382, pdiv=1, pre=2,freq=2000
<f_ridge> <x​2x6_/D> PLLB: CTRL:0002103e,FRAC:00080000,PRE:00140000
<f_ridge> <x​2x6_/D> PLLB: n=62, fra:524288, pdiv=1, pre=1,freq=1200
<f_ridge> <x​2x6_/D> PLLC: CTRL:00021034,FRAC:00015556,PRE:00144000
<f_ridge> <x​2x6_/D> PLLC: n=52, fra:87382, pdiv=1, pre=2,freq=2000
<f_ridge> <x​2x6_/D> PLLD: CTRL:00021034,FRAC:00015556,PRE:00144000
<f_ridge> <x​2x6_/D> PLLD: n=52, fra:87382, pdiv=1, pre=2,freq=2000
<f_ridge> <x​2x6_/D> PLLH: CTRL:0002102d,FRAC:00000000,PRE:0000000c
<f_ridge> <x​2x6_/D> PLLH: n=45, fra:0, pdiv=1, pre=1,freq=864
<f_ridge> <x​2x6_/D> with prescale PLLA runs on 2GHz?
<f_ridge> <x​2x6_/D> with prescale PLLA,PLLC,PLLD run on 2GHz?(edited)
<f_ridge> <c​lever___/D> that sounds reasonable, i forget what the usual defaults are, but you can also ask linux via `clk_summary`
<f_ridge> <c​lever___/D> the `clk_summary` for an old pi1b
<f_ridge> <x​2x6_/D> what's a CCP2?
<f_ridge> <x​2x6_/D> I'd be glad to do that, but probably not tonight. For that I'd need sdcard with linux
<f_ridge> <x​2x6_/D> PLLC chan:'per',00000002,f:1000,ena:0,bypass:0
<f_ridge> <x​2x6_/D> PLLC: CTRL:00021034,FRAC:00015556,PRE:00144000
<f_ridge> <x​2x6_/D> PLLC: n=52, fra:87382, pdiv=1, pre=2,freq=2000
<f_ridge> <x​2x6_/D> So, Crystal is 19.2MHz. PLLC outputs 19.2 * 2 * (52 * 87382.0f/(1<<20) = 2GHz
<f_ridge> <x​2x6_/D> According to register PLLC_PER is just divided by 2, but its neither enabled nor bypassed. So its 1GHz, but not enabled.
<f_ridge> <x​2x6_/D> But according to spec , this should clock EMMC
<f_ridge> <x​2x6_/D> So, Crystal is 19.2MHz. PLLC outputs 19.2 * 2 * (52 * 87382.0f/(1<<20) = 2GHz
<f_ridge> <x​2x6_/D> According to register PLLC_PER is just divided by 2, but its neither enabled nor bypassed. So its 1GHz, but not enabled.
<f_ridge> <x​2x6_/D> But according to (undocumented) spec , this should clock EMMC(edited)
<f_ridge> <c​lever___/D> i'm not sure it can be disabled, id have to check more
<f_ridge> <x​2x6_/D> Doesn't look complete
<f_ridge> <c​lever___/D> the `periph muxes` stack, is a pile of duplicate mux and divider blocks, about 16 of them
<f_ridge> <c​lever___/D> each has an 8 way input mux, to select one of the 8 clocks listed there, and its own divider
<f_ridge> <c​lever___/D> `CM_GP0DIV` and `CM_GP0CTL` are the registers for the `GP0` clock
<f_ridge> <c​lever___/D> `CM_EMMCDIV` and `CM_EMMCCTL` are for the `EMMC` clock
<f_ridge> <c​lever___/D> repeat that pattern for all clocks
<f_ridge> <x​2x6_/D> CM_EMMCCTL_FRAC also has a division with 1<20?
<f_ridge> <x​2x6_/D> aa, no, this is a one bit something
<f_ridge> <G​itHub Lines/D> ```c
<f_ridge> <G​itHub Lines/D> /* Arasan EMMC clock */
<f_ridge> <G​itHub Lines/D> [BCM2835_CLOCK_EMMC] = REGISTER_PER_CLK(
<f_ridge> <G​itHub Lines/D> SOC_ALL,
<f_ridge> <G​itHub Lines/D> .name = "emmc",
<f_ridge> <G​itHub Lines/D> .ctl_reg = CM_EMMCCTL,
<f_ridge> <G​itHub Lines/D> .div_reg = CM_EMMCDIV,
<f_ridge> <G​itHub Lines/D> .int_bits = 4,
<f_ridge> <G​itHub Lines/D> .frac_bits = 8,
<f_ridge> <G​itHub Lines/D> .tcnt_mux = 39),
<f_ridge> <G​itHub Lines/D> ```
<f_ridge> <c​lever___/D> 4 bits of integer, 8bits of fraction
<f_ridge> <c​lever___/D> so its a 4.8bit fixed-point int
<f_ridge> <c​lever___/D> for these registers, it is stored as a plain 12bit int in the register itself
<f_ridge> <c​lever___/D> just do `/256` to convert it from fixed-point to floating point
<f_ridge> <G​itHub Lines/D> ```c
<f_ridge> <G​itHub Lines/D> [BCM2835_CLOCK_PWM] = REGISTER_PER_CLK(
<f_ridge> <G​itHub Lines/D> SOC_ALL,
<f_ridge> <G​itHub Lines/D> .name = "pwm",
<f_ridge> <G​itHub Lines/D> .ctl_reg = CM_PWMCTL,
<f_ridge> <G​itHub Lines/D> .div_reg = CM_PWMDIV,
<f_ridge> <G​itHub Lines/D> .int_bits = 12,
<f_ridge> <G​itHub Lines/D> .frac_bits = 12,
<f_ridge> <G​itHub Lines/D> ```
<f_ridge> <c​lever___/D> but each divider, uses a different number of bits
<f_ridge> <G​itHub Lines/D> ```c
<f_ridge> <G​itHub Lines/D> int divisor_fixed = desired_divider * 4096;
<f_ridge> <G​itHub Lines/D> ```
<f_ridge> <c​lever___/D> this converts from floating point back to 12.12bit fixed point
<f_ridge> <x​2x6_/D> EMMC_CTRL:00000295,DIV:00005000