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 interface consists of:
- A Verilog module to be included in FPGA designs.
- A software routine and an interactive command-line utility for communicating with the FPGA module through the parallel port.
- A custom-made connector allowing the existing JTAG cable to be connected to pins on the B1 expansion connector.
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
- 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.
- Disconnect the JTAG cable from the Starter Kit Board and exit iMPACT (not strictly necessary).
- 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. - Now connect the JTAG cable to the B1 expansion connector through the custom-made connector (you need to make this; see below).
- 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 pattern10101010
. - 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 stubsiglist.v
. A stub like this was originally pasted intoS3board.v
in the example project. - Run
parint uninstall
to uninstall the PortTalk driver.
parint
interactive utility
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:
- The character "
i
" or "o
", specifying the directionality of the signal from the FPGA's point of view (input or output, respectively). - The width of the signal, in bits (minimum one).
- 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).
JTAG | B1 | Signal |
---|---|---|
1 → | 4 | TMS
|
2 → | 6 | TDI
|
3 ← | 8 | TDO
|
4 → | 10 | TCK
|
5 — | 1 | GND
|
6 — | 3 | VCC
|
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 Cable | S3 Starter Kit Board | |||||||||
Parallel port | JTAG | B1 Exp. port | FPGA Pins | |||||||
---|---|---|---|---|---|---|---|---|---|---|
Pin | Name | Register | Inv? | Direction | Pin | Name | Pin | Name | Pin | Name |
4 | Data 2 | Data | No | → | 1 | TMS | 4 | PB-ADR0 | C10 | IO3
|
2 | Data 0 | Data | No | → | 2 | TDI | 6 | PB-ADR1 | E10 | l30P_1
|
13 | Select | Status | No | ← | 3 | TDO | 8 | PB-ADR2 | C11 | L29P_1
|
3 | Data 1 | Data | No | → | 4 | TCK | 10 | PB-ADR3 | D11 | L28N_1
|
18 | GND | — | 5 | GND | 1 | GND
| ||||
6 | VCC | 3 | Vcco (3.3V)
| |||||||
9 | Data 7 (6?) | Data | No | →| | ||||||
11 | Busy | Status | Yes | ←| | ||||||
12 | Paper-Out | Status | No | ←| |
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:
- 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. - The computer sets
TMS
low. - The computer reads a bit from
TDO
and sets a signal onTDI
, then clocks. - The computer repeats from (3) until there are no more bits to send or receive.
- 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.