FPGA Hardware Loading Loading of the FPGA hardware must obviously occur before a Nios Application can be loaded. Here is a brief synopsis of how to load fpga hardware. (Altera did fortunately provide enough information on how to do this.) • FPGA must be in ‘Passive Serial’ mode • Incoming hardware load data arrives LSBit first on pin ‘Data0’ • Incoming hardware load data originates from ‘.rbf’ file generated by Quartus environment. (rbf-Raw Binary File) • Strobe ‘nconfig’ line low-to-high to start hardware • Read ‘config_done’ • Read ‘init_done’ line to verify hardware is initialized • Read ‘nstatus’, is high if everthing is ok The Boot-Loader Application In the NiosII IDE you must create your own ‘boot-loader’ project. A Nios project relies on the fpga hardware definition file (.ptf) provided by the Quartus/SOPC environment. The Nios IDE then knows how to generate start-up code and provide drivers for all of the peripherals by the hardware definitions. However, a boot-loader does not necessarily need access to all of the hardware peripherals. Allowing a full hardware definition could possibly consume too much of the available ‘onchip_memory’ device. Therefore, you may have to manually edit the .ptf file. Simply remove any hardware definition references to any devices that are not critical for your boot-loader application. The memory mapping of the devices will remain the same as the ‘application’ hardware that will be loaded by the external boot-loader device. The result of omitting these hardware definitions will result in a smaller memory foot-print for your on-chip boot memory device. Boot-Loader Application Hardware Round Trip…Chicken vs Egg When you perform a build on your ‘Boot-Application’ an output file named ‘onchip_memory.hex’ is generated for each on-chip memory. (Normally Quartus has its own ‘onchip_memory.hex’ file when booting from flash.) However, now your boot-application generates this file. You now have to give ‘onchip_memory.hex’ back to Quartus so it can rebuild the hardware correctly. Quartus, in turn hands the Nios environment a new .ptf, .sof , and .rbf files. Therefore, to get hardware and software to know each other properly, you may have to build hardware twice before everything is kosher with ‘boot’ and ‘uploaded’ application hardware/software cohesiveness. The first hardware build gets used so Nios IDE can compile boot-loader code, the second hardware build is needed after the nios software compile generates ‘onchip_memory.hex’, which Quartus then uses to put the boot-code in hardware. Finally, compile the Nios code again with the .ptf and .sof files. You will have to edit the .ptf to omit unnecessary hardware definitions for the boot-loader. Boot-Loader Application Do’s and Don’ts Obviously, the boot-loader application should know where to receive incoming application code from. In my case the incoming Nios application code is received from a SPI port. The reading of this incoming data port should be polled and not interrupt driven. Why? A Nios processor can only have one exception vector table location whether the Boot-Loader application or the loaded ‘Application’ is running. The exception vector table should live in external RAM memory. It will get copied over when the run-time application is loaded into memory (external ram). When the exception table gets written over, you’ll no longer be able to get the rest of your applications nios code. Therefore, use a polling methed in your ‘Boot-Loader’ to get the nios code from your host loader micro. The reset vector of both the Boot-Loader application and the loaded ‘Application’ should be the same. That is, upon reset, the Nios should always vector to the same location whether the boot-loader or the run-time application is running. The reset vector will live in ‘onchip_memory’ as it is always available to either the ‘Boot-Loader’ or the ‘Application’. Boot-Loader Memory vs Application Memory The Boot-Loader should not use any memory that the ‘Application’ uses with the exception that the ‘exception vector table’ (external SDRAM for example) and the actual copying of the application to external SDRAM by the boot-loader. The boot-loader therefore does not trash itself as it is uploading the ‘Application’ from the host loader micro. In my case, the ‘Application’ did not use any memory the ‘Boot-Loader’ uses. I could not get the ‘Application’ to run if any memories were shared between the ‘Boot-loader’ and the ‘Application’. Nios Boot-Loader System Library Setup The Boot-Loader memory foot-print should remain as small as possible in order to fit in the ‘on-chip memory’ device. Therefore in the Nios Boot-Loader project ‘_syslib’ project properties box the follow parameters should be used: RTOS: none (single-threaded) stdout: null (could use a uart with small printf….add about 2Kbytes to memory footprint) stderr: null stdin: null System clock timer: none Timestamp timer: none Max file descriptors: 4 Check: Small C library Check: Reduced Device drivers Uncheck: everything else Program memory (.text): onchip_memory_0 Read-only data memory (.rodata): onchip_memory_0 Read/write data memory (.rwdata): onchip_memory_1 Where ‘onchip_memory_0’ is a Read-Only 4-kbyte device Where ‘onchip_memory_1’ is a Read/Write 1-kbyte RAM device In the C/C++ Build menu, set the ‘Optimization Level’ to ‘Optimize size (-0s)’. To enable ‘object dump file’ in Nios IDE: Window->Preferences->Nios II->check ‘Generate objdump file’. (You’ll need to do this to view your Nios Application’s ‘_start’ location) Nios Application System Library Setup Do whatever you want. I didn’t use any memory that the boot-loader uses however. Therefore all memory for the ‘Application’ uses external sdram. Boot-Loader C-Code /* * The following code is only an example of a boot-loader application. * Your results may and application will most certainly vary. * Use this code at your own risk. */ #include #include "system.h" #include "altera_avalon_pio_regs.h" #include "altera_avalon_spi_regs.h" alt_u8 spi_read(alt_u8* read_byte); #define TIMEOUT_JMP_TO_APP 0x001fffff #define APP_START_ADDR 0x00000318 // '_start'....you have to look at the object dump file (of your nios application you are loading) to get this address alt_u8 *pSdram; alt_u16 switcher=0; static volatile alt_u8 pio_0_val=0; #define LED_DS14 0x08 // bit-3, LED DS14, high-on #define LED_DS6 0x02 // bit-1, LED DS6, high-on int main (int argc, char* argv[], char* envp[]) { alt_u32 time_out = 0; pSdram = (alt_u8 *)SDRAM_0_BASE; IOWR_ALTERA_AVALON_SPI_STATUS(SPI_CONTROLLER_BASE, 0); void (*AppStartP)(); // a pointer to a function; alt_u8 spi_data; // read the next byte while(!spi_read(&spi_data)); // dummy read to clear up the port; while(1) { if(spi_read(&spi_data)) { *pSdram++ = spi_data; time_out = 0; if(switcher++ & 0x1) { pio_0_val |= LED_DS14; } else { pio_0_val &= ~LED_DS14; } IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, pio_0_val); } else { // increment timer if even one byte has been received if(pSdram > SDRAM_0_BASE) { time_out++; } if(time_out >= TIMEOUT_JMP_TO_APP) { pio_0_val |= LED_DS6; // turn on led, the ‘application’ turns it // off if successfully launched. IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, pio_0_val); // pio_0_val; alt_icache_flush_all(); alt_dcache_flush_all(); AppStartP = (void(*)())APP_START_ADDR; //_start AppStartP(); } } } // returns 0 if no byte read, returns 1 if byte was read alt_u8 spi_read(alt_u8* read_byte) { alt_u8 byte_avail=0; if(IORD_ALTERA_AVALON_SPI_STATUS(SPI_CONTROLLER_BA SE)&ALTERA_AVALON_SPI_STATUS_RRDY_MSK) { *read_byte = IORD_ALTERA_AVALON_SPI_RXDATA(SPI_CONTROLLER_BASE) ; IOWR_ALTERA_AVALON_SPI_STATUS(SPI_CONTROLLER_BASE, 0); byte_avail=1; } return byte_avail; } Prepare Application code for loading to host boot-loader flash Converting your Nios ‘Application’ code to something you can download to your host boot-loader micro. We want to convert an elf to s-record so go to: ‘Windows Start’->All Program->Altera->NiosII SDK Shell: (change directory to get to where your application elf file is located then use this command): elf2flash –input=appname.elf –base= --output=appname.s19 –end= example for me: elf2flash –input=exgine_smallest.elf –base=0 –output=exgine.s19 –end=0xfffff --verbose You don’t want to burn the s-record to flash, only the binary representation of it so that it doesn’t take up twice as much space as with a full s-record. Conclusion: I hope this can help somebody since most of the above is not obvious but not necessarily difficult once you know how to do it. Altera has gaping holes in their documentation on Nios software boot-loading….something that I would perceive as something very common to modern day embedded systems applications. I opened requests for assistance on two separate occasions but only achieved ridiculous suggestions on how I might achieve delivering a nios-code boot-loader.