More examples¶
Simulavr is designed to interact in a few different ways. These
examples briefly explain the examples that can be found in the source
distribution’s examples
directory.
Simple Example¶
This sample uses only simulavr to execute a hacked AVR program. I say “hacked” because is shows using 3 simulator features that provide input, output and simulation termination based on “magic” port access and reaching a particular symbol. It is only really useful for getting your feet wet with simulavr, it is not a great example of how to use simulavr. It is thought to be useful enough to the absolute newbie to get you started though. See here for a more detailed information.
Go to examples/simple_ex1
and build there the avr program:
> avr-gcc -g -O2 -mmcu=at90s8515 -o fred.elf fred.c
After performing the build, run it. Notice the use of -W, -R and -T flags:
> simulavr -d at90s8515 -f fred.elf -W 0x20,- -R 0x22,- -T exit
Then type something followed by ENTER. Your typed string will be printed out. For explanation of simulavr options see usage.
TCL Examples¶
There are examples, which use Tcl/Tk. For that you must also install Itcl package for your Tcl. It will be used in all examples with Tcl and a Tk GUI! Over that you can find also examples for python interface and for the verilog module.
The anacomp example is all we have started with. Anacomp brings up an Itcl based GUI which shows two analog input simulations, a comparison output value, and a toggle button on bottom. After changing the inputs, hit the corresponding update to clock the simulation to respond to the changed inputs.
The avr-gdb session for me requires a “load” before hitting “continue”, which actually starts the simulation.
It is strongly recommended to implement own simulation scripts very
closely to the examples. Usage of a different name than .x
for
the grahic frame need changes of gui.tcl as well as some simulavr
sources. So stay better close to the example.
To use the TCL examples you have to prepare 2 files in :file:`examples` directory!
In examples
directory you can find 2 files: simulavr.tcl.sample
and gui.tcl.sample
Copy this files to simulavr.tcl
and gui.tcl
and edit the copied files.
Edit simulavr.tcl:
You need before 3 file paths, the path from tclsh tool, the path from wish tool (both from the TCL/TK suite) and the path, where your TCL extention for simulavr is installed. You can find out the paths for tclsh and wish with the following commands:
> which tclsh # prints out the path for tclsh, if found
> which wish # prints out the path for wish, if found
Later you need also the ddd
debugger graphical interface to avr-gdb:
> which ddd # prints out the path for ddd, if found
For the path, where libsimulavr.so (the TCL extension) is installed, you have to
seek, where it’s installed. This is system dependend! On debian systems and if you
have made the install with debian packages, it would be:
/usr/lib/simulavr/libsimulavr.so
, that means, that you have to choose
/usr/lib/simulavr
.
Then edit the beginning of file simulavr.tcl
:
#! @TCL_SHELL@
# configuration area
set WishCMD "@TCL_WISH@"
set buildPrefix "@prefix@"
# end configuration area
#
# This demonstrates how to implement a custom Tcl "main" for the
...
Replace “@TCL_SHELL@” with the path for tclsh
, “@TCL_WISH@” with the path for
wish
and “@prefix@” with the path for libsimulavr.so
.
And the same for the beginnig of gui.tcl
:
#! @TCL_WISH@
# configuration area
set installPrefix "@prefix@"
# end configuration area
package require Itcl
namespace import itcl::*
...
Now you’re prepared to run the TCL examples.
TCL Anacomp Example¶
Note
You must have installed the ITCL extension for TCL/TK to run this example!
This is Klaus’ very nice original example simulation.
To build the avr program go to examples/anacomp
directory:
> avr-gcc -g -O2 -mmcu=at90s4433 -o main.elf main.c
After performing the build you can start the simulation:
> ../simulavr.tcl -d at90s4433 -f main.elf -u -s anacomp.tcl
This starts a simple gui and enables the user to enter analog values (0.0 .. 5.0) in the input fields. After entering a new analog value in ain0 or ain1, you must press the update button!
At this point, the output of the analog comparator will be used to determine the output state of the “->B0” field. “->B0” displays the state of the Port B 0 pin. Its value is determined by the following logic:
if ain0 > ain1 B0 = H(igh)
if ain0 == ain1 B0 = L(ow)
if ain0 < ain1 B0 = L(ow)
And not to forget, you can run this simulation together with gdb debugger or also ddd:
> tclsh ../simulavr.tcl -d at90s4433 -f main.elf -u -s anacomp.tcl -g
The one and only difference to the simulation command before is the “-g” option!
LCD and SerialRx, SerialTx Example¶
Note
You must have installed the ITCL extension for TCL/TK to and ddd to run this example!
This example is written by Knut Schwichtenberg and based on Klaus’ Anacomp Example and uses the avr-libc example stdiodemo to display characters on the LCD.

First we build the avr program. Go to examples/stdiodemo
directory:
> avr-gcc -g -mmcu=atmega128 -Os -Wall -DF_CPU=3686400UL \
-o stdiodemo.elf hd44780.c lcd.c stdiodemo.c uart.c -lm
Then we have to prepare a tcl file too (in the same way as described before), copy and edit checkdebug.tcl:
#! @TCL_WISH@
# configuration area
set WishCMD "@TCL_WISH@"
set DDDCMD "@DDD@"
set installPrefix "@prefix@"
# end configuration area
#
###############################################################################
#
# LCD and Serial IO example for simulavrxx
...
And now you can start:
> wish ./checkdebug.tcl
The following commands are taken from the LCD-specific
examples/stdiodemo/checkdebug.tcl
script:
Lcd mylcd $ui "lcd0" ".x"
sc AddAsyncMember mylcd
The first command creates a LCD instance mylcd
with the name
lcd0
The second command adds the LCD instance to the simulavr
timer subsystem as an asynchronous member. Asynchronous Timer objects
are updated every 1ns - which means every iteration in the simulavr
main-loop. All timing is done internally in the lcd.c
. The
rest of this simulation script is the normal business create Nets for
each LCD pin, wire the Nets to the CPU pins. The stdiodemo application
contains a serial receiver and transmitter part to receive commands and
interpret it and if possible prints it on the LCD or sends a response to
the serial receiver. Transmitter and receiver application are implemented
by polling opposite to the Keyboard example. The components used for
the SerialRx/Tx are described below. Together with the comments in the
script you should be able to understand what happens. Please mind the
different names for the functions SetBaudRate and GetPin for SerialRx
and SerialTx! Not optimal but that’s it at the moment…
And you can try to simulate it with gdb or ddd:
> tclsh ../simulavr.tcl -d atmega128 -f stdiodemo.elf -u -F 271 \
-s stdiodemo.tcl -g
Keyboard and SerialRx Example¶
Note
You must have installed the ITCL extension for TCL/TK to run this example!
This example is written by Knut Schwichtenberg and based on Klaus’ Anacomp Example and uses the Atmel application note AVR313 to convert the incoming data from the keyboard into a serial ASCII stream and sends this stream via the serial interface. Atmel’s C-Code is ported to a current avr-gcc (4.x) and a Mega128. For this example only the serial transmitter is used. Atmel implemented the serial transmitter as interrupt controlled application, opposite to the serial transmitter / receiver of the LCD example. Here a polled solution is implemented.
To build the avr program go to examples/atmel_key
directory:
> avr-gcc -g -mmcu=atmega128 -I. -DF_CPU=4000000UL -Os \
-funsigned-char -funsigned-bitfields -fpack-struct \
-fshort-enums -Wall -Wstrict-prototypes -o atmel_key.elf \
kb.c main.c serial.c StdDefs.c -lm
After performing the build you can start the simulation:
> ../simulavr.tcl -d atmega128 -f atmel_key.elf -u -F 250 \
-s atmel_key.tcl
This example by itself is good to show how the GUI needs to be setup to make the Keyboard component work. The output of the keyboard is displayed into SerialRx component. Let’s look into the simulation script to point out some details:
Keyboard:
Keyboard kbd $ui "kbd1" ".x"
Keyboard_SetClockFreq kbd 40000
sc Add kbd
These three commands create a Keyboard instance kbd
with
the name "kbd1"
. For this instance the clock timing is
set to 40000ns. simulavr internal timing for any asynchronous
activity are multiples of 1ns. The third command adds the keyboard
instance to the simulavr timer.
Create a CPU AtMega128 with 4MHz clock. Create indicators for the digital pins (not necessary but good looking). Create a Net for each signal - here Clock(key_clk), Data(key_data), Run-LED(key_runLED), Test-Pin(key_TestPin), and Serial Output(key_txD0). Wire the pins Net specific. Run-LED and Test-Pin are specific to the Atmel AP-Note AVR313. The output of the keyboard converter is send to the serial interface. Based on an “implementation speciality” of simulavr a serial output must be either set by the AVR program to output or a Pin with a Pull-Up activated has to be wired.
SerialRx:
SerialRx mysrx $ui "serialRx0" ".x"
SerialRxBasic_SetBaudRate mysrx 19200
These two commands create a SerialRx instance mysrx
with the name
"serialRx0"
. For this instance the baud rate is set to 19200. This SerialRx
is wired to the controller pin, a display pin by the following commands:
ExtPin exttxD0 $Pin_PULLUP $ui "txD0" ".x"
key_txD0 Add [AvrDevice_GetPin $dev1 "E1"]
key_txD0 Add exttxD0
key_txD0 Add [SerialRxBasic_GetPin mysrx "rx"]
The last command ExtPin shows an alternative default value for txD0-Pin. Here it is pulled high - what is identical of adding any pull-up resistor to the device pin - no matter which resistor value is used.
While creating this example, simulavr helped to find the bugs left in the AP-Note.
atmega128_timer example¶
This example uses Timer 2 on the ATMega128 to generate a periodic interrupt. It prints 1 to 500 as the number of ticks increases. It’s not a dedicated tcl example, but shows, that you can use :file:`simulavr.tcl` in the same way as the original simulavr program.
To build the avr program go to examples/atmega128_timer
directory:
> avr-gcc -g -mmcu=atmega128 -DF_CPU=4000000UL -Os \
-o timer.elf main.c debugio.c
After performing the build you can start the simulation with simulavr:
> simulavr -d atmega128 -f timer.elf -W 0x20,- -R 0x22,- -T exit
or with simulavr.tcl
:
> tclsh ../simulavr.tcl -d atmega128 -f timer.elf -W 0x20,- -R 0x22,- -T exit
atmega48 example¶
Demonstrates the ATMega48 and following Stimulation classes:
HWAdmux - with additional pin inputs for not GPIO port support.
SpiSink - monitors the /SS, SCLK and MISO pins and prints each byte to stdout.
SpiSource - drives the /SS, SCLK and MOSI pins with data from the spidata file.
PinMonitor - monitors Port A Bit 0 and prints changes in its binary status to stdout.
AdcPin - Stimulates Port F Bit 0 with the values contained in the anadata{1,2,3} files.
The AVR program alternately (every other byte) echoes the byte received on the SPI or the ADCH value read from a recenet A/D converter, to the SPI. Also, the value from the A/D converter rotates through the values at the pins PC5, ADC6, and ADC7.
The spidata file contains an HDLC encoded stream of mostly flags that was used in my project at work. (We’re running a form of PPP/HDLC over SPI.)
The format of the spidata file consists of comments (lines that start with a ‘#’) and data lines. Each data line consists of 3 values.
First Value - the value (0 or non-zero)of /SS
Second Value - the value (0 or non-zero) of SCLK
Third Value - the value (0 or non-zero) of MOSI
When the SpiSource program stimulator reaches the end-of-file, it rewinds and repeats ad-nauseum.
The anadata{1,2,3} files contains analog data that that is read by the AdcPin class and written to the Port C Bit 5, ADC6 and ADC7 analog inputs of the ATMega48.
The format of the anadata{1,2,3} files consists of comments (lines that start with a ‘#’ character) and analog input lines. Each input line consists of 2 values separated by whitespace.
First Value - number of nano-seconds before the next value is read and applied to the analog input.
Second Value - signed integer “analogValue” to be applied to the analog input.
To try it:
- Step 1:
Build the AVR test program in
examples/atmega48
directory:> avr-gcc -g -mmcu=atmega48 -Os -o atmega48.elf main.cpp
- Step 2:
Prepare
check.tcl
in the same way as in other TCL examples before:#! @TCL_WISH@ # configuration area set installPrefix "@prefix@" # end configuration area #load the avr-simulator package load ${installPrefix}/libsimulavr.so
Replace the pathes for
wish
and the install path for libsimulavr.so- Step 3:
Run the test TCL script from this directory:
> wish check.tcl
- Step 4:
Marvel at the stdio activity.
- Step 5:
Try modifying the spidata file and see the results.
What you’ll see on stdout:
Note: Comments added on the right.
spisink: /SS negated ; SPI /SS goes HIGH (printed by SpiSink)
spisink: /SS asserted ; SPI /SS goes LOW (printed by SpiSink)
spisink: 0x7E ; echoed HDLC Data from AVR on SPI MISO
spisink: 0x66 ; Analog Data from AVR PC5 as decoded on SPI MISO
spisink: 0x02 ; echoed HDLC Data from AVR on SPI MISO
spisink: 0x33 ; Analog Data from AVR ADC6 as decoded on SPI MISO
spisink: 0xD3 ; echoed HDLC Data from AVR on SPI MISO
spisink: 0x28 ; Analog Data from AVR ADC7 as decoded on SPI MISO
spisink: 0x7E ; echoed HDLC Data from AVR on SPI MISO
spisink: 0x23 ; Analog Data from AVR PC5 as decoded on SPI MISO
...
...
spisink: 0x7E ; echoed HDLC Data from AVR on SPI MISO
spisink: 0x04 ; Analog Data from AVR {PC5,ADC6, ADC7} as decoded on SPI MISO
PORTB0: NEGATE ; Port B Bit 0 (interrupt output) set high by AVR (printed by PinMonitor)
...
...
feedback example¶
This example illustrates how one can provide a program external to the simulated AVR which provides “feedback” to the simulated program. A feedback program can interact with the AVR hosted program just like devices would in the “real world.”
This example is certainly a primitive example of this but it illustrates the principle. The application writes the following lines to UART0:
hello world #1
hello world #2
hello world #3
hello world #1
The initial input value of ADC0 is 0. When the feedback modules sees 1, 2 or 3, it changes the “voltage” on ADC0. The debug output expected is:
ADC0=10 expect 10
ADC0=20 expect 20
ADC0=30 expect 30
ADC0=10 expect 10
To build the avr program go to examples/feedback
directory:
> avr-gcc -g -mmcu=atmega128 -DF_CPU=4000000UL -Os \
-o feedback.elf main.c debugio.c uart.c adc.c
Prepare simfeedback.tcl
in the same way as in other TCL examples before:
#! @TCL_SHELL@
package require Itcl
namespace import itcl::*
Replace the path for tclsh
. If this is done, you can start the simulation
with simulavr.tcl
:
> tclsh ../simulavr.tcl -d atmega128 -f feedback.elf -s feedback.tcl \
-W 0x20,/dev/stderr -R 0x22,- -F 4000000 -T exit -S simfeedback.tcl
Python examples¶
This are some examples to demonstrate usage of pysimulavr. You need to build
python simulavr module named pysimulavr
. Maybe you have installed the
pysimulavr debian package, then you can test it:
> python3
>>> import pysimulavr
>>>
If this works without a error message, then the python module is ready.
If not, e.g. you want to run the tests against the module, you have to build
just before, then you can give the environment variable PYTHONPATH with the
path to _pysimulavr.so
and pysimulavr.py
in the same line just before
the python command:
> PYTHONPATH=<path-to-_pysimulavr.so> python3 <other-options>
All python examples are to find on examples/python
directory. Go there and
try it:
Simple timer unittest¶
We have to build the avr program for the simulation:
> avr-gcc -g -mmcu=atmega128 -O2 -o example.elf example.c
The program is a modified variant from tcl example atmega128_timer before. The test is written as a unittest. You can start it by:
> python3 example.py atmega128:example.elf
As result you should see something like this:
test_01 (__main__.TestBaseClass)
just run 3000 ns + 250 ns ... ok
test_02 (__main__.TestBaseClass)
just run 2 steps ... ok
test_03 (__main__.TestBaseClass)
check PC and PC size ... ok
test_04 (__main__.TestBaseClass)
check address of data symbols ... ok
test_05 (__main__.TestBaseClass)
access to data by symbol ... ok
test_06 (__main__.TestBaseClass)
write access to data by symbol ... ok
test_07 (__main__.TestBaseClass)
test toggle output pin ... ok
test_08 (__main__.TestBaseClass)
work with breakpoints ... ok
----------------------------------------------------------------------
Ran 8 tests in 0.842s
OK
So you can see, how easy it’s to write unittests for simulavr or also for your avr code. But you can use pysimulavr also for other things, look at example.py how to use pysimulavr.
Connect pins and change state¶
Shows the usage of Pin and Net. A net connect pins together. Change the output state of one pin will result in changing the input state of the other pins. This can be used as starting point to understand usage of SetOutState/SetInState methods of Pin class and how it works. This is not a real simulation. It demonstrates to use Pin and Net class without a simulation target. You can start it by:
> python3 example_pin.py
You see the following:
set vcc=5.00V ...
create 2 pins ...
pin1: (char)pin='L', (bool)pin=0, pin.GetAnalogValue(vcc)=2.75V
pin2: (char)pin='t', (bool)pin=1, pin.GetAnalogValue(vcc)=2.75V
create net ...
add pin1 to net:
<pin1 change: in=L/0.00V, out=L/0.00V>
add pin2 to net:
<pin1 change: in=L/0.00V, out=L/0.00V> <pin2 change: in=L/0.00V, out=t/0.00V>
pin1: (char)pin='L', (bool)pin=0, pin.GetAnalogValue(vcc)=0.00V
pin2: (char)pin='t', (bool)pin=0, pin.GetAnalogValue(vcc)=0.00V
set pin2 output to PULLUP:
<pin1 change: in=L/0.00V, out=L/0.00V> <pin2 change: in=L/0.00V, out=h/0.00V>
pin1: (char)pin='L', (bool)pin=0, pin.GetAnalogValue(vcc)=0.00V
pin2: (char)pin='h', (bool)pin=0, pin.GetAnalogValue(vcc)=0.00V
set pin1 output to HIGH:
<pin1 change: in=H/5.00V, out=H/5.00V> <pin2 change: in=H/5.00V, out=h/5.00V>
pin1: (char)pin='H', (bool)pin=1, pin.GetAnalogValue(vcc)=5.00V
pin2: (char)pin='h', (bool)pin=1, pin.GetAnalogValue(vcc)=5.00V
set pin2 output to TRISTATE:
<pin1 change: in=H/5.00V, out=H/5.00V> <pin2 change: in=H/5.00V, out=t/5.00V>
pin1: (char)pin='H', (bool)pin=1, pin.GetAnalogValue(vcc)=5.00V
pin2: (char)pin='t', (bool)pin=1, pin.GetAnalogValue(vcc)=5.00V
set pin1 output to TRISTATE:
<pin1 change: in=t/2.75V, out=t/2.75V> <pin2 change: in=t/2.75V, out=t/2.75V>
pin1: (char)pin='t', (bool)pin=1, pin.GetAnalogValue(vcc)=2.75V
pin2: (char)pin='t', (bool)pin=1, pin.GetAnalogValue(vcc)=2.75V
set pin2 output to LOW:
<pin1 change: in=L/0.00V, out=t/0.00V> <pin2 change: in=L/0.00V, out=L/0.00V>
pin1: (char)pin='t', (bool)pin=0, pin.GetAnalogValue(vcc)=0.00V
pin2: (char)pin='L', (bool)pin=0, pin.GetAnalogValue(vcc)=0.00V
How to control pins¶
This is a more complex example. It demonstrates, how you can simply watch for pin output changes and how you could inject external pin changes to the simulator to stimulate your program functionality. We build at first the avr program for the simulation:
> avr-gcc -g -mmcu=atmega128 -O2 -o example_io.elf example_io.c
The program is a modified variant from tcl example atmega128_timer before.
In this simulation we have a external connection to pin A0, A1 and A7 from port A and set the state of pin A1 and A7 to low or high at a defined simulation time. And we can see, when and how the state of this pin is changed.
You can start the simulation by:
> python3 example_io.py atmega128:example_io.elf
As result you should see something like this:
simulation start: (t=0µs)
simulation end: (t=15000µs)
pin A0
change to 't' at 0µs (dt=0µs)
change to 'L' at 17µs (dt=17µs)
change to 'H' at 2032µs (dt=2015µs)
change to 'L' at 4032µs (dt=2000µs)
change to 'H' at 6036µs (dt=2005µs)
change to 'L' at 8035µs (dt=1999µs)
change to 'H' at 10034µs (dt=1999µs)
change to 'L' at 12033µs (dt=1999µs)
change to 'H' at 14037µs (dt=2004µs)
pin A1
change to 'H' at 0µs (dt=0µs)
change to 'L' at 7000µs (dt=7000µs)
change to 'H' at 14000µs (dt=7000µs)
pin A7
change to 'H' at 0µs (dt=0µs)
change to 'L' at 12000µs (dt=12000µs)
value 'timer2_ticks'=7
value 'port_val'=0x7e
value 'port_cnt'=3
How to get a more detailed view¶
This example is closed to the example before. ex_pinout.c initialise timer2 in
CTC mode for a period of 2ms on 4MHz clock frequency. Example output shows the
toggle of pin A0. But we will also write a VCD dump. If you have installed
gtkwave
you can open this VCD dump file ex_pinout.vcd
with gtkwave
.
So you can compare time written out by this example with the results shown in
gtkwave
. The signal IRQ.VECTOR9 in VCD dump shows when and how long the ISR
was running! First build the avr program:
> avr-gcc -g -mmcu=atmega128 -O2 -o ex_pinout.elf ex_pinout.c
Then start it by:
> python3 ex_pinout.py atmega128:ex_pinout.elf
Output is:
port A.0 set to 't' (t=0ns)
port A.0 set to 't' (t=0ns)
simulation start: (t=0ns)
port A.0 set to 'L' (t=10750ns)
port A.0 set to 'L' (t=11000ns)
port A.0 set to 'H' (t=2017750ns)
port A.0 set to 'L' (t=4018000ns)
port A.0 set to 'H' (t=6018500ns)
port A.0 set to 'L' (t=8015750ns)
port A.0 set to 'H' (t=10016250ns)
port A.0 set to 'L' (t=12016500ns)
port A.0 set to 'H' (t=14017000ns)
simulation end: (t=15000000ns)
value 'timer2_ticks'=7
And now (if you have installed gtkwave
) you can view the traced waveforms:
> gtkwave -a ex_pinout.sav ex_pinout.vcd
The full view:

Let’s look on a detail. You can see, how long the interrupt procedure was running and when the port value was changed. Compare it with the print out on standard out before! (in picture the value for “Marker:”)

Multicore example¶
This example demonstrates using python interface for multicore simulation. We simulate 2 ATmega16 cores:
+----------+ +----------+
| Core A | PB3 PD2 | Core B |
| 4MHz |------>------| 10MHz |
| | | |
+----------+ +----------+
Core A runs as a 250Hz clock generator on pin B3. B3 from core A is conected with pin D2 on core B. Core B counts now all rising edges on pin D2 and measures the time distance between 2 events with timer T0.
This example shows:
how to use python interface
how it is possible to run a multicore simulation, in this example also with different clock sources for the cores
how to connect pins between cores
how to access global variables, how to get address for a global variable and how to read RAM values from a address
Build the 2 avr programs:
> avr-gcc -g -mmcu=atmega16 -O2 -DDUAL_A=1 -o multicore_a.elf multicore.c
> avr-gcc -g -mmcu=atmega16 -O2 -DDUAL_B=1 -o multicore_b.elf multicore.c
And run the simulation:
> python3 multicore.py
Resulting output should then look like:
multicore example:
create core A ...
create core B ...
connect core A with core B ...
core B: address(cnt_irq)=0x61
core B: address(cnt_res)=0x61
run simulation ...
t= 4ms, cnt_irq=1, cnt_res= 78
t= 8ms, cnt_irq=2, cnt_res=156
t=20ms, cnt_irq=5, cnt_res=157
t=32ms, cnt_irq=8, cnt_res=156
ADC example¶
A example to simulate analog input and how to simulate adc conversion. Build avr program and run the simulation:
> avr-gcc -g -mmcu=atmega16 -O2 -o adc.elf adc.c
> python3 adc.py atmega16:adc.elf
The output shows:
before simulation start:
value 'adc_value'=43690 (before init)
aref set to 2.5V
a0 set to 0.3V, this will expect an converted adc int value=122
simulation start: (t=0ns)
run till main function ...
simulation main entrance: (t=24250ns)
value 'adc_value'=5555 (after init)
simulation break: (t=144250ns)
value 'conversions'=1
value 'adc_value'=122 (simulation break)
simulation end: (t=474250ns)
value 'conversions'=6
value 'adc_value'=122 (simulation end)
Verilog examples¶
To use this examples you have to build simulavr together with the verilog extension.
See here how to make it. You can find the example files
in examples/verilog
directory. Further, if you want to see the waveform you need
the gtkwave
program. It’s a program to display digital waveforms.
baretest example¶
First compile and link avr program:
> avr-gcc -mmcu=at90s4433 -Os -o toggle.elf toggle.c
Then compile and run the verilog source file:
> iverilog baretest.v -s test -v avr.v -o baretest.vvp
> vvp -M<path-to-avr.vpi-directory> -mavr baretest.vvp
Replace <path-to-avr.vpi-directory> to the directory, where your avr.vpi
is situated.
(could be, for example, in <root-of-repository>/build/libsim
) This will create a file
baretest.vcd
. And if you now start gtkwave
, you can see the result:
> gtkwave -a baretest.sav baretest.vcd
What this example do?
This is the code:
int main() {
DDRB = 1;
while(1) {
PORTB = 1;
PORTB = 0;
}
}
It sets port B pin 0 to output and start a endless loop toggeling pin 0 at port B. And the result is:

loop example¶
Steps are the same as before for baretest example:
> avr-gcc -mmcu=attiny2313 -Os -o loop.elf loop.c
> iverilog loop.v -s test -v avr.v avr_ATtiny2313.v -o loop.vvp
> vvp -M<path-to-avr.vpi-directory> -mavr loop.vvp
The code is similar to toggle.c but with a twist:
int main() {
DDRB = 0xff;
PORTB = 1;
while(1) {
PORTB = PINB << 1;
}
}
Lets see the result:
> gtkwave -a loop.sav loop.vcd

spi waveform examples¶
A more complicated example: send data via spi:
> avr-gcc -mmcu=atmega8 -Os -o spi-waveforms.elf spi-waveforms.c
> iverilog spi-waveforms.v -s test -v avr.v avr_ATmega8.v -o spi-waveforms.vvp
> vvp -M<path-to-avr.vpi-directory> -mavr spi-waveforms.vvp
> gtkwave -a spi-waveforms.sav spi-waveforms.vcd
And we can see the spi signals (just the first byte sequence) in gtkwave:

And a second example where data will be send out from controller and received by controller:
> avr-gcc -mmcu=atmega8 -Os -o spi.elf spi.c
> iverilog spi.v -s test -v avr.v avr_ATmega8.v -o spi.vvp
> vvp -M<path-to-avr.vpi-directory> -mavr spi.vvp
> gtkwave -a spi.sav spi.vcd
And the simulation result:

vst example¶
The example shows how two cores can be instantiated. Both cores are driven with different clocks:
> avr-gcc -mmcu=atmega32 -Os -o vst.elf vst.cpp
> iverilog vst.v -s test -v avr.v avr_ATmega32.v -o vst.vvp
> vvp -M<path-to-avr.vpi-directory> -mavr vst.vvp
> gtkwave -a vst.sav vst.vcd
There is a wire “out” which is connected to pins of both devices.
What the graph shows:

driver_enabledX
(X is 1 or 2 for core 1 or core 2) shows the pin from the port
driven by coreX - this represents DDR bit. logic_levelX
represents the setting
of PORT bit. outX
represents the resulting signal from above values.
Because there are 2 devices driving the sum
wire, we get the following results:
if both core have the driver disabled and none has pull up enabled, the result is “x”
if only one core has the driver enabled, the wire
sum
is the value of “out” of the driver.if two cores have the driver enabled and both “out” signals are the same,
sum
shows the same level as “out”.if both cores drive the signal but with different level, the result on
sum
is “x” (short circuit!)
If the avr reads from the pins ( mirrored in the signals mirrorX ) the read value is “1” if the wire is in “1”, “z” or “x”! There is no definition of “z” or “x” so we simply use “1”. A logic “0” is read as “0”.
spc example¶
A last verilog example show also the use of 2 cores with 2 different device types and also different clocks:
> # create right-unit.elf
> avr-gcc -c -Wa,-gstabs -x assembler-with-cpp -o right-unit.o right-unit.s
> avr-gcc -c -Wa,-gstabs -x assembler-with-cpp -o singlepincomm.o singmepincomm.s
> avr-ld -e _start -o right-unit.elf right-unit.o singlepincomm.o
> # create left-unit.elf
> avr-gcc -mmcu=attiny2313 -Os -o left-unit.elf left-unit.c csinglepincomm.c
> # compile verilog source and run it
> iverilog spc.v -s test -v avr.v avr_ATtiny2313.v avr_ATtiny25.v -o spc.vvp
> vvp -M<path-to-avr.vpi-directory> -mavr spc.vvp
> # show result
> #gtkwave -a spc.sav spc.vcd