Some hours ago i published my version of a simavr package-build for arch linux. It is a derived version of the already existing package from the aur wich is extended by the libsimavr.so shared library and headerfiles. To work with the internals of simavr as covered in my recent article about capturing waveforms they are essential. So i decided to publish my version which i use for development with simavr.
I don't know if the location for the headers is ok, but i placed them in '/usr/include/simavr'. If you want to use them in your projects, simply add "-l simavr" to your compiler flags and put somthing like this in your source-files:
#include <simavr/sim_avr.h>
#include <simavr/sim_elf.h>
int main(){
return 0;
}
To get fancy graphs of the signals your avr produces, you don't need an expensive logic-analyzer. Those graphs can help you debugging you code, to see if your code produces the right signals or to check for correct timing.
The first step to produce nice looking waveforms is capturing data. We have to collect the data we are intrested in over a specific amount of time. This could be archived by using libsimavr, which offers a way to collect arbitrary data from our avr microcontroller. As mentioned earlier, libsimavr is the library behind simavr, a simulator for atmels avr microcontrollers like the popular atmega series. You can use libsimavr as a virtual logic-analyzer. But using libsimavr for data acquisition has some advantages over typically used logic-analyzers.
Understanding logic analyzers and the process of data sampling
A logic-analyzer is a piece of hardware with probes that can be connected to the pins of your microcontroller. Most usual logic-analyzers have 8 up to 32 channels. Every channel is capable of capturing the logical level of a pin on your microcontroller. It only recognizes one of two states: if the pin is in high- or low-state. So if you want to capture the states of all pins on PORTB of an atmega8 you would need a logic analyzer with at least 8 channels. The logic analyzer is only capable of capturing signals of physical accessible pins on your microcontroller. Another limitation is the samplerate and the amount of samples the logic analyzer can save.
Using libsimavr for data acquisition there are no limits that we have to care about. It doesn't matter if we want to capture all pins available on the microcontroller at the same time. It also doesn't matter at which frequency our signal is generated. Libsimavr can be hooked up to the virtual pins of the simulated microcontroller and save the captured data to a .vcd-file (value change dump). To analyze the captured data we can use GTKWave.
Some preparations first
The first thing we need is a working version of libsimavr in place. As most linux distributions don't provide packages for simavr there is no easy way to use libsimavr. Sadly simavr doesn't ship with a make file that supplies an install target. That means building a packges needs some extra work.
A quick and dirty solution (as described in the simavr-wiki) is to download the sources into your home directory and build a running version. One way to use libsimavr is to include the built shared library and all needed headers into your project directory or create a symlink to it. Then instruct the compiler to recognize the headers and the shared library using -I and -L options. I don't find this a nice solution working with libsimavr. Because i don't like to struggle with this, i built an archlinux package, which installs all needed files to the appropriate locations. If you are able to build packages for your distribution of choice, i highly encourage you to do so and to publish it.
Capturing data: a simple example
In this example i will capture the waveforms of a firmware 'toggle_portb.elf'. All it does is to toggle PORTB in an infinite loop.
In the first lines we read the firmware "toggle_portb.elf" from disk. After loading the firmware, i assigned an mcu-type and a frequecny to it. In this case i decided to emulate an atmega8 running on 8 MHz. Having this basic information in place i create a virtual instance of this microcontroller, initialize it and load the firmware into its flash-memory.
Now it's time to initialize our vcd file. I chose the filename "portb_output.vcd" and a file-flushing-period of 100 us. To determine wich data should be captured i use the function "avr_vcd_add_signal()". The first parameter is the vcd-struct, followed by the irq-struct connected to PORTB, the bit-width of (you can think of number of channels) and a label attached to the data from PORTB. The label is for display purposes as we will see later.
To start the data acquisition, i use "avr_vcd_start()". I then start the avr microcontroller for 10000 cycles. "avr_run()" executes exactly one machine-instruction per call. As i understand from reading the source-code the cycle count is not 100% correct. There seem to be some special cases that don't do correct cycle-counting. After stoping the data acquisition and closing the vcd-file, there should be a populated file called "portb_output.vcd".
Inspecting captured data using GTKWave
To inspect the results of the data acquisition, i use a program called GTKWave. It's capable of reading the collected data from the vcd-file and present it as nice waveforms.
Open you file using File > Open new tab and select the .vcd file. Than click on "logic". The label that you assigned earlier to your captured data will appear. Drag it into the part of the window with the title "signal". To expand it and see all your captured "channels" as in the screenshot double-click on it. A very useful option is the reload shortcut. Just press Ctrl+Shift+r to reload the captured data from the file. To get the time an event ocurred, select the channel of interest and press "find next edge". There are a lot of things you can do with GTKWave. Just play around to find the functions you need to test your collected data for correctness.
Summary
I just wanted to give you a basic idea of howto capture data from avr outputs and display it as nice waveforms. There is a lot of more advanded stuff you can do with it but it needs more knowledge about libsimavr. So in my next blog post i will explain a little bit more about using libsimavr and how i used it to write fully automated blackbox/unit tests. There are also some drawbacks over a real logic-analyzer that should be mentioned. Keep in mind that simavr only tries to simulate a avr. The behavior of a real avr microcontroller could differ so don't rely to much on those virtual waveforms. So in case you want to check you results against the real behaviour, i recommend you the "Open Logic Sniffer". It's an opensource FPGA based logic analyzer for around 42 € with a really good performance.
Today i celebrate the "I love free software day" proclaimed by the fsfe. A good opportunity to say thanks to all that are producing, using, writing about or also loving free software.
I try to use free software solely. It's not only because i can't afford expensive software but because of the meaning behind free software. I don't think that making money is a good way to forward things in the right direction. People that are producing software with the goal of archiving the maximum amount of money possible out of it never have that focus on software development than people participating in free software. Free software means a wider view on all aspects. Refusing someone to use a software because they can't afford it or refusing someone to participate in the software they use because it's commercially driven, that all inhibits the development of software and at last the development of society. Refusing people without a certain amount of money to participate is a problem in genral.
Thats why i try to share things that i made. If i am programming or writing articles and that could support or enhance the work of others, that's great. I am feeling good if my work could be the basis for derived works published under the same principles. I hope that more and more things in society could follow those free software principals.
As my first post, I would like to introduce this blog, myself, and everything.
At the moment I am mostly doing microcontroller programming, so most of the content will be about this topic. Besides microcontroller programming I am also doing webdevelopment (mainly ruby/rails/coffeescript) and I prefer python as my language of choice doing all kinds of stuff. I also have a standard knowledge of a few standard languages such as java, php, shell-scripting, make and so on. But they dont play an important role to my life.
The main reason for starting this blog is that I am constantly doing stuff that lacks documentation. So, after figuring out how things work, i thought something like "man, why isn't there anybody who documents this stuff in a way that I don't have to waste my time figuring out how things work?!" But most of the time such thoughts gets blown away quickly by all the cool stuff I am able to do with my new gained knowledge. So everything stays the same and I don't do any better than the guys that failed to document things that I had to figure out myself :) Ok, but for now it took me some time building this blog and hopefully things will change in the future.
Another reason is a very classic one: It happens a lot that I figure out something which is not that much of public interest, but after some weeks or months, I can't remember how I got things to work earlier. Before I built this blog, I used a trac-wiki to document this stuff on a local machine. But it happened a lot that someone said to me 'Yeah, I have to set up that kind of ldap-configuration, but man, it's really weird stuff'. At that point I remembered an ldap-configuration I had done some years ago, and I also remembered that I wrote a short documentation about it. But there is absolutely no way to access the machine, where I had saved the documentation or even worse: The article didn't exist anymore because I reinstalled the machine and somewhere there was an old tar.gz containing old database- or filesystem-backups of the machine in question. So I want to use this blog as a central point of writing and (in case of need) publishing my documentations.
At last, there is another reason I am writing this blog. It's because I am working on some projects for an application for admission on some courses of studies. Because of this, I have to document some projects that I am currently working on.
Alright, after this short introduction to the blog, I will start my first series of articles about simavr. It's a nice and small project that tries to write a complete simulator of the avr microcontrollers. Unlike other projects, simavr has a public git repo for managing their code (which I really like!) and the code is easy to understand. So checkout my first article on this topic.
Since most average hobbyist avr-developers dont have a JTAG-interface, expensive logic-analyzers or scopes in thier toolbox the typical workflow could be described as following:
writing some lines of code
compiling that code
flashing the code into the device and debugging the written code through leds or a uart
When done, the whole process starts over and over again.
Debugging timing critical or more complex code sections this way could be very uncomfortable up to impossible. Also doing cycle accurate measurement of specific code-sections could be hard to manage. But here simavr comes to help making it more fun debugging and programming complex or very timing critical code. With simavr one could easily do all these things that would otherwise require expensive tools.
In the first part of this series i will introduce how to use simavr with gdb to inspect and debug code-sections of interest.
Overview
Simavr is a nice small project. There are some options to simulate a avr microcontroller. The propreitary tools delivered by Atmel allow you to debug your code in a gui-based simulator. There are some other opensource projects like simulavr, but i think they are not under active development. I found that simavr was the first simulator that meets my requirements and i gave it a try.
The code is available in a public git repo. This is very cool, as so many other avr based projects dont use proper version control software. There are allready some branches. They also have some documentation in their wiki and ship the sources with some examples.
Simavr mainly consists of two parts:
A gdb-server is used to simulate code with the standard gdb frontend. You have to compile your code with debuging-symbols enabled, connect your gdb to the simavr-backend and load your elf-file into the simulator. Its possible to debug the loaded code now using the standard gdb commands you are used to.
The second part is libsimavr. Using this library, you are also able to debug your code using gdb but the main feature lies in it´s more custom abilities. libsimavr provides a api for manipulating and debugging your code in a more programatically way. Using this api gives you the ability to inspect or manipulate the avr-core and its periphrials from inside a c-program. This gives you a more powerful way of automatic testing or debugging, without manually stepping through your breakpoints using the gdb. As mentioned before, its also possible to start a gdb-session/server using libsimavr but furthermore you could also do data-logging of events that are happening inside the avr. libsimavr provides the functionality to dump changes of different registers or whatever you want into .vcd files.
I figured out some realy cool use-cases in wich simavr supports my everyday work, but for now i will tell you about the most common ones.
Getting started
One purpose of simavr as mentioned is simulating a avr-microncontroller including some of the more basic pereferials. A very basic usage of simavr is using it ´s standalone binary 'run_avr' in conjunction with gdb.
The first thing you have to do is compiling your code with debugging-symbols enabled. This is very easy and required by gdb, so that it knows what piece of c-code maps to what address. Just add the following flags to your build-script:
-g3 -gdwarf-2
I found it very helpful using -g level 3 because it includes macro informations inside the compiled binary. A very cool feature if lets say you want to inspect the memory of a register. Instead of knowing the exact location (eg. 0x18) you only supply the macro you are used to (eg, PORTB).
You are ready for debugging now. Lets place a breakpoint in line one of the code and start the program.
(gdb) b 1
Breakpoint 1 at 0xf2: file avr/toggle_b.c, line 1.
(gdb) c
Continuing.
Breakpoint 1, main () at avr/toggle_b.c:6
6 DDRB = 0xff;
(gdb) list
1 #include
2
3 void main(){
4
5 // config outputs
6 DDRB = 0xff;
7 DDRC = _BV(5);
8
9 // config inputs
10 DDRD = 0;
(gdb)
The list command shows you the sourcecode around the current position of execution. You can simply hit enter to repeat your last command. If you repeat the list command, gdb shows you the next lines of your source. Using "list -" does the same going in the other direction.
Inspecting local variables or registers could be done using the "p my_local_var" command. To get an overview of all available commands simply type help.
Inspecting code-sections using gdb
Something that i found very useful is inspecting assembly-code. This could also be done by compiling your code to plain assembler-files instead of building a .hex file but to me, it feels a lot more intuitive inspecting asm with gdb. Just type:
(gdb) disassemble /m main
Dump of assembler code for function main:
3 void main(){
4
5 // config outputs
6 DDRB = 0xff;
=> 0x000000f2 <+0>: ldi r24, 0xFF ; 255
0x000000f4 <+2>: out 0x17, r24 ; 23
7 DDRC = _BV(5);
0x000000f6 <+4>: ldi r24, 0x20 ; 32
0x000000f8 <+6>: out 0x14, r24 ; 20
8
9 // config inputs
10 DDRD = 0;
0x000000fa <+8>: out 0x11, r1 ; 17
11 PORTD |= _BV(2);
0x000000fc <+10>: sbi 0x12, 2 ; 18
12
13 while(1){
14
15 PORTB = ~PORTB;
0x000000fe <+12>: in r24, 0x18 ; 24
0x00000100 <+14>: com r24
0x00000102 <+16>: out 0x18, r24 ; 24
16 if(!(PIND & _BV(2))){
0x00000104 <+18>: sbic 0x10, 2 ; 16
---Type to continue, or q to quit---q
Quit
This way you get your c-code side by side with the resulting asm-code. This is very handy if you write timing-critical stuff. It s also very useful if the compiler optimizes code and you want to understand how things work. Simply step through your code instruction by instruction and inspect the memory. Sometimes the compiler simply optimizes some code the wrong way, resulting in a lot more code than you would expect. I used this feature a lot to optimize my code and understand how c-code maps to avr-instructions. This way, i gained a lot of knowledge 'how to do things right'.
Tips and Tricks
I have modified my build-script in a way, that it compiles my code with debugging-options, starts run_avr and drops me in a running gdb-session with the compiled file loaded. This saves me a lot of time.
Making myself familar with gdb-commands made me a lot more flexibile and productive than before and i could only recommend you to checkout the following links and play around with your code inside a gdb-session.