Search code examples
cfpgastm32f4ice40

How to program Lattice iCE40 ultra with a microcontroller


I am trying to program a iCE40 ultra FPGA with a stm32F4 microcontroller and I am trying to figure out how to load the configuration file onto the micro so that it can be sent to the FPGA by SPI to program it. The application note says that this can be done and has some pseudo code but it seems like it is for use with a computer and not a microcontroller. Both the micro and FPGA are on a PCB that I designed and have a SPI channel between them. This will get used after the programming for just communication between the two.

The configuration file is a .bin file or .hex file and I am unsure of how to load one of these files onto the stm32 and send it to the FPGA. I am trying to do it this way because I was hoping to not have to buy a programming cable and include flash memory on my PCB to store the program since I already need SPI communication between the micro and FPGA for the application I was hoping to make things easier on my self and may have done the opposite...

Here is the function that I have written to send the file across. I know that I need to open the file somehow and send it across. All of the other timing comes from the programming guild for the FPGA.

static void FPGA_Programming(void){

    char FPGA_TimeOut = 0; // Error timeout
    uint8_t dummyBits[7]; 
    for(char i = 0; i < 7; i++){ // loads dummy bits to be transmitted
        dummyBits[i] = 0b01010101;
    }
    /* Write FPGA config pin and SS to low for at least 200 ns to reset and start FPGA
     * in SPI Slave Configuration
     */
    HAL_GPIO_WritePin(FPGA_Config_GPIO_Port, FPGA_Config_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(FPGA_SS_GPIO_Port,FPGA_SS_Pin, GPIO_PIN_RESET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(FPGA_Config_GPIO_Port, FPGA_Config_Pin, GPIO_PIN_SET); // Set high to turn on FPGA THIS PIN SHOULD STAY HIGH!
    HAL_Delay(2); // Wait for FPGA to clear sys memory
    // SEND FPGA CONFIG FILE 
    while(!HAL_GPIO_ReadPin(FPGA_CDone_GPIO_Port, FPGA_CDone_Pin)){ // wait for CDone to go high when config is done and successfull 
        HAL_Delay(1);
        if (FPGA_TimeOut == 5) { // if 5 milliseconds have passed and CDone has not gone low
            // send to debug FPGA ERROR Programming
            break;
        }
        FPGA_TimeOut++;
    }
    HAL_SPI_Transmit(&hspi1, dummyBits, 7, 10 ); // wait for FPGA to start

}

Any help with loading the file and opening it with the micro would be a great help.


Solution

  • The ice40 image needs to be included in the stm32F4 image. There are at least two possibilities.

    • As suggested in an other answer. Let the linker do the job. The GCC linker can include arbitrary binary data, several ways to do this are outlined here. This require use of objcopy, or modifications to the linker script.
    • Make a small utility to transform the binary ice40 image to a C data object, something like this (warning, code not well tested):

      #include <stdio.h>
      int main( void ) {
              int i=0,c;
              printf( "#include <stdint.h>\nconst uint8_t ice40image[] = {\n" );
              while ( (c = getchar()) != EOF ) 
                      printf( "%#2.2x%s", c, (++i & 15) ? "," : ",\n" );
              printf( "};\n#define ICE40IMAGE_LEN %d\n", i );
              return 0;
      }
      

      The binary ice40 image is transformed into a header file:

      ./bin2uint8_t < ice40image.bin > ice40image.h

      and ice40image.h is included in your stm32F4 program. I prefer this method, it lends itself to later refinements such as compressing the FPGA image (it often has long runs of 0x00). In my opinion, a very good utility for this is 'icecmpr' in project iceStorm, see https://github.com/cliffordwolf/icestorm/tree/master/icecompr