Tuesday, September 1, 2009

software development using a simulator

The software in the prior entry, msploader, contains a directory openmsp430. This contains just enough of the openmsp430 core to allow you to simulate your msp430 program. The openmsp430 package available from opencores appears to be very polished. It is not just a dozen verilog files, there is a debugger, documentation on the test bench and debugger as well as software tools to help debugging if/when you use this core in an fpga. I recommend you log in to opencores and download the whole openmsp430 package.

http://www.opencores.org/project,openmsp430

My goal is not to use this core in an fpga but to instead do software development with rom, ram, register, pin, etc, visibility for a real msp430. Also provides a good learning tool for those wanting to learn embedded or assembly or just how to develop in a simulation environment where you have your software and the hardware engineers logic.

I have only just barely started with this core and simulation so there are no frills, eventually I would like to be able to demonstrate how to stimulate inputs using C such that a non-verilog programmer can develop in this sort of an environment.

For the last howevermany years my day job has included just such a thing. Long before the boards/chips arrive you can start your software development and/or debug the logic being developed by the hardware engineers. Using a small amount of throwaway code as an abstraction layer and maybe some code for stimulus if needed, you can write programs in a native programming language (like C). Basically you can write and debug code right now that that can be reused when the hardware arrives. Traditionally you either wait for the hardware or you create some sort of software solution to wrap around your code, but that software solution does not include a functional simulation of the actual logic, so you may get well into your development only to find that the hardware and software do not agree on how each other should work. How many thousands of lines of code to you have to undo and unravel to fix this blunder?

I wanted to do this with free tools like verilator or icarus verilog. I like verilator in concept because it produces C/C++ and the interface with your programs is easier and more comfortable lets say. But it is very picky about the verilog and right off the bat started complaining about openmsp430 code. I did not want to get into that just yet so I stuck with icarus verilog for now which was happy with the code as-was. I have been using http://cyclicity-cdl.sourceforge.net recently. To use this the hardware has to be designed in cdl then you can turn cdl into C++ and/or verilog. To use it here I would need to re-write the msp430 core in cdl, something I would like to do some day, but have not even started.

Again, I have only just started using icarus verilog and this openmsp430 core so all I have to offer at the moment is a simple blink the led demo.

I am running the 8.x and 9.x ubuntus, the tools you will need in addition to those mentioned in prior blog entries are icarus verilog and gtkwave. The icarus verilog apt-got from ubuntu worked fine, the package name is just verilog. For gtkwave though the version of gtkwave varies across the linux distros so I prefer to build the latest from sources. I prefer the current gktwave user interface to the prior ones.

I have modified the openmsp430 .inc file and test bench so that it runs so many thousands of clock cycles and stops. Some verilog is required (explained below) to change this for now, I have a few ideas to work around that in the future.

To convert the logic into something you can simulate:

cd /path/to/msploader/openmsp430/
make

This builds sim.vvp.

Next you need a rom.mem file. The verilog language includes a way to load memories/proms before the simulation starts. Kinda like using msploader to program the flash in your part before you reset it and let it run. The .mem file is trivial, one line is one memory location, you need to know what memory space the hardware engineer is expecting, in this case using the .inc settings I have left the rom.mem file wants addresses 0xF800- 0xFFFF, with a 16 bit word per line (it is an ascii file, have a look). The mspdiss program in the msploader package has a bit of code that creates a rom.mem file from a .elf binary. So lets take a sample program:

.equ WDTCTL , 0x120
.equ P1OUT , 0x021
.equ P1DIR , 0x022

mov.w #0x0280,r1
mov.w #0x5A80,&0x0120

bis.b #1, &P1DIR
top:
xor.b #1, &P1OUT
mov #13,r15
a:
dec r15
jnz a

jmp top


I named mine blink.s, here were my steps to get a rom.mem from it:

PATH=/msp430/bin/:$PATH
msp430-as blink.s -o blink.o
msp430-ld blink.o -o blink.elf
../trunk/mspdiss blink.elf

Then run the simulation:

vvp sim.vvp

VCD info: dumpfile sim.vcd opened for output.
SIMULATION DONE



This is where gtkwave comes into play. The key to using this technology is the mastery of gtkwave or some other similar tool. I will go through some basics but I cannot in this space cover every little thing, learning how to do it is part of the fun.

Note, you do not have to re-build the sim.vvp file every time you change your rom.mem file, you only have to re-build it if you change the verilog.

The output if the simulation is sim.vcd (there is a line in the verilog hardcoding this file name, you can change it if you wish).

Open the file with gtkwave:

gtkwave sim.vcd

There is a window with the letters SST above it and inside you see a plus and tb_openMSP430, that is a verilog module, the top level module.

I have modified openmsp430 by adding a chip.v layer to make it feel more like a real chip.

So click on the plus next to chip_0, then click on the plus next to core, then click on the plus next to execution_unit_0 to expand it. Finally, click on register_file_0. The window under the top SST window now has a list of signal names, alu_stat[3:0], alu_stat_wr, cpuoff, etc. There is a filter edit box at the bottom, you can use it to limit the signals show in that list, if the signal names for a particular thing have something in common you can use this to select just one bus or modules data. Lets try that, enter the letters pc in that filter box. I now see only four signals. The first one pc[15:0] is the one I was interested in. Click on that signal name to select it (note: you can use the standard ctrl-A or click and shift click or ctrl-click to select more than one signal at a time).

With the signal pc selected press the Append button below the filter edit box, Append, Insert, and Replace take the selected signal and then place it in the waveform window based on which of the buttons pressed.

This signal happens to be the program counter, which is also r0, you can verify this by removing the pc letters from the filter and then finding r0[15:0] and adding it to the waveform window.

What I typically do at this point is on the gtkwave I am using there are three blue magnifying glasses, one with a minus one with a plus and one with a box like thing. The one with a box like thing makes the waveform fit to the window, press it. If you dont have that tool bar then under the Time menu select Zoom->Full

Now hopefully your window is wide enough that you see some green stuff next to the signal names. You may need to widen your window then fit the signals again. I see a little bit of red (red is normally bad that means the signals are undefined, in this case it is fine the chip is coming out of reset), then four zeros then the green boxes have pluses because the content wont fit in the small box. Here is where you start to really learn to use gtkwave. In one of the green boxes that has the 0000 in it. On the right side of the box not right on the place where it has a transition but just to the left of it click the mouse cursor. If you do it right a vertical red line shows up and it snaps to that transition.

Now expand the signals window and or move the slider at the bottom. What you should see is pc[15:0]=FFFE. What this is telling you is that the register pc is changing from the value 0x0000 to 0xFFFE at that point in time. Now go up to the magnifying glass with the plus sign and click it a few times. Pretty quick you can see the plus in the box to the right of your red line turn into the FFFE value. For the pc and r0 waveforms right now you wont see this but when you click near a transition gtkwave (other waveform viewers work the same way), if close enough it will snap to that transition but on that side of the transition. So if you click to the left of the transition other signals along that time line may show one value, then if you click just to the right of that transition you may see another value in other signals. Again for these two you wont see that. If you click far enough away from a transition it will not snap, which is also desirable depending on what you want to look at.

In the signals window click on pc[15:0]=FFFE to select it. On the tool bar to the right of the magnifying glasses I see two sets of blue arrows, one has an arrow pointing at vertical bars the other does not. Click on the right arrow that does not have a vertical bar with it. If you hit the right one what should have happened is the red line moves to the next transition of pc[15:0].

So when you click on the right arrow you should see 0xFC00, Click this again and again several more times. You will see the program counter incrementing by twos then eventually it hits a loop.

Go back to the signals list ad add r15[15:0]. The in the upper SST window select chip_0 which is one level down from the top, then find the signal p1_dout[7:0]. And zoom to full so that you see the whole simulation at one time in the waveform window.

There is the blinking led. The lsbit of p1_dout is a gpio pin. The program loads register 15 with a value and counts to zero, then it toggles p1_dout bit 0 and loads the value and counts down, repeating this forever. The sim was limited but long enough that you can see r15 counting down and the output pin toggling. Certainly toggling every 13 or so clock cycles when run on a real msp at real speeds is way too fast for your eye to see the blinking. I picked a small number to make it easy to follow in the simulation.

If you click on tb_openMSP430 in the SST window. Then select p1_dout_0 and add it to the waveform. I made a separate signal name for port 1 pin 0 output, this should look like a square wave, when tied to an led this one signal will make the led blink.

gtkwave has some quirks as far as removing signals from the waveform window. You select the signal names like a normal list window but you have to ctrl-X to remove them, the delete key doesnt do it.

A feature that I dont often take advantage of, but should, is that if you leave the gtkwave window up, change your program, re-run it so that it creates another sim.vcd, you can go to File->Reload Waveform in gtkwave and re-load the new simulation results without having to reselect the waveforms that you were interested in.

Before letting you loose, under chip_0 select rom_0. This example program did not write to ram so I cant show you anything there, but it does execute from rom, so you can select all the signals in the rom_0 module and add them to the waveform. If you go back to where the PC was set to FFFE at the beginning and then look down your marker what you see is some pre-fetching. The rom appears to know ahead of time what the pc is wanting to fetch. There is a timing game here that you/I would have to look into the logic to understand, basically the 0x4031 comes from the pc address 0xFC00 since that instruction contains an immediate the core has to fetch the next value from memory which is the 0x280 constant at 0xFC02, you see that. If you add register r1 to the waveforms you will see when r1 gets written (I am thinking all of the registers shown here are behind by a clock cycle, that is fine this kind of thing happens in hardware designs and you have to get used to it). The real point here was to show you that you can look at the rom memory bus and see the instruction fetches in execution order. Unlike a debugger you do not get a snapshot of ram or rom at any one time you only get the what is the core reading/writing right now view. Watching this in execution order is quite useful, when you think your code should have branched to something and it does not you can see from the fetch that it did not and you can look at the registers to see what the values and flags were to see why it did not do the branch you wanted. It is kinda like single stepping a debugger I guess but with much greater visibility.

If you want your sim to run longer, then edit the file tb_openMSP430.v, at the bottom you will see:

initial // Normal end of test
begin
#10000;
$display("SIMULATION DONE");
$finish;
end

Verilog is both a language you can use for hardware designs and a language you can use for testing hardware designs. There are some language features that cannot be represented in hardware and this is one of them (using the tb in the filename usually indicates test bench which is a bit of code you put around the design under test, that tb code is usually written using non-synthesizable verilog, meaning it simulates but you cannot make a logic chip from it. Anyway the lines between the begin and end can be viewed like a software program that execute in order. The line #10000; means wait 10000 clocks or nanoseconds or whatever, then it goes to the next line which displays output on the console and then the $finish ends the simulation. Make that #10000; number bigger to make the sim longer.

There is an initial begin end block just above the one described above:

initial // Timeout
begin
#5000000;
$display(" (simulation Timeout) ");
$finish;
end


The engineer put a bit of a safety in there so that the sim didnt run forever. So if you make that #10000 bigger than #5000000 then the sim will end with a simulation Timeout message. Remember the longer the sim the bigger the .vcd file.

Which leads to the last comment. vcd files are ascii and compress really really well, so if you want to share with your friends compress the .vcd file. Gtkwave supports a compressed file format that takes up much less disk space it also loads much faster so as your projects get bigger you will want to know about:

vcd2lxt sim.vcd

This takes the file sim.vcd and makes a compressed version sim.lxt, then you can use gtkwave to view the .lxt file just as you would the .vcd file:

gtkwave sim.lxt

Be careful, if you do not put the vcd2lxt in your make file, it is quite easy to run a sim that creates a .vcd file, you forget to convert it to lxt, then you open the lxt file to look at the results of the new sim and nothing has changed. That is because you are looking at the old .lxt file. It happens to the best of us.

1 comment: