It's finally time to work on some firmware!
But, since I seem to be compelled to develop a design environment rather than actually work on a design (what was I working on? Something about turning something on, or off... something like that), let's talk about debugging tools. Well. Let's talk about bugs, first.
Bugs that, say, the compiler fails on, are simple: they're right there in red text. Those bugs die quickly. (Are these even bugs? Perhaps not. But I'll assume they are, for the sake of my point.) On the other end of the spectrum are those extremely intermittent bugs which seem to occur only when we're not looking. We know these bugs through stale logfile tracings, collections of disproven hypotheses, and a body of murky, often contradictory and superstitious lore which grows throughout the long, long lifespan of the bug.
Bugs elude us by hiding. So, what do I want from a debugging tool? I wouldn't ask for too much - just something which:
- is no more difficult to use than it ought to be
- lets me see everywhere
- has no effect on the normal operation of the system
- lets me gather trace data on the workings of the system, in its actual working environment
- provides a way to inject artificial stimulus into the system, for testing purposes
I do have a few debugging tools handy. How do they rate?
- LEDs. These are very easy to use: write a value, look at the blinky lights. LEDs can answer questions like:
- Is it on?
- Is it the correct version (like my testbench id on the 7-segment display)?
- Is it toggling? (At human-perceptible rate, at least.)
- The IAR IDE debugger. Single-stepping through your program is a great way to find dumb errors. Breakpoints are available too. This is a very user-interface-intensive debugging methodology, though, and can dramatically affect program execution time, leading to the dreaded Heisenbug.
- Signaltap(tm). Signaltap can show me anything happening inside the FPGA (so, in my little rig, anything on the f2013 pins), and can capture a trace of that data, sampled on any clock I choose. This is great stuff! But, it's kind of a pain in the neck to set up, and the penalty for the frequent "I just want to see one more thing" moments in debugging is usually a hardware recompile. Signaltap is not suited to a pure scripted flow, since trace data is stowed in an undocumented file format. The amount of data that can be captured is limited to what fits in the onchip memory.
- Custom logic using the sld_virtual_jtag. Think of this as a hand-crafted signaltap, accessible via tcl script. This answers the scripting flow problem of signaltap. Captured data size is unlimited, as long as the bandwidth out of the jtag link is sufficient to keep up with the data generation rate. The information flow can go the other way, too: the system-under-observation's inputs can be driven from the custom logic, ultimately from a script running on the host, which opens up an interesting world of test possibilities. Naturally this more powerful debugging tool is even more work to set up than signaltap, since you have to design the custom logic and write scripts to access the link.
So, it looks like I'm a little heavy on the powerful-but-hard-to-use side. What I'm missing is something simple and quick to iterate on, which can give me lots of data without burdening the system too much. What I need is something like... printf.
Something like printf
Most microcontrollers have some form of built-in serial communications module. The f2013 is a bit odd: rather than a plain-ol' UART, its communications module speaks SPI or I2C. But that's ok - my laptop doesn't have a UART either. Fortunately, to assist me in the simple goal of streaming bytes from the f2013 to my laptop under firmware control, I have a giant heap of programmable logic right next to my f2013, which I can use to bridge the gap between the f2013 and the laptop. Think of the solution as an "SPI-to-JTAG bridge". Here's a block diagram showing the path of a byte from f2013 to laptop:
The components of the system are:
- The f2013, configured with an 8-bit SPI master
- The 1c20, configured with
- An 8-bit SPI slave
- A DMA component, configured to (forever) read bytes from the SPI slave and write them to...
- A "JTAG UART"
- The USB Blaster (the same device that I use for downloading sofs, running signaltap, etc.
- The terminal program nios2-terminal, running on the host computer
Say, not to pull a fast one: I realize that this is the first time I'm using a system which consists of other than hand-typed Verilog and the odd megafunction module. I designed this system using Altera's SOPC Builder, which you can think of as a heap of useful hardware components and an automatic bus generator. The system consumes 303 logic elements - pretty small. For reference, tb_6, my most complex system so far, consumed 312 LEs. SOPC Builder generation and Quartus compilation complete in about 2.5 minutes.
For anyone reading who happens to be familiar with SOPC Builder, I used these tricks to optimize the system for low logic consumption and fast generation:
- The DMA's registers are reset to an actively-running state, so that I don't need a complicated master to configure the DMA at run-time. I used an undocumented feature of the DMA for this trick, which, sadly, requires running in "--classic" mode
- I set the DMA's internal FIFO depth to 1 location (another undocumented feature)
- I limited the DMA to performing 8-bit transactions
- I reduced the JTAG UART FIFO transmit and receive FIFO depths to minimal values
f2013 firmware This is a pretty simple firmware project: all I'm doing is sending a string of bytes, over and over, so I can see it in the terminal program. It took a bit of research to hit upon the correct combination of control register values; see utility routines init_spi and send_spi in the attached project archive.
By the way, I have to create my own SPI chipselect (SS_n), using a generic f2013 pin, since the built-in SPI master doesn't provide that automatically. Also notice that send_spi is a polling transmit routine: clearly the next step is to create an IRQ-based transmitter.
Hello, world!
Here's a screen capture of the spi test firmware in action:
P.S. The bug is in the pin assignments
For my own reference, mostly, here's a table of pin names and functions for this little test bench:
f2013 function | f2013 SPI function | 1c20 pin | J15 pin |
---|---|---|---|
P1.4 | <none> | U11 | J15-12 |
P1.5 | SCLK | Y11 | J15-14 |
P1.6 | MOSI | W11 | J15-13 |
P1.7 | MISO | V11 | J15-11 |
Here's the tb_8 archive.
20070922: a small optimization: SOPC Builder's DMA is a bit overpowered for the simple task of moving bytes from one place to another. Also, needing to use undocumented features annoys me. It was about a half hour's work to create a new component (simple_byte_pipe) that does the same job, wiith less logic. The new system (files attached as tb_8a.zip) uses only 248 LEs, and builds in 1.5 minutes.
Comments (2)
This is grand!
These undocumented features sure do sound useful, on the DMA. Seems like much lost potential for "Nios-less" SOPC Builder usefulness.
...why does Safari only notify me of your posts a week late?
Posted by david van brink | September 19, 2007 9:18 PM
Posted on September 19, 2007 21:18
I see what you mean, Sept 13 -> Sept 19. But that first date is the creation date of the article, which I didn't publish until the 19th, so.
The undocumented features of the DMA are useful, but as I understand it, this sort of thing is better accomplished by Avalon-ST (which is similarly Nios-detangled).
Posted by Aaron Ferrucci | September 20, 2007 1:25 PM
Posted on September 20, 2007 13:25