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++;
    }
}

Log in or registerto write something here or to contact authors.