NOW WE GIVE MUCH LUFF TO TEH XILINX SPARTAN TOO. YEAH YEAH!
Whoa-ho!
You are most happy to get down with the embedded programming technical nodes! Do not deny they are TEH HOTTTNESS!@#!! Right now you are drooling recklessly in anticipation, I will not keep you away any longer!
A short introduction (we love acronyms!) :
FPGA stands for
Field Programmable Gate Array, this a microchip which is capable of performing
digital logic operations. FPGAs have an internal computing architecture which may be altered by re-programming, this makes them very versatile devices capable of quickly solving specific problems. They resemble
CPLDs (
Custom Programmable Logic Devices) in that they may interface with on-chip I/O and perform aggregate logic operations which are often used for signal / data processing. Both may be re-programmed to perform different tasks.
One difference between the two device types is in how they load their program. A CPLD is typically "flashed" using an
in-system programmer connected to a
JTAG header or on a mass programming device at the suppliers factory, once flashed it will retain its "program" (the term program is used here in a very loose sense, the finer details of FPGAs can be found
here) until it is re-flashed. This means that as soon as it is turned on, it can begin to do it's job. Most FPGAs do not have an integrated
PROM, they are not capable of storing the task which they will perform within them and require an external source to load the code into it. Some have
battery backup which enables the program to stay in
memory until the battery fails, others contain logic which will auto-load code from a external PROM. In other circumstances,
microcontrollers are used to program the FPGAs over serial or parallel bus interfaces. There are many internal differences between how FPGAs and CPLDs work and what they are capable of, as well as the differences between Logic Devices and
CPUs /
micro-controllers. Reading up in the related nodes is recommended if you want to know more on this stuff.
Before going further, the two chips need to be connected together. There are specific
pins on the FPGA which are designated for serial programming, these should be wired up to
General Purpose I/O on the chip which will be doing the programming.
Notes about the code :
To simplify the code and facilitate
portability, it is useful to make
macro functions for the I/O control. Most serial wire communication protocols are fairly simple and can be implemented with minimal source code. Understanding the complete
protocol by reading the
specification documents will allow you to evaluate which features to include and which you can skip if I/O or
code space is limited.
The details
This writeup is about programming FPGAs over their serial interfaces, I have attempted to make the code reasonably portable.
OMG!?!! WELCOME BACK TO ALTERA APEX LAND!
The following code was originally written for programming an
Altera APEX FPGA from a
Texas Instruments DSP, the code was used in a design a few years ago. Much like other components in the high-tech industry, FPGA
product lines and features are subject to
constant upgrade and change. The material in this writeup is slightly dated and may be obsolete. (This code is not an implementation of programming via JTAG.)
Procedure :
- Take nCONFIG low, then do a low -> high transition on nCONFIG, and target device must release nSTATUS.
- The configuration data is placed one bit at a time on the DATA pin (LSB first), data is clocked continuously until CONF_DONE goes high.
- After all the data is transfered, DCLK must be strobed an additional 10 times to init the device.
- There is no handshaking in PS configuration mode, therefore the config clock speed must be below the specified device frequency. There is no maximum DCLK period, you may pause configuration by halting DCLK.
- If the device detects an error during configuration then it drives nSTATUS pin low.
- The programming device may then pulse nCONFIG low to restart the process.
- If the configuration is complete, but CONF_DONE does not go high, then it must be reconfigured.
- Upon powerup and before config, CONF_DONE is low.
- DATA should not be left floating, it should be driven high or low depending on which is more convenient.
- drive data bit to DATA, DCLK high period, DCLOKlow period, drive data again, etc.
// Definitions
#define FPGA_ERROR_DURINGCONFIG 1
#define FPGA_ERROR_AFTERCONFIG 2
#define cLo 0
#define cHi 1
#define CLOCKWAIT 30 // 30 nanoseconds min clock wait
#define STATUSWAIT 10 // 10 microseconds min status wait
// Macros
// (an sbit maps a single bit addressable register)
// replace ... with register/pin assignment
sbit FPGAconf_nCONFIG ... // output
sbit FPGAconf_DATA ... // output
sbit FPGAconf_DCLK ... // output
sbit FPGAconf_nSTATUS ... // input
sbit FPGAconf_CONF_DONE ... // input
// Functions
BYTE FPGAconf_ConfigMain(WORD);
extern unsigned int LED_val;
/*
---------------------------------------------------
Confitgure an ALTERA Apex FPGA via Serial Interface
Codesize is in WORDS
Returns
0: success
1: failure during config
2: device not configed
(as expected)after completion
---------------------------------------------------
*/
BYTE FPGAconf_ConfigMain(WORD CodeSize, WORD * CodeAddress)
{
WORD DataWord;
BYTE c;
BYTE status = 0;
FPGAconf_nCONFIG = cLo;
// wait for nSTATUS to go low
while ( FPGAconf_nSTATUS );
// wait for nSTATUS to go low
delay_microsec ( STATUSWAIT );
// create a low to high transition to start
FPGAconf_nCONFIG = cHi;
// wait for nstatus to go high
while (!( FPGAconf_nSTATUS ));
// loop until the fpga signals that it is done
while (!( FPGAconf_CONF_DONE ))
{
// there is no more code to feed into the device
// then we have an error condition
if (CodeSize == 0)
{
status = FPGA_ERROR_DURINGCONFIG;
break;
}
// read a WORD of config data fron memory
DataWord = *CodeAddress;
//loop through the 16 bits of the word
for(c=0;c < 16;c++)
{
//start with the LSbit and work our way up
if ((DataWord >> c) & 0x01)
FPGAconf_DATA = cHi;
else
FPGAconf_DATA = cLo;
// set clock high
FPGAconf_DCLK = cHi;
// wait for at least 30 ns
delay_nanosec ( CLOCKWAIT );
// set clock back low
FPGAconf_DCLK = cLo;
// wait for at least 30 ns
delay_nanosec ( CLOCKWAIT );
// if we are done then drop out
if ( FPGAconf_CONF_DONE )
break;
// if the config failed then the fpga
// alerts us, drop out and restart.
// nStatus must be high after
// a write cycle completes.
if (!( FPGAconf_nSTATUS ))
{
status = FPGA_ERROR_DURINGCONFIG;
break;
}
}
CodeAddress++;
CodeSize--;
}
for(c=0;c < 10;c++)
// next we have to pulse the clock 10 more times
// after finishing to get the fpga running
{
// set clock high
FPGAconf_DCLK = cHi;
// wait for at least 30 ns
delay_nanosec ( CLOCKWAIT );
//and the clock goes back low
FPGAconf_DCLK = cLo;
// wait for at least 30 ns
delay_nanosec ( CLOCKWAIT );
}
// config done, if CONF_DONE is still low
// then something is busted and the device
// is not configured as expected
if (!( FPGAconf_CONF_DONE ))
{
// error after config done
status = FPGA_ERROR_AFTERCONFIG;
}
return ( status );
}
The Xilinx Spartan code :
The following code is written to program a XILINX FPGA from a microcontroller using a bitstream file (.bit). A minimalist approach has been taken that operates just on the edge of the specfication.
Procedure :
- Init : Take RSET low for FPGA_CONF_INIT_CLKS, then take RSET HIGH
- Wait : Wait FPGA_CONF_DELAY_CLKS before starting data transfer
- Send : Clock in all data bitwise on DAT using CLK valid high
Structure of .bit file
The layout of the bitstream file allows it to control the FPGA requiring minimal intervention from the programming code if desired. The FPGA will ignore any data sent to it (non-config data header) until the FPGA configuration initialization byte sequence is detected, the configuration data follows, and then the FPGA boot sequence is also conveniently embedded in the file. This sets the device up, loads the code and then sets it running.
- non-config data header
- fpga config init byte sequence
- fpga config data
- fpga boot byte sequence
// definitions
// number of clocks to hold RSET low during init
#define FPGA_PROG_INIT_CLKS 20
// number of clocks to wait after releasing RSET
#define FPGA_PROG_DELAY_CLKS 20
// clock delay (length between clock transitions)
// this should be adjusted based upon the minimum
// timing in the spec, and the execution speed of
// the delay function on the hardware on which it
// is running.
#define FPGA_PROG_CLK_LENGTH 0
// pin assignments
sbit FPGA_PROG_CLK = (pin);
sbit FPGA_PROG_DAT = (pin);
sbit FPGA_PROG_RSET = (pin);
// function definitions
void Config_FPGA_Delay ( unsigned char );
void Config_FPGA_Xilinx_Init ( void );
void Config_FPGA_Xilinx_Send( int , unsigned char * );
// functions
/* --------------------------------------------
Variable delay used to time clock width
Set this as needed
--------------------------------------------*/
void Config_FPGA_Delay(unsigned char nDelay)
{
return;
}
/* ------------------------------------------
Initialize FPFA for programming.
This sets up the FPGA to recieve a serial
bitstream using Configure_FPGA_Send()
-----------------------------------------*/
void Config_FPGA_Xilinx_Init()
{
unsigned char DelayIdx;
// Stage 1 : Init
// (take RSET low for N clocks and release)
FPGA_PROG_RSET = 1; // RSET high
FPGA_PROG_RSET = 0; // RSET low
for (DelayIdx = 0; DelayIdx < FPGA_PROG_INIT_CLKS; DelayIdx++)
{
FPGA_PROG_CLK = 1; // CLK high
Config_FPGA_Delay(FPGA_PROG_CLK_LENGTH);
FPGA_PROG_CLK = 0; // CLK low
Config_FPGA_Delay(FPGA_PROG_CLK_LENGTH);
}
FPGA_PROG_RSET = 1; // RSET high
// Stage 2 :
// Wait for FPGA (wait N clocks after releasing RSET)
for (DelayIdx = 0; DelayIdx < FPGA_PROG_DELAY_CLKS; DelayIdx++)
{
FPGA_PROG_CLK = 1; // CLK high
Config_FPGA_Delay(FPGA_PROG_CLK_LENGTH);
FPGA_PROG_CLK = 0; // CLK low
Config_FPGA_Delay(FPGA_PROG_CLK_LENGTH);
}
}
/* ---------------------------------------
Send configuration data to FPGA.
Will be called multiple times as new data comes in
until the configuration process is complete.
This works because the programming device controls
the programming clock and the FPGA does not have
a maximum clock period limit. If there was
sufficient memory, then this could be done
all at once instead of in chunks.
nBytes : number of bytes to write
DataSource : memory location of bytes
Specifications :
MSBit should always be written first
---------------------------------------*/
void Config_FPGA_Xilinx_Send(int nBytes, unsigned char * ConfDataSource)
{
// Stage 3 : Send Config Data
unsigned char ConfData;
unsigned char BitIdx;
int DataIdx;
// step through each byte that is to be sent
for (DataIdx = 0; DataIdx < nBytes; DataIdx++)
{
ConfData = *ConfDataSource;
// each byte is sent one bit per clock pulse
// (data is valid high)
for (BitIdx = 0; BitIdx < 8; BitIdx++)
{
if (ConfData & 0x80)
{
FPGA_PROG_DAT = 1; // DAT high
}
else
{
FPGA_PROG_DAT = 0; // DAT high
}
ConfData = ConfData << 1;
FPGA_PROG_CLK = 1; // CLK high
Config_FPGA_Delay(FPGA_PROG_CLK_LENGTH);
FPGA_PROG_CLK = 0; // CLK low
Config_FPGA_Delay(FPGA_PROG_CLK_LENGTH);
}
ConfDataSource++;
}
}