Next step on getting an NTP server running on the ch32v307 dev board is verifying the local clock.

This is part of a series on the ch32v307 dev board

There's two local clocks to consider: the PTP hardware timestamp clock, and the PPS input capture clock. I'll focus just on the PTP hardware timestamp clock in this post.

PTP hardware timestamp clock

The main registers that everything is built around are: PTPTSHR, which is a 32 bit counter of seconds and PTPTSLR, which counts 31 bits of fractional seconds (in other words, each count is 1/(2**31) seconds or 465 picoseconds).

There's two different modes of operation to keep these registers updated: fine and coarse.

PTP clock coarse mode

coarse mode

With coarse mode, a value is added to the subsecond register with every microprocessor clock cycle. When the subsecond register overflows 31 bits, one second is added to the second register. Since this microprocessor runs at 144MHz, each clock cycle is 6944 picoseconds. The closest value to add to the subsecond register is 15/2**31, or 6984 picoseconds. This runs the clock 0.57% fast, which isn't great.

PTP clock fine mode

fine mode

With fine mode, a value is added to an accumulator with every clock cycle, and when that accumulator overflows, another value is added to the subsecond register. That gives you control of the frequency in 1/2**32 steps. To correct for the 0.57% error in the PTPSSIR/PTPTSLR combination, I would set PTPTSAR to 4270485982/2**32, which should update the PTPTSLR register at 99.43% normal speed.

There's two problems with this mode on this hardware: when subsecond overflows, multiple seconds are added to the second register, and the accumulator does not work properly.

Fine mode problem: subsecond overflows

comparing computer's clock vs micro's clock

To compare my computer's clock to the micro's clock, I had the micro print out the time once per second (second column). The computer then took its timestamp of that message (first column) and compared the difference in duration from the start. The ratio (third column) between the durations of both clocks should be very close to 1.0 if everything is running properly.

I set the seconds register to 1690513986 at startup, but it reads as x2 that amount. The subsecond register is running at half the speed it should be, but every time it overflows, it adds two to the seconds register. This ends up at nearly the right ratio, but this isn't a quality clock. I explored shifting the seconds right by 1 bit (effectively a divide by 2), but that lead me to my second problem.

Fine mode problem: subsecond register updates

The subsecond register should be moving at 99.43% original speed, not 50% speed. To measure this more closely, I polled the subsecond register once per clock cycle and measured how frequently it updated:

  for(int i = 0; i < 10; i++) {
    a = ETH->PTPTSLR;
    b = ETH->PTPTSLR;
    c = ETH->PTPTSLR;
    d = ETH->PTPTSLR;
    e = ETH->PTPTSLR;
    f = ETH->PTPTSLR;
    g = ETH->PTPTSLR;

    printf("%u %u %u %u %u %u %u\r\n", a, b, c, d, e, f, g);

The unrolled loop reading PTPTSLR compiles down to this nice assembly:

     670:       454c                    lw      a1,12(a0)
     672:       4550                    lw      a2,12(a0)
     674:       4554                    lw      a3,12(a0)
     676:       4558                    lw      a4,12(a0)
     678:       455c                    lw      a5,12(a0)
     67a:       00c52803                lw      a6,12(a0)
     67e:       00c52883                lw      a7,12(a0)

I counted how many times I saw PTPTSLR change out of the total number of measurements.

With 4270485982, I would expect it to change 99.43% of the time, but it changed 49.7% of the time, which is why the clock was running at 50%. I picked some values to set PTPTSAR to see how it behaved. It got back some pretty strange results.

PTPTSAR Expected Actual
4270485982 99.43% 49.7%
3221225472 75% 50%
3049426780 71% 42%
2920577761 68% 36%
2662879723 62% 38%
2190433320 51% 49%
2147483648 50% 50%
2126008811 49.5% 49.5%
2104533975 49% 50%
1632087572 38% 38%
1073741824 25% 25%

Additionally, the setting of PTPTSAR affected the updates of the second register:

PTPTSAR second register
4294967295 +2s on overflow
2147483648 +2s
1073741824 +4s
536870912 +8s
268435456 +16s
134217728 +32s
67108864 +64s
33554432 +128s
16777216 +256s
8388608 +512s
4194304 +1024s

The ch32v307 PTP hardware is very similiar to the stm32f4xx PTP hardware. Except fine mode doesn't have these problems on the stm32f4xx.

So I'll be exploring adjusting the frequency via a software clock instead of a hardware clock.