-
Spartan II configuration
I needed a simple way of configuring a Xilinx Spartan 2 FGPA using ubiquitous, cheap parts. The Xilinx configuration chips are probably the easiest but a little expensive so I ended up using a serial EEPROM and PIC microcontroller with the FPGA in “slave serial” mode. Not the fastest method, but good enough.
The choice of microcontroller wasn’t going to be much of a problem, any of the 8-pin PICs would do – I already had some 12F675s so I used one of those. Both the serial EEPROM and the PIC will work at 3.3 Volts which is the FPGA’s native I/O voltage (the IOs can be 5 volt tolerant on the Spartan2 but I wasnt 100% certain the config pins were too). The PIC’s internal 4MHz clock could be used to save parts and pins without sacrficing too much speed. The EEPROM has a maximim data rate of around 500KHz with a 1 microsecond clock high / 1 microsecond clock low duty cycle at 3.3 Volts anyway – so the 1 microsecond instruction clock of the PIC wouldnt be slowing things down too much.
Next I needed a serial EEPROM which a) was big enough to hold the configuration file and b) I could program with the cheapo “Willem Enhanced EPROM programmer” I bought off eBay :) The Spartan 2 FPGA chips I was targetting were the low-end XC2S15 and XC2S30, these have config files of less than 64K Bytes so it just a case of scanning through the list of serial EEPROMs supported by my Willem programmer. The AT24C512 looked like a suitable candidate, its a common cheap chip with readily available datasheets etc.
Before reading up on the I2C serial protocol used in the EEPROM I kind of expected the data would just clock out in a stream of bits every input clock, but of course there’s a little more to it than that. Firstly there are commands to tell the EEPROM what to do (read or write, from which address etc) and secondly each data byte requires a 9th clock cycle for an “acknowledge” signal. Electrically its all very simple, just two pins form the comms channel, a clock input pin and a serial data input/output pin. The Serial Data pin is an input with open-drain output so that multiple device outputs can be connected together on a simple bus, as such it requires a pull-up resistor around 2Kohms.
As mentioned, the EEPROM’s Serial Data pin is bidirectional and the PIC needs to be able to pull it low when sending commands etc. The PIC I was using doesnt have an open drain output driver so I tried a few different ideas: Switching the port data direction of a PIC pin from input to output (driven with a zero) would sink the current from the Serial Data pull-up resistor and make the pin low – this worked but felt a bit bodgy and cumbersome (especially having to switch register pages in the PIC software to access the port IO direction register). Next I tried simply driving a transistor, (its collector to the serial_data pin) this didnt seem to work well (my scope showed the switching was delayed too much). Finally I realized I could just use a diode (1N4148, cathode to PIC) to pull the line low when the PIC port pin was loaded with a zero (a “one” would be blocked by the diode). The Vin_Lo spec of the EEPROM is 0.3 x Vcc so at 3.3 Volts, even with a voltage drop up to 0.7V on the silicon diode, the pin would still be below the threshold for a “zero” – I settled on this method.
The I2C EEPROM protocol…
The EEPROM clocks in data on the rising edge of the CLOCK line and outputs data on the falling edge of the clock.
You can only change the Serial_Data line when the clock line is low, otherwise the EEPROM thinks you are sending it a START command (data falls during high clock) or END command (data rises during high clock).
Nine clocks are required per byte. When sending command bytes to the EEPROM, the EEPROM responds with an acknowledge during the 9th clock. When receiving bytes from the EEPROM, you must send an acknowledge signal on the 9th clock (see below).
There are various ways of reading and writing data (random, sequential) etc. For the FPGA config project I was only interested in sequencially reading the chip (I left the writing to my EEPROM programmer PCB) so that’s what I’ll describe here.
To set up a sequential read from the EEPROM, the process is:
Send a START command (pull data low whilst clock is high)
Send the CONTROL byte “10100000” (Set address)
Send high ADDRESS byte (zero to start from beginning, obviously:)
Send low ADDRESS byte (“”)*
Send another START command (pull data low whilst clock is high)
Send a CONTROL byte “10100001” (request read)
(Remember, after each byte, release the data line and send an additional clock to receive the EEPROM’s acknowledge.)
* Some smaller capacity I2C type EEPROMs use only one address byte – check datasheets
Now, to read the data…
LOOP:
Bit 7 of the byte addressed above will be present on the Serial_Data pin
Clock high, Clock low
Bit 6 appears on the Serial_Data Pin
Clock high, Clock low
Bit 5 appears on the Serial_Data Pin
Clock high, Clock low
Bit 4 appears on the Serial_Data Pin
Clock high, Clock low
Bit 3 appears on the Serial_Data Pin
Clock high, Clock low
Bit 2 appears on the Serial_Data Pin
Clock high, Clock low
Bit 1 appears on the Serial_Data Pin
Clock high, Clock low
Bit 0 appears on the Serial_Data Pin
If you’re done reading goto “ESCAPE” otherwise you have to now send an Acknowledge pulse to the EEPROM.. Like this:
Pull serial_data pin low – This is our Acknowledge to the EEPROM
Clock high, Clock low – “”
Release serial_data pin – “”
(the internal address counter is incremented after each byte read so there is no need to specify another address).
Goto LOOP
ESCAPE:
Release serial_data pin (this is a “non-ack”)
Clock high, Clock low
Pull data low ready for STOP command
Send a STOP command (pull data high whilst clock is high)
Naturally, unless the PIC itself is reading in the data from the EEPROM for internal use you’ll need your PIC code to generate a seperate clock signal for your target device (you dont want it seeing the 9th bits / control commands etc). You’ll always going to get a gap of between the bytes where the Acknowledge signals are taking place but most of the time it wont be an issue.
On the FPGA side of things..
Serial configuration is quite straightforward. The various pins (other than the usual VCCint, VCCIO, GND etc) involved are shown below:
INIT – goes high when FPGA is ready for config data (low if config fails CRC check)
DONE – goes high when FPGA is configured
PROGRAM – pull low to reconfigure the FPGA.
D_In(D0) – Serial Data in
C_CLK – Data clock in
M0,M1,M2 – Config mode pins (see below)
When using slave serial mode (ie: external clock) the FPGA’s config mode pins M0/M1/M2 need to be pulled high. The Serial_Data line from the EEPROM and Output_Clock from PIC were connected to the FPGA’s D_In/D0 and C_CLK pins respectively. INIT,DONE and PROGRAM were pulled high via 3.3Kohm resitors and connected to inputs on the PIC.
(The Spartan2 data sheet mentions that CS and WRITE are not used during slave serial config but should not be toggled during the process so I pulled these lines up, as well as the JTAG pins (TD0,TDI,TMS.TCK) just to be on the safe side).
Upon power up, my PIC code waits a fraction of second for things to settle, ensures the INIT line is high, sends the config file, checks to make sure the DONE and INIT lines are high and then busy waits until PROGRAM is pulled low, at which point the config sequence restarts. If, after the config file was sent, either INIT or DONE are low, the code briefly switches the data direction of the PIC pin connected to the PROGRAM line, this pulls it low signalling the FPGA to reboot – the PIC then restarts the config process. This way the PIC or an external switch can cause the FPGA to reconfigure.
The config file:
The Xilinx ISE (7.1) software outputs a config file in raw binary (as a *.bit file in the project’s folder) when you click “Generate Programming File” – Note: This version has some header data which you can remove with a hex editor – the real config data begins with $FFFFFFFF,$AA995566. Alternatively, to save trimming the header each time, right click “Generate Programming File”, choose “Properties” and tick the “Create binary configuration file” option. Then when you generate the config file you’ll have a *.bin file with no header info ready to burn straight to the EEPROM. (The FPGA accepts the bit sequence with MSB first and the EEPROM outputs the bytes in the same manner, so there’s no need to change the bit order or anything).
Naturally for the larger FPGA’s you need much bigger EEPROMs. Its possible to use multiple AT24C512s, using the hardwired device address bits as chip selects but it probably wouldnt be cost effective – you’d probably want a faster solution too.
And.. that’s about it – my PIC code and schematic is here. The code could probably be optimized to gain some extra speed (I avoided using read-modify-write instructions on the GPIO port for example)
Links / Further Reading:
AT24C512 Datasheet
The I2C bus FAQ
PIC 12F629 Datasheet
Xilinx Spartan 2 Datasheet 1[EDIT] Here’s an update of my previous PIC-based FPGA configuration solution. In this version, larger and faster SPI EEPROMs are used with a PIC 16F628 – the extra IO ports allow two FPGAs to configured from the same EEPROM if required. The source code can easily be adapted to other PICs (EG: using a 16F627 would save a few pence:)
I wont go into great detail here, there’s source with full info and a schematic in the archive. If you need a simple DOS-based 25XXX SPI EEPROM programmer, try my own here. Download: PIC & SPI Single/Dual FPGA configurator source and schematic