When you make your own NTP server, you probably spend a lot of time trying to get it as accurate and stable as possible. To get good results, you need to connect your server to a good time reference, like an atomic clock, GPS or an GPSDO to get the PPS (Pulse Per Second) to sync your server with the reference clock. In both Linux and BSD, there is kernel support to slave the very inaccurate CPU clock crystal to the PPS signal, constantly adjusting the frequency, because of the cheap crystals of computers constant drift due to temperature and barometric pressure differences. So most of the work is to get a crappy crystal to behave. What if you just jumped over that step so the CPU clock is as accurate as the PPS signal and used a SI5351C Clock Generator?
Read on to see a simple way of doing it and get a extremely accurate NTP server for under $200.
You may be wondering why not just either buy a complete NTP server, or at least use a fast Linux computer directly, instead of trying to make it as small, energy efficient and low cost as possible. But there’s a reason. I’m planning to help install a bunch of NTP servers in different countries in Africa (yes Sarah Palin, Africa isn’t a country), so I need to keep the cost down, but still have it perform very accurately. I’ve managed to get a deal with a large internet provider so we have room to install servers in each of their nodes around the continent. Because right now, the NTP server situation in Africa are quite abysmal.
For fun and (no) profit!
My first experiment has been with the Raspberry Pi 3. It’s a great little computer and it’s possible to get good results on it. But only inside the server, because the Ethernet is connected via the USB bus which makes it slow (besides the fact that it’s only 100Mbit/s.) So even when you have very accurate time locally on the server, when sent out to clients it degrades because of the USB to Ethernet conversion, and network latency.
So I looked around for another card with a proper 1Gbit Ethernet with a dedicated controller. I settled on the Odroid C2. It’s a quad-core ARM 64-bit computer, running at 1.56GHz. It has 1Gbit Ethernet, 2 GB of DDR3 RAM and run a number of different flavours of Linux, so it’s perfect for my project. The below $50 price tag was a nice surprise as well. It also has provisions for using eMMC5.0 or SD-card, where the speed ot the eMMC is much faster than the SD-card.
The NTP Server Setup
I use a Trimble GPSDO that I bought from eBay as my local time reference, giving me both a PPS signal and two 10MHz signals (which we’ll use later) coming out from it. The signals are synchronised with the atomic clocks onboard the GPS satellites, giving me an accuracy between 10 to 50 nanoseconds. The PPS pulse is fed into one of the GPIO pins on the computer as a time reference for syncing the kernel and the NTPD server application. Unfortunately, the pulse width it too short for Linux to pick it up, so I’ve designed a pulse extender circuit which I’ve made Open Source Hardware, so the system can pick up the interrupt and do the adjustments. Usually you use a feature of the Linux kernel, called PPS Consumer which adjusts the clock frequency of the entire system to get the very unstable local CPU crystal more in line with the correct time. I’ve been working for months trying to improve the latency and the stability of the kernel to make the crystal as precise as possible. But then it dawned on me. Why? Couldn’t I just replace the unstable clock with something better? My first idea was to just install a more stable crystal, but you still have the problem of it drifting from the PPS signal. But I finally found a solution.
Slaved CPU Crystal
There is a clock generator IC called SI5351 from Silicon Labs. It’s an IC, where you have a local 25 or 27MHz crystal and then you can program it to output different clock signals at different frequencies between 100kHz to 200MHz, all derived from the local crystal. Both Adafruit sells a breakout board, and there are others as well. But they use the SI5351A or SI5351B versions of the IC, where the differences are the number of outputs and that the B version can have a Voltage Controlled Oscillator. So the A version has three outputs and the B versions have eight. But the most interesting one for me is the SI5351C, which can use an external 10-100MHz reference instead of the normal 25 or 27MHz crystal as its reference. And it also has eight outputs.
Oh, lightbulb over my head!
Obviously the solution to my problem was all there. I have a GPSDO that outputs a 10MHz signal that is slaved to extremely accurate atomic clocks. So for testing, I built a breakout board PCB and did a test. You can read all about the SI5351C breakout board here.
The initial test was just to see how stable it was. The data sheet for the SI5351C said that the jitter from the clock generator would be around 70 nanoseconds. So I hooked up my contraption connected to an old Arduino (for programming the frequencies via I2C) to my old modified Philips PM6665 frequency counter and let it run for 24 hours. And it was bang on. I tested a lot of different frequencies, but the CPU on the Odroid C2 is 24MHz, so I let that frequency run for a long time. And it’s bang on, without a glitch. The only thing I needed to do was to add two resistors to form a voltage divider to get down to 1.5 volts to match the old crystals voltage.
Odroid C2 Modifications
Time to replace the CPU crystal on the Odroid C2 with my newfangled module. Looking at the schematic, both of the capacitors C159 & C160 needs to be removed. We don’t need to create the phase shift anymore. The same goes for R107. And obviously the crystal itself.
I use Capton© tape around all the components around the crystal to protect them from the heat from my SMD heat gun. A magnifying glass or microscope helps, because the components are really tiny.
Here’s how the Odroid C2 PCB looks like without the heatsink. To remove the heatsink, just press down on the two tabs and squeeze the pins together on the other side.
Here’s a closeup of the area we are interested in.
So when the crystal and associated components are removed, it should look something like this. The crystal and C159, C160 and R107 are also removed.
Next up, let’s connect the clock generator board to the Odroid C2 board. Try to use thin wires which makes it easier to fit the heatsink back again without a problem.
And here’s the experimental setup as it runs until I find a fitting enclosure.
On my first prototype of the SI5351C Clock Generator card, I didn’t get the interrupt pin out from the IC. The board you can download here, has the line connected. So if the external reference should fail and the interrupt pin goes high, hopefully I can switch over to the onboard oscillator quick enough so the Odroid C2 board doesn’t hang. Still waiting on the new version of the PCB design from China to test that.
So, what’s the result then?
Well, I’ve been fiddling around and testing with different options. When leaving the Kernel Consumer turned on, I get extremely low values on the loopback statistics from NTP, but the NTPD daemon constantly updates the board frequency, so it’s not as good as just polling the PPS signal every 16 seconds in NTP. Here’s how the loopback statistics looks like.
And this is when I have four machines on 1Gbit Ethernet, having a sh*tload of stress tests topping out at 22000 requests per second on the Odroid C2! The processor was utilising 22% of CPU load. So if you have clients using the normal once every 64 of 1024 seconds, it can handle a couple of hundres of thousand clients or more.
I’m using the excellent Etherkit SI5351 library. To get it up and running, this is all code you need…
// Start serial
// Initialize the Si5351 to use a 10 MHz clock input on CLKIN
si5351.init(SI5351_CRYSTAL_LOAD_0PF, 10000000UL, 0);
// Set PLLA and PLLB to use the signal on CLKIN instead of the XTAL
// Set CLK0 to output 24 MHz for CPU clock on Odroid C2
// Set CLK2 to output 25 MHz for Ethernet
// Set CLK4 to output 12 MHz for USB controller
// Read the Status Register and print it every 10 seconds
Serial.print(" SYS_INIT: ");
Serial.print(" LOL_A: ");
Serial.print(" LOL_B: ");
Serial.print(" LOS: ");
Serial.print(" REVID: ");
As you can see, I’m planning to replace the crystals for both Ethernet and USB as well. Why? Why not? It’s there!
There is a pin header for turning on or off the Odroid C2, so I’m planning to have the Odroid turned off until the SI5351C is fully configured, then turn on the computer. I’ll put up the schematic (one transistor!) when I finalise the enclosure and software.
The SI5351C Breakout Board
It uses a really low noise 3.3 volt regulator, I have also added level shifting so you can use both 5 Volt Arduinos or if you prefer, any micro controller that can do I2C at 3.3 Volt.
Naturally, this board can be used for a lot of other interesting stuff as well. How about a VFO for you HAM-guys out there? With a cheap U-Blox GPS receiver and some modifications, you can get the steady 10MHz frequency you need. And you will be accurate down a couple of decimal places. Go nuts!
So I decided to release the board as Open Source Hardware, so you can download it here. If you’re not up to doing SMD soldering (the SI5351C is 3x3mm so you need an SMD oven) you can send me an email because I’m going to make some for sale. You can contact me at firstname.lastname@example.org