Spartan-3 Starter Kit Board Parallel Port Interface

— Access an unlimited number of internal FPGA signals from the Windows XP command line

The Xilinx Spartan-3 Starter Kit includes the Spartan-3 Starter Kit Board and the Digilent JTAG3 Low-Cost JTAG Download/Debug Cable. By making a custom connector which allows the latter to be directly connected to pins on the Starter Kit Board's B1 expansion connector, it is possible to emulate an unlimited number of I/O signals between the FPGA and a laptop or desktop computer. [The parallel port interface in its setting]

The interface consists of:

ZIP archive Download source code and binaries here. (112Kb, ZIP archive). Includes a Microsoft Visual C++ 2005 project for the interface routines and interactive utility (C code), and a sample Xilinx ISE project demonstrating the use of the interface. The sample project lets the LEDs and switches on the Starter Kit Board be controlled and monitored from the command-line utility.

The interface routine uses Craig Peacock's PortTalk driver to gain access to the parallel port I/O registers under Windows XP. See Beyond Logic for more excellent information on communications port interfacing.

Example Setup

  1. Open the sample Xilinx ISE project, synthesize, and program the Spartan-3 FPGA. For this step, be sure to have the JTAG programming cable connected to the normal JTAG programming connector on the Starter Kit Board.
  2. Disconnect the JTAG cable from the Starter Kit Board and exit iMPACT (not strictly necessary).
  3. Run parint siglist.txt (assuming the computer still has the JTAG cable connected to its parallel port). If this is the first time you are running the utility, it will attempt to install the PortTalk driver. You need administrative privileges for this to work.
  4. Now connect the JTAG cable to the B1 expansion connector through the custom-made connector (you need to make this; see below).
  5. Interact with parint. For instance:
    • Press Enter to show the current state of all signals.
    • Type ld 252 to set the Starter Kit Board's LEDs to the pattern 10101010.
    • Flip a switch on the Starter Kit Board and press enter to see the value of sw change.
    • Type quit to exit.
    • Run parint siglist.txt siglist.v to see the utility generate a Verilog stub siglist.v. A stub like this was originally pasted into S3board.v in the example project.
    • Run parint uninstall to uninstall the PortTalk driver.

parint interactive utility

[The parallel port interface in its setting]

parint is an interactive utility for getting and setting FPGA signals by name through the parallel port interface. Usage:

parint [<signals list input file> [<verilog output file>] | uninstall]

The PortTalk driver will automatically be installed. Run parint uninstall to remove the driver from the system.

The first file specified must contain a list of signals, one line each, with three white-space separated columns:

  1. The character "i" or "o", specifying the directionality of the signal from the FPGA's point of view (input or output, respectively).
  2. The width of the signal, in bits (minimum one).
  3. The name of the signal (may not contain whitespace).

Empty lines or lines starting with the comment character "#" are ignored.

For instance:

# This is an example

i    1    one_bit_input
i    2    two_bit_input
o    4    four_bit_output

If an additional file name is specified on the command line, a Verilog stub corresponding to the signals list will be generated. For instance, the example list above will result in the following stub:

/******* Auto-generated, do not modify *******/
Parint #(3, 4) mparint(tdi, tck, tms, tdo,
    {one_bit_input, two_bit_input},
    {four_bit_output});
/*********************************************/

This can be pasted into the Verilog module where the parallel port interface is to be used. The signals tdi, tck, tms, and tdo must be provided; they are expected to be wired to the corresponding pins on the B1 expansion connector.

When run with parint <signals list name> the utility will accept commands from stdin, as follows:

# <comment>
Ignores the rest of the line.
quit | <end-of-file>
Ends the session
update
Simultaneously exchange input and output data with the FPGA. The last specified values of input signals will be transmitted while the values of the output signals at the time the transmission started will be received. This implies that another update must be done in order to read any response from the FPGA that was due to the changed input signals. Usually only makes sense under update_level 0.
update_level 0 (manual mode)
In this mode, update must be requested manually in order to transmit previously set input values and receive new output values. This allows several values to be set at the exact same time, for increased speed or for testing purposes.
update_level 1 (automatic mode)
Data is automatically kept updated by exchanging data with the FPGA after an input signal has been set or before an output signal is read.
update_level 2 (interactive mode; default)
Data is automatically kept updated, and a complete listing of the values of all signals is shown after every time the user enters an empty command or changes an input signal (two updates are performed so that the FPGA's actual response can be seen). Values are shown in binary and, for signals wider than one bit, in octal as well. In this mode, input errors or signal assertion failures are considered non-fatal.
<input signal name> <octal value>
Set the input signal to the specified value.
<input signal name>
Complement the input signal for a very brief period before complementing it back again. Useful for triggering clocks or simulated push-buttons. (Note: there is currently no way to specify the exact time during which the signal is kept complemented.)
<output signal name> <octal value>
Assert that the specified output signal has the given value. In update levels 0 and 1 the program will exit with a non-zero exit code if this is not the case.
<output signal name>
Print the value of the specified output signal to stdout, in octal.

Scripts can of course be piped to the utility. These will probably want to set the update level to either 1 or 0 before doing anything else. See also Michael Peek's "bar" UNIX utility for a way to display the progress of a script piped to parint (compiles on Cygwin without complications).

Custom JTAG to B1 Expansion Port Connector

[]

A simple custom connector must be made in order to be able to connect the Digilent JTAG3 Low-Cost JTAG Download/Debug Cable to the B1 expansion connector on the Spartan-3 Starter Kit Board. A photo of the my prototype is shown below (never mind the resistors; use plain wires instead).

The six pins on the JTAG connector must be made to connect to six corresponding pins on the B1 connector, as specified in the table below and the illustration to the right (connector drawings from the Starter Kit Board User Guide).

JTAGB1Signal
1 →4TMS
2 →6TDI
3 ←8TDO
4 →10TCK
5 —1GND
6 —3VCC
[The custom-made parallel port interface connector]

Different I/O pins can be used if desired, as long as the FPGA pins are mapped accordingly. The configuration above was chosen for the pins' physical proximity of each other so that the connector could be made small. For the configuration above, a more detailed view of the interconnections involved is given in the following table:

Custom Connector
Digilent JTAG3 L-C JTAG D/D CableS3 Starter Kit Board
Parallel portJTAGB1 Exp. portFPGA Pins
PinNameRegisterInv?DirectionPinNamePinNamePinName
4Data 2DataNo1TMS4PB-ADR0C10IO3
2Data 0DataNo2TDI6PB-ADR1E10l30P_1
13SelectStatusNo3TDO8PB-ADR2C11L29P_1
3Data 1DataNo4TCK10PB-ADR3D11L28N_1
18GND5GND1GND
6VCC3Vcco (3.3V)
9Data 7 (6?)DataNo→|
11BusyStatusYes←|
12Paper-OutStatusNo←|

The Digilent cable internally connects four parallel port pins to four JTAG pins through a series of buffers, Schmitt triggers and low-pass filters. It also loops back one signal (the schematic says Data 7; my experiments indicate Data 6). The custom connector connects the four JTAG pins to the B1 expansion connector, plus VCC/GND. The Starter Kit Board finally connects these to pins on the FPGA itself.

It is essential that the TCK signal is clean; this will be the case if direct connections are made between the JTAG cable and the expansion port. In the first versions of the connector I had placed series resistors between the JTAG signals and the expansion connector as a feature to avoid bus contention. This unfortunately rendered TCK useless as a clock signal as the low-pass filter in the cable no longer had the expected behavior. For this reason, do not insert series resistors between the JTAG cable and the expansion port, at least not for TCK.

Theory of Operation

The Verilog module Parint.v contains three registers: a PISO shift register for outputs, and a SIPO shift register plus a buffer register for inputs. The registers are all clocked by TCK, which comes from the computer interfacing the parallel port. The circuit is thus entirely host-driven, with no internal clock needed. []

When TMS is high, clocking the circuit with TCK will cause output values to be read into the PISO and new input values to be read into the buffer. When TMS is low, clocking will cause the shift registers to shift right by one bit. The SIPO will thus store a new bit from TDI, while the PISO will expose a new bit on TDO.

A typical data exchange between the computer and the FPGA is as follows:

  1. The computer sets TMS high and clocks. This causes output signals to be read into the PISO. Under the protocol described here, inputs will meanwhile stay the same as before.
  2. The computer sets TMS low.
  3. The computer reads a bit from TDO and sets a signal on TDI, then clocks.
  4. The computer repeats from (3) until there are no more bits to send or receive.
  5. The computer sets TMS high and clocks. This causes the newly received input signal to be read from the SIPO to the buffer on the FPGA side. The PISO is also loaded, but this is not a concern at this point in the exchange.

The tricky part is to get step (3) right when the number of inputs and outputs are not the same. A working routine can be found in the Parint_update() function in parint.c.

Note that the interface does not follow the JTAG protocol in any way; it merely uses the same cable in order to take advantage of its integrated signal cleaning circuitry. The schematics of the cable are given in Appendix A of the Starter Kit Board User Guide.