01:26
jcea has quit [Ping timeout: 246 seconds]
07:48
Stromeko has quit [Quit: Going… gone.]
07:49
Stromeko has joined ##raspberrypi-internals
07:49
ungeskriptet has joined ##raspberrypi-internals
10:41
jcea has joined ##raspberrypi-internals
11:10
tortoise has quit [Ping timeout: 255 seconds]
11:11
mithro has quit [Ping timeout: 255 seconds]
11:11
strages has quit [Ping timeout: 272 seconds]
11:22
mithro has joined ##raspberrypi-internals
11:22
tortoise has joined ##raspberrypi-internals
11:24
strages has joined ##raspberrypi-internals
13:58
angerisagift has quit [Read error: Connection reset by peer]
14:18
dolphinana has joined ##raspberrypi-internals
14:21
dolphinana_ has joined ##raspberrypi-internals
14:25
dolphinana has quit [Ping timeout: 268 seconds]
15:28
angerisagift has joined ##raspberrypi-internals
16:07
dolphinana_ has quit [Ping timeout: 252 seconds]
18:41
Stromeko has quit [Quit: Going… gone.]
18:42
Stromeko has joined ##raspberrypi-internals
18:44
<
f_ridge >
<x2x6_/D> Hi, all
18:44
<
f_ridge >
<x2x6_/D> So, today is PLL day.
18:45
<
f_ridge >
<x2x6_/D> I am going to parse this doc
18:46
<
f_ridge >
<x2x6_/D> In the past I already tried to approach this picture, but did not manage it
18:47
<
f_ridge >
<x2x6_/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.
18:47
<
f_ridge >
<x2x6_/D> But actually looks pretty basic algebra
18:48
<
f_ridge >
<x2x6_/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. ?
20:23
<
f_ridge >
<x2x6_/D> 6 static void bcm2835_pll_report_one(char pll_symbol, ioreg32_t ctrl_reg,
20:23
<
f_ridge >
<x2x6_/D> 7 ioreg32_t frac_reg)
20:23
<
f_ridge >
<x2x6_/D> 8 {
20:23
<
f_ridge >
<x2x6_/D> 9 uint32_t pll_ctrl, ndiv, pdiv, ndiv_frac;
20:24
<
f_ridge >
<x2x6_/D> 10 pll_ctrl = ioreg32_read(ctrl_reg);
20:24
<
f_ridge >
<x2x6_/D> 11 ndiv = pll_ctrl & 0x3ff;
20:24
<
f_ridge >
<x2x6_/D> 12 pdiv = (pll_ctrl >> 12) & 7;
20:24
<
f_ridge >
<x2x6_/D> 13 ndiv_frac = ioreg32_read(frac_reg) & 0xfffff;
20:24
<
f_ridge >
<x2x6_/D> 14 printf("PLL%c: N=%d.%d, PDIV=%d\n", pll_symbol, ndiv, ndiv_frac, pdiv);
20:24
<
f_ridge >
<x2x6_/D> 15 }
20:24
<
f_ridge >
<x2x6_/D> I have this output
20:24
<
f_ridge >
<x2x6_/D> PLLA: N=52.87382, PDIV=1
20:24
<
f_ridge >
<x2x6_/D> So on raspbery pi that would mean 19.2Mhz * 52.87382 / 1 = 1015.104Mhz ?
20:24
<
f_ridge >
<x2x6_/D> 17 void bcm2835_pll_report(void)
20:24
<
f_ridge >
<x2x6_/D> 18 {
20:24
<
f_ridge >
<x2x6_/D> 19 bcm2835_pll_report_one('A', A2W_PLLA_CTRL, A2W_PLLA_FRAC);
20:24
<
f_ridge >
<x2x6_/D> 20 }
21:43
<
f_ridge >
<clever___/D> yeah, pretty much
21:44
<
f_ridge >
<clever___/D> you need to consider the `ndiv_frac` as a fraction out of something, not just a bare number
21:45
<
f_ridge >
<clever___/D> ```
21:45
<
f_ridge >
<clever___/D> > 19.2 * (52 + (87382/0x100000))
21:45
<
f_ridge >
<clever___/D> 1000.0000122070312
21:45
<
f_ridge >
<clever___/D> ```
21:45
<
f_ridge >
<clever___/D> this looks right to me
22:11
<
f_ridge >
<x2x6_/D> how you came up with 0x1000000?
22:11
<
f_ridge >
<clever___/D> brute force, i forget the right answer, but i know it should be close to 1000mhz, not 1015mhz
22:12
<
f_ridge >
<GitHub Lines/D> ```cpp
22:12
<
f_ridge >
<GitHub Lines/D> float divisor = (float)ndiv + ((float)frac / (1<<20));
22:12
<
f_ridge >
<GitHub Lines/D> ```
22:12
<
f_ridge >
<clever___/D> i also just remembered, i have this util
22:12
<
f_ridge >
<clever___/D> this runs under linux, reads everything from `/dev/mem`, and tells you what it all means
22:13
<
f_ridge >
<clever___/D> ```
22:13
<
f_ridge >
<clever___/D> > 19.2 * (52 + (87382/(1<<20)))
22:13
<
f_ridge >
<clever___/D> 1000.0000122070312
22:13
<
f_ridge >
<clever___/D> ```
22:13
<
f_ridge >
<clever___/D> that gets you this
22:59
<
f_ridge >
<x2x6_/D> There is also a prescaler in the feedback path, which is controlled by a bit in the A2W_PLLx_ANA1
22:59
<
f_ridge >
<x2x6_/D> how do I use that
22:59
<
f_ridge >
<x2x6_/D> Now that I print out all PLLS I want to print clocks that depend on them
22:59
<
f_ridge >
<clever___/D> thats just an extra `*2` in the math
23:00
<
f_ridge >
<clever___/D> basically, the VCO is generating a somewhat random clock in the area of 1-2ghz
23:00
<
f_ridge >
<clever___/D> the `NDIV & FRAC` part, then counts to 52 or 53 (in a ratio that meets the fraction)
23:00
<
f_ridge >
<clever___/D> and on every 52nd or 53rd clock pulse, it will invert its output
23:01
<
f_ridge >
<clever___/D> the `phase detector` will compare that generated signal, to the 19.2mhz clock
23:01
<
f_ridge >
<clever___/D> and adjust the speed of the `VCO`
23:01
<
f_ridge >
<clever___/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
23:01
<
f_ridge >
<clever___/D> the problem, is that the `NDIV&FRAC` part, cant count at 2ghz
23:02
<
f_ridge >
<clever___/D> so you slap an extra `/2` infront of it, turning that 2ghz into 1ghz, and now it can count pulses
23:02
<
f_ridge >
<clever___/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
23:03
<
f_ridge >
<GitHub Lines/D> ```cpp
23:03
<
f_ridge >
<GitHub Lines/D> void print_pll_subdivider(volatile uint8_t *base, const char *name, uint32_t offset) {
23:03
<
f_ridge >
<GitHub Lines/D> uint32_t control = *reinterpret_cast<volatile uint32_t*>(base + offset);
23:03
<
f_ridge >
<GitHub Lines/D> uint8_t div = control & 0xff;
23:03
<
f_ridge >
<GitHub Lines/D> bool channel_enable = control & 0x00000100;
23:03
<
f_ridge >
<GitHub Lines/D> bool bypass_enable = control & 0x00000200;
23:04
<
f_ridge >
<GitHub Lines/D> printf("%7s: divisor:%d enable:%c bypass:%c\n", name, div, channel_enable?'1':'0', bypass_enable?'1':'0');
23:04
<
f_ridge >
<GitHub Lines/D> }
23:04
<
f_ridge >
<GitHub Lines/D> ```
23:04
<
f_ridge >
<GitHub Lines/D> ```cpp
23:04
<
f_ridge >
<GitHub Lines/D> print_pll_subdivider(pll_base, "C_CORE2", 0x320);
23:04
<
f_ridge >
<GitHub Lines/D> print_pll_subdivider(pll_base, "C_CORE1", 0x420);
23:04
<
f_ridge >
<GitHub Lines/D> print_pll_subdivider(pll_base, "C_PER", 0x520);
23:04
<
f_ridge >
<GitHub Lines/D> print_pll_subdivider(pll_base, "C_CORE0", 0x620);
23:04
<
f_ridge >
<GitHub Lines/D> ```
23:04
<
f_ridge >
<clever___/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
23:10
<
f_ridge >
<x2x6_/D> PLLA: CTRL:00021034,FRAC:00015556,PRE:00144000
23:10
<
f_ridge >
<x2x6_/D> PLLA: n=52, fra:87382, pdiv=1, pre=2,freq=2000
23:10
<
f_ridge >
<x2x6_/D> PLLB: CTRL:0002103e,FRAC:00080000,PRE:00140000
23:10
<
f_ridge >
<x2x6_/D> PLLB: n=62, fra:524288, pdiv=1, pre=1,freq=1200
23:10
<
f_ridge >
<x2x6_/D> PLLC: CTRL:00021034,FRAC:00015556,PRE:00144000
23:10
<
f_ridge >
<x2x6_/D> PLLC: n=52, fra:87382, pdiv=1, pre=2,freq=2000
23:11
<
f_ridge >
<x2x6_/D> PLLD: CTRL:00021034,FRAC:00015556,PRE:00144000
23:11
<
f_ridge >
<x2x6_/D> PLLD: n=52, fra:87382, pdiv=1, pre=2,freq=2000
23:11
<
f_ridge >
<x2x6_/D> PLLH: CTRL:0002102d,FRAC:00000000,PRE:0000000c
23:11
<
f_ridge >
<x2x6_/D> PLLH: n=45, fra:0, pdiv=1, pre=1,freq=864
23:12
<
f_ridge >
<x2x6_/D> with prescale PLLA runs on 2GHz?
23:12
<
f_ridge >
<x2x6_/D> with prescale PLLA,PLLC,PLLD run on 2GHz?(edited)
23:21
<
f_ridge >
<clever___/D> that sounds reasonable, i forget what the usual defaults are, but you can also ask linux via `clk_summary`
23:22
<
f_ridge >
<clever___/D> the `clk_summary` for an old pi1b
23:26
<
f_ridge >
<x2x6_/D> what's a CCP2?
23:26
<
f_ridge >
<x2x6_/D> I'd be glad to do that, but probably not tonight. For that I'd need sdcard with linux
23:41
<
f_ridge >
<x2x6_/D> PLLC chan:'per',00000002,f:1000,ena:0,bypass:0
23:41
<
f_ridge >
<x2x6_/D> PLLC: CTRL:00021034,FRAC:00015556,PRE:00144000
23:41
<
f_ridge >
<x2x6_/D> PLLC: n=52, fra:87382, pdiv=1, pre=2,freq=2000
23:46
<
f_ridge >
<x2x6_/D> So, Crystal is 19.2MHz. PLLC outputs 19.2 * 2 * (52 * 87382.0f/(1<<20) = 2GHz
23:46
<
f_ridge >
<x2x6_/D> According to register PLLC_PER is just divided by 2, but its neither enabled nor bypassed. So its 1GHz, but not enabled.
23:46
<
f_ridge >
<x2x6_/D> But according to spec , this should clock EMMC
23:46
<
f_ridge >
<x2x6_/D> So, Crystal is 19.2MHz. PLLC outputs 19.2 * 2 * (52 * 87382.0f/(1<<20) = 2GHz
23:46
<
f_ridge >
<x2x6_/D> According to register PLLC_PER is just divided by 2, but its neither enabled nor bypassed. So its 1GHz, but not enabled.
23:46
<
f_ridge >
<x2x6_/D> But according to (undocumented) spec , this should clock EMMC(edited)
23:47
<
f_ridge >
<clever___/D> i'm not sure it can be disabled, id have to check more
23:47
<
f_ridge >
<x2x6_/D> Doesn't look complete
23:47
<
f_ridge >
<clever___/D> the `periph muxes` stack, is a pile of duplicate mux and divider blocks, about 16 of them
23:48
<
f_ridge >
<clever___/D> each has an 8 way input mux, to select one of the 8 clocks listed there, and its own divider
23:48
<
f_ridge >
<clever___/D> `CM_GP0DIV` and `CM_GP0CTL` are the registers for the `GP0` clock
23:48
<
f_ridge >
<clever___/D> `CM_EMMCDIV` and `CM_EMMCCTL` are for the `EMMC` clock
23:48
<
f_ridge >
<clever___/D> repeat that pattern for all clocks
23:53
<
f_ridge >
<x2x6_/D> CM_EMMCCTL_FRAC also has a division with 1<20?
23:53
<
f_ridge >
<x2x6_/D> aa, no, this is a one bit something
23:54
<
f_ridge >
<GitHub Lines/D> ```c
23:54
<
f_ridge >
<GitHub Lines/D> /* Arasan EMMC clock */
23:54
<
f_ridge >
<GitHub Lines/D> [BCM2835_CLOCK_EMMC] = REGISTER_PER_CLK(
23:54
<
f_ridge >
<GitHub Lines/D> SOC_ALL,
23:54
<
f_ridge >
<GitHub Lines/D> .name = "emmc",
23:54
<
f_ridge >
<GitHub Lines/D> .ctl_reg = CM_EMMCCTL,
23:54
<
f_ridge >
<GitHub Lines/D> .div_reg = CM_EMMCDIV,
23:54
<
f_ridge >
<GitHub Lines/D> .int_bits = 4,
23:55
<
f_ridge >
<GitHub Lines/D> .frac_bits = 8,
23:55
<
f_ridge >
<GitHub Lines/D> .tcnt_mux = 39),
23:55
<
f_ridge >
<GitHub Lines/D> ```
23:55
<
f_ridge >
<clever___/D> 4 bits of integer, 8bits of fraction
23:55
<
f_ridge >
<clever___/D> so its a 4.8bit fixed-point int
23:55
<
f_ridge >
<clever___/D> for these registers, it is stored as a plain 12bit int in the register itself
23:55
<
f_ridge >
<clever___/D> just do `/256` to convert it from fixed-point to floating point
23:55
<
f_ridge >
<GitHub Lines/D> ```c
23:55
<
f_ridge >
<GitHub Lines/D> [BCM2835_CLOCK_PWM] = REGISTER_PER_CLK(
23:56
<
f_ridge >
<GitHub Lines/D> SOC_ALL,
23:56
<
f_ridge >
<GitHub Lines/D> .name = "pwm",
23:56
<
f_ridge >
<GitHub Lines/D> .ctl_reg = CM_PWMCTL,
23:56
<
f_ridge >
<GitHub Lines/D> .div_reg = CM_PWMDIV,
23:56
<
f_ridge >
<GitHub Lines/D> .int_bits = 12,
23:56
<
f_ridge >
<GitHub Lines/D> .frac_bits = 12,
23:56
<
f_ridge >
<GitHub Lines/D> ```
23:56
<
f_ridge >
<clever___/D> but each divider, uses a different number of bits
23:56
<
f_ridge >
<GitHub Lines/D> ```c
23:56
<
f_ridge >
<GitHub Lines/D> int divisor_fixed = desired_divider * 4096;
23:56
<
f_ridge >
<GitHub Lines/D> ```
23:56
<
f_ridge >
<clever___/D> this converts from floating point back to 12.12bit fixed point
23:59
<
f_ridge >
<x2x6_/D> EMMC_CTRL:00000295,DIV:00005000