I decided it might be worth implementing a simple UART to allow the use of a PC serial port when testing the I/O peripheral interface. And it sounded like fun.
I looked on OpenCores to see if there was one I wanted to grab instead of writing my own, but most were complex clones of the 16550 UART chip, complete with a bus interface to control registers; far too complex for what I wanted. After all, the receiver is a simple state machine, and the transmitter almost trivial.
I coded up the transmitter in one evening. The receiver took a bit longer, about two evenings. Another evening was spent running it through the Xilinx iSIM simulator working out the kinks. I wrote a top module that took the output of the receiver and looped it back into the transmitter; something of a very complex loopback plug. I connected a PmodRS232 module to the board and connected that to one of my computer's serial ports. All that was left was loading it into the iCEblink40 board and giving it a test. I tapped a key... and got back garbage.
I found my Intronix logic analyzer pod and wired it to one of the I/O connectors that drives the VFD interface board, which I'm not using for this test. I recoded the test top module to feed a dozen internal signals to this connector, the most important ones being the incoming serial data signal and the internal signal that controls input state sampling. What I saw confounded me: my sample clock was about 5.5% slow, which was causing the sample that should be in the middle of the last data bit to be just at the beginning of the stop bit. How did I get the math wrong?
I went back through my simulations and reassured myself that I should be sampling in the middle of the 9600 bps bit stream, with an error of about 1.4% due to clock divisor approximations. Yet here was a clear indication that I was way off. Why?
One of the interesting features of this logic analyzer pod is the ability to have it report the frequency of a signal input on one of its pins. Another internal signal I'd wired to the connector was the 3.33 MHz system clock generated on the board. When I asked the pod what the clock frequency was it reported 3.18 MHz, not 3.33 MHz. That's 4.6% slow. Combine that with 1.4% error in the bit sample clock, and the answer is clear.
Even worse, as I watched the frequency it drifted about, seeming to trend downward as time progressed. Breathing on it caused the output frequency to drop to around 3.16 MHz. What would cause a crystal oscillator module to drift that badly? A quick look at the data sheet for the module answered the question: it's not a crystal oscillator. It's not even a ceramic resonator. It appears to be some sort of "high-precision" RC oscillator, with a claimed frequency error of less than 1.5% at room temperature and 2% over its operating range. That's not great, but it should be good enough, right?
The problem is you only get that error when it's configured as specified in the data sheet. That means using a frequency-setting resistor of 10K or greater. There's a warning that "accuracy may suffer" when using a resistor between 5K and 10K with a V+ of less than 4V. The graphs show it drastically degrades with a resistor less than 5K, and no information is given for a resistor less than about 4K. The iCEblink board uses a 3.0K resistor with a V+ of 3.3V, which is well below the specified minimum frequency-setting resistor value.
Okay, I'll admit, this is probably fine for a cheap little demo board. I think it cost me $20 retail a few years ago. But was there some reason they couldn't have put a 10K resistor on there instead?
For those of you who just have to hear the end of the story, I changed the prescaler from ÷22 to ÷21 and the UART began working. But I have no faith that it will continue to work over even room temperature variations. I'm probably going to replace their 3.0K resistor with a precision 10K resistor to get into the range allowed by the specs, even if that means soldering a nearly-invisible 0402 SMD. By strapping it for ÷1 operation instead of the default ÷10 I'll get 10 MHz out with (claimed) 0.5% accuracy, which is fine for my uses. It also gets to the low limit of the FPGA's PLL operation range, so I could use one if I decide I want to duplicate the Spartan-3E board's 50 MHz clock rate.