So… I was describing part of the product I’m working on to somebody at dorkbotpdx last week. He was more impressed with the idea than I expected, so I figure it might be interesting to post here.
A requirement of the product is that I have a series of ADC’s running in very close synchronization, yet they’re separated by 10’s or 100’s of meters by a bus. They’re slow ADC’s running 4Ksps and the target is in the 0.1 – 1.0% sample accuracy, meaning a 0.25 – 2.5ppm relative accuracy. The fact that these might be running continuously for minutes or hours (and eventually days to months) and still need to be in sync means that I have to have a zero-drift system. These are not exactly easy things to achieve on the same PCB, let alone separated by lots of wire.
The key component here is the FD77T from Pletronics (see my previous post about the part’s capabilities), which combines a tunable crystal with a highly-capable PLL. The PLL is set up to simultaneously drive both the microcontroller (an Xmega, of course) and the ADC at the required speeds (4.096MHz for the ADC, 6.400MHz for the MCU’s PLL, would be 32MHz except I misrouted the PLL outputs). This means the MCU and ADC are always synchronized on the PCB, and any change to one will be reflected in the other. The MCU then has a DAC output wired to the PLL VCXO’s control voltage, which allows me to push & pull the frequency of the crystal by around 150ppm.
To sync up the multiple units, I use carefully-timed edge capture of the bus itself. The master unit sends out the packet preamble and turns on the Xmega edge trigger (via event) for a very specific window as the serial data trickles out various levels of in-chip buffering (TEMP register, clock-domain bridge, etc.). The 16+16(+32)-bit cycle counter is the recipient of this edge capture event, and locks the cycle number for subsequent retrieval. A second packet is sent out on the bus that contains this cycle number as the reference point. The slaves do this same trick except during a receive interrupt function (lots of fun to get timed right, let me tell you…) and eventually feed the local and remote cycle numbers to the clock engine.
The first time we get these two sync numbers, the difference between them is stored and used for the rest of the unit run-time to convert the local cycle counter to the master’s. All subsequent sync packets give us a chance to determine how much drift has been going on since the last packet. The relative difference is fed into a PID controller, which has been manually (and very roughly) tuned to output a control value that is fed out the DAC and into the VCXO of the FD77T.
The current development code runs this sync operation at 10Hz, and at this point will coalesce to bouncing between -1 and +1 within a few seconds. This +- 1 difference is measured in units of 31.25nS per 100mS sync period. The PID keeps this narrow window indefinitely, which means that years later I could come back to these two units and be able to tell you exactly what the master’s clock is solely by looking at the slave.
To sync the ADC, I will simply have the master pick a particular cycle number and push that value to all the slaves. They’ll flip their cycle counters around into PWM mode (without stopping their counting!), and set up to fire off the required SYNC pulse to their local ADCs. Because the ADC is driven by the PLL which also drives the MCU that’s running a feedback loop against the PLL, all the distributed ADC’s will stay locked forever.
(I’ll see if I can make a diagram at some point to make the arrangement clearer)