Tuesday, June 6, 2023

Xilinx SPI flash programming with MCU

Sometimes we wish we could program the SPI flash connected to Xilinx FPGA using some MCU. This is easy if the SPI flash pins are accessible. But what can we do if they are not?

Option 1:

If we want we still can use an external MCU for this we need to add "SPI bypass" wires into all designs. SPI flash pins are accessible for the design, we can just wire them to the pins that go to the MCU. We need to use STARTUP directive to get access to the SPI clock, other pins are simply assignable in the constraints.

We have to use JTAG tools once to program the SPI flash with the design that includes the SPI bypass. And later we need to be sure only program images that include the bypass logic. To make it failsafe we can also write a golden image to the end of the SPI flash that we never erase, so if the first image is corrupt the FPGA would still configure with the golden image.

Option 2: We connect the SPI flash to FPGA embedded processor (MicroBlaze) and implement the SPI flash update with Microblaze code, while the external MCU simply gives the code bytes to program into the flash.

Xilinx JTAG configuration with MCU

It is sometimes needed to configure a Xilinx FPGA via JTAG with MCU. This is possible and doable.

Option 1: we look at the XC3SPROG source code and implement similar functions in our code. This is possible if XC3SPROG already supports our FPGA. XC3SPROG has not developed much in the last few years so the FPGA you need may not yet supported. You can try to add support your self.

Option 2: we implement a FULL SVF player function in your code. This is a possible way but you need to be able to create a line buffer that is as large as the SVF file. This is possible when you run embedded Linux, but not doable if you have a microcontroller in your system.

Option 3: This is a method that can be implemented on pretty much any MCU. We split the programming into 3 operations:

  • playback_tms_tdi(constant_buffer_HEADER);
  • playback_tdi( BIT_FILE );
  • playback_tms_tdi(constant_buffer_FOOTER);

Function playback_tms_tdi takes a string of bytes and pushes the lower two bits to TMS and TDI pins of the JTAG interface then toggles TCK

Function playback_tdi takes raw data and pushes it to the TDI pin and toggles TCK, this is where the actual bitstream is sent to the device.

Now we only need the header and footer arrays, right? Ok, we can easily get the header/footer from Xilinx generated SVF file, well in SVF format. We can use a text editor to extract the header/footer portion. But how to get the constant array that is suitable for our simple playback function? For this we will use an SVF player from GitHub! This SVF player parses the SVF file and creates an array with TMS TDI in the lowest bits, exactly what we need for our simple playback functions. We can a bit modify the header and footer SVF before conversion by removing all READ commands we do not need them when we are doing plain bitfile programming.