Tuesday, December 16, 2014

Storing Nios II Application Code in Non-Volatile Storage on MAX 10 FPGA

When I first heard about the Altera MAX 10 before it's release, I was thrilled to finally have a device from Altera with nonvolatile configuration memory.  Through the rumor mill, I later found out that the device had an integrated ADC as well!  This thing sounded perfect.

Well, it finally gets released and I immediately get an evaluation kit, the EK-10M08E144ES/P.  I did nearly a complete design before trying to program my application into the internal flash memory.  I could program the FPGA hardware portion into flash no problem, but I could not figure out a way to get my application into it.

I was used to Cyclone devices where you would treat the situation such that everything actually lives in RAM, even when the power is turned off.  In the past I have had absolutely no problems using sof2flash/elf2flash/nios-elf-objcopy to merge my hardware and software images into one programming file.  I would then create a .jic file to program it all into the external EPCS memory, and everything worked beautifully.

With MAX 10, I thought it would be similar except for instead of having an external flash, it would be internal, but the process would remain the same.  The first stumbling block is when I tried to use sof2flash - I frustratingly got an error message saying "Unrecognized device family in SOF."  I heard from an inside source that in 14.0.2, sof2flash did not have MAX 10 support and to wait for 14.1 in December, which would be considered the "full" MAX 10 release.  I wasn't happy but I just accepted it and waited for 14.1.

14.1 was released yesterday.  First thing I try is sof2flash - still get the same error message.  After screwing around with it some more and trying other approaches such as setting the reset vector to flash memory, but I was still having no luck.  In fact, things were going even worse than with 14.0.2!  It's probably better to explain later when it is in context, but for now I would stay away from Quartus 14.1.

Enough history, let's get to the solution.  The key is to have your software stored in pre-initialized RAM, which then gets wrapped up into your. sof.  This allows you to further process it into a .pof and program the device's internal flash with this.

For a quick example, I made a basic system in Qsys to blink some LEDs on the MAX 10 evaluation board from software.  I'm not going to explain how to use Qsys.  The system looks like this:


When creating the system, I left the memory initialization options as such:

I generated the HDL, wrote a little bit from scratch to wire up the clock and LEDs, and compiled my design.  Everything seemed to be going well until the assembler ran.  I'd get the error "invalid internal configuration mode for design with memory initialization."  Googling this I easily found a solution from Altera which was to add "set_global_assignment -name ENABLE_ERAM_PRELOAD ON" to the .qsf file.  Simple enough, right?  Well I close my project, add the line, and try to reopen in Quartus (still using 14.1 at this point).  I got an error saying there was an invalid line in the .qsf file.  Really, Altera?  I knew that this had to be buried somewhere in the settings.  The MAX 10 embedded memory user guide said this setting could be changed in Analysis and Synthesis settings.  I open up the settings window, and the Analysis and Synthesis settings were nowhere to be found!



I still had 14.0.2 installed, so I opened that up and checked it out.  Lo and behold, the option was there.  But when I opened it, there was no "Enable ERAM Preload" option available under the "More options" button.  I thought that was strange, but 14.0.2 accepted when I put "set_global_assignment -name ENABLE_ERAM_PRELOAD ON" in the .qsf file, so I crossed my fingers and hoped for the best.

I recompiled the design in 14.0.2, this time successfully.  I knew from earlier experimenting that 14.0.2 didn't like preinitialized memory either, but the change to the .qsf seemed to do the trick.  Just to double check, I looked at the compilation report, and the Enable ERAM Preload was in there.  Cool!


This is why I am avoiding Quartus 14.1 - it seems that the option was removed or moved elsewhere, and it is not documented anywhere.  The manual I linked in earlier contains instructions that point to a non-existent configuration option.  Way to go, Altera...

I downloaded the .sof to the FPGA.  I then went through the process of creating a new Nios II project in Eclipse to blink the LEDs.  Everything worked well, so now I had to figure out how to get this code into my .sof.  I tried manually running elf2hex, but it kept generating hex files with all zeroes, so that wasn't the way to do it.  I screwed around with a few other things until I noticed a makefile in the project called "mem_init.mk" with a target of "mem_init_generate".  I opened up the Nios command shell and ran this makefile, and it created a hex file that had some data in it.  So far so good!

Going back to Qsys, I reopened the memory instance and specified to use this hex file to preload the RAM.  I regenerated the HDL, compiled everything, and everything seemed to go okay.  I programmed the .sof and the LEDs started blinking immediately.  I finally figured out this problem that has been annoying me for months.

The actually proof was converting the .sof to a .pof and programming the internal flash memory.  I slowly waited for the programmer to write to the flash, but once it was finished, I still had my blinking LEDs.  Success!

I know there is probably a better way to do this.  But the best way to get the correct answer is to present the wrong one, so if you know of another way, I'd love to hear from you.  Likewise, if there is a way to change this option in Quartus 14.1, please let me know.

If you're interested, the project files can be downloaded here.