Wednesday, September 29, 2021

Lady Killer Flanger



 Finally finished the circuit design after 8 years of on and off development. Figured I make a post about it before somebody else comes along and decides it’s a good name!

I have a few prototypes around, but so far I’ve only made two “final” units. I’m keeping the first one for myself and I sent the second to my former bandmate.



Sunday, January 18, 2015

Quartus 14.1 - Enable ERAM Preload Update

It seems that Altera has noticed that their own manuals are wrong and have released a knowledge base article explaining how to enable ERAM Preload in Quartus 14.1.  I'll repost it here:

In the Quartus II software, go to Assignments -> Device -> Device and Pin Options -> Configuration -> Configuration Mode to choose from the following options :

1.Dual Compressed Images
2.Single Uncompressed Image
3.Single Uncompressed Image with ERAM Preload
4.Single Compressed Image with ERAM Preload
5.Single Compressed Image

Options 3 or 4 enable the ERAM Preload feature.
 I tried it and it does work.  I wonder why it took so long for Altera to get this article out.

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.

Monday, March 3, 2014

Attaching Peripherals to MicroBlaze MCS: 8-Bit PWM

Last time, I showed how to implement a MicroBlaze MCS soft processor core on the Papilio One.  However fun that is, in reality we have just created an expensive microcontroller with no peripherals to work with.  Today we will modify our existing project to add a simple 8-bit PWM module that we can control from our C application.

Our PWM module will consist of an 8-bit counter whose value is compared against a provided value.  If at any point in time the counter's value is less than the compare value, the output will go high.  Otherwise, it will stay low.  The diagram below, shamelessly stolen from the ATmega328P datasheet, explains the concept fairly well.  TCNTn would be the value of our counter, the dashes represent our compare value, and OCnx would be our output.


To accomplish this, we will first make an 8-bit counter Verilog module.  Afterwards, we will make a PWM module that instantiates the 8-bit counter and performs the comparison.  Once this is done, we can attach the PWM module to the GPO port of our microcontroller to set our compare value in software.

Our 8-bit counter module is very simple.  We have a clock input and a value output.  Every time we see a rising edge of the clock, we will increment the counter by one.  The counter will eventually overflow to zero, starting the next PWM period.
`timescale 1ns / 1ps

module Counter(clk, value);
 input wire clk;
 output reg [7:0] value = 0;

 always @(posedge clk)
 begin
  value <= value + 1;
 end
endmodule
 Next, the PWM module.  The PWM module will take an input clock and 8-bit compare value, and output a single bit.  The module instantiates the counter and feeds the clock signal through to it.  A separate combinational logic block compares the output of the counter to the specified compare value.  If counterVal is less than the compare value, the expression will evaluate to true, setting out high.  Otherwise, it evaluates to false and out will be low.
`timescale 1ns / 1ps

module PWM(clk, compare, out);

 input wire clk;
 input wire [7:0] compare;
 output reg out = 0;

 wire [7:0] counterVal;

 Counter c(clk, counterVal);

 always @(*)
 begin
  out <= counterVal < compare;
 end

endmodule
Simple as that!  Now, all that's left on the hardware side is to connect the PWM module to the MicroBlaze.  We can accomplish this first instantiating the PWM module in our top-level module, and then connecting our 8-bit GPO from the MicroBlaze to the compare input of the PWM module instead of the pins on the Papilio.  We can then connect the output of the PWM module to one bit of our existing output register.  We need to set the rest of the bits to something in order for the design to compile correctly.  I chose to connect them to zero.  One thing to note is that this is setting the remaining bits to a 7-bit integer zero; it does not set every individual bit to zero.  Here is the modified top-level module:
`timescale 1ns / 1ps

module TopLevel(Clk, Reset, UART_Rx, UART_Tx, GPO1, GPI1);

 input wire Clk;
 input wire Reset;
 input wire UART_Rx;
 output wire UART_Tx;
 output wire [7:0] GPO1;
 input wire [7:0] GPI1;

 wire pwmOut; 
 assign GPO1[7] = pwmOut;
 assign GPO1[6:0] = 0;

 wire [7:0] pwmCompare;

 PWM pwmModule (Clk, pwmCompare, pwmOut);

 SoftProc MySoftProc (
  .Clk(Clk), // input Clk
  .Reset(Reset), // input Reset
  .UART_Rx(UART_Rx), // input UART_Rx
  .UART_Tx(UART_Tx), // output UART_Tx
  .UART_Interrupt(/*UART_Interrupt*/), // output UART_Interrupt
  .GPO1(pwmCompare), // output [7 : 0] GPO1
  .GPI1(GPI1), // input [7 : 0] GPI1
  .GPI1_Interrupt(/*GPI1_Interrupt*/), // output GPI1_Interrupt
  .INTC_IRQ(/*INTC_IRQ*/) // output INTC_IRQ
 );

endmodule
Now, double-click "Generate Programming File" to resynthesize and create the new bitstream to work with.

Once it is done compiling, we can go back to the SDK environment to write some code to control the PWM module.  Since we connected the compare value to GPO1, writing to it will change the compare value of the PWM.  Since its 8 bits wide, valid values are 0-255.  I wrote a small program that will ramp the value up and down:
#include <xparameters.h>
#include <xiomodule.h>

#define STEP_DELAY 5000

int main()
{
 //Instantiate IOs
 XIOModule gpi;
 XIOModule gpo;

 //Initialize IOs
 XIOModule_Initialize(&gpi, XPAR_IOMODULE_0_DEVICE_ID);
 XIOModule_Start(&gpi);
 XIOModule_Initialize(&gpo, XPAR_IOMODULE_0_DEVICE_ID);
 XIOModule_Start(&gpo);

 int increment = 1;
 int val = 0;

 for (;;)
 {
  //Set the PWM pulse width
  XIOModule_DiscreteWrite(&gpo, 1, val);

  //Determine if we need to switch directions
  if (val == 255)
   increment = -1;
  else if (val == 0)
   increment = 1;

  //Increment/decrement the PWM value
  val += increment;

  //Wait for a bit
  for (int i=0;i<STEP_DELAY;++i);
 }
}
You should be able to build the project and transfer the design to the FPGA.  If all went well and you kept your LED on pin C8 from last time, you should see the LED fade in and out.


If we take a look on the oscilloscope, we can see a square wave of varying duty cycle.


Hope this has been helpful.  Next time, maybe we will try connecting multiple custom peripherals.

EDIT:  Some reddit users have pointed out that connecting the PWM module to the GPO instead of the I/O bus is "awkward" and non-canonical.  I did so in this tutorial just to get up and running quickly.  Perhaps in the future we will cover the I/O bus.

Sunday, March 2, 2014

Implementing MicroBlaze MCS on the Papilio One

Not too long ago, I was working on a microcontroller project of mine whose requirements soon outgrew the hardware it was running on.  This led me down the path of FPGAs, so I picked up a Papilio One.  I wanted a barebones FPGA development board just to play around with.  The Papilio One sports a Xilinx Spartan 3E device with on-board oscillator, voltage regulators, SPI flash, FTDI serial port, and an integrated programmer.  Not knowing what my requirements would be, I bought the 500k model.  For $65, you can't really go wrong!

I started playing around with the free Xilinx ISE WebPACK design software and noticed it included a free soft processor - the MicroBlaze MCS.  The MicroBlaze is a highly configurable 32-bit soft processor that lives inside the FPGA and can interface with whatever other programmable hardware you decide to write.  It seemed like it would fit my needs very nicely.  While the Papilio website suggests using the AVR8 soft processor with the Arduino IDE, I wasn't convinced.  I despise the Arduino IDE and its lack of step-by-step debugging.  The MicroBlaze can be debugged using a Xilinx Platform Cable.  The Papilio has broken out the JTAG pins to the FPGA, so you can use the Xilinx cable for debug.  But that is another story for another day.

To start I decided to stick with the Papilio Loader software just to get things up and running.  I spent the afternoon trying to get the traditional LED blink "hello world" working, so I figured I'd share how I did it.

To use the MicroBlaze, you need to install the aforementioned Xilinx ISE WebPACK along with the Vivado WebPACK and SDK.  Vivado is not used to program the Spartan 3E series, but the SDK is required to write your C application that will run on the MicroBlaze.  You will also need to install the Papilio Loader software to download your design to the Papilio.

Once you have downloaded and installed both pieces of software, start up Xilinx ISE.  To begin, click New Project under the File menu.  Choose a directory and name for your project, and make sure the top-level source is HDL.  Click next.


In the next window, you need to select your target device.  For the Papilio 250k, it is the XC3S250E, and for the 500k it is the XC3S500E in the Spartan 3E family.  Select the VQ100 package and the -4 speed grade.  Further down you need to choose your preferred HDL.  I will be using Verilog today.  Click next and then finish.


Now we are going to add the MicroBlaze core to the project.  In the left panel under "Hierarchy", right click your device and select "Add Source" from the pop-up menu.  In the window that comes up, you want to select "IP (CORE Generator & Architecture Wizard).  You also need to specify a file name.  I chose SoftProc.

The next window will present you will a whole bunch of premade IP cores.  There is a lot of cool stuff in here, I would recommend poking around and checking out what's available.  Today, we are going to add the MicroBlaze MCS.  It can be found under Embedded Processing/Processor.  Click next and then finish.


After a couple of seconds, a window will pop up.  This window lets us customize the MicroBlaze core to fit our needs.  In the MCS tab, you will need to give the core an instance name.  I chose "MySoftProc".  For the Papilio, you need to change the input clock frequency to 32 MHz. I decided to up the memory to 16kB and enable the debug support for later.  On the UART tab, I enabled both the UART receive and transmit at 9600 baud.  I also enabled the UART receive interrupt.



I am not going to be playing around with the timers, so I skipped right to the IO.  For starters I selected an 8-bit general purpose output (GPO) and 8-bit general purpose input (GPI).


When you have finished, click "Generate".  ISE will then work to churn out your MicroBlaze core.  This can take considerable amount of time.  It took nearly 15 minutes on my machine, so get up and make a cup of coffee while it does its magic.

Once it is done, we need to add our top-level module that will instantiate the core.  To do this, right click the device on the left, and select "New Source".  Select "Verilog Module" from the list, and give it a name.  I named it "TopLevel".  Click next and then finish.


You will presented with a blank module.  We need to paste in the source code that will instantiate our MicroBlaze core.  To find this, click the core in the "Hierarchy" list.  In the "Processes" panel below, double-click "View HDL Instantiation Template".  A new source file will open up.  There are a lot of comments, so scroll down a bit to where it says "Begin Cut Here".  This is the actual code we will paste into our top-level module.


Navigate back to the top-level module and paste this in.  Before we can get working, we need to define the inputs and outputs to our top-level module, and wire them to the core.  You will also want to give your instance a name.  I chose "MySoftProc".  Since I am not using the interrupts right away, I've commented out the corresponding signals until I get around to using them.


At this point, we are ready to synthesize.  Click the top-level module in the "Hierarchy" list, and in the "Processes" panel, double-click "Synthesize - XST".  After a bit the synthesis should complete successfully, but with three warnings.  These are not a big deal and can be ignored:
WARNING:Xst:2211 - "ipcore_dir/SoftProc.v" line 12: Instantiating black box module <SoftProc>.
WARNING:Xst:616 - Invalid property "SYN_BLACK_BOX 1": Did not attach to MySoftProc.
WARNING:Xst:616 - Invalid property "SYN_NOPRUNE 1": Did not attach to MySoftProc.
 Now it is time to connect our inputs and outputs to actual pins on the FPGA device.  To do this, click the "Tools" menu at the top, "PlanAhead", and then "I/O Pin Planning (PlanAhead) - Post Synthesis."  A message will appear prompting you to add a pin constraints file to your project.  Click yes, and PlanAhead will then open up.

At the bottom, you will see a list of all of the pins in the design.  Expand them all and assign them to whatever pins you desire, referencing the Papilio One Pin Assignments chart.  I decided to put most of the IO on the headers on the left side of the board, with the inputs at the top and the outputs at the bottom.  I selected all the inputs to have a pull-up resistor, because we don't like to have CMOS gates floating.  You must map the Clk pin to the oscillator input at P89.  The Reset pin must be held low for the processor to run, so I selected a pull-down resistor.  I mapped the UART pins to the corresponding UART pins on the Papilio so I could eventually send messages to and from the PC using the already-existing hardware.  I selected 3.3V IO for these pins to interface with the FTDI chip.


At this point, save and exit PlanAhead.  The pin constraint UCF file will automatically be updated in ISE.

We are now almost ready to implement our design.  Before we do this, we need to include the block memory map file that will tell the FPGA where to store our application program data.  To do this, we need to input a Tcl command.  From the "View" menu, select "Panels", "Tcl Console".  Paste the following into the command box:
source ipcore_dir/microblaze_mcs_setup.tcl
You should get back the following message:
Command>source ipcore_dir/microblaze_mcs_setup.tcl
microblaze_mcs_setup: Found 1 MicroBlaze MCS core.
microblaze_mcs_setup: Added "-bm" option for "SoftProc.bmm" to ngdbuild command line options.
microblaze_mcs_setup: Done.
Now we are ready to implement the design and generate our bitstream.  Double click "Generate Programming File" in the "Processes" panel.  This will perform the implementation steps automatically before generating our bit file.  You will get some errors concerning unconnected pins.  These are safe to ignore.

Once everything is complete, we are ready to start writing our application code in C.  To begin, start up the Xilinx SDK.  Upon startup, it will ask about a workspace.  I created a new folder called "Workspace" in the folder containing the rest of my project files:


You should then be greeted by a welcome screen.  We are now ready to begin our project.  Before writing our application code, we need to configure our project to work with the hardware we have just synthesized.  To begin, we need to create a Hardware Platform Specification.  From the "File" menu, select "New", and then "Project".  Select "Hardware Platform Specification" under "Xilinx".  Click next.

The next window that shows up will prompt for a project name and hardware specification file.  I chose "SoftProc" as the project name.  For the hardware specification file, you need to choose the file that was generated by ISE during implementation.  This is located in the "ipcore_dir" folder where your ISE project is:


After selecting this, you can expand the "Bitstream and BMM Files" tab to see that the BMM file was filled in.  We will be revisiting this later, but for now, ensure that the BMM File field has been filled in.

Next, we need to make a Board Support Package which will bridge our C code to the hardware by including all of the appropriate libraries.  To do this, select "New" from the "File" menu, and then select "Board Support Package".  Give it a name, I chose "SoftProc_BSP".  Under "Target Hardware", select the name of the hardware specification that you just created.  Ensure that "standalone" is selected under "Board Support Package OS".  Click Finish to add this to your project.

Now we can finally add our C project and start coding.  From "File", select "New", and then "Project".  Select "C Project" from the "C/C++" folder.  Click next.

You will need to give your C application project a name.  I chose "SoftProc_App".  The project type is a "Xilinx MicroBlaze Executable" and the toolchain is "Xilinx MicroBlaze GNU Toolchain".  Click next.


In the next window, we need to reference the board support package we made earlier.  To do this, click the "Advanced settings" button.  In the list on the left, select "Project References", and choose the board support package you made earlier in the list on the right.  Click OK, then click finish.


There is one more thing we need to do to make sure that all the proper libraries are included.  I am not sure if this is a bug in the IDE or just my own stupidity, but it seemed that despite referencing the board support package, the compiler was unable to find any of the libraries.  After slamming my head on the desk for a while, I found that you need to right-click your application project, and select "Change Referenced BSP" from the pop-up menu.  There should be only one selection available.  Select this and click OK.  Now, a little purple box should be included underneath your application project.



We are finally ready to write some C.  I threw together a little application that will blink an LED connected to bit 7 of GPO1.  In the case of my pin mapping, this would be pin C8 on the Papilio.  Right click the application, click "New" and then "Source".  You need to give the file a name, I chose "main.c".  You can now start writing your code.  My example code can be found here.  Paste it into your new file.

Press CTRL+B to build.  You will find that the compiler spits out an error because it doesn't like the variable declaration in the for loop.  I think this is silly, so we need to change the C standard to allow us to declare variables in the for loop.  To do this, we need to add a compiler directive to target the C99 standard.  To do this, right click your project in the left panel, and select "Properties" all the way at the bottom.  Under "C/C++ Build/Settings", we need to add -std=c99 to the "Other Flags" box.  This will allow the code to compile.  Press CTRL+B to build, and it should build successfully this time.


It is finally time to load our application onto the FPGA.  The first step in doing this is to merge our compiled application with the hardware bitstream we made earlier.  From the "Xilinx Tools" menu, select "Program FPGA".  In this window, we need to select the both the appropriate BIT and BMM files.  The BIT file is the same one that was generated by ISE.  It should be in the main project directory.  There are two BMM files to choose from.  The correct one is the file suffixed by "_bd" in the "ipcore_dir" subdirectory in the main project directory.  Once you select these, you need to select your application binary to be merged.  Under "ELF File to Initialize in Block RAM", select the only ELF file in the dropdown.


Now, click "Program".  A box with a progress bar will show up.  First it merges our application code into the hardware bitstream, and then it attempts to program the FPGA.  This will fail because we are not using the Xilinx programmer.  However, this doesn't matter at all because it still generates the complete bitstream that we can load using the Papilio loader.  The path to the file will be in the console at the bottom.


Copy this filename to the clipboard, and open up the Papilio loader.  Paste this into the "Target .bit file" box, and click "Run".  The file should successfully be downloaded to the FPGA.


To verify, I soldered a 270 Ohm resistor to an LED and connected them across pin C8 and GND.  If you've done everything correctly, you should see the LED blinking!


And that's it.  I hope this tutorial is helpful, as getting the MicroBlaze up and running can be quite a chore if you don't know what you are doing.  Now that we know the processor is running correctly, we can really start to delve into the fun stuff.  Next, I'll probably try sending and receiving data to the computer through the UART.  After that, probably interfacing with some custom hardware written in Verilog.  Stay tuned.

Edit: I should mention that this tutorial was invaluable in helping me get the MicroBlaze to synthesize correctly: http://ece.wpi.edu/~rjduck/Microblaze%20MCS%20Tutorial%20v2.pdf