Today, I discovered, that implementing differential signals on the Spartan-IIe is not as easy as one would think. OK, who would work with a Spartan-IIe FPGA nowadays? Yet, I am working with an Uxibo (uxibo.de), which has quite some nice features.
To implement differential signals, one should make sure, that the input pins of the FPGA support differential signals. The pin assignment can be found on the support site of Xilinx.
The following section is aimed to explain how to get the differential input/output working on this FPGA. In later versions, this can be readily ignored, since the IBUFDS/OBUFDS command does the work of making 1 input out of the differential input pair or an output pair out of 1 signal in the FPGA.
For the Spartan-IIe, only the IBUF_LVPECL or OBUF_LVPECL command is available (or IBUF_LVDS, OBUF_LVDS if one works with LVDS). This command in association with the right location constraints does the work in this case.
First, we look at a differential input signal, going in from pin 88 and pin 89, let’s say. We call the internal net node „datain“.
The constraints file (*.ucf) should then read:
NET „datain“ LOC = „p88“ | IOSTANDARD = LVPECL;
Note, that we don’t assign pin 89 here, that is the P pin of the differential signal. The N signal is automatically routed inside the FPGA, if we define the input in the HDL file. So let’s look at that (Verilog):
IBUF_LVPECL ibuf_inst (.I(datain), .O(usedata));
We use our input from the constraints file (i.e. „datain“) and make it a LVPECL signal. We are now free to use our „usedata“ signal as we are used to.
The differential output requires some more statements. Suppose we want to use the pins 88 and 89 for outputs instead of inputs.First, the constraints file reads:
NET „out_p“ LOC = „p88“ | IOSTANDARD = LVPECL;
NET „out_n“ LOC = „p89“ | IOSTANDARD = LVPECL;
Now, we have to state both lines. So we have to make them differential in our HDL file ourselves. We need three lines, to route our output signal („dataout“) to the right places (Verilog):
OBUF_LVPECL obuf_inst (.I(dataout), .O(out_p));
INV inv_inst (.I(dataout, .O(dataoutinv);
OBUF_LVPECL obuf_inst2 (.I(dataoutinv), .O(out_n));
Looking at these statements, it is clear, why it works. We assign our data output to the P pin of the differential signal, invert it at the same moment and route the inverted output to the N pin.
I have used the IOSTANDARD LVPECL for this example. If you are working with LVDS, just replace every „LVPECL“ with „LVDS“.
Thankfully, lives have been made easier in newer versions.