When working with certain ARM Cortex-M1 microcontrollers that have an integrated FPGA fabric, it is necessary to merge the FPGA bitstream file with the Cortex-M1 application hex file into a single binary that can be flashed onto the device. This allows the FPGA fabric to be programmed with custom logic while also loading the Cortex-M1 application code. The key steps are understanding the memory map, generating the bitstream, compiling the Cortex-M1 code, merging the files, and flashing the binary. With the right tools and basic knowledge, this process can be straightforward.
Overview of the Cortex-M1 and FPGA Fabric
The Cortex-M1 processor is a 32-bit ARM microcontroller optimized for low power, low cost applications. Some versions integrate an FPGA fabric on the same chip, allowing custom logic implementation. The FPGA and Cortex-M1 have separate memories and interfaces. The FPGA accesses its own block RAM and flash memory for configuration. The Cortex-M1 loads code and data into the system SRAM and flash. To utilize both, the FPGA bitstream and Cortex-M1 code must be combined.
Understanding the Memory Map
The system memory map provides the addresses for the memories and interfaces. There are regions for the Cortex-M1 code/data, FPGA fabric, peripherals, etc. Addresses may be pre-defined or configurable. It is critical to understand the memory map to properly combine the bitstream and firmware.
For example, the FPGA bitstream may reside in a dedicated region from 0x6000_0000 to 0x7FFF_FFFF. The Cortex-M1 code starts at 0x0000_0000. Configuring the memory map incorrectly during merging could result in overwriting the Cortex-M1 firmware with the bitstream!
Generating the FPGA Bitstream
The FPGA bitstream contains the configuration data defining the custom logic implementation. It is generated from HDL code (Verilog, VHDL) using the FPGA vendor’s synthesis and place-and-route tools. For example, Xilinx Vivado can take RTL code and output a bitstream for Xilinx 7-series FPGAs.
The target device and memory interfaces must be specified. Some tools support generating a raw binary bitstream for easy integration with the Cortex-M1 firmware. The bitstream data should be placed in a binary file for merging later.
Compiling the Cortex-M1 Application Code
The Cortex-M1 application firmware can be written in C/C++ or assembly. It should be compiled into a hex file targeting the appropriate memory range. Most ARM compilers, like ARM Compiler 6, can output Intel HEX or Motorola S-record formats.
Make sure to compile for the correct Cortex-M1 variant. Optimization flags can reduce code size. Debug symbols can be omitted. The linker script should match the expected memory map. This ensures the hex file contains the code and data properly mapped for merging.
Merging the Bitstream and Hex Files
With the bitstream and hex files generated, a merge utility combines them into a single binary for flashing. The open-source Snickerdoodle project provides some simple Python scripts for merging.
The merge utility overlays the bitstream data over the firmware hex file, placing it in the address range configured earlier. With both pieces in place within the merged output, it can be flashed to the device.
Flashing the Combined Binary
The merged binary can be flashed to the device using the standard Cortex-M1 interface, like SWD or JTAG. On reset, the bootloader loads the FPGA bitstream portion to the programmable fabric. Then it copies the Cortex-M1 application code from flash to SRAM for execution.
With the FPGA programmed and Cortex-M1 running, they can interact according to the defined interfaces. This allows custom hardware offloading tasks from software for greater efficiency.
While merging the FPGA bitstream and Cortex-M1 firmware requires some upfront effort, the result is a powerful system with software and hardware working in harmony. With diligence during the build and merge process, the integration can be smooth and effective. The Cortex-M1’s tight coupling with FPGA fabric opens up many possibilities for embedded developers.