Python - Configuring the CII via an FT232R
This is the final post in a series of three to explain how to use the FT232RL USB to UART Bridge to program a Cyclone II FPGA.

The first part in the series describes how to compile and install the hardware drivers in Ubuntu that we need to use to access the FT232RL functions. The second part describes the hardware modifications and connections required to interface the FT232RL to SparkFun’s Cyclone II Breakout board.

Let me give you a quick high-level overview of what we must do in order to configure the Cyclone II using the FT232RL, after which we will dive head first into all the juicy details.


High Level Steps:


In Quartus II:

  1. Convert the FPGA Config file from SOF to RBF (Raw Binary File) in Quartus.

In Python:

  1. Read and process the .rbf configuration file into a serial bit stream. We then need to “weave” in the DCLK transitions to create a parallel byte stream which will be sent to the FT232RL. More on that in a bit.
  2. Open a connection to the FT232RL hardware, and configure it for Bit Bang mode operation.
  3. Trigger the Cyclone II into configuration mode.
  4. Push the bit stream to the FPGA.
  5. Close our FT232RL connection.



Generate the RBF File (Quartus II)

Once you “compile” (synthesis) an FPGA design in the Quartus II software, the tool generates a file which contains all the information required to configure your FPGA. However, the default file type is a .SOF which I’m not sure how to manage. However there are other file types available to use for configuration, so much so that Quartus II has a built in tool to convert between formats.

We will be converting the SOF file to an RBF, Raw Binary File which appears to be easier to work with.

The graphic below highlights these steps you will need to take:
(First, open the Convert Programming File tool from the menu.)

  1. Select the Programming file type as “Raw Binary File (.rbf)
  2. Set the new file’s path and name.
  3. Add the source SOF file to be converted. (This file should be in the root directory of your FPGA’s project after you synthesis the design)
  4. Click on the Properties button and enable Compression. This reduces the number of bits we need to send. (The CII has an onboard decompression engine)
  5. The final step is to click the “Generate” button.

Convert to RBF


Python Code

First, I just want to throw all the code out there for people who would like to copy & paste it in it’s entirety, next I’ll break it down and explain its components.

'''
Created on Feb 21, 2011

@author: Chris Zeh

'''

from binascii import hexlify
import ftdi

NCONFIG_HIGH = 0x4 #Bitbang Pin #3 (NCONFIG)
DCLK_HIGH = 0x2    #Bitbang Pin #2 (DCLK)


    
def InitFDTI():
    _ftdic = ftdi.ftdi_context()
    ftdi.ftdi_init(_ftdic)
    ftdi.ftdi_usb_open(_ftdic,0x0403,0x6001) #The last 2 params identify the Vendor_ID and Product_ID programmed into your FT chip. 0x6001 is FT232RL
    #Go here to find the default Product_ID for different parts:
    #www.ftdichip.com/Documents/technicalnotes/tn_100_usb_vid-pid_guidelines.pdf

    ftdi.ftdi_enable_bitbang(_ftdic,0xFF)
    ftdi.ftdi_set_baudrate(_ftdic,9600*16) #Not certain what is the fastest setting possible yet.
    return _ftdic



def StartConfig(ftdic):
    #To start the config process we need to take FPGA's NCONFIG pin LOW then HIGH
    ftdi.ftdi_write_data(ftdic,chr(0),1)
    ftdi.ftdi_write_data(ftdic,chr(NCONFIG_HIGH),1)
    

    
    
def toBINstr(hexstr):
    #Converts a hex string ('\xff' to a binary str 11111111)
    
    data_byte = eval('0x' + hexlify(hexstr))
    bindata = ''
    for b in range(7,-1,-1):
        bindata+=str(data_byte>>b & 1)
                
    return bindata


def ReadFile(path):
    
    f = open(path,'r')
    dat = f.read()
    f.close()
    return dat #Read all of the data into a string




ftdic = InitFDTI()

print 'Device Opened'
StartConfig(ftdic)


data = ReadFile('/home/chris/Projects/Saturn Project/FPGA Code/Hello World/Hello_World_Compressed.rbf')
NewData=''
print 'Preparing the Config Data'


#We set the current state of all 8 Bit-Bang pins of the FT232RL by sending it one byte.
#      each bit in said byte cooresponds to one pin on the FT232RL.

#So we need to convert the bits in the RBF file into bytes to send to the FT232RL, setting the DATA0 pin and then "clocking" it in.

#The following generates the byte stream from the RBF file.
#Basically, every config bit we send to the FPGA needs to be clocked in.
#So take the DCLK low. Set the DATA0 to the value of the bit we want
#to send. Then take the DCLK high. Repeat for all bits in the file.
#You can see we need to send 3 Bytes to the FT232RL in order to send one
#configuration bit.

for byte in range(0,len(data)):
    bits = toBINstr(data[byte])
    for bit in range(-1,-9,-1): #The strange range parameters are to reorder the bits in the RBF correctly for the FPGA
        NewData += chr(int(bits[bit]) | NCONFIG_HIGH) #Set DATA0 to the bit, Set DCLK Low. (Be sure to keep holding up the NCONFIG pin)
        NewData += chr(int(bits[bit]) | NCONFIG_HIGH | DCLK_HIGH) #Clock the data in
        NewData += chr(int(bits[bit]) | NCONFIG_HIGH) #Falling edge of the clock, this can probably be removed but I have to check the timing to be sure (setup/hold times).


print 'Writing to the FPGA'
#The following code is/was needed for version 0.17-1 of the ftdi driver (There is a bug where too large a buffer kicks back a -14 memory error)
#Re = ''
#for x in range(1,24):
    #Re = NewData[(x-1)*2**18:x*2**18]
    #rc = ftdi.ftdi_write_data(ftdic,Re,len(Re))
    #print rc


#Version 0.18 allows for the full buffer to be passed:
rc = ftdi.ftdi_write_data(ftdic,NewData,len(NewData))
print 'Configured!'



ftdi.ftdi_usb_close(ftdic)



Breaking down the code.
Alright, we’ll skip past the declarations for now and start with the code in the order it executes.

First step is to initialize our connection with the ftdi driver and connect to the FT232RL device:

ftdic = InitFDTI()
def InitFDTI():
    _ftdic = ftdi.ftdi_context()
    ftdi.ftdi_init(_ftdic)
    ftdi.ftdi_usb_open(_ftdic,0x0403,0x6001) #The last 2 params identify the Vendor_ID and Product_ID programmed into your FT chip. 0x6001 is FT232RL
    #Go here to find the default Product_ID for different parts:
    #www.ftdichip.com/Documents/technicalnotes/tn_100_usb_vid-pid_guidelines.pdf
 
    ftdi.ftdi_enable_bitbang(_ftdic,0xFF)
    ftdi.ftdi_set_baudrate(_ftdic,9600*16) #Not certain what is the fastest setting possible yet.
    return _ftdic

If you are using a device other than the FT232RL you might need to modify the Product_ID in the ftdi_usb_open function. See the comments in the code for more details.

The ftdi_enable_bitbang function configures the 8 bit-bang pins to either an Input or an Output. For this example I’m setting all 8 pins to be Outputs, hence the 0xFF. If you wanted all the pins to be Inputs you would send 0x00, and if you wanted half to be input and half output: 0xF0.

According to the documentation this ftdi_enable_bitbang function is depreciated and favors the new function ftdi_set_bitmode. This new function wasn’t working for me, but maybe you’ll have more luck with it than I did.

The next step is to signal to the FPGA that we are going to send it a configuration stream. We need to yank its NCONFIG line low then high:

StartConfig(ftdic)
def StartConfig(ftdic):
    #To start the config process we need to take FPGA's NCONFIG pin LOW then HIGH
    ftdi.ftdi_write_data(ftdic,chr(0),1)
    ftdi.ftdi_write_data(ftdic,chr(NCONFIG_HIGH),1)

We send data to the FT232RL as a Byte of data (ftdi_write_data), with each bit corresponding to the state of each pin. You can see I defined the NCONFIG_HIGH to set the 3rd pin high:

NCONFIG_HIGH = 0x4 #Bitbang Pin #3 (NCONFIG)
DCLK_HIGH = 0x2    #Bitbang Pin #2 (DCLK)$

Next I load the FPGA’s configuration file into memory:

data = ReadFile('/home/chris/Projects/Saturn Project/FPGA Code/Hello World/Hello_World_Compressed.rbf')

Next we need to process the RBF file to get it ready to send to the FT232RL and the Cyclone II:

for byte in range(0,len(data)):
    bits = toBINstr(data[byte])
    for bit in range(-1,-9,-1): #The strange range parameters are to reorder the bits in the RBF correctly for the FPGA
        NewData += chr(int(bits[bit]) | NCONFIG_HIGH) #Set DATA0 to the bit, Set DCLK Low. (Be sure to keep holding up the NCONFIG pin)
        NewData += chr(int(bits[bit]) | NCONFIG_HIGH | DCLK_HIGH) #Clock the data in
        NewData += chr(int(bits[bit]) | NCONFIG_HIGH) #Falling edge of the clock, this can probably be removed but I have to check the timing to be sure (setup/hold times).

Here is the key sentence from the Altera CII documentation explaining how the data needs to be sent to the FPGA:

send the configuration data on the DATA0 pin one bit at a time. If you are using configuration data in RBF, HEX, or TTF format, send the least significant bit (LSB) of each data byte first. For example, if the RBF contains the byte sequence 02 1B EE 01 FA, you should transmit the serial bitstream 0100-0000 1101-1000 0111-0111 1000-00000101-1111 to the device first.

Now, every bit we send on DATA0 needs to be clocked-in, which is why we set the DCLK low, put the data on the DATA0 pin, then take DCLK high. You can see for each configuration bit we want to send to the FPGA, we are sending 3 bytes to the FT232RL.

(If you are trying to use this code on Windows, you might have to adjust the code to avoid Endian problems)

Now that we have the byte stream generated we need to send it to the FT232RL. Fortunately, the ftdi library makes this pretty easy for us:

rc = ftdi.ftdi_write_data(ftdic,NewData,len(NewData))

I had a problem using the older version of the libftdi driver (v0.17-1) where sending the full buffer in one go would kick back a -14 memory error. The following code was a workaround to send the data in smaller bits:

#The following code is/was needed for version 0.17-1 of the ftdi driver (There is a bug where too large a buffer kicks back a -14 memory error)
#Re = ''
#for x in range(1,24):
    #Re = NewData[(x-1)*2**18:x*2**18]
    #rc = ftdi.ftdi_write_data(ftdic,Re,len(Re))
    #print rc

Finally, let’s close our connection with the ftdi device:

ftdi.ftdi_usb_close(ftdic)

And that is all there is to it! After running the code your FPGA should be up and running.

I had a lot of problems initially because of Ubuntu’s security blocking me from accessing the FT232RL, so be sure to read my first post describing how to setup a udev rule to allow non-root access to the device.

I recommend looking at the libftdi documentation as well as the Cyclone II Configuration documentation if you have any problems with the code and/or configuration process.

Best of luck. Please leave a comment and let me know if this code has helped you, or if you have any questions.

You can download the Python file here:

Download
CII_Config_ExampleSimple.zip

Contains:

  • CII_Config_ExampleSimple.py