Commit 46b57676df609623515f691b0af46192ecc74615
receivedSun, 27. Nov 2022, 16:24:45 (by user sx)
Sun, 27 Nov 2022 15:24:45 +0000 (16:24 +0100)
authorManfred Steiner <sx@htl-kaindorf.at>
Sun, 27 Nov 2022 15:24:15 +0000 (16:24 +0100)
committerManfred Steiner <sx@htl-kaindorf.at>
Sun, 27 Nov 2022 15:24:15 +0000 (16:24 +0100)
96 files changed:
README.md
examples/Makefile
examples/simuc/.gitignore [new file with mode: 0644]
examples/simuc/.vscode/launch.json [new file with mode: 0644]
examples/simuc/.vscode/settings.json [new file with mode: 0644]
examples/simuc/.vscode/tasks.json [new file with mode: 0644]
examples/simuc/Makefile [new file with mode: 0644]
examples/simuc/README.md [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/DEBIAN/control [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/bin/simuc [new symlink]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/copyright [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/readme [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/readme [new symlink]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr/avr_mcu_section.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/fifo_declare.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/run_avr.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr_types.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_network.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_regbit.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_time.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/simuc [new file with mode: 0755]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/main.cpp [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.cpp [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.cpp [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/fifo_declare.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.c [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/semaphore.h [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.cpp [new file with mode: 0644]
examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.h [new file with mode: 0644]
examples/simuc/sim/vcd/.gitkeep [new file with mode: 0644]
examples/simuc/src/main.cpp [new file with mode: 0644]
examples/simuc/src/sim/error.cpp [new file with mode: 0644]
examples/simuc/src/sim/error.h [new file with mode: 0644]
examples/simuc/src/sim/sim.cpp [new file with mode: 0644]
examples/simuc/src/sim/sim.h [new file with mode: 0644]
examples/simuc/src/simavr/parts/fifo_declare.h [new file with mode: 0644]
examples/simuc/src/simavr/parts/uart_pty.c [new file with mode: 0644]
examples/simuc/src/simavr/parts/uart_pty.h [new file with mode: 0644]
examples/simuc/src/simavr/semaphore.h [new file with mode: 0644]
examples/simuc/src/simavr/simavr.cpp [new file with mode: 0644]
examples/simuc/src/simavr/simavr.h [new file with mode: 0644]

index 606e9f9218e10a1abff32ec8b1e0cda2cb4cb83f..450d4db95ab904446f2db73d087f56ce291ae34a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -2,29 +2,46 @@
 
 This repository is mirrored from [https://github.com/buserror/simavr.git](https://github.com/buserror/simavr.git), commit 7003af0 (Mon Jul 18 10:29:17 2022)
 
-Original `README.md`: see [README-simavr.md](README-simavr.md)
+* **simuc**: see [examples/](examples/simuc/README.md)
+* Original `README.md`: see [README-simavr.md](README-simavr.md)
 
-## build
+------------------------------------------
+
+## Build simavr library on Debian- or Ubuntu-systems
 
 ### Prepare system
 ```sh
 $ sudo apt install gcc make gcc-avr avr-libc
 $ sudo apt install libelf-dev
 $ sudo apt install freeglut3 freeglut3-dev
+$ sudo apt install libncurses3
 ```
 
-### make simavr libs
+### Clone repository
+
+```sh
+user@host:~ $ git clone https://git.htl-mechatronik.at/public/sx/simavr.git
+user@host:~ $ cd simavr
+user@host:~/simavr $
+```
 
-Make sure your shell working directory is simavr
+### Make libaries and example executables
 
 ```sh
-$ cd simavr
-$ make
+user@host:~/simavr $ make
 ``` 
 
 Result is available in:
-* static lib: `obj-x86_64-linux-gnu/libsimavr.a`
-* shared lib: `obj-x86_64-linux-gnu/libsimavr.so`
+
+* static lib: `obj-.../libsimavr.a`
+* shared lib: `obj-.../libsimavr.so`
+
+The ... is a placeholder for the *architecture*, which is the result of `gcc -dumpmachine` and depends on system architecture:
+* i386: `obj-i686-linux-gnu`
+* amd64: `obj-x86_64-linux-gnu`
+* arm64: `obj-aarch64-linux-gnu`
+* ...
+
 
 Project examples available in folder `examples`
 
index 65cc8933bfe3d6a8708865354532fb500be5c609..7fe92ee576ce40ae8aafe928e9f09c41042192f1 100644 (file)
@@ -9,9 +9,11 @@ endif
 
 all:
        for bi in ${boards}; do $(MAKE) -C $$bi; done
+       $(MAKE) -C simuc
 
 clean:
        for bi in ${boards}; do $(MAKE) -C $$bi clean; done
+       $(MAKE) -C simuc clean
 
 #
 # The USB example is not made by default, as it downloads stuff
diff --git a/examples/simuc/.gitignore b/examples/simuc/.gitignore
new file mode 100644 (file)
index 0000000..d1ad022
--- /dev/null
@@ -0,0 +1,4 @@
+dist/**
+build/**
+dpkg/**/*.deb
+**/*.vcd
diff --git a/examples/simuc/.vscode/launch.json b/examples/simuc/.vscode/launch.json
new file mode 100644 (file)
index 0000000..64e527c
--- /dev/null
@@ -0,0 +1,21 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/dist/simuc",
+            "args": [],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "preLaunchTask": "build"
+        }
+    ]
+}
diff --git a/examples/simuc/.vscode/settings.json b/examples/simuc/.vscode/settings.json
new file mode 100644 (file)
index 0000000..189a435
--- /dev/null
@@ -0,0 +1,87 @@
+{
+    "files.associations": {
+        "cstdio": "cpp",
+        "array": "cpp",
+        "atomic": "cpp",
+        "bit": "cpp",
+        "*.tcc": "cpp",
+        "cctype": "cpp",
+        "chrono": "cpp",
+        "clocale": "cpp",
+        "cmath": "cpp",
+        "compare": "cpp",
+        "concepts": "cpp",
+        "condition_variable": "cpp",
+        "cstdarg": "cpp",
+        "cstddef": "cpp",
+        "cstdint": "cpp",
+        "cstdlib": "cpp",
+        "ctime": "cpp",
+        "cwchar": "cpp",
+        "cwctype": "cpp",
+        "deque": "cpp",
+        "list": "cpp",
+        "map": "cpp",
+        "string": "cpp",
+        "unordered_map": "cpp",
+        "vector": "cpp",
+        "exception": "cpp",
+        "algorithm": "cpp",
+        "functional": "cpp",
+        "iterator": "cpp",
+        "memory": "cpp",
+        "memory_resource": "cpp",
+        "numeric": "cpp",
+        "random": "cpp",
+        "ratio": "cpp",
+        "string_view": "cpp",
+        "system_error": "cpp",
+        "tuple": "cpp",
+        "type_traits": "cpp",
+        "utility": "cpp",
+        "future": "cpp",
+        "initializer_list": "cpp",
+        "iomanip": "cpp",
+        "iosfwd": "cpp",
+        "iostream": "cpp",
+        "istream": "cpp",
+        "limits": "cpp",
+        "mutex": "cpp",
+        "new": "cpp",
+        "numbers": "cpp",
+        "ostream": "cpp",
+        "semaphore": "cpp",
+        "sstream": "cpp",
+        "stdexcept": "cpp",
+        "stop_token": "cpp",
+        "streambuf": "cpp",
+        "thread": "cpp",
+        "cinttypes": "cpp",
+        "typeinfo": "cpp",
+        "variant": "cpp"
+    },
+    "cSpell.words": [
+        "aref",
+        "atmega",
+        "avcc",
+        "avrsim",
+        "ddrb",
+        "eeprom",
+        "eesize",
+        "evws",
+        "ioend",
+        "lfcrlf",
+        "lockbits",
+        "megaavr",
+        "millis",
+        "mmcu",
+        "picocom",
+        "PORTB",
+        "ramend",
+        "simavr",
+        "simuc",
+        "slavename",
+        "SRAM",
+        "sreg"
+    ]
+}
\ No newline at end of file
diff --git a/examples/simuc/.vscode/tasks.json b/examples/simuc/.vscode/tasks.json
new file mode 100644 (file)
index 0000000..4e8bd30
--- /dev/null
@@ -0,0 +1,26 @@
+{
+       // See https://go.microsoft.com/fwlink/?LinkId=733558
+       // for the documentation about the tasks.json format
+       "version": "2.0.0",
+       "tasks": [
+               {
+                       "label": "build",
+                       "type": "shell",
+                       "command": "make",
+                       "args": [],
+                       "problemMatcher": [ "$gcc" ],
+                       "group": {
+                               "kind": "build",
+                               "isDefault": true
+                       }
+               },
+               {
+                       "label": "clean",
+                       "type": "shell",
+                       "command": "make",
+                       "args": [
+                               "clean"
+                       ]
+               }
+       ]
+}
diff --git a/examples/simuc/Makefile b/examples/simuc/Makefile
new file mode 100644 (file)
index 0000000..456d1d5
--- /dev/null
@@ -0,0 +1,111 @@
+$(shell mkdir -p dist >/dev/null)
+$(shell mkdir -p build >/dev/null)
+
+PRJ="simuc"
+$(PRJ): dist/simuc
+
+start2: $(PRJ)
+       LD_LIBRARY_PATH=$(pwd);dist/simuc
+
+start: $(PRJ)
+       dist/simuc
+
+CFLAGS = -Os -Wall
+#X_CFLAGS = -O0 -g -Wall
+X_CFLAGS = -Os -g -Wall
+
+
+dist/simuc: build/main.o build/sim.o build/simavr.o build/error.o build/uart_pty.o
+       #g++ -o $@ -g -Wall -gdwarf-2 $^ -Wl,-rpath,$(CURDIR) -L$(CURDIR) -lsimavr  -lpthread -lutil
+       g++ -o $@ -g -Wall -gdwarf-2 $^ ../../simavr/obj-$(shell gcc -dumpmachine)/libsimavr.a -lelf -lpthread -lutil
+
+
+#dist/libsim.so: build/sim.o build/simavr.o build/error.o build/uart_pty.o
+#      gcc -o $@ $(CFLAGS) -shared $^
+
+build/sim.o: src/sim/sim.cpp src/sim/sim.h src/sim/error.h src/simavr/simavr.h
+       gcc -o $@ $(CFLAGS) -fPIC -c $<
+
+build/simavr.o: src/simavr/simavr.cpp src/simavr/simavr.h src/sim/error.h
+       gcc -o $@ $(CFLAGS) -fPIC -c $<
+
+build/error.o: src/sim/error.cpp src/sim/error.h
+       gcc -o $@ $(CFLAGS) -fPIC -c $<
+
+build/uart_pty.o: src/simavr/parts/uart_pty.c src/simavr/parts/uart_pty.h src/simavr/parts/fifo_declare.h
+       gcc -o $@ $(CFLAGS) -Wno-unused-result -fPIC -c $<
+
+build/main.o: src/main.cpp src/sim/sim.h src/simavr/simavr.h
+       g++ -o $@ $(X_CFLAGS) -c $<
+
+
+# ############################################
+# Debian Packet for htl-simuc_version_arch.deb
+# ############################################
+
+ARCH := $(shell arch)
+ifeq ($(ARCH), x86_64)
+       DEBARCH="amd64"
+       OBJARCH=""
+endif
+ifeq ($(ARCH), x86_32)
+       DEBARCH="i386"
+endif
+ifeq ($(ARCH), aarch64)
+       DEBARCH="arm64"
+endif
+
+DEBVERSION := 0.0.1~1
+DEBNAME := dpkg/htl-simuc_version_arch
+DEBSRC := dpkg/htl-simuc_version_arch
+
+DEB := $(DEBNAME:%version=%$(DEBVERSION),%arch=%$(DEBARCH).deb)
+
+deb: dpkg $(DEBSRC)/DEBIAN/control $(DEBSRC)/usr/share/doc/htl-simuc/readme
+       @dpkg-deb --root-owner-group --build $(DEBSRC) ${DEB}
+
+.PHONY: $(DEBSRC)/DEBIAN/control $(DEBSRC)/usr/share/doc/htl-simuc/readme
+
+$(DEBSRC)/DEBIAN/control:
+       echo "Package: htl-simuc" > $@
+       echo "Version:" ${DEBVERSION} >> $@
+       @echo "Section: devel" >> $@
+       echo "Architecture:" $(DEBARCH) >> $@
+       @echo "Depends: libelf1" >> $@
+       @echo "Recommends: gcc-avr, avr-libc, gdb-avr, binutils-avr, avrdude" >> $@
+       echo "Installed-Size:" $(shell du -s $(DEBSRC) | cut -f1) >> $@
+       @echo "Priority: optional" >> $@
+       @echo "Maintainer: Manfred Steiner <sx@htl-kaindorf.at>" >> $@
+       @echo "Description: megaavr microcontroller simulation" >> $@
+       @chmod 644 $@
+
+$(DEBSRC)/usr/share/doc/htl-simuc/readme: 
+       @echo "created: $(shell date)" > $@
+       @echo "https://git.htl-mechatronik.at/public/?p=sx/simavr.git;a=home" >> $@
+       @echo  >> $@
+       @echo "$ git clone https://git.htl-mechatronik.at/public/sx/simavr.git" >> $@
+       @echo "$ git checkout $(shell git rev-parse HEAD)" >> $@
+       @chmod 644 $@
+
+dpkg: $(PRJ) 
+       @test -d $(DEBSRC)/usr/share/htl-simuc || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc
+       @test -d $(DEBSRC)/usr/share/htl-simuc/simuc || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc/simuc
+       rsync -a --delete dist/simuc $(DEBSRC)/usr/share/htl-simuc/simuc
+       @test -d $(DEBSRC)/usr/share/htl-simuc/simuc/src || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc/simuc/src
+       rsync -a --delete src/ $(DEBSRC)/usr/share/htl-simuc/simuc/src/
+       @test -d $(DEBSRC)/usr/share/htl-simuc/simavr || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc/simavr
+       @test -d $(DEBSRC)/usr/share/htl-simuc/simavr/sim || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc/simavr/sim
+       rsync -a --delete ../../simavr/sim/ $(DEBSRC)/usr/share/htl-simuc/simavr/sim/
+       @find $(DEBSRC) -type d -exec chmod 755 {} \;
+       @find $(DEBSRC)/usr/share/htl-simuc/ -type f -exec chmod 644 {} \;
+       @chmod 755 $(DEBSRC)/usr/share/htl-simuc/simuc/simuc
+       @touch $(DEBSRC)
+
+# #####################################################################
+
+clean:
+       -@rm -r dist
+       -@rm -r build
+       -@find dpkg -name "*.deb" -delete
+       -@rm -rf $(DEBSRC)/usr/share/htl-simuc
+       -@rm -f $(DEBSRC)/DEBIAN/control
diff --git a/examples/simuc/README.md b/examples/simuc/README.md
new file mode 100644 (file)
index 0000000..5dee7f8
--- /dev/null
@@ -0,0 +1,64 @@
+# simuc
+
+**simuc** is used to simulate a megaAvr microcontroller.
+
+If the cpu is stopped by command or `avr-gdb`, some information is printed on standard output:  
+* Number of cycles and elapsed time
+* Status Flags
+* General Purpose registers r0..r31
+* address registers X, Y and Z
+* Stack pointer SP and stack content
+
+Commands available via standard input (keyboard):
+* *Enter* print current cycles and elapsed time
+* `interrupt`: stop cpu
+* `continue`: continue cpu run
+* `stack`: print complete stack content on standard output
+
+Microcontrollers UART interface(s) is/are connected to local devices and can be used with terminal programs (like `picocom`).
+
+After start of `simuc` the program waits for an keyboard action to start simulation. So you are able to connect with avr-gdb before the simulation is executing some machine code.
+
+1) start `simuc` with proper options and arguments
+2) start new shell and execute `avr-gdb`
+3) inside (gdb) connect to simuc with the gdb shell command `target remote :1234`
+4) load symbols with gdb command `file ...`
+5) optionally set source directory with `directory ...`
+6) press enter in the shell of simuc
+7) now debug avr program step by step with gdb commands `stepi`, `step`, `nexti`, `next` or continue execution with `continue`
+
+-------------------------------------------------------------
+
+## Build simavr
+
+### Prepare system
+```sh
+$ sudo apt install gcc make gcc-avr avr-libc
+$ sudo apt install libelf-dev
+$ sudo apt install freeglut3 freeglut3-dev
+$ sudo apt install libncurses3
+```
+
+### Clone repository
+
+```sh
+user@host:~ $ git clone https://git.htl-mechatronik.at/public/sx/simavr.git
+user@host:~ $ cd simavr/examples/simuc
+user@host:~/simavr/examples/simuc $
+```
+
+### Make simuc executable
+
+```sh
+user@host:~/simavr/examples/simuc $ make
+``` 
+
+Executable `simuc` available in folder `dist`
+
+### Make debian packet `htl-simuc`
+
+```sh
+user@host:~/simavr/examples/simuc $ make deb
+``` 
+
+Debian packet available in folder `dpkg`
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/DEBIAN/control b/examples/simuc/dpkg/htl-simuc_version_arch/DEBIAN/control
new file mode 100644 (file)
index 0000000..6b155e4
--- /dev/null
@@ -0,0 +1,10 @@
+Package: htl-simuc
+Version: 0.0.1~1
+Section: devel
+Architecture: amd64
+Depends: libelf1
+Recommends: gcc-avr, avr-libc, gdb-avr, binutils-avr, avrdude
+Installed-Size: 4328
+Priority: optional
+Maintainer: Manfred Steiner <sx@htl-kaindorf.at>
+Description: megaavr microcontroller simulation
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/bin/simuc b/examples/simuc/dpkg/htl-simuc_version_arch/usr/bin/simuc
new file mode 120000 (symlink)
index 0000000..c184c52
--- /dev/null
@@ -0,0 +1 @@
+../share/htl-simuc/simuc/simuc
\ No newline at end of file
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/copyright b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/copyright
new file mode 100644 (file)
index 0000000..5cf1991
--- /dev/null
@@ -0,0 +1,43 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: htl-simuc
+
+Files: *
+Copyright: 2022 Manfred Steiner <sx@htl-kaindorf.at>
+License: GPL-3+
+
+License: GPL-3+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General Public
+ License version 3 can be found in "/usr/share/common-licenses/GPL-3".
+
+License: LGPL-3+
+ This library is free software: you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+ .
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library.  If not, see
+ <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License version 3 can be found in
+ "/usr/share/common-licenses/LGPL-3".
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/readme b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/readme
new file mode 100644 (file)
index 0000000..06453d5
--- /dev/null
@@ -0,0 +1,5 @@
+created: So 27 Nov 2022 16:21:49 CET
+https://git.htl-mechatronik.at/public/?p=sx/simavr.git;a=home
+
+git clone https://git.htl-mechatronik.at/public/sx/simavr.git
+git checkout 0a12726a5335284495e557078a73381dc5fc8599
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/readme b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/readme
new file mode 120000 (symlink)
index 0000000..bf9863d
--- /dev/null
@@ -0,0 +1 @@
+../doc/htl-simuc/readme
\ No newline at end of file
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr/avr_mcu_section.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr/avr_mcu_section.h
new file mode 100644 (file)
index 0000000..3deae7b
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+       avr_mcu_section.h
+
+       Copyright 2008-2013 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_MCU_SECTION_H__
+#define __AVR_MCU_SECTION_H__
+
+/*
+ * This header is used to pass "parameters" to the programmer or the simulator,
+ * it tags the ELF file with a section that contains parameters about the physical
+ * AVR this was compiled for, including the speed, model, and signature bytes.
+ *
+ * A programmer software can read this and verify fuses values for example, and a
+ * simulator can instantiate the proper "model" of AVR, the speed and so on without
+ * command line parameters.
+ *
+ * Example of use:
+ *
+ * #include "avr_mcu_section.h"
+ * AVR_MCU(F_CPU, "atmega88");
+ *
+ */
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+       AVR_MMCU_TAG = 0,
+       AVR_MMCU_TAG_NAME,
+       AVR_MMCU_TAG_FREQUENCY,
+       AVR_MMCU_TAG_VCC,
+       AVR_MMCU_TAG_AVCC,
+       AVR_MMCU_TAG_AREF,
+       AVR_MMCU_TAG_LFUSE,
+       AVR_MMCU_TAG_HFUSE,
+       AVR_MMCU_TAG_EFUSE,
+       AVR_MMCU_TAG_SIGNATURE,
+       AVR_MMCU_TAG_SIMAVR_COMMAND,
+       AVR_MMCU_TAG_SIMAVR_CONSOLE,
+       AVR_MMCU_TAG_VCD_FILENAME,
+       AVR_MMCU_TAG_VCD_PERIOD,
+       AVR_MMCU_TAG_VCD_TRACE,
+       AVR_MMCU_TAG_VCD_PORTPIN,
+       AVR_MMCU_TAG_VCD_IRQ,
+       AVR_MMCU_TAG_PORT_EXTERNAL_PULL,
+};
+
+enum {
+       SIMAVR_CMD_NONE = 0,
+       SIMAVR_CMD_VCD_START_TRACE,
+       SIMAVR_CMD_VCD_STOP_TRACE,
+       SIMAVR_CMD_UART_LOOPBACK,
+};
+
+#if __AVR__
+/*
+ * WARNING. Due to newer GCC being stupid, they introduced a bug that
+ * prevents us introducing variable length strings in the declaration
+ * of structs. Worked for a million years, and no longer.
+ * So the new method declares the string as fixed size, and the parser
+ * is forced to skip the zeroes in padding. Dumbo.
+ */
+#define _MMCU_ __attribute__((section(".mmcu"))) __attribute__((used))
+struct avr_mmcu_long_t {
+       uint8_t tag;
+       uint8_t len;
+       uint32_t val;
+} __attribute__((__packed__));
+
+struct avr_mmcu_string_t {
+       uint8_t tag;
+       uint8_t len;
+       char string[64];
+} __attribute__((__packed__));
+
+struct avr_mmcu_addr_t {
+       uint8_t tag;
+       uint8_t len;
+       void * what;
+} __attribute__((__packed__));
+
+struct avr_mmcu_vcd_trace_t {
+       uint8_t tag;
+       uint8_t len;
+       uint8_t mask;
+       void * what;
+       char name[32];
+} __attribute__((__packed__));
+
+#define AVR_MCU_STRING(_tag, _str) \
+       const struct avr_mmcu_string_t _##_tag _MMCU_ = {\
+               .tag = _tag,\
+               .len = sizeof(struct avr_mmcu_string_t) - 2,\
+               .string = _str,\
+       }
+/*
+ * This trick allows concatenation of tokens. We need a macro redirection
+ * for it to work.
+ * The goal is to make unique variable names (they don't matter anyway)
+ */
+#define DO_CONCAT2(_a, _b) _a##_b
+#define DO_CONCAT(_a, _b) DO_CONCAT2(_a,_b)
+
+#define AVR_MCU_LONG(_tag, _val) \
+       const struct avr_mmcu_long_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
+               .tag = _tag,\
+               .len = sizeof(struct avr_mmcu_long_t) - 2,\
+               .val = _val,\
+       }
+
+#define AVR_MCU_BYTE(_tag, _val) \
+       const uint8_t _##_tag _MMCU_ = { _tag, 1, _val }
+
+/*!
+ * This Macro allows you to specify traces for the VCD file output
+ * engine. This specifies a default header, and let you fill in the
+ * relevant bits.
+ * Example:
+ *     const struct avr_mmcu_vcd_trace_t _mytrace[]  _MMCU_ = {
+ *             { AVR_MCU_VCD_SYMBOL("UDR0"), .what = (void*)&UDR0, },
+ *             { AVR_MCU_VCD_SYMBOL("UDRE0"), .mask = (1 << UDRE0), .what = (void*)&UCSR0A, },
+ *     };
+ * This structure will automatically tell simavr to add a VCD trace
+ * for the UART register, and the UDRE0 bit, so you can trace exactly
+ * the timing of the changed using gtkwave.
+ */
+#define AVR_MCU_VCD_SYMBOL(_name) \
+       .tag = AVR_MMCU_TAG_VCD_TRACE, \
+       .len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
+       .name = _name
+
+/*!
+ * Specifies the name and wanted period (in usec) for a VCD file
+ * this is not mandatory for the VCD output to work, if this tag
+ * is not used, a VCD file will still be created with default values
+ */
+#define AVR_MCU_VCD_FILE(_name, _period) \
+       AVR_MCU_STRING(AVR_MMCU_TAG_VCD_FILENAME, _name);\
+       AVR_MCU_LONG(AVR_MMCU_TAG_VCD_PERIOD, _period)
+
+/*!
+ * It is possible to send "commands" to simavr from the
+ * firmware itself. For this to work you need to specify
+ * an IO register that is to be used for a write-only
+ * bridge. A favourite is one of the usual "GPIO register"
+ * that most (all ?) AVR have.
+ * See definition of SIMAVR_CMD_* to see what commands can
+ * be used from your firmware.
+ */
+#define AVR_MCU_SIMAVR_COMMAND(_register) \
+       const struct avr_mmcu_addr_t _simavr_command_register _MMCU_ = {\
+               .tag = AVR_MMCU_TAG_SIMAVR_COMMAND,\
+               .len = sizeof(void *),\
+               .what = (void*)_register, \
+       }
+/*!
+ * Similar to AVR_MCU_SIMAVR_COMMAND, The CONSOLE allows the AVR code
+ * to declare a register (typically a GPIO register, but any unused
+ * register can work...) that will allow printing on the host's console
+ * without using a UART to do debug.
+ */
+#define AVR_MCU_SIMAVR_CONSOLE(_register) \
+       const struct avr_mmcu_addr_t _simavr_console_register _MMCU_ = {\
+               .tag = AVR_MMCU_TAG_SIMAVR_CONSOLE,\
+               .len = sizeof(void *),\
+               .what = (void*)_register, \
+       }
+/*!
+ * Allows the firmware to hint simavr as to wether there are external
+ * pullups/down on PORT pins. It helps if the firmware uses "open drain"
+ * pins by toggling the DDR pins to switch between an output state and
+ * a "default" state.
+ * The value passed here will be output on the PORT IRQ when the DDR
+ * pin is set to input again
+ */
+#define AVR_MCU_EXTERNAL_PORT_PULL(_port, _mask, _val) \
+       AVR_MCU_LONG(AVR_MMCU_TAG_PORT_EXTERNAL_PULL, \
+               (((unsigned long)((_port)&0xff) << 16) | \
+               ((unsigned long)((_mask)&0xff) << 8) | \
+               ((_val)&0xff)));
+/*!
+ * Add this port/pin to the VCD file. The syntax uses the name of the
+ * port as a character, and not a pointer to a register.
+ * AVR_MCU_VCD_PORT_PIN('B', 5);
+ */
+#define AVR_MCU_VCD_PORT_PIN(_port, _pin, _name) \
+       const struct avr_mmcu_vcd_trace_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
+               .tag = AVR_MMCU_TAG_VCD_PORTPIN, \
+               .len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
+               .mask = _port, \
+               .what = (void*)_pin, \
+               .name = _name, \
+       }
+
+/*!
+ * These allows you to add a trace showing how long an IRQ vector is pending,
+ * and also how long it is running. You can specify the IRQ as a vector name
+ * straight from the firmware file, and it will be named properly in the trace
+ */
+
+#define AVR_MCU_VCD_IRQ_TRACE(_vect_number, __what, _trace_name) \
+       const struct avr_mmcu_vcd_trace_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
+               .tag = AVR_MMCU_TAG_VCD_IRQ, \
+               .len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
+               .mask = _vect_number, \
+               .what = (void*)__what, \
+               .name = _trace_name, \
+       };
+#define AVR_MCU_VCD_IRQ(_irq_name) \
+       AVR_MCU_VCD_IRQ_TRACE(_irq_name##_vect_num, 1, #_irq_name)
+#define AVR_MCU_VCD_IRQ_PENDING(_irq_name) \
+       AVR_MCU_VCD_IRQ_TRACE(_irq_name##_vect_num, 0, #_irq_name "_pend")
+#define AVR_MCU_VCD_ALL_IRQ() \
+       AVR_MCU_VCD_IRQ_TRACE(0xff, 1, "IRQ")
+#define AVR_MCU_VCD_ALL_IRQ_PENDING() \
+       AVR_MCU_VCD_IRQ_TRACE(0xff, 0, "IRQ_PENDING")
+
+/*!
+ * This tag allows you to specify the voltages used by your board
+ * It is optional in most cases, but you will need it if you use
+ * ADC module's IRQs. Not specifying it in this case might lead
+ * to a divide-by-zero crash.
+ * The units are Volts*1000 (millivolts)
+ */
+#define AVR_MCU_VOLTAGES(_vcc, _avcc, _aref) \
+       AVR_MCU_LONG(AVR_MMCU_TAG_VCC, (_vcc));\
+       AVR_MCU_LONG(AVR_MMCU_TAG_AVCC, (_avcc));\
+       AVR_MCU_LONG(AVR_MMCU_TAG_AREF, (_aref));
+
+/*!
+ * This the has to be used if you want to add other tags to the .mmcu section
+ * the _mmcu symbol is used as an anchor to make sure it stays linked in.
+ */
+#define AVR_MCU(_speed, _name) \
+       AVR_MCU_STRING(AVR_MMCU_TAG_NAME, _name);\
+       AVR_MCU_LONG(AVR_MMCU_TAG_FREQUENCY, _speed);\
+       const uint8_t _mmcu[2] _MMCU_ = { AVR_MMCU_TAG, 0 }
+
+/*
+ * The following MAP macros where copied from
+ * https://github.com/swansontec/map-macro/blob/master/map.h
+ *
+ * The license header for that file is reproduced below:
+ *
+ * Copyright (C) 2012 William Swanson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the authors or
+ * their institutions shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without
+ * prior written authorization from the authors.
+ */
+
+#define _EVAL0(...) __VA_ARGS__
+#define _EVAL1(...) _EVAL0 (_EVAL0 (_EVAL0 (__VA_ARGS__)))
+#define _EVAL2(...) _EVAL1 (_EVAL1 (_EVAL1 (__VA_ARGS__)))
+#define _EVAL3(...) _EVAL2 (_EVAL2 (_EVAL2 (__VA_ARGS__)))
+#define _EVAL4(...) _EVAL3 (_EVAL3 (_EVAL3 (__VA_ARGS__)))
+#define _EVAL(...)  _EVAL4 (_EVAL4 (_EVAL4 (__VA_ARGS__)))
+
+#define _MAP_END(...)
+#define _MAP_OUT
+
+#define _MAP_GET_END() 0, _MAP_END
+#define _MAP_NEXT0(test, next, ...) next _MAP_OUT
+#define _MAP_NEXT1(test, next) _MAP_NEXT0 (test, next, 0)
+#define _MAP_NEXT(test, next)  _MAP_NEXT1 (_MAP_GET_END test, next)
+
+#define _MAP0(f, x, peek, ...) f(x) _MAP_NEXT (peek, _MAP1) (f, peek, __VA_ARGS__)
+#define _MAP1(f, x, peek, ...) f(x) _MAP_NEXT (peek, _MAP0) (f, peek, __VA_ARGS__)
+#define _MAP(f, ...) _EVAL (-MAP1 (f, __VA_ARGS__, (), 0))
+
+/* End of original MAP macros. */
+
+// Define MAP macros with one additional argument
+#define _MAP0_1(f, a, x, peek, ...) f(a, x) _MAP_NEXT (peek, _MAP1_1) (f, a, peek, __VA_ARGS__)
+#define _MAP1_1(f, a, x, peek, ...) f(a, x) _MAP_NEXT (peek, _MAP0_1) (f, a, peek, __VA_ARGS__)
+#define _MAP_1(f, a, ...) _EVAL (_MAP1_1 (f, a, __VA_ARGS__, (), 0))
+
+#define _SEND_SIMAVR_CMD_BYTE(reg, b)            reg = b;
+
+// A helper macro for sending multi-byte commands
+#define SEND_SIMAVR_CMD(reg, ...)              \
+       do { \
+               _MAP_1(_SEND_SIMAVR_CMD_BYTE, reg, __VA_ARGS__) \
+       } while(0)
+
+#endif /* __AVR__ */
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.c
new file mode 100644 (file)
index 0000000..f3e14e1
--- /dev/null
@@ -0,0 +1,241 @@
+/*\r
+       avr_acomp.c\r
+\r
+       Copyright 2017 Konstantin Begun\r
+\r
+       This file is part of simavr.\r
+\r
+       simavr is free software: you can redistribute it and/or modify\r
+       it under the terms of the GNU General Public License as published by\r
+       the Free Software Foundation, either version 3 of the License, or\r
+       (at your option) any later version.\r
+\r
+       simavr is distributed in the hope that it will be useful,\r
+       but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+       GNU General Public License for more details.\r
+\r
+       You should have received a copy of the GNU General Public License\r
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
+\r
+#include <stdlib.h>\r
+#include "avr_acomp.h"\r
+#include "avr_timer.h"\r
+\r
+static uint8_t\r
+avr_acomp_get_state(\r
+               struct avr_t * avr,\r
+               avr_acomp_t *ac)\r
+{\r
+       if (avr_regbit_get(avr, ac->disabled))\r
+               return 0;\r
+\r
+       // get positive voltage\r
+       uint16_t positive_v;\r
+\r
+       if (avr_regbit_get(avr, ac->acbg)) {            // if bandgap\r
+               positive_v = ACOMP_BANDGAP;\r
+       } else {\r
+               positive_v = ac->ain_values[0]; // AIN0\r
+       }\r
+\r
+       // get negative voltage\r
+       uint16_t negative_v = 0;\r
+\r
+       // multiplexer is enabled if acme is set and adc is off\r
+       if (avr_regbit_get(avr, ac->acme) && !avr_regbit_get(avr, ac->aden)) {\r
+               if (!avr_regbit_get(avr, ac->pradc)) {\r
+                       uint8_t adc_i = avr_regbit_get_array(avr, ac->mux, ARRAY_SIZE(ac->mux));\r
+                       if (adc_i < ac->mux_inputs && adc_i < ARRAY_SIZE(ac->adc_values)) {\r
+                               negative_v = ac->adc_values[adc_i];\r
+                       }\r
+               }\r
+\r
+       } else {\r
+               negative_v = ac->ain_values[1]; // AIN1\r
+       }\r
+\r
+       return positive_v > negative_v;\r
+}\r
+\r
+static avr_cycle_count_t\r
+avr_acomp_sync_state(\r
+       struct avr_t * avr,\r
+       avr_cycle_count_t when,\r
+       void * param)\r
+{\r
+       avr_acomp_t * p = (avr_acomp_t *)param;\r
+       if (!avr_regbit_get(avr, p->disabled)) {\r
+\r
+               uint8_t cur_state = avr_regbit_get(avr, p->aco);\r
+               uint8_t new_state = avr_acomp_get_state(avr, p);\r
+\r
+               if (new_state != cur_state) {\r
+                       avr_regbit_setto(avr, p->aco, new_state);               // set ACO\r
+\r
+                       uint8_t acis0 = avr_regbit_get(avr, p->acis[0]);\r
+                       uint8_t acis1 = avr_regbit_get(avr, p->acis[1]);\r
+\r
+                       if ((acis0 == 0 && acis1 == 0) || (acis1 == 1 && acis0 == new_state)) {\r
+                               avr_raise_interrupt(avr, &p->ac);\r
+                       }\r
+\r
+                       avr_raise_irq(p->io.irq + ACOMP_IRQ_OUT, new_state);\r
+               }\r
+\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+static inline void\r
+avr_schedule_sync_state(\r
+       struct avr_t * avr,\r
+       void *param)\r
+{\r
+       avr_cycle_timer_register(avr, 1, avr_acomp_sync_state, param);\r
+}\r
+\r
+static void\r
+avr_acomp_write_acsr(\r
+       struct avr_t * avr,\r
+       avr_io_addr_t addr,\r
+       uint8_t v,\r
+       void * param)\r
+{\r
+       avr_acomp_t * p = (avr_acomp_t *)param;\r
+\r
+       avr_core_watch_write(avr, addr, v);\r
+\r
+       if (avr_regbit_get(avr, p->acic) != (p->timer_irq ? 1:0)) {\r
+               if (p->timer_irq) {\r
+                       avr_unconnect_irq(p->io.irq + ACOMP_IRQ_OUT, p->timer_irq);\r
+                       p->timer_irq = NULL;\r
+               }\r
+               else {\r
+                       avr_irq_t *irq = avr_io_getirq(avr, AVR_IOCTL_TIMER_GETIRQ(p->timer_name), TIMER_IRQ_IN_ICP);\r
+                       if (irq) {\r
+                               avr_connect_irq(p->io.irq + ACOMP_IRQ_OUT, irq);\r
+                               p->timer_irq = irq;\r
+                       }\r
+               }\r
+       }\r
+\r
+       avr_schedule_sync_state(avr, param);\r
+}\r
+\r
+static void\r
+avr_acomp_dependencies_changed(\r
+       struct avr_irq_t * irq,\r
+       uint32_t value,\r
+       void * param)\r
+{\r
+       avr_acomp_t * p = (avr_acomp_t *)param;\r
+       avr_schedule_sync_state(p->io.avr, param);\r
+}\r
+\r
+static void\r
+avr_acomp_irq_notify(\r
+       struct avr_irq_t * irq,\r
+       uint32_t value,\r
+       void * param)\r
+{\r
+       avr_acomp_t * p = (avr_acomp_t *)param;\r
+\r
+       switch (irq->irq) {\r
+               case ACOMP_IRQ_AIN0 ... ACOMP_IRQ_AIN1: {\r
+                               p->ain_values[irq->irq - ACOMP_IRQ_AIN0] = value;\r
+                               avr_schedule_sync_state(p->io.avr, param);\r
+                       }       break;\r
+               case ACOMP_IRQ_ADC0 ... ACOMP_IRQ_ADC15: {\r
+                               p->adc_values[irq->irq - ACOMP_IRQ_ADC0] = value;\r
+                               avr_schedule_sync_state(p->io.avr, param);\r
+                       }       break;\r
+       }\r
+}\r
+\r
+static void\r
+avr_acomp_register_dependencies(\r
+       avr_acomp_t *p,\r
+       avr_regbit_t rb)\r
+{\r
+       if (rb.reg) {\r
+               avr_irq_register_notify(\r
+                                       avr_iomem_getirq(p->io.avr, rb.reg, NULL, rb.bit),\r
+                                       avr_acomp_dependencies_changed,\r
+                                       p);\r
+       }\r
+}\r
+\r
+static void\r
+avr_acomp_reset(avr_io_t * port)\r
+{\r
+       avr_acomp_t * p = (avr_acomp_t *)port;\r
+\r
+       for (int i = 0; i < ACOMP_IRQ_COUNT; i++)\r
+               avr_irq_register_notify(p->io.irq + i, avr_acomp_irq_notify, p);\r
+\r
+       // register notification for changes of registers comparator does not own\r
+       // avr_register_io_write is tempting instead, but it requires that the handler\r
+       // updates the actual memory too. Given this is for the registers this module\r
+       // does not own, it is tricky to know whether it should write to the actual memory.\r
+       // E.g., if there is already a native handler for it then it will do the writing\r
+       // (possibly even omitting some bits etc). IInterefering would probably be wrong.\r
+       // On the  other hand if there isn't a handler already, then this hadnler would have to,\r
+       // as otherwise nobody will.\r
+       // This write notification mechanism should probably need reviewing and fixing\r
+       // For now using IRQ mechanism, as it is not intrusive\r
+\r
+       avr_acomp_register_dependencies(p, p->pradc);\r
+       avr_acomp_register_dependencies(p, p->aden);\r
+       avr_acomp_register_dependencies(p, p->acme);\r
+\r
+       // mux\r
+       for (int i = 0; i < ARRAY_SIZE(p->mux); ++i) {\r
+               avr_acomp_register_dependencies(p, p->mux[i]);\r
+       }\r
+}\r
+\r
+static const char * irq_names[ACOMP_IRQ_COUNT] = {\r
+       [ACOMP_IRQ_AIN0] = "16<ain0",\r
+       [ACOMP_IRQ_AIN1] = "16<ain1",\r
+       [ACOMP_IRQ_ADC0] = "16<adc0",\r
+       [ACOMP_IRQ_ADC1] = "16<adc1",\r
+       [ACOMP_IRQ_ADC2] = "16<adc2",\r
+       [ACOMP_IRQ_ADC3] = "16<adc3",\r
+       [ACOMP_IRQ_ADC4] = "16<adc4",\r
+       [ACOMP_IRQ_ADC5] = "16<adc5",\r
+       [ACOMP_IRQ_ADC6] = "16<adc6",\r
+       [ACOMP_IRQ_ADC7] = "16<adc7",\r
+       [ACOMP_IRQ_ADC8] = "16<adc0",\r
+       [ACOMP_IRQ_ADC9] = "16<adc9",\r
+       [ACOMP_IRQ_ADC10] = "16<adc10",\r
+       [ACOMP_IRQ_ADC11] = "16<adc11",\r
+       [ACOMP_IRQ_ADC12] = "16<adc12",\r
+       [ACOMP_IRQ_ADC13] = "16<adc13",\r
+       [ACOMP_IRQ_ADC14] = "16<adc14",\r
+       [ACOMP_IRQ_ADC15] = "16<adc15",\r
+       [ACOMP_IRQ_OUT] = ">out"\r
+};\r
+\r
+static avr_io_t _io = {\r
+       .kind = "ac",\r
+       .reset = avr_acomp_reset,\r
+       .irq_names = irq_names,\r
+};\r
+\r
+void\r
+avr_acomp_init(\r
+       avr_t * avr,\r
+       avr_acomp_t * p)\r
+{\r
+       p->io = _io;\r
+\r
+       avr_register_io(avr, &p->io);\r
+       avr_register_vector(avr, &p->ac);\r
+       // allocate this module's IRQ\r
+       avr_io_setirqs(&p->io, AVR_IOCTL_ACOMP_GETIRQ, ACOMP_IRQ_COUNT, NULL);\r
+\r
+       avr_register_io_write(avr, p->r_acsr, avr_acomp_write_acsr, p);\r
+}\r
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.h
new file mode 100644 (file)
index 0000000..caa3c4e
--- /dev/null
@@ -0,0 +1,89 @@
+/*\r
+       avr_acomp.h\r
+\r
+       Copyright 2017 Konstantin Begun\r
+\r
+       This file is part of simavr.\r
+\r
+       simavr is free software: you can redistribute it and/or modify\r
+       it under the terms of the GNU General Public License as published by\r
+       the Free Software Foundation, either version 3 of the License, or\r
+       (at your option) any later version.\r
+\r
+       simavr is distributed in the hope that it will be useful,\r
+       but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+       GNU General Public License for more details.\r
+\r
+       You should have received a copy of the GNU General Public License\r
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
+\r
+#ifndef __AVR_COMP_H___\r
+#define __AVR_COMP_H___\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+#include "sim_avr.h"\r
+\r
+/*\r
+ * simavr Analog Comparator allows external code to feed real voltages to the\r
+ * simulator, and the simulator uses it's 'real' reference voltage\r
+ * to set comparator output accordingly and trigger in interrupt, if set up this way\r
+ *\r
+ */\r
+\r
+enum {\r
+       // input IRQ values. Values are /always/ volts * 1000 (millivolts)\r
+       ACOMP_IRQ_AIN0 = 0, ACOMP_IRQ_AIN1,\r
+       ACOMP_IRQ_ADC0, ACOMP_IRQ_ADC1, ACOMP_IRQ_ADC2, ACOMP_IRQ_ADC3,\r
+       ACOMP_IRQ_ADC4, ACOMP_IRQ_ADC5, ACOMP_IRQ_ADC6, ACOMP_IRQ_ADC7,\r
+       ACOMP_IRQ_ADC8, ACOMP_IRQ_ADC9, ACOMP_IRQ_ADC10, ACOMP_IRQ_ADC11,\r
+       ACOMP_IRQ_ADC12, ACOMP_IRQ_ADC13, ACOMP_IRQ_ADC14, ACOMP_IRQ_ADC15,\r
+       ACOMP_IRQ_OUT,          // output has changed\r
+       ACOMP_IRQ_COUNT\r
+};\r
+\r
+// Get the internal IRQ corresponding to the INT\r
+#define AVR_IOCTL_ACOMP_GETIRQ AVR_IOCTL_DEF('a','c','m','p')\r
+\r
+enum {\r
+       ACOMP_BANDGAP = 1100\r
+};\r
+\r
+typedef struct avr_acomp_t {\r
+       avr_io_t                io;\r
+\r
+       uint8_t                 mux_inputs; // number of inputs (not mux bits!) in multiplexer. Other bits in mux below would be expected to be zero\r
+       avr_regbit_t    mux[4];\r
+       avr_regbit_t    pradc;          // ADC power reduction, this impacts on ability to use adc multiplexer\r
+       avr_regbit_t    aden;           // ADC Enabled, this impacts on ability to use adc multiplexer\r
+       avr_regbit_t    acme;           // AC multiplexed input enabled\r
+\r
+       avr_io_addr_t   r_acsr;         // control & status register\r
+       avr_regbit_t    acis[2];        //\r
+       avr_regbit_t    acic;           // input capture enable\r
+       avr_regbit_t    aco;            // output\r
+       avr_regbit_t    acbg;           // bandgap select\r
+       avr_regbit_t    disabled;\r
+\r
+       char                    timer_name;     // connected timer for incput capture triggering\r
+\r
+       // use ACI and ACIE bits\r
+       avr_int_vector_t ac;\r
+\r
+       // runtime data\r
+       uint16_t                adc_values[16]; // current values on the ADCs inputs\r
+       uint16_t                ain_values[2];  // current values on AIN inputs\r
+       avr_irq_t*              timer_irq;\r
+} avr_acomp_t;\r
+\r
+void avr_acomp_init(avr_t * avr, avr_acomp_t * port);\r
+\r
+#ifdef __cplusplus\r
+};\r
+#endif\r
+\r
+#endif // __AVR_COMP_H___\r
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.c
new file mode 100644 (file)
index 0000000..6adf1f2
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+       avr_adc.c
+
+       Copyright 2008, 2010 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sim_time.h"
+#include "avr_adc.h"
+
+static avr_cycle_count_t
+avr_adc_int_raise(
+               struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_adc_t * p = (avr_adc_t *)param;
+       if (avr_regbit_get(avr, p->aden)) {
+               // if the interrupts are not used, still raised the UDRE and TXC flag
+               avr_raise_interrupt(avr, &p->adc);
+               avr_regbit_clear(avr, p->adsc);
+               if( p->adts_mode == avr_adts_free_running )
+                       avr_raise_irq(p->io.irq + ADC_IRQ_IN_TRIGGER, 1);
+                if (!p->read_status) {
+                    /* Update I/O registers. */
+
+                    avr->data[p->r_adcl] = p->result & 0xff;
+                    avr->data[p->r_adch] = p->result >> 8;
+                }
+       }
+       return 0;
+}
+
+static avr_cycle_count_t
+avr_adc_convert(struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+        avr_adc_t *p = (avr_adc_t *)param;
+
+        p->first = 0; // Converter initialised
+
+        /* Ask the calling program for inputs. */
+
+       avr_adc_mux_t mux = p->muxmode[p->current_muxi];
+        union {
+                avr_adc_mux_t mux;
+                uint32_t v;
+        } e = { .mux = mux };
+        avr_raise_irq(p->io.irq + ADC_IRQ_OUT_TRIGGER, e.v);
+
+       // optional shift left/right
+       uint8_t shift = p->current_extras.adjust ? 6 : 0; // shift LEFT
+
+       int32_t reg = 0, clipped = 0;
+       switch (mux.kind) {
+               case ADC_MUX_SINGLE:
+                       reg = p->adc_values[mux.src];
+                       break;
+               case ADC_MUX_DIFF:
+                       if (mux.gain == 0)
+                               mux.gain = 1;
+                       reg = ((uint32_t)p->adc_values[mux.src] * mux.gain) -
+                                       ((uint32_t)p->adc_values[mux.diff] * mux.gain);
+                       break;
+               case ADC_MUX_TEMP:
+                       reg = p->temp; // assumed to be already calibrated somehow
+                       break;
+               case ADC_MUX_REF:
+                       reg = mux.src; // reference voltage
+                       break;
+               case ADC_MUX_VCC4:
+                       if ( !avr->vcc) {
+                               AVR_LOG(avr, LOG_WARNING, "ADC: missing VCC analog voltage\n");
+                       } else
+                               reg = avr->vcc / 4;
+                       break;
+       }
+
+       int32_t vref = 3300;
+       uint16_t ref = p->ref_values[p->current_refi];
+
+       switch (ref) {
+               case ADC_VREF_VCC:
+                       if (!avr->vcc)
+                               AVR_LOG(avr, LOG_WARNING, "ADC: missing VCC analog voltage\n");
+                       else
+                               vref = avr->vcc;
+                       break;
+               case ADC_VREF_AREF:
+                       if (!avr->aref)
+                               AVR_LOG(avr, LOG_WARNING, "ADC: missing AREF analog voltage\n");
+                       else
+                               vref = avr->aref;
+                       break;
+               case ADC_VREF_AVCC:
+                       if (!avr->avcc)
+                               AVR_LOG(avr, LOG_WARNING, "ADC: missing AVCC analog voltage\n");
+                       else
+                               vref = avr->avcc;
+                       break;
+               default:
+                       vref = ref;
+       }
+//     printf("ADCL %d:%3d:%3d read %4d vref %d:%d=%d\n",
+//                     mux.kind, mux.diff, mux.src,
+//                     reg, refi, ref, vref);
+
+        if (mux.kind == ADC_MUX_DIFF) {
+                if (p->current_extras.negate)
+                        reg = -reg;
+                if (p->current_extras.bipolar) {
+                        reg = (reg * (int32_t)0x1ff) / vref; // scale to 9 bits
+                        if (reg > (int32_t)0x1ff) {
+                                clipped = 0x1ff;
+                        } else if (reg < -(int32_t)0x1ff) {
+                                clipped = 0x200;
+                        }
+                } else {
+                        reg = (reg * (int32_t)0x3ff) / vref; // scale to 10 bit
+                        if (reg < 0 || reg > (int32_t)0x3ff)
+                                clipped = 0x1ff;
+                }
+        } else {
+            reg = (reg * (int32_t)0x3ff) / vref;       // scale to 10 bits
+            if (reg < 0 || reg > (int32_t)0x3ff)
+                clipped = 0x3ff;
+        }
+//     printf("ADC to 9/10 bits 0x%x %d\n", reg, reg);
+       if (clipped) {
+               AVR_LOG(avr, LOG_WARNING,
+                        "ADC: channel %d clipped %u/%u VREF %d\n",
+                        p->current_muxi, reg, clipped, vref);
+               reg = clipped;
+       }
+        reg &= 0x3ff;
+       reg <<= shift;
+//     printf("ADC to 9/10 bits %x shifted %d\n", reg, shift);
+        p->result = reg;
+
+        /* Schedule the interrupt in 11 ADC cycles. */
+
+        avr_cycle_timer_register(avr, p->current_prescale * 11,
+                                 avr_adc_int_raise, p);
+        return 0;
+}
+
+/*
+ * From Datasheet:
+ * "When ADCL is read, the ADC Data Register is not updated until ADCH is read.
+ * Consequently, if the result is left adjusted and no more than 8-bit
+ * precision is required, it is sufficient to read ADCH.
+ * Otherwise, ADCL must be read first, then ADCH."
+ */
+
+static uint8_t
+avr_adc_read_l(
+               struct avr_t * avr, avr_io_addr_t addr, void * param)
+{
+       avr_adc_t * p = (avr_adc_t *)param;
+
+       p->read_status = 1; // Set the update interlock.
+       return avr_core_watch_read(avr, addr);
+}
+
+static uint8_t
+avr_adc_read_h(
+               struct avr_t * avr, avr_io_addr_t addr, void * param)
+{
+       avr_adc_t * p = (avr_adc_t *)param;
+
+        p->read_status = 0; // Clear the update interlock.
+        return avr_core_watch_read(avr, addr);
+}
+
+static void
+avr_adc_configure_trigger(
+               struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       avr_adc_t * p = (avr_adc_t *)param;
+       
+       uint8_t adate = avr_regbit_get(avr, p->adate);
+       uint8_t old_adts = p->adts_mode;
+       
+       static char * auto_trigger_names[] = {
+               "none",
+               "free_running",
+               "analog_comparator_0",
+               "analog_comparator_1",
+               "analog_comparator_2",
+               "analog_comparator_3",
+               "external_interrupt_0",
+               "timer_0_compare_match_a",
+               "timer_0_compare_match_b",
+               "timer_0_overflow",
+               "timer_1_compare_match_b",
+               "timer_1_overflow",
+               "timer_1_capture_event",
+               "pin_change_interrupt",
+               "psc_module_0_sync_signal",
+               "psc_module_1_sync_signal",
+               "psc_module_2_sync_signal",
+       };
+       
+       if( adate ) {
+               uint8_t adts = avr_regbit_get_array(avr, p->adts, ARRAY_SIZE(p->adts));
+               p->adts_mode = p->adts_op[adts];
+               
+               switch(p->adts_mode) {
+                       case avr_adts_free_running: {
+                               // do nothing at free running mode
+                       }       break;
+                       // TODO: implement the other auto trigger modes
+                       default: {
+                               AVR_LOG(avr, LOG_WARNING,
+                                               "ADC: unimplemented auto trigger mode: %s\n",
+                                               auto_trigger_names[p->adts_mode]);
+                               p->adts_mode = avr_adts_none;
+                       }       break;
+               }
+       } else {
+               // TODO: remove previously configured auto triggers
+               p->adts_mode = avr_adts_none;
+       }
+       
+       if( old_adts != p->adts_mode )
+               AVR_LOG(avr, LOG_TRACE, "ADC: auto trigger configured: %s\n",
+                               auto_trigger_names[p->adts_mode]);
+}
+
+static void
+avr_adc_write_adcsra(
+               struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       
+       avr_adc_t * p = (avr_adc_t *)param;
+       uint8_t adsc = avr_regbit_get(avr, p->adsc);
+       uint8_t aden = avr_regbit_get(avr, p->aden);
+       uint8_t new_aden;
+
+        if (p->adc.raised.reg == addr) {
+                uint8_t mask;
+
+                mask = 1 << p->adc.raised.bit;
+                if (mask & v) {
+                        // Clear interrupt flag on bit set.
+
+                        avr_clear_interrupt(avr, &p->adc);
+                        v &= ~mask;
+                } else {
+                        v |= (mask & avr->data[p->adsc.reg]);
+                }
+        }
+
+       avr->data[p->adsc.reg] = v;
+        new_aden = avr_regbit_get(avr, p->aden);
+
+       // can't write zero to adsc
+       if (adsc && !avr_regbit_get(avr, p->adsc)) {
+               avr_regbit_set(avr, p->adsc);
+               v = avr->data[p->adsc.reg];
+       }
+       if (!aden && new_aden) {
+               // first conversion
+               p->first = 1;
+               AVR_LOG(avr, LOG_TRACE, "ADC: Start AREF %d AVCC %d\n", avr->aref, avr->avcc);
+       }
+       if (aden && !avr_regbit_get(avr, p->aden)) {
+               // stop ADC
+
+                avr_cycle_timer_cancel(avr, avr_adc_convert, p);
+               avr_cycle_timer_cancel(avr, avr_adc_int_raise, p);
+               avr_regbit_clear(avr, p->adsc);
+               v = avr->data[p->adsc.reg];     // Peter Ross pross@xvid.org
+       }
+       if (new_aden && !adsc && avr_regbit_get(avr, p->adsc)) {
+               // start one!
+
+                /* Copy mux, prescaler and ADSRB settings, as they may change
+                 * before conversion.
+                 */
+
+               p->current_muxi = avr_regbit_get_array(avr, p->mux,
+                                                       ARRAY_SIZE(p->mux));
+                p->current_refi = avr_regbit_get_array(avr, p->ref,
+                                                       ARRAY_SIZE(p->ref));
+
+               // clock prescaler are just a bit shift.. and 0 means 1
+
+               uint32_t div = avr_regbit_get_array(avr, p->adps,
+                                                    ARRAY_SIZE(p->adps));
+               if (!div) div++;
+
+               if (p->first)
+                       AVR_LOG(avr, LOG_TRACE, "ADC: starting at %uKHz\n",
+                                (avr->frequency >> div) / 13 / 100);
+               div = (1 << div);
+               div *= (p->first ? 14 : 2);     // first conversion is longer
+                p->current_prescale = div;
+                avr_cycle_timer_register(avr, div, avr_adc_convert, p);
+                p->current_extras.bipolar =
+                        p->bin.reg && avr_regbit_get(avr, p->bin);
+                p->current_extras.negate =
+                        p->ipr.reg && avr_regbit_get(avr, p->ipr);
+                p->current_extras.adjust = avr_regbit_get(avr, p->adlar);
+        }
+       avr_core_watch_write(avr, addr, v);
+       avr_adc_configure_trigger(avr, addr, v, param);
+}
+
+static void
+avr_adc_write_adcsrb(
+               struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       avr_core_watch_write(avr, addr, v);
+       avr_adc_configure_trigger(avr, addr, v, param);
+}
+
+static void
+avr_adc_irq_notify(
+               struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       avr_adc_t * p = (avr_adc_t *)param;
+       avr_t * avr = p->io.avr;
+
+       switch (irq->irq) {
+               case ADC_IRQ_ADC0 ... ADC_IRQ_ADC15: {
+                       p->adc_values[irq->irq] = value;
+               }       break;
+               case ADC_IRQ_TEMP: {
+                       p->temp = value;
+               }       break;
+               case ADC_IRQ_IN_TRIGGER: {
+                       if (avr_regbit_get(avr, p->adate)) {
+                               // start a conversion only if it's not running
+                               // otherwise ignore the trigger
+                               if(!avr_regbit_get(avr, p->adsc) ) {
+                                       uint8_t addr = p->adsc.reg;
+                                       if (addr) {
+                                               uint8_t val = avr->data[addr] | (1 << p->adsc.bit);
+                                                if (p->adc.raised.reg == addr) {
+                                                    uint8_t mask;
+
+                                                    mask = 1 << p->adc.raised.bit;
+                                                    val &= ~mask;
+                                                }
+
+                                               // write ADSC to ADCSRA
+
+                                               avr_adc_write_adcsra(avr, addr, val, param);
+                                       }
+                               }
+                       }
+               }
+                break;
+       }
+}
+
+static void avr_adc_reset(avr_io_t * port)
+{
+       avr_adc_t * p = (avr_adc_t *)port;
+
+       // stop ADC
+       avr_cycle_timer_cancel(p->io.avr, avr_adc_int_raise, p);
+       avr_regbit_clear(p->io.avr, p->adsc);
+
+       for (int i = 0; i < ADC_IRQ_COUNT; i++)
+               avr_irq_register_notify(p->io.irq + i, avr_adc_irq_notify, p);
+}
+
+static const char * irq_names[ADC_IRQ_COUNT] = {
+       [ADC_IRQ_ADC0] = "16<adc0",
+       [ADC_IRQ_ADC1] = "16<adc1",
+       [ADC_IRQ_ADC2] = "16<adc2",
+       [ADC_IRQ_ADC3] = "16<adc3",
+       [ADC_IRQ_ADC4] = "16<adc4",
+       [ADC_IRQ_ADC5] = "16<adc5",
+       [ADC_IRQ_ADC6] = "16<adc6",
+       [ADC_IRQ_ADC7] = "16<adc7",
+       [ADC_IRQ_ADC8] = "16<adc0",
+       [ADC_IRQ_ADC9] = "16<adc9",
+       [ADC_IRQ_ADC10] = "16<adc10",
+       [ADC_IRQ_ADC11] = "16<adc11",
+       [ADC_IRQ_ADC12] = "16<adc12",
+       [ADC_IRQ_ADC13] = "16<adc13",
+       [ADC_IRQ_ADC14] = "16<adc14",
+       [ADC_IRQ_ADC15] = "16<adc15",
+       [ADC_IRQ_TEMP] = "16<temp",
+       [ADC_IRQ_IN_TRIGGER] = "<trigger_in",
+       [ADC_IRQ_OUT_TRIGGER] = ">trigger_out",
+};
+
+static avr_io_t        _io = {
+       .kind = "adc",
+       .reset = avr_adc_reset,
+       .irq_names = irq_names,
+};
+
+void avr_adc_init(avr_t * avr, avr_adc_t * p)
+{
+       p->io = _io;
+
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->adc);
+       // allocate this module's IRQ
+       avr_io_setirqs(&p->io, AVR_IOCTL_ADC_GETIRQ, ADC_IRQ_COUNT, NULL);
+
+       avr_register_io_write(avr, p->r_adcsra, avr_adc_write_adcsra, p);
+       // some ADCs don't have ADCSRB (atmega8/16/32)
+       if (p->r_adcsrb)
+               avr_register_io_write(avr, p->r_adcsrb, avr_adc_write_adcsrb, p);
+       avr_register_io_read(avr, p->r_adcl, avr_adc_read_l, p);
+       avr_register_io_read(avr, p->r_adch, avr_adc_read_h, p);
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.h
new file mode 100644 (file)
index 0000000..c9dbfa0
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+       avr_adc.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_ADC_H___
+#define __AVR_ADC_H___
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+/*
+ * simavr ADC allows external code to feed real voltages to the
+ * simulator, and the simulator uses it's 'real' reference voltage
+ * to do the right thing and return the 'proper' 10 bits ADC value
+ * to the AVR firmware.
+ *
+ * To send values to the ADC, register your code to wait for the
+ * ADC_IRQ_OUT_TRIGGER irq, and at that point send any of the
+ * ADC_IRQ_ADC* with Millivolts as value.
+ *
+ * External trigger is not done yet.
+ */
+
+enum {
+       // input IRQ values. Values are /always/ volts * 1000 (millivolts)
+       ADC_IRQ_ADC0 = 0, ADC_IRQ_ADC1, ADC_IRQ_ADC2, ADC_IRQ_ADC3,
+       ADC_IRQ_ADC4, ADC_IRQ_ADC5, ADC_IRQ_ADC6, ADC_IRQ_ADC7,
+       ADC_IRQ_ADC8, ADC_IRQ_ADC9, ADC_IRQ_ADC10, ADC_IRQ_ADC11,
+       ADC_IRQ_ADC12, ADC_IRQ_ADC13, ADC_IRQ_ADC14, ADC_IRQ_ADC15,
+       ADC_IRQ_TEMP,                   // see the datasheet
+       ADC_IRQ_IN_TRIGGER,
+       ADC_IRQ_OUT_TRIGGER,    // sends a avr_adc_mux_t
+       ADC_IRQ_COUNT
+};
+
+// Get the internal IRQ corresponding to the INT
+#define AVR_IOCTL_ADC_GETIRQ AVR_IOCTL_DEF('a','d','c','0')
+
+/*
+ * Definition of a ADC mux mode.
+ */
+enum {
+       ADC_MUX_NONE = 0,               // Nothing. return 0
+       ADC_MUX_NOISE,                  // Nothing. return something random
+       ADC_MUX_SINGLE,                 // Normal ADC pin reading
+       ADC_MUX_DIFF,                   // differential channels (src-diff)
+       ADC_MUX_TEMP,                   // internal temp sensor
+       ADC_MUX_REF,                    // reference voltage (in src * 100)
+       ADC_MUX_VCC4,                   // VCC/4
+};
+typedef struct avr_adc_mux_t {
+       unsigned long kind : 3, gain : 8, diff : 8, src : 13;
+} avr_adc_mux_t;
+
+enum {
+       ADC_VREF_AREF   = 0,    // default mode
+       ADC_VREF_VCC,
+       ADC_VREF_AVCC,
+       ADC_VREF_V110   = 1100,
+       ADC_VREF_V256   = 2560,
+};
+
+// ADC trigger sources
+typedef enum {
+       avr_adts_none = 0,
+       avr_adts_free_running,
+       avr_adts_analog_comparator_0,
+       avr_adts_analog_comparator_1,
+       avr_adts_analog_comparator_2,
+       avr_adts_analog_comparator_3,
+       avr_adts_external_interrupt_0,
+       avr_adts_timer_0_compare_match_a,
+       avr_adts_timer_0_compare_match_b,
+       avr_adts_timer_0_overflow,
+       avr_adts_timer_1_compare_match_b,
+       avr_adts_timer_1_overflow,
+       avr_adts_timer_1_capture_event,
+       avr_adts_pin_change_interrupt,
+       avr_adts_psc_module_0_sync_signal,
+       avr_adts_psc_module_1_sync_signal,
+       avr_adts_psc_module_2_sync_signal,
+} avr_adts_type;
+
+typedef struct avr_adc_t {
+       avr_io_t                io;
+
+       uint8_t                 r_admux;
+       // if the last bit exists in the mux, we are an extended ADC
+       avr_regbit_t    mux[6];
+       avr_regbit_t    ref[3];         // reference voltages bits
+       uint16_t        ref_values[8];  // ADC_VREF_*
+
+       avr_regbit_t    adlar;          // left/right adjustment bit
+
+       uint8_t                 r_adcsra;       // ADC Control and Status Register A
+       avr_regbit_t    aden;           // ADC Enabled
+       avr_regbit_t    adsc;           // ADC Start Conversion
+       avr_regbit_t    adate;          // ADC Auto Trigger Enable
+
+       avr_regbit_t    adps[3];        // Prescaler bits. Note that it's a frequency bit shift
+
+       uint8_t                 r_adcl, r_adch; // Data Registers
+
+       uint8_t                 r_adcsrb;       // ADC Control and Status Register B
+       avr_regbit_t    adts[4];        // Timing Source
+       avr_adts_type   adts_op[16];    // ADTS type
+       uint8_t         adts_mode;      // the extracted ADTS mode
+       avr_regbit_t    bin;            // Bipolar Input Mode (tinyx5 have it)
+       avr_regbit_t    ipr;            // Input Polarity Reversal (tinyx5 have it)
+
+       // use ADIF and ADIE bits
+       avr_int_vector_t adc;
+
+       avr_adc_mux_t   muxmode[64];    // maximum 6 bits of mux modes
+
+        /*
+        * runtime bits
+        */
+
+       uint16_t                adc_values[16]; // current values on the ADCs
+       uint16_t                temp;           // temp sensor reading
+       uint8_t                 first;
+       uint8_t                 read_status;    // marked one when adcl is read
+
+        /* Conversion parameters saved at start (ADSC is set). */
+
+        uint8_t                 current_muxi;
+        uint8_t                 current_refi;
+        uint8_t                 current_prescale;
+        struct {
+                unsigned int bipolar : 1;      // BIN bit.
+                unsigned int negate : 1;       // IPR bit.
+                unsigned int adjust : 1;       // ADLAR bit.
+        }                       current_extras;
+
+        /* Buffered conversion result. */
+
+        uint16_t                result;
+} avr_adc_t;
+
+void avr_adc_init(avr_t * avr, avr_adc_t * port);
+
+
+/*
+ * Helper macros for the Cores definition of muxes
+ */
+#define AVR_ADC_SINGLE(_chan) { \
+               .kind = ADC_MUX_SINGLE, \
+               .src = (_chan), \
+       }
+#define AVR_ADC_DIFF(_a,_b,_g) { \
+               .kind = ADC_MUX_DIFF, \
+               .src = (_a), \
+               .diff = (_b), \
+               .gain = (_g), \
+       }
+#define AVR_ADC_REF(_t) { \
+               .kind = ADC_MUX_REF, \
+               .src = (_t), \
+       }
+#define AVR_ADC_TEMP() { \
+               .kind = ADC_MUX_TEMP, \
+       }
+
+#define AVR_ADC_VCC4() { \
+               .kind = ADC_MUX_VCC4, \
+       }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __AVR_ADC_H___ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.c
new file mode 100644 (file)
index 0000000..e34aab2
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+       avr_bitbang.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+                         2011 Stephan Veigl <veig@gmx.net>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_BITBANG_H__
+#define __AVR_BITBANG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "avr_bitbang.h"
+
+#include "sim_regbit.h"
+#include "sim_core.h"
+#include "avr_ioport.h"
+
+///@todo refactor SPI to bitbang
+
+#define BITBANG_MASK   0xFFFFFFFFUL
+
+/**
+ * read (sample) data from input pin
+ *
+ * @param p            internal bitbang structure
+ */
+static void avr_bitbang_read_bit(avr_bitbang_t *p)
+{
+       avr_ioport_state_t iostate;
+       uint8_t bit = 0;
+
+       if ( !p->enabled )
+               return;
+
+       // read from HW pin
+       if ( p->p_in.port ) {
+               avr_ioctl(p->avr, AVR_IOCTL_IOPORT_GETSTATE( p->p_in.port ), &iostate);
+               bit = ( iostate.pin >> p->p_in.pin ) & 1;
+
+               if ( p->data_order ) {
+                       // data order: shift right
+                       p->data = (p->data >> 1) | ( bit << (p->buffer_size-1));
+               } else {
+                       // data order: shift left
+                       p->data = (p->data << 1) | bit;
+               }
+
+       }
+
+       // module callback
+       if ( p->callback_bit_read ) {
+               p->callback_bit_read(bit, p->callback_param);
+       }
+
+       // data sanitary
+       p->data = p->data & ~(BITBANG_MASK << p->buffer_size);
+}
+
+/**
+ * write data to output pin
+ *
+ * @param p            bitbang structure
+ */
+static void avr_bitbang_write_bit(avr_bitbang_t *p)
+{
+       uint8_t bit = 0;
+
+       if ( !p->enabled )
+               return;
+
+       if ( p->data_order ) {
+               // data order: shift right
+               bit = p->data & 1;
+       } else {
+               // data order: shift left
+               bit = (p->data >> (p->buffer_size-1)) & 1;
+       }
+
+       // output to HW pin
+       if ( p->p_out.port ) {
+               avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_out.port ), p->p_out.pin), bit);
+       }
+
+       // module callback
+       if ( p->callback_bit_write ) {
+               p->callback_bit_write(bit, p->callback_param);
+       }
+}
+
+
+/**
+ * process clock edges (both: positive and negative edges)
+ *
+ * @param p            bitbang structure
+ *
+ */
+static void avr_bitbang_clk_edge(avr_bitbang_t *p)
+{
+       uint8_t phase = (p->clk_count & 1) ^ p->clk_phase;
+       uint8_t clk = (p->clk_count & 1) ^ p->clk_pol;
+
+       if ( !p->enabled )
+               return;
+
+       // increase clock
+       p->clk_count++;
+       clk ^= 1;
+       phase ^= 1;
+
+       // generate clock output on HW pin
+       if ( p->clk_generate && p->p_clk.port ) {
+               avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), clk);
+       }
+
+       if ( phase ) {
+               // read data in
+               avr_bitbang_read_bit(p);
+
+       } else {
+               // write data out
+               avr_bitbang_write_bit(p);
+       }
+
+       if ( p->clk_count >= (p->buffer_size*2) ) {
+               // transfer finished
+               if ( p->callback_transfer_finished ) {
+                       p->data = p->callback_transfer_finished(p->data, p->callback_param);
+               }
+               p->clk_count = 0;
+       }
+}
+
+static avr_cycle_count_t avr_bitbang_clk_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_bitbang_t * p = (avr_bitbang_t *)param;
+
+       avr_bitbang_clk_edge(p);
+
+       if ( p->enabled )
+               return when + p->clk_cycles/2;
+       else
+               return 0;
+}
+
+static void avr_bitbang_clk_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       avr_bitbang_t * p = (avr_bitbang_t *)param;
+       uint8_t clk = (p->clk_count & 1) ^ p->clk_pol;
+
+       // no clock change
+       if ( clk == value )
+               return;
+
+       avr_bitbang_clk_edge(p);
+}
+
+/**
+ * reset bitbang sub-module
+ *
+ * @param avr  avr attached to
+ * @param p            bitbang structure
+ */
+void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p)
+{
+       p->avr = avr;
+       p->enabled = 0;
+       p->clk_count = 0;
+       p->data = 0;
+
+       if ( p->buffer_size < 1 || p->buffer_size > 32 ) {
+               AVR_LOG(avr, LOG_ERROR,
+                               "Error: bitbang buffer size should be between 1 and 32. set value: %d\n", p->buffer_size);
+               abort();
+       }
+
+}
+
+/**
+ * start bitbang transfer
+ *
+ * buffers should be written / cleared in advanced
+ * timers and interrupts are connected
+ *
+ * @param p                    bitbang structure
+ */
+void avr_bitbang_start(avr_bitbang_t * p)
+{
+       p->enabled = 1;
+       p->clk_count = 0;
+
+       if ( p->clk_phase == 0 ) {
+               // write first bit
+               avr_bitbang_write_bit(p);
+       }
+
+       if ( p->clk_generate ) {
+               // master mode, generate clock -> set timer
+               avr_cycle_timer_register(p->avr, (p->clk_cycles/2), avr_bitbang_clk_timer, p);
+       } else {
+               // slave mode -> attach clock function to clock pin
+               ///@todo test
+               avr_irq_register_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p);
+       }
+
+}
+
+
+/**
+ * stop bitbang transfer
+ *
+ * timers and interrupts are disabled
+ *
+ * @param p                    bitbang structure
+ */
+void avr_bitbang_stop(avr_bitbang_t * p)
+{
+
+       p->enabled = 0;
+       avr_cycle_timer_cancel(p->avr, avr_bitbang_clk_timer, p);
+       avr_irq_unregister_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p);
+}
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__AVR_BITBANG_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.h
new file mode 100644 (file)
index 0000000..3d8832f
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+       avr_bitbang.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+                         2011 Stephan Veigl <veig@gmx.net>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+       @defgroup avr_bitbang   Generic BitBang Module
+       @{
+
+       Generic BitBang Module of simavr AVR simulator.
+
+       @par Features / Implementation Status
+               - easy buffer access with push() / pop() functions
+               - one input and one output pin (can be the same HW pin for I2C)
+
+       @todo
+               - one input and one output pin (can be the same HW pin for I2C)
+               - one clock pin which can be configured as input or output
+                       when the clock is output, the clock signal is generated with a
+                       configured frequency (master / slave mode)
+               - 2x 32-bit buffers (input / output) (allows start, stop bits for UART, etc.)
+               - on each read / write a callback is executed to notify the master module
+
+*/
+
+
+#ifndef AVR_BITBANG_H_
+#define AVR_BITBANG_H_
+
+#include "sim_avr.h"
+#include "avr_ioport.h"
+
+
+
+
+/// SPI Module initialization and state structure
+typedef struct avr_bitbang_t {
+       avr_t *                         avr;            ///< avr we are attached to
+
+       uint8_t enabled;                ///< bit-bang enabled flag
+       uint8_t clk_generate;   ///< generate clock and write to clock pin (if available) -> master / slave mode
+       uint8_t clk_pol;                ///< clock polarity, base (inactive) value of clock
+       uint8_t clk_phase;              ///< clock phase / data sampling edge
+                                                       ///             - 0: data are sampled at first clock edge
+                                                       ///             - 1: data are sampled at second clock edge
+       uint32_t clk_cycles;    ///< cycles per clock period - must be multiple of 2! (used if clk_generate is enabled)
+       uint8_t data_order;             ///< data order / shift
+                                                       ///             - 0: shift left
+                                                       ///             - 1: shift right
+
+       uint8_t buffer_size;    ///< size of buffer in bits (1...32)
+
+       void *callback_param;   /// anonymous parameter for callback functions
+       void (*callback_bit_read)(uint8_t bit, void *param);    ///< callback function to notify about bit read
+       void (*callback_bit_write)(uint8_t bit, void *param);   ///< callback function to notify about bit write
+       uint32_t (*callback_transfer_finished)(uint32_t data, void *param);     ///< callback function to notify about a complete transfer
+                                                                                                                                                       ///             (read received data and write new output data)
+
+       avr_iopin_t     p_clk;          ///< clock pin (optional)
+       avr_iopin_t     p_in;           ///< data in pin
+       avr_iopin_t     p_out;          ///< data out pin
+
+// private data
+       uint32_t data;                  ///< data buffer
+                                                       ///             - latest received bit the is lowest / most right one, bit number: 0
+                                                       ///             - next bit to be written is the highest one, bit number: (buffer_size-1)
+       int8_t          clk_count;      ///< internal clock edge count
+} avr_bitbang_t;
+
+/**
+ * reset bitbang sub-module
+ *
+ * @param avr  avr attached to
+ * @param p            bitbang structure
+ */
+void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p);
+
+/**
+ * start bitbang transfer
+ *
+ * buffers should be written / cleared in advanced
+ * timers and interrupts are connected
+ *
+ * @param p                    bitbang structure
+ */
+void avr_bitbang_start(avr_bitbang_t * p);
+
+
+/**
+ * stop bitbang transfer
+ *
+ * timers and interrupts are disabled
+ *
+ * @param p                    bitbang structure
+ */
+void avr_bitbang_stop(avr_bitbang_t * p);
+
+
+#endif /* AVR_BITBANG_H_ */
+/// @} end of avr_bitbang group
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.c
new file mode 100644 (file)
index 0000000..c348f3f
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+       avr_eeprom.c
+
+       IO module that simulates the AVR EEProm
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "avr_eeprom.h"
+
+static avr_cycle_count_t avr_eempe_clear(struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_eeprom_t * p = (avr_eeprom_t *)param;
+       avr_regbit_clear(p->io.avr, p->eempe);
+       return 0;
+}
+
+static avr_cycle_count_t avr_eei_raise(struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_eeprom_t * p = (avr_eeprom_t *)param;
+       avr_raise_interrupt(p->io.avr, &p->ready);
+       return 0;
+}
+
+static void avr_eeprom_write(avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       avr_eeprom_t * p = (avr_eeprom_t *)param;
+       uint8_t eempe = avr_regbit_get(avr, p->eempe);
+
+       avr_core_watch_write(avr, addr, v);
+
+       if (!eempe && avr_regbit_get(avr, p->eempe)) {
+               avr_cycle_timer_register(avr, 4, avr_eempe_clear, p);
+       }
+
+       uint16_t ee_addr;
+       if (p->r_eearh)
+               ee_addr = avr->data[p->r_eearl] | (avr->data[p->r_eearh] << 8);
+       else
+               ee_addr = avr->data[p->r_eearl];
+       if (((eempe && avr_regbit_get(avr, p->eepe)) || avr_regbit_get(avr, p->eere)) &&
+                       ee_addr >= p->size) {
+               AVR_LOG(avr, LOG_ERROR, "EEPROM: *** %s address out of bounds: %04x > %04x,"
+                                                               " wrapping to %04x (PC=%04x)\n",
+                               eempe ? "Write" : "Read",
+                               ee_addr, p->size-1, ee_addr & (p->size-1),
+                               avr->pc);
+               ee_addr = ee_addr & (p->size-1);
+       }
+       if (eempe && avr_regbit_get(avr, p->eepe)) {    // write operation
+               //      printf("eeprom write %04x <- %02x\n", addr, avr->data[p->r_eedr]);
+               p->eeprom[ee_addr] = avr->data[p->r_eedr];
+               // Automatically clears that bit (?)
+               avr_regbit_clear(avr, p->eempe);
+
+               avr_cycle_timer_register_usec(avr, 3400, avr_eei_raise, p); // 3.4ms here
+       }
+       if (avr_regbit_get(avr, p->eere)) {     // read operation
+               avr->data[p->r_eedr] = p->eeprom[ee_addr];
+               //      printf("eeprom read %04x : %02x\n", addr, p->eeprom[addr]);
+       }
+
+       // autocleared
+       avr_regbit_clear(avr, p->eepe);
+       avr_regbit_clear(avr, p->eere);
+}
+
+static int avr_eeprom_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param)
+{
+       avr_eeprom_t * p = (avr_eeprom_t *)port;
+       int res = -1;
+
+       switch(ctl) {
+               case AVR_IOCTL_EEPROM_SET: {
+                       avr_eeprom_desc_t * desc = (avr_eeprom_desc_t*)io_param;
+                       if (!desc || !desc->size || !desc->ee || (desc->offset + desc->size) > p->size) {
+                               AVR_LOG(port->avr, LOG_WARNING, "EEPROM: %s: AVR_IOCTL_EEPROM_SET Invalid argument\n",
+                                               __FUNCTION__);
+                               return -2;
+                       }
+                       memcpy(p->eeprom + desc->offset, desc->ee, desc->size);
+                       AVR_LOG(port->avr, LOG_TRACE, "EEPROM: %s: AVR_IOCTL_EEPROM_SET Loaded %d at offset %d\n",
+                                       __FUNCTION__, desc->size, desc->offset);
+               }       break;
+               case AVR_IOCTL_EEPROM_GET: {
+                       avr_eeprom_desc_t * desc = (avr_eeprom_desc_t*)io_param;
+                       if (!desc || (desc->offset + desc->size) > p->size) {
+                               AVR_LOG(port->avr, LOG_WARNING, "EEPROM: %s: AVR_IOCTL_EEPROM_GET Invalid argument\n",
+                                               __FUNCTION__);
+                               return -2;
+                       }
+                       if (desc->ee)
+                               memcpy(desc->ee, p->eeprom + desc->offset, desc->size);
+                       else    // allow to get access to the read data, for gdb support
+                               desc->ee = p->eeprom + desc->offset;
+               }       break;
+       }
+       
+       return res;
+}
+
+static void avr_eeprom_dealloc(struct avr_io_t * port)
+{
+       avr_eeprom_t * p = (avr_eeprom_t *)port;
+       if (p->eeprom)
+               free(p->eeprom);
+       p->eeprom = NULL;
+}
+
+static avr_io_t        _io = {
+       .kind = "eeprom",
+       .ioctl = avr_eeprom_ioctl,
+       .dealloc = avr_eeprom_dealloc,
+};
+
+void avr_eeprom_init(avr_t * avr, avr_eeprom_t * p)
+{
+       p->io = _io;
+//     printf("%s init (%d bytes) EEL/H:%02x/%02x EED=%02x EEC=%02x\n",
+//                     __FUNCTION__, p->size, p->r_eearl, p->r_eearh, p->r_eedr, p->r_eecr);
+
+       p->eeprom = malloc(p->size);
+       memset(p->eeprom, 0xff, p->size);
+       
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->ready);
+
+       avr_register_io_write(avr, p->r_eecr, avr_eeprom_write, p);
+}
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.h
new file mode 100644 (file)
index 0000000..9ad1c5a
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+       avr_eeprom.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_EEPROM_H__
+#define __AVR_EEPROM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+typedef struct avr_eeprom_t {
+       avr_io_t        io;
+
+       uint8_t *       eeprom; // actual bytes
+       uint16_t        size;   // size for this MCU
+       
+       uint8_t r_eearh;
+       uint8_t r_eearl;
+       uint8_t r_eedr;
+
+       // eepm -- eeprom write mode
+       uint8_t r_eecr; // shortcut, assumes these bits fit in that register
+       avr_regbit_t    eepm[4];
+       avr_regbit_t    eempe;  // eeprom master program enable
+       avr_regbit_t    eepe;   // eeprom program enable
+       avr_regbit_t    eere;   // eeprom read enable
+       
+       avr_int_vector_t ready; // EERIE vector
+} avr_eeprom_t;
+
+void avr_eeprom_init(avr_t * avr, avr_eeprom_t * port);
+
+typedef struct avr_eeprom_desc_t {
+       uint8_t *       ee;
+       uint16_t        offset;
+       uint32_t        size;
+} avr_eeprom_desc_t;
+
+#define AVR_IOCTL_EEPROM_GET   AVR_IOCTL_DEF('e','e','g','p')
+#define AVR_IOCTL_EEPROM_SET   AVR_IOCTL_DEF('e','e','s','p')
+
+
+/*
+ * the eeprom block seems to be very similar across AVRs, 
+ * so here is a macro to declare a "typical" one in a core.
+ */
+
+#define AVR_EEPROM_DECLARE(_vector) \
+       .eeprom = {\
+               .size = E2END+1,\
+               .r_eearh = EEARH,\
+               .r_eearl = EEARL,\
+               .r_eedr = EEDR,\
+               .r_eecr = EECR,\
+               .eepm = { AVR_IO_REGBIT(EECR, EEPM0), AVR_IO_REGBIT(EECR, EEPM1) },\
+               .eempe = AVR_IO_REGBIT(EECR, EEMPE),\
+               .eepe = AVR_IO_REGBIT(EECR, EEPE),\
+               .eere = AVR_IO_REGBIT(EECR, EERE),\
+               .ready = {\
+                       .enable = AVR_IO_REGBIT(EECR, EERIE),\
+                       .vector = _vector,\
+               },\
+       }
+
+/*
+ * no EEPM registers in atmega128
+ */
+#define AVR_EEPROM_DECLARE_NOEEPM(_vector)             \
+       .eeprom = {\
+               .size = E2END+1,\
+               .r_eearh = EEARH,\
+               .r_eearl = EEARL,\
+               .r_eedr = EEDR,\
+               .r_eecr = EECR,\
+               .eepm = { },            \
+               .eempe = AVR_IO_REGBIT(EECR, EEMWE),\
+               .eepe = AVR_IO_REGBIT(EECR, EEWE),\
+               .eere = AVR_IO_REGBIT(EECR, EERE),\
+               .ready = {\
+                       .enable = AVR_IO_REGBIT(EECR, EERIE),\
+                       .vector = _vector,\
+               },\
+       }
+
+
+/*
+ * macro definition without a high address bit register,
+ * which is not implemented in some tiny AVRs.
+ */
+
+#define AVR_EEPROM_DECLARE_8BIT(_vector) \
+       .eeprom = {\
+               .size = E2END+1,\
+               .r_eearl = EEAR,\
+               .r_eedr = EEDR,\
+               .r_eecr = EECR,\
+               .eepm = { AVR_IO_REGBIT(EECR, EEPM0), AVR_IO_REGBIT(EECR, EEPM1) },\
+               .eempe = AVR_IO_REGBIT(EECR, EEMPE),\
+               .eepe = AVR_IO_REGBIT(EECR, EEPE),\
+               .eere = AVR_IO_REGBIT(EECR, EERE),\
+               .ready = {\
+                       .enable = AVR_IO_REGBIT(EECR, EERIE),\
+                       .vector = _vector,\
+               },\
+       }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __AVR_EEPROM_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.c
new file mode 100644 (file)
index 0000000..6f85660
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+       avr_extint.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "avr_extint.h"
+#include "avr_ioport.h"
+
+typedef struct avr_extint_poll_context_t {
+       uint32_t        eint_no; // index of particular interrupt source we are monitoring
+       avr_extint_t *extint;
+} avr_extint_poll_context_t;
+
+static avr_cycle_count_t avr_extint_poll_level_trig(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_extint_poll_context_t *poll = (avr_extint_poll_context_t *)param;
+       avr_extint_t * p = poll->extint;
+
+       /* Check for change of interrupt mode. */
+
+       if (avr_regbit_get_array(avr, p->eint[poll->eint_no].isc, 2))
+               goto terminate_poll;
+
+       uint8_t port = p->eint[poll->eint_no].port_ioctl & 0xFF;
+       avr_ioport_state_t iostate;
+       if (avr_ioctl(avr, AVR_IOCTL_IOPORT_GETSTATE( port ), &iostate) < 0)
+               goto terminate_poll;
+       uint8_t bit = ( iostate.pin >> p->eint[poll->eint_no].port_pin ) & 1;
+       if (bit)
+               goto terminate_poll; // Only poll while pin level remains low
+
+       if (avr->sreg[S_I]) {
+               uint8_t raised = avr_regbit_get(avr, p->eint[poll->eint_no].vector.raised) || p->eint[poll->eint_no].vector.pending;
+               if (!raised)
+                       avr_raise_interrupt(avr, &p->eint[poll->eint_no].vector);
+       }
+
+       return when+1;
+
+terminate_poll:
+       free(poll);
+       return 0;
+}
+
+static avr_extint_t * avr_extint_get(avr_t * avr)
+{
+       if (!avr)
+               return NULL;
+       avr_io_t * periferal = avr->io_port;
+       while (periferal) {
+               if (!strcmp(periferal->kind, "extint")) {
+                       return (avr_extint_t *)periferal;
+               }
+               periferal = periferal->next;
+       }
+       return NULL;
+}
+
+static inline uint8_t avr_extint_exists(avr_extint_t *extint, int8_t extint_no)
+{
+       return (extint_no < EXTINT_COUNT) && (extint->eint[extint_no].port_ioctl);
+}
+
+/**
+ * @brief avr_extint_is_strict_lvl_trig
+ * @param avr
+ * @param extint_no: an ext interrupt number, e.g. 0 or 1 (corresponds to INT0 or INT1)
+ * @return -1 if irrelevant extint_no given, strict
+ * level triggering flag otherwise.
+ */
+int avr_extint_is_strict_lvl_trig(avr_t * avr, uint8_t extint_no)
+{
+       avr_extint_t *p = avr_extint_get(avr);
+       if (!p || !avr_extint_exists(p, extint_no))
+               return -1;
+       if (!p->eint[extint_no].isc[1].reg)
+               return -1; // this is edge-only triggered interrupt
+       return p->eint[extint_no].strict_lvl_trig;
+}
+
+/**
+ * @brief avr_extint_set_strict_lvl_trig
+ * @param avr
+ * @param extint_no: an ext interrupt number, e.g. 0 or 1 (corresponds to INT0 or INT1)
+ * @param strict: new value for level triggering flag
+ */
+void avr_extint_set_strict_lvl_trig(avr_t * avr, uint8_t extint_no, uint8_t strict)
+{
+       avr_extint_t *p = avr_extint_get(avr);
+       if (!p || !avr_extint_exists(p, extint_no))
+               return;
+       if (!p->eint[extint_no].isc[1].reg)
+               return; // this is edge-only triggered interrupt
+       p->eint[extint_no].strict_lvl_trig = strict;
+}
+
+static void avr_extint_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       avr_extint_t * p = (avr_extint_t *)param;
+       avr_t * avr = p->io.avr;
+
+       int up = !irq->value && value;
+       int down = irq->value && !value;
+
+       // ?? uint8_t isc_bits = p->eint[irq->irq + 1].isc->reg ? 2 : 1;
+       uint8_t isc_bits = p->eint[irq->irq].isc[1].reg ? 2 : 1;
+       uint8_t mode = avr_regbit_get_array(avr, p->eint[irq->irq].isc, isc_bits);
+
+       // Asynchronous interrupts, eg int2 in m16, m32 etc. support only down/up
+       if (isc_bits == 1)
+               mode +=2;
+
+       switch (mode) {
+               case 0: // Level triggered (low level) interrupt
+                       {
+                               /**
+                                 Datasheet excerpt:
+                                       >When the external interrupt is enabled and is configured as level triggered (only INT0/INT1),
+                                       >the interrupt will trigger as long as the pin is held low.
+                                       Thus we have to query the pin value continiously while it's held low and try to trigger the interrupt.
+                                       This can be expensive, so avr_extint_set_strict_lvl_trig function provisioned to allow the user
+                                       to turn this feature off. In this case bahaviour will be similar to the falling edge interrupt.
+                                */
+                               if (!value) {
+                                       if (avr->sreg[S_I]) {
+                                               uint8_t raised = avr_regbit_get(avr, p->eint[irq->irq].vector.raised) || p->eint[irq->irq].vector.pending;
+                                               if (!raised)
+                                                       avr_raise_interrupt(avr, &p->eint[irq->irq].vector);
+                                       }
+                                       if (p->eint[irq->irq].strict_lvl_trig) {
+                                               avr_extint_poll_context_t *poll = malloc(sizeof(avr_extint_poll_context_t));
+                                               if (poll) {
+                                                       poll->eint_no = irq->irq;
+                                                       poll->extint = p;
+                                                       avr_cycle_timer_register(avr, 1, avr_extint_poll_level_trig, poll);
+                                               }
+                                       }
+                               }
+                       }
+                       break;
+               case 1: // Toggle-triggered interrupt
+                       if (up || down)
+                               avr_raise_interrupt(avr, &p->eint[irq->irq].vector);
+                       break;
+               case 2: // Falling edge triggered
+                       if (down)
+                               avr_raise_interrupt(avr, &p->eint[irq->irq].vector);
+                       break;
+               case 3: // Rising edge trigggerd
+                       if (up)
+                               avr_raise_interrupt(avr, &p->eint[irq->irq].vector);
+                       break;
+       }
+}
+
+static void avr_extint_reset(avr_io_t * port)
+{
+       avr_extint_t * p = (avr_extint_t *)port;
+
+       for (int i = 0; i < EXTINT_COUNT; i++) {
+               if (p->eint[i].port_ioctl) {
+                       avr_irq_register_notify(p->io.irq + i, avr_extint_irq_notify, p);
+
+                       if (p->eint[i].isc[1].reg) // level triggering available
+                               p->eint[i].strict_lvl_trig = 1; // turn on repetitive level triggering by default
+                       avr_irq_t * irq = avr_io_getirq(p->io.avr,
+                                       p->eint[i].port_ioctl, p->eint[i].port_pin);
+
+                       avr_connect_irq(irq, p->io.irq + i);
+               }
+       }
+}
+
+static const char * irq_names[EXTINT_COUNT] = {
+       [EXTINT_IRQ_OUT_INT0] = "<int0",
+       [EXTINT_IRQ_OUT_INT1] = "<int1",
+       [EXTINT_IRQ_OUT_INT2] = "<int2",
+       [EXTINT_IRQ_OUT_INT3] = "<int3",
+       [EXTINT_IRQ_OUT_INT4] = "<int4",
+       [EXTINT_IRQ_OUT_INT5] = "<int5",
+       [EXTINT_IRQ_OUT_INT6] = "<int6",
+       [EXTINT_IRQ_OUT_INT7] = "<int7",
+};
+
+static avr_io_t        _io = {
+       .kind = "extint",
+       .reset = avr_extint_reset,
+       .irq_names = irq_names,
+};
+
+void avr_extint_init(avr_t * avr, avr_extint_t * p)
+{
+       p->io = _io;
+
+       avr_register_io(avr, &p->io);
+       for (int i = 0; i < EXTINT_COUNT; i++) {
+               if (!p->eint[i].port_ioctl)
+                       break;
+               avr_register_vector(avr, &p->eint[i].vector);
+       }
+       // allocate this module's IRQ
+
+       avr_io_setirqs(&p->io, AVR_IOCTL_EXTINT_GETIRQ(), EXTINT_COUNT, NULL);
+}
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.h
new file mode 100644 (file)
index 0000000..62aa154
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+       avr_extint.h
+
+       External Interrupt Handling (for INT0-3)
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+       Copyright 2014 Doug Szumski <d.s.szumski@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_EXTINT_H__
+#define __AVR_EXTINT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+
+enum {
+       EXTINT_IRQ_OUT_INT0 = 0,
+       EXTINT_IRQ_OUT_INT1, EXTINT_IRQ_OUT_INT2, EXTINT_IRQ_OUT_INT3,
+       EXTINT_IRQ_OUT_INT4, EXTINT_IRQ_OUT_INT5, EXTINT_IRQ_OUT_INT6,
+       EXTINT_IRQ_OUT_INT7,
+       EXTINT_COUNT
+};
+
+// Get the internal IRQ corresponding to the INT
+#define AVR_IOCTL_EXTINT_GETIRQ() AVR_IOCTL_DEF('i','n','t',' ')
+
+/*
+ * This module is just a "relay" for the pin change IRQ in the IO port
+ * module. We hook up to their IRQ and raise out interrupt vectors as needed
+ *
+ * "isc" is handled, apart from the "level" mode that doesn't make sense here (?)
+ */
+typedef struct avr_extint_t {
+       avr_io_t        io;
+
+       struct {
+               avr_regbit_t    isc[2];         // interrupt sense control bits
+               avr_int_vector_t vector;        // interrupt vector
+
+               uint32_t                port_ioctl;             // ioctl to use to get port
+               uint8_t                 port_pin;               // pin number in said port
+               uint8_t                 strict_lvl_trig;// enforces a repetitive interrupt triggering while the pin is held low
+       }       eint[EXTINT_COUNT];
+
+} avr_extint_t;
+
+void avr_extint_init(avr_t * avr, avr_extint_t * p);
+int avr_extint_is_strict_lvl_trig(avr_t * avr, uint8_t extint_no);
+void avr_extint_set_strict_lvl_trig(avr_t * avr, uint8_t extint_no, uint8_t strict);
+
+
+// Declares a typical INT into a avr_extint_t in a core.
+// this is a shortcut since INT declarations are pretty standard.
+// The Tinies as well as the atmega1280 are slightly different.
+// See sim_tinyx5.h and sim_mega1280.h
+#define AVR_EXTINT_DECLARE(_index, _portname, _portpin) \
+               .eint[_index] = { \
+                       .port_ioctl = AVR_IOCTL_IOPORT_GETIRQ(_portname), \
+                       .port_pin = _portpin, \
+                       .isc = { AVR_IO_REGBIT(EICRA, ISC##_index##0), AVR_IO_REGBIT(EICRA, ISC##_index##1) },\
+                       .vector = { \
+                               .enable = AVR_IO_REGBIT(EIMSK, INT##_index), \
+                               .raised = AVR_IO_REGBIT(EIFR, INTF##_index), \
+                               .vector = INT##_index##_vect, \
+                       },\
+               }
+
+// Asynchronous External Interrupt, for example INT2 on the m16 and m32
+// Uses only 1 interrupt sense control bit
+#define AVR_ASYNC_EXTINT_DECLARE(_index, _portname, _portpin) \
+               .eint[_index] = { \
+                       .port_ioctl = AVR_IOCTL_IOPORT_GETIRQ(_portname), \
+                       .port_pin = _portpin, \
+                       .isc = { AVR_IO_REGBIT(MCUCSR, ISC##_index) },\
+                       .vector = { \
+                               .enable = AVR_IO_REGBIT(GICR, INT##_index), \
+                               .raised = AVR_IO_REGBIT(GIFR, INTF##_index), \
+                               .vector = INT##_index##_vect, \
+                       },\
+               }
+
+#define AVR_EXTINT_MEGA_DECLARE(_index, _portname, _portpin, _EICR) \
+               .eint[_index] = { \
+                       .port_ioctl = AVR_IOCTL_IOPORT_GETIRQ(_portname), \
+                       .port_pin = _portpin, \
+                       .isc = { AVR_IO_REGBIT(EICR##_EICR, ISC##_index##0), AVR_IO_REGBIT(EICR##_EICR, ISC##_index##1) },\
+                       .vector = { \
+                               .enable = AVR_IO_REGBIT(EIMSK, INT##_index), \
+                               .raised = AVR_IO_REGBIT(EIFR, INTF##_index), \
+                               .vector = INT##_index##_vect, \
+                       },\
+               }
+
+#define AVR_EXTINT_TINY_DECLARE(_index, _portname, _portpin, _IFR) \
+               .eint[_index] = { \
+                       .port_ioctl = AVR_IOCTL_IOPORT_GETIRQ(_portname), \
+                       .port_pin = _portpin, \
+                       .isc = { AVR_IO_REGBIT(MCUCR, ISC##_index##0), AVR_IO_REGBIT(MCUCR, ISC##_index##1) }, \
+                       .vector = { \
+                               .enable = AVR_IO_REGBIT(GIMSK, INT##_index), \
+                               .raised = AVR_IO_REGBIT(_IFR, INTF##_index), \
+                               .vector = INT##_index##_vect, \
+                       }, \
+               }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__AVR_EXTINT_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.c
new file mode 100644 (file)
index 0000000..cc1fd96
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+       avr_flash.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "avr_flash.h"
+
+static avr_cycle_count_t avr_progen_clear(struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_flash_t * p = (avr_flash_t *)param;
+       avr_regbit_clear(p->io.avr, p->selfprgen);
+       AVR_LOG(avr, LOG_WARNING, "FLASH: avr_progen_clear - SPM not received, clearing PRGEN bit\n");
+       return 0;
+}
+
+
+static void avr_flash_write(avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       avr_flash_t * p = (avr_flash_t *)param;
+
+       avr_core_watch_write(avr, addr, v);
+
+//     printf("** avr_flash_write %02x\n", v);
+
+       if (avr_regbit_get(avr, p->selfprgen))
+               avr_cycle_timer_register(avr, 4, avr_progen_clear, p); // 4 cycles is very little!
+}
+
+static void avr_flash_clear_temppage(avr_flash_t *p)
+{
+       for (int i = 0; i < p->spm_pagesize / 2; i++) {
+               p->tmppage[i] = 0xff;
+               p->tmppage_used[i] = 0;
+       }
+}
+
+static int avr_flash_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param)
+{
+       if (ctl != AVR_IOCTL_FLASH_SPM)
+               return -1;
+
+       avr_flash_t * p = (avr_flash_t *)port;
+       avr_t * avr = p->io.avr;
+
+       avr_flashaddr_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8);
+       if (avr->rampz)
+               z |= avr->data[avr->rampz] << 16;
+       uint16_t r01 = avr->data[0] | (avr->data[1] << 8);
+
+//     printf("AVR_IOCTL_FLASH_SPM %02x Z:%04x R01:%04x\n", avr->data[p->r_spm], z,r01);
+       if (avr_regbit_get(avr, p->selfprgen)) {
+               avr_cycle_timer_cancel(avr, avr_progen_clear, p);
+
+               if (avr_regbit_get(avr, p->pgers)) {
+                       z &= ~1;
+                       AVR_LOG(avr, LOG_TRACE, "FLASH: Erasing page %04x (%d)\n", (z / p->spm_pagesize), p->spm_pagesize);
+                       for (int i = 0; i < p->spm_pagesize; i++)
+                               avr->flash[z++] = 0xff;
+               } else if (avr_regbit_get(avr, p->pgwrt)) {
+                       z &= ~(p->spm_pagesize - 1);
+                       AVR_LOG(avr, LOG_TRACE, "FLASH: Writing page %04x (%d)\n", (z / p->spm_pagesize), p->spm_pagesize);
+                       for (int i = 0; i < p->spm_pagesize / 2; i++) {
+                               avr->flash[z++] = p->tmppage[i];
+                               avr->flash[z++] = p->tmppage[i] >> 8;
+                       }
+                       avr_flash_clear_temppage(p);
+               } else if (avr_regbit_get(avr, p->blbset)) {
+                       AVR_LOG(avr, LOG_TRACE, "FLASH: Setting lock bits (ignored)\n");
+               } else if (p->flags & AVR_SELFPROG_HAVE_RWW && avr_regbit_get(avr, p->rwwsre)) {
+                       avr_flash_clear_temppage(p);
+               } else {
+                       AVR_LOG(avr, LOG_TRACE, "FLASH: Writing temppage %08x (%04x)\n", z, r01);
+                       z >>= 1;
+                       if (!p->tmppage_used[z % (p->spm_pagesize / 2)]) {
+                               p->tmppage[z % (p->spm_pagesize / 2)] = r01;
+                               p->tmppage_used[z % (p->spm_pagesize / 2)] = 1;
+                       }
+               }
+       }
+       avr_regbit_clear(avr, p->selfprgen);
+       return 0;
+}
+
+static void
+avr_flash_reset(avr_io_t * port)
+{
+       avr_flash_t * p = (avr_flash_t *) port;
+
+       avr_flash_clear_temppage(p);
+}
+
+static void
+avr_flash_dealloc(struct avr_io_t * port)
+{
+       avr_flash_t * p = (avr_flash_t *) port;
+
+       if (p->tmppage)
+               free(p->tmppage);
+
+       if (p->tmppage_used)
+               free(p->tmppage_used);
+}
+
+static avr_io_t        _io = {
+       .kind = "flash",
+       .ioctl = avr_flash_ioctl,
+       .reset = avr_flash_reset,
+       .dealloc = avr_flash_dealloc,
+};
+
+void avr_flash_init(avr_t * avr, avr_flash_t * p)
+{
+       p->io = _io;
+//     printf("%s init SPM %04x\n", __FUNCTION__, p->r_spm);
+
+       if (!p->tmppage)
+               p->tmppage = malloc(p->spm_pagesize);
+
+       if (!p->tmppage_used)
+               p->tmppage_used = malloc(p->spm_pagesize / 2);
+
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->flash);
+
+       avr_register_io_write(avr, p->r_spm, avr_flash_write, p);
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.h
new file mode 100644 (file)
index 0000000..07747fc
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+       avr_flash.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __AVR_FLASH_H___
+#define __AVR_FLASH_H___
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+/*
+ * Handles self-programming subsystem if the core
+ * supports it.
+ */
+typedef struct avr_flash_t {
+       avr_io_t        io;
+
+       uint16_t        flags;
+       uint16_t        *tmppage;
+       uint8_t *tmppage_used;
+       uint16_t        spm_pagesize;
+       uint8_t r_spm;
+       avr_regbit_t selfprgen;
+       avr_regbit_t pgers;             // page erase
+       avr_regbit_t pgwrt;             // page write
+       avr_regbit_t blbset;    // lock bit set
+       avr_regbit_t rwwsre;    // read while write section read enable
+       avr_regbit_t rwwsb;             // read while write section busy
+
+       avr_int_vector_t flash; // Interrupt vector
+} avr_flash_t;
+
+/* Set if the flash supports a Read While Write section */
+#define AVR_SELFPROG_HAVE_RWW (1 << 0)
+
+void avr_flash_init(avr_t * avr, avr_flash_t * p);
+
+
+#define AVR_IOCTL_FLASH_SPM            AVR_IOCTL_DEF('f','s','p','m')
+
+#define AVR_SELFPROG_DECLARE_INTERNAL(_spmr, _spen, _vector) \
+               .r_spm = _spmr,\
+               .spm_pagesize = SPM_PAGESIZE,\
+               .selfprgen = AVR_IO_REGBIT(_spmr, _spen),\
+               .pgers = AVR_IO_REGBIT(_spmr, PGERS),\
+               .pgwrt = AVR_IO_REGBIT(_spmr, PGWRT),\
+               .blbset = AVR_IO_REGBIT(_spmr, BLBSET),\
+               .flash = {\
+                       .enable = AVR_IO_REGBIT(_spmr, SPMIE),\
+                       .vector = _vector,\
+               }\
+
+#define AVR_SELFPROG_DECLARE_NORWW(_spmr, _spen, _vector) \
+       .selfprog = {\
+               .flags = 0,\
+               AVR_SELFPROG_DECLARE_INTERNAL(_spmr, _spen, _vector),\
+       }
+
+#define AVR_SELFPROG_DECLARE(_spmr, _spen, _vector) \
+       .selfprog = {\
+               .flags = AVR_SELFPROG_HAVE_RWW,\
+               AVR_SELFPROG_DECLARE_INTERNAL(_spmr, _spen, _vector),\
+               .rwwsre = AVR_IO_REGBIT(_spmr, RWWSRE),\
+               .rwwsb = AVR_IO_REGBIT(_spmr, RWWSB),\
+       }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __AVR_FLASH_H___ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.c
new file mode 100644 (file)
index 0000000..91cf106
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+       avr_ioport.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include "avr_ioport.h"
+
+#define D(_w)
+
+static void
+avr_ioport_flag_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_ioport_t * p = (avr_ioport_t *)param;
+
+       // Clear interrupt if 1 is written to flag.
+
+       if (avr_regbit_from_value(avr, p->pcint.raised, v))
+               avr_clear_interrupt(avr, &p->pcint);
+}
+
+static uint8_t
+avr_ioport_read(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               void * param)
+{
+       avr_ioport_t * p = (avr_ioport_t *)param;
+       uint8_t ddr = avr->data[p->r_ddr];
+       uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
+       avr->data[addr] = v;
+       avr_raise_irq(p->io.irq + IOPORT_IRQ_REG_PIN, v);
+       D(if (avr->data[addr] != v) printf("** PIN%c(%02x) = %02x\r\n", p->name, addr, v);)
+
+       // made to trigger potential watchpoints
+       v = avr_core_watch_read(avr, addr);
+       return v;
+}
+
+static void
+avr_ioport_update_irqs(
+               avr_ioport_t * p)
+{
+       avr_t * avr = p->io.avr;
+       uint8_t ddr = avr->data[p->r_ddr];
+       // Set the PORT value if the pin is marked as output
+       // otherwise, if there is an 'external' pullup, set it
+       // otherwise, if the PORT pin was 1 to indicate an
+       // internal pullup, set that.
+       for (int i = 0; i < 8; i++) {
+               if (ddr & (1 << i))
+                       avr_raise_irq(p->io.irq + i, (avr->data[p->r_port] >> i) & 1);
+               else if (p->external.pull_mask & (1 << i))
+                       avr_raise_irq(p->io.irq + i, (p->external.pull_value >> i) & 1);
+               else if ((avr->data[p->r_port] >> i) & 1)
+                       avr_raise_irq(p->io.irq + i, 1);
+       }
+       uint8_t pin = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
+       pin = (pin & ~p->external.pull_mask) | p->external.pull_value;
+       avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, pin);
+
+       // if IRQs are registered on the PORT register (for example, VCD dumps) send
+       // those as well
+       avr_io_addr_t port_io = AVR_DATA_TO_IO(p->r_port);
+       if (avr->io[port_io].irq) {
+               avr_raise_irq(avr->io[port_io].irq + AVR_IOMEM_IRQ_ALL, avr->data[p->r_port]);
+               for (int i = 0; i < 8; i++)
+                       avr_raise_irq(avr->io[port_io].irq + i, (avr->data[p->r_port] >> i) & 1);
+       }
+}
+
+static void
+avr_ioport_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_ioport_t * p = (avr_ioport_t *)param;
+
+       D(if (avr->data[addr] != v) printf("** PORT%c(%02x) = %02x\r\n", p->name, addr, v);)
+       avr_core_watch_write(avr, addr, v);
+       avr_raise_irq(p->io.irq + IOPORT_IRQ_REG_PORT, v);
+       avr_ioport_update_irqs(p);
+}
+
+/*
+ * This is a reasonably new behaviour for the io-ports. Writing 1's to the PIN register
+ * toggles the PORT equivalent bit (regardless of direction
+ */
+static void
+avr_ioport_pin_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_ioport_t * p = (avr_ioport_t *)param;
+
+       avr_ioport_write(avr, p->r_port, avr->data[p->r_port] ^ v, param);
+}
+
+/*
+ * This is a the callback for the DDR register. There is nothing much to do here, apart
+ * from triggering an IRQ in case any 'client' code is interested in the information,
+ * and restoring all PIN bits marked as output to PORT values.
+ */
+static void
+avr_ioport_ddr_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_ioport_t * p = (avr_ioport_t *)param;
+
+       D(if (avr->data[addr] != v) printf("** DDR%c(%02x) = %02x\r\n", p->name, addr, v);)
+       avr_raise_irq(p->io.irq + IOPORT_IRQ_DIRECTION_ALL, v);
+       avr_core_watch_write(avr, addr, v);
+       avr_ioport_update_irqs(p);
+}
+
+/*
+ * this is our "main" pin change callback, it can be triggered by either the
+ * AVR code, or any external piece of code that see fit to do it.
+ * Either way, this will raise pin change interrupts, if needed
+ */
+void
+avr_ioport_irq_notify(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       avr_ioport_t * p = (avr_ioport_t *)param;
+       avr_t * avr = p->io.avr;
+
+       int output = value & AVR_IOPORT_OUTPUT;
+       value &= 0xff;
+       uint8_t mask = 1 << irq->irq;
+       uint8_t ddr = avr->data[p->r_ddr];
+
+       if (output) {
+               if ((mask & ddr) == 0)
+                       return;    // TODO: stop further processing of IRQ.
+
+               // If the IRQ was marked as Output, also do the IO write.
+
+               avr_ioport_write(avr,
+                                p->r_port,
+                                (avr->data[p->r_port] & ~mask) |
+                                    (value ? mask : 0),
+                                p);
+       } else {
+               // Set the real PIN bit. Ignore DDR as it's masked when read.
+
+               avr_core_watch_write(avr, p->r_pin,
+                                                        (avr->data[p->r_pin] & ~mask) |
+                                                               (value ? mask : 0));
+
+               /* BUG: If DDR bit is set here, there should be no
+                * interrupt.  But a spurious IRQ call by the user
+                * is indistinguishable from an internal one
+                * caused by writing the output port register and
+                * that should cause an interrupt. Doh!
+                */
+       }
+
+       if (p->r_pcint) {
+               // Ignore lingering copy of AVR_IOPORT_OUTPUT, or
+               // differing non-zero values.
+
+               if (!value == !(irq->value & 0xff))
+                       return;
+
+               // if the pcint bit is on, try to raise it
+
+               int raisedata = avr->data[p->r_pcint];
+               uint8_t uiRegMask = p->mask;
+               int8_t iShift = p->shift;
+
+               if (uiRegMask) // If mask is 0, do nothing (backwards compat)
+                       raisedata &= uiRegMask; // Mask off
+
+               if (iShift>0) // Shift data if necessary for alignment.
+                       raisedata <<= iShift;
+               else if (iShift<0)
+                       raisedata >>= -iShift;
+
+               int raise = raisedata & mask;
+               if (raise)
+                       avr_raise_interrupt(avr, &p->pcint);
+       }
+}
+
+static void
+avr_ioport_reset(
+               avr_io_t * port)
+{
+       avr_ioport_t * p = (avr_ioport_t *)port;
+       for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++)
+               avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p);
+}
+
+static int
+avr_ioport_ioctl(
+               struct avr_io_t * port,
+               uint32_t ctl,
+               void * io_param)
+{
+       avr_ioport_t * p = (avr_ioport_t *)port;
+       avr_t * avr = p->io.avr;
+       int res = -1;
+
+       // all IOCTls require some sort of valid parameter, bail if not
+       if (!io_param)
+               return -1;
+
+       switch(ctl) {
+               case AVR_IOCTL_IOPORT_GETIRQ_REGBIT: {
+                       avr_ioport_getirq_t * r = (avr_ioport_getirq_t*)io_param;
+
+                       if (r->bit.reg == p->r_port || r->bit.reg == p->r_pin || r->bit.reg == p->r_ddr) {
+                               // it's us ! check the special case when the "all pins" irq is requested
+                               int o = 0;
+                               if (r->bit.mask == 0xff)
+                                       r->irq[o++] = &p->io.irq[IOPORT_IRQ_PIN_ALL];
+                               else {
+                                       // otherwise fill up the ones needed
+                                       for (int bi = 0; bi < 8; bi++)
+                                               if (r->bit.mask & (1 << bi))
+                                                       r->irq[o++] = &p->io.irq[r->bit.bit + bi];
+                               }
+                               if (o < 8)
+                                       r->irq[o] = NULL;
+                               return o;
+                       }
+               }       break;
+               default: {
+                       /*
+                        * Return the port state if the IOCTL matches us.
+                        */
+                       if (ctl == AVR_IOCTL_IOPORT_GETSTATE(p->name)) {
+                               avr_ioport_state_t state = {
+                                       .name = p->name,
+                                       .port = avr->data[p->r_port],
+                                       .ddr = avr->data[p->r_ddr],
+                                       .pin = avr->data[p->r_pin],
+                               };
+                               if (io_param)
+                                       *((avr_ioport_state_t*)io_param) = state;
+                               res = 0;
+                       }
+                       /*
+                        * Set the default IRQ values when pin is set as input
+                        */
+                       if (ctl == AVR_IOCTL_IOPORT_SET_EXTERNAL(p->name)) {
+                               avr_ioport_external_t * m = (avr_ioport_external_t*)io_param;
+                               p->external.pull_mask = m->mask;
+                               p->external.pull_value = m->value;
+                               res = 0;
+                       }
+               }
+       }
+
+       return res;
+}
+
+static const char * irq_names[IOPORT_IRQ_COUNT] = {
+       [IOPORT_IRQ_PIN0] = "=pin0",
+       [IOPORT_IRQ_PIN1] = "=pin1",
+       [IOPORT_IRQ_PIN2] = "=pin2",
+       [IOPORT_IRQ_PIN3] = "=pin3",
+       [IOPORT_IRQ_PIN4] = "=pin4",
+       [IOPORT_IRQ_PIN5] = "=pin5",
+       [IOPORT_IRQ_PIN6] = "=pin6",
+       [IOPORT_IRQ_PIN7] = "=pin7",
+       [IOPORT_IRQ_PIN_ALL] = "8>all",
+       [IOPORT_IRQ_DIRECTION_ALL] = "8>ddr",
+       [IOPORT_IRQ_REG_PORT] = "8>port",
+       [IOPORT_IRQ_REG_PIN] = "8>pin",
+};
+
+static avr_io_t        _io = {
+       .kind = "port",
+       .reset = avr_ioport_reset,
+       .ioctl = avr_ioport_ioctl,
+       .irq_names = irq_names,
+};
+
+void avr_ioport_init(avr_t * avr, avr_ioport_t * p)
+{
+       if (!p->r_port) {
+               printf("skipping PORT%c for core %s\n", p->name, avr->mmcu);
+               return;
+       }
+       p->io = _io;
+//     printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__,
+//             p->name, p->r_pin,
+//             p->name, p->r_ddr,
+//             p->name, p->r_port);
+
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->pcint);
+       // allocate this module's IRQ
+       avr_io_setirqs(&p->io, AVR_IOCTL_IOPORT_GETIRQ(p->name), IOPORT_IRQ_COUNT, NULL);
+
+       for (int i = 0; i < IOPORT_IRQ_REG_PIN; i++) {
+               p->io.irq[i].flags |= IRQ_FLAG_FILTERED;
+                if (i < IOPORT_IRQ_PIN_ALL)
+                    p->io.irq[i].flags &= ~IRQ_FLAG_INIT;
+       }
+       avr_register_io_write(avr, p->r_port, avr_ioport_write, p);
+       avr_register_io_read(avr, p->r_pin, avr_ioport_read, p);
+       avr_register_io_write(avr, p->r_pin, avr_ioport_pin_write, p);
+       avr_register_io_write(avr, p->r_ddr, avr_ioport_ddr_write, p);
+       if (p->pcint.raised.reg) {
+               avr_register_io_write(avr, p->pcint.raised.reg,
+                                     avr_ioport_flag_write, p);
+       }
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.h
new file mode 100644 (file)
index 0000000..024b695
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+       avr_ioport.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_IOPORT_H__
+#define __AVR_IOPORT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+enum {
+       IOPORT_IRQ_PIN0 = 0,
+       IOPORT_IRQ_PIN1,IOPORT_IRQ_PIN2,IOPORT_IRQ_PIN3,IOPORT_IRQ_PIN4,
+       IOPORT_IRQ_PIN5,IOPORT_IRQ_PIN6,IOPORT_IRQ_PIN7,
+       IOPORT_IRQ_PIN_ALL,
+       IOPORT_IRQ_DIRECTION_ALL,
+       IOPORT_IRQ_REG_PORT,
+       IOPORT_IRQ_REG_PIN,
+       IOPORT_IRQ_COUNT
+};
+
+#define AVR_IOPORT_OUTPUT 0x100
+
+// add port name (uppercase) to get the real IRQ
+#define AVR_IOCTL_IOPORT_GETIRQ(_name) AVR_IOCTL_DEF('i','o','g',(_name))
+
+
+// this ioctl takes a avr_regbit_t, compares the register address
+// to PORT/PIN/DDR and return the corresponding IRQ(s) if it matches
+typedef struct avr_ioport_getirq_t {
+       avr_regbit_t bit;       // bit wanted
+       avr_irq_t * irq[8];     // result, terminated by NULL if < 8
+} avr_ioport_getirq_t;
+
+#define AVR_IOCTL_IOPORT_GETIRQ_REGBIT AVR_IOCTL_DEF('i','o','g','r')
+
+/*
+ * ioctl used to get a port state.
+ *
+ * for (int i = 'A'; i <= 'F'; i++) {
+ *     avr_ioport_state_t state;
+ *     if (avr_ioctl(AVR_IOCTL_IOPORT_GETSTATE(i), &state) == 0)
+ *             printf("PORT%c %02x DDR %02x PIN %02x\n",
+ *                     state.name, state.port, state.ddr, state.pin);
+ * }
+ */
+typedef struct avr_ioport_state_t {
+       unsigned long name : 7,
+               port : 8, ddr : 8, pin : 8;
+} avr_ioport_state_t;
+
+// add port name (uppercase) to get the port state
+#define AVR_IOCTL_IOPORT_GETSTATE(_name) AVR_IOCTL_DEF('i','o','s',(_name))
+
+/*
+ * ioctl used to set default port state when set as input.
+ *
+ */
+typedef struct avr_ioport_external_t {
+       unsigned long name : 7,
+               mask : 8, value : 8;
+} avr_ioport_external_t;
+
+// add port name (uppercase) to set default input pin IRQ values
+#define AVR_IOCTL_IOPORT_SET_EXTERNAL(_name) AVR_IOCTL_DEF('i','o','p',(_name))
+
+/**
+ * pin structure
+ */
+typedef struct avr_iopin_t {
+       uint16_t port : 8;                      ///< port e.g. 'B'
+       uint16_t pin : 8;               ///< pin number
+} avr_iopin_t;
+#define AVR_IOPIN(_port, _pin) { .port = _port, .pin = _pin }
+
+/*
+ * Definition for an IO port
+ */
+typedef struct avr_ioport_t {
+       avr_io_t        io;
+       char name;
+       avr_io_addr_t r_port;
+       avr_io_addr_t r_ddr;
+       avr_io_addr_t r_pin;
+
+       avr_int_vector_t pcint; // PCINT vector
+       avr_io_addr_t r_pcint;  // pcint 8 pins mask
+
+       // Mask and shift for PCINTs.  This is needed for chips like the 2560
+       // where PCINT do not align with IRQs.
+
+       uint8_t         mask;
+       int8_t          shift;
+
+       // This represent the default IRQ value when
+       // the port is set as input.
+       // If the mask is not set, no output value is sent
+       // on the output IRQ. If the mask is set, the specified
+       // value is sent.
+       struct {
+               uint8_t pull_mask, pull_value;
+       } external;
+} avr_ioport_t;
+
+void avr_ioport_init(avr_t * avr, avr_ioport_t * port);
+
+#define AVR_IOPORT_DECLARE(_lname, _cname, _uname) \
+       .port ## _lname = { \
+               .name = _cname, .r_port = PORT ## _uname, .r_ddr = DDR ## _uname, .r_pin = PIN ## _uname, \
+       }
+
+#define AVR_IOPORT_DECLARE_PC(_lname, _cname, _uname, _pcnum)  \
+       .port ## _lname = { \
+               .name = _cname, .r_port = PORT ## _uname, \
+               .r_ddr = DDR ## _uname, .r_pin = PIN ## _uname, \
+               .pcint = { \
+                        .enable = AVR_IO_REGBIT(PCICR, PCIE ## _pcnum), \
+                        .raised = AVR_IO_REGBIT(PCIFR, PCIF ## _pcnum), \
+                        .vector = PCINT ## _pcnum ## _vect, \
+               }, \
+               .r_pcint = PCMSK ## _pcnum, \
+       }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __AVR_IOPORT_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.c
new file mode 100644 (file)
index 0000000..001b34c
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+       avr_lin.h
+
+       Copyright 2008, 2011 Michel Pollet <buserror@gmail.com>
+       Copyright 2011 Markus Lampert  <mlampert@telus.net>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include "avr_lin.h"
+#include "sim_time.h"
+
+
+static void
+avr_lin_baud_write(
+        struct avr_t *avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void *param)
+{
+       avr_lin_t *p = (avr_lin_t*) param;
+
+       if (p->r_linbtr != p->ldisr.reg || p->r_linbtr != p->lbt.reg) { // sanity check
+               AVR_LOG(avr, LOG_ERROR, "LIN: LDISR and LBT[x] register different!\n");
+               return;
+       }
+
+       AVR_LOG(avr, LOG_TRACE, "LIN: addr[%04x] = %02x\n", addr, v);
+       if (addr == p->ldisr.reg) {
+               if (avr_regbit_get(avr, p->lena)) {
+                       AVR_LOG(avr, LOG_WARNING, "LIN: LENA bit set on changing LBTR\n");
+                       return;
+               }
+               if ((v >> p->ldisr.bit) & p->ldisr.mask) {
+                       uint8_t lbt = (v >> p->lbt.bit) & p->lbt.mask;
+                       uint8_t ov = v;
+                       v = (1 << p->ldisr.bit) | (lbt << p->lbt.bit);
+                       AVR_LOG(avr, LOG_TRACE, "LIN: v=%02x -> LBT = %02x -> LINBT = %02x\n", ov, lbt, v);
+               } else {
+                       v = 0x20;
+               }
+       }
+       avr_core_watch_write(avr, addr, v); // actually set the value
+
+       uint32_t lbt = avr_regbit_get(avr, p->lbt); // Min value CANNOT be zero
+       uint32_t lbrr = (avr->data[p->r_linbrrh] << 8) | avr->data[p->r_linbrrl];
+       AVR_LOG(avr, LOG_TRACE, "LIN: UART LBT/LBRR to %04x/%04x\n", lbt, lbrr);
+       // there is no division by zero case here, lbt is >= 8
+       //uint32_t baud = avr->frequency / (lbt * (lbrr + 1));
+       uint32_t word_size = 1 /*start*/+ 8 /*data bits*/+ 1 /*parity*/+ 1 /*stop*/;
+       int cycles_per_bit = lbt * (lbrr + 1);
+       double baud = ((double)avr->frequency) / cycles_per_bit; // can be less than 1
+       p->uart.cycles_per_byte = cycles_per_bit * word_size;
+
+       AVR_LOG(avr, LOG_TRACE, "LIN: UART configured to %04x/%04x = %.4f bps, 8 data 1 stop\n", lbt,
+               lbrr, baud);
+
+       //p->uart.cycles_per_byte = 1000000 / (baud / word_size);
+       AVR_LOG(avr, LOG_TRACE, "LIN: Roughly %d usec per byte\n",
+                       avr_cycles_to_usec(avr, p->uart.cycles_per_byte));
+}
+
+static void
+avr_lin_reset(
+               avr_io_t *port)
+{
+       avr_lin_t *p = (avr_lin_t*) port;
+       avr_t * avr = p->io.avr;
+
+       AVR_LOG(avr, LOG_TRACE, "LIN: UART: reset\n");
+
+       p->uart.io.reset(&p->uart.io);
+       avr->data[p->r_linbtr] = 0x20;
+}
+
+static avr_io_t _io = {
+               .kind = "lin",
+               .reset = avr_lin_reset,
+};
+
+void
+avr_lin_init(
+               avr_t *avr,
+               avr_lin_t *p)
+{
+       // init uart part
+       avr_uart_init(avr, &p->uart);
+
+       p->io = _io;
+       avr_register_io_write(avr, p->r_linbtr, avr_lin_baud_write, p);
+       avr_register_io_write(avr, p->r_linbrrl, avr_lin_baud_write, p);
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.h
new file mode 100644 (file)
index 0000000..b1ecadf
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+       avr_lin.h
+
+       Copyright 2008, 2011 Michel Pollet <buserror@gmail.com>
+       Copyright 2011 Markus Lampert  <mlampert@telus.net>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_LIN_H__
+#define __AVR_LIN_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+#include "avr_uart.h"
+
+typedef struct avr_lin_t {
+       avr_io_t io;
+
+       avr_io_addr_t r_linbtr;
+       avr_io_addr_t r_linbrrh, r_linbrrl;
+
+       avr_regbit_t lena;
+       avr_regbit_t ldisr;
+       avr_regbit_t lbt;
+
+       avr_uart_t uart;  // used when LIN controller is setup as a UART
+} avr_lin_t;
+
+void
+avr_lin_init(
+               avr_t *avr,
+               avr_lin_t *port);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __AVR_LIN_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.c
new file mode 100644 (file)
index 0000000..bb602ac
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+       avr_spi.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+       Modified 2020 by VintagePC <https://github.com/vintagepc> to support clock divisors
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include "avr_spi.h"
+
+static avr_cycle_count_t avr_spi_raise(struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_spi_t * p = (avr_spi_t *)param;
+
+       if (avr_regbit_get(avr, p->spe)) {
+               // in master mode, any byte is sent as it comes..
+               if (avr_regbit_get(avr, p->mstr)) {
+                       avr_raise_interrupt(avr, &p->spi);
+                       avr_raise_irq(p->io.irq + SPI_IRQ_OUTPUT, avr->data[p->r_spdr]);
+               }
+       }
+       return 0;
+}
+
+static uint8_t avr_spi_read(struct avr_t * avr, avr_io_addr_t addr, void * param)
+{
+       avr_spi_t * p = (avr_spi_t *)param;
+       uint8_t v = p->input_data_register;
+       p->input_data_register = 0;
+       avr_regbit_clear(avr, p->spi.raised);
+//     printf("avr_spi_read = %02x\n", v);
+       return v;
+}
+
+static void avr_spi_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+
+       static const uint8_t _avr_spi_clkdiv[4] = {4,16,64,128};
+       avr_spi_t * p = (avr_spi_t *)param;
+
+       if (addr == p->r_spdr) {
+               /* Clear the SPIF bit. See ATmega164/324/644 manual, Section 18.5.2. */
+               avr_regbit_clear(avr, p->spi.raised);
+
+               avr_core_watch_write(avr, addr, v);
+               uint16_t clock_shift = _avr_spi_clkdiv[avr->data[p->r_spcr]&0b11];
+               // If master && 2X, double rate (half divisor)
+               if (avr_regbit_get(avr, p->mstr) && avr_regbit_get(avr, p->spr[2]))
+                       clock_shift>>=1;
+
+               // We can wait directly in clockshifts, it is a divisor, so /4 means 4 avr cycles to clock out one bit.
+               avr_cycle_timer_register(avr, clock_shift<<3, avr_spi_raise, p); // *8 since 8 clocks to a byte.
+       }
+}
+
+static void avr_spi_irq_input(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       avr_spi_t * p = (avr_spi_t *)param;
+       avr_t * avr = p->io.avr;
+
+       // check to see if receiver is enabled
+       if (!avr_regbit_get(avr, p->spe))
+               return;
+
+       // double buffer the input.. ?
+       p->input_data_register = value;
+       avr_raise_interrupt(avr, &p->spi);
+
+       // if in slave mode,
+       // 'output' the byte only when we received one...
+       if (!avr_regbit_get(avr, p->mstr)) {
+               avr_raise_irq(p->io.irq + SPI_IRQ_OUTPUT, avr->data[p->r_spdr]);
+       }
+}
+
+void avr_spi_reset(struct avr_io_t *io)
+{
+       avr_spi_t * p = (avr_spi_t *)io;
+       avr_irq_register_notify(p->io.irq + SPI_IRQ_INPUT, avr_spi_irq_input, p);
+}
+
+static const char * irq_names[SPI_IRQ_COUNT] = {
+       [SPI_IRQ_INPUT] = "8<in",
+       [SPI_IRQ_OUTPUT] = "8<out",
+};
+
+static avr_io_t        _io = {
+       .kind = "spi",
+       .reset = avr_spi_reset,
+       .irq_names = irq_names,
+};
+
+void avr_spi_init(avr_t * avr, avr_spi_t * p)
+{
+       p->io = _io;
+
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->spi);
+       // allocate this module's IRQ
+       avr_io_setirqs(&p->io, AVR_IOCTL_SPI_GETIRQ(p->name), SPI_IRQ_COUNT, NULL);
+
+       avr_register_io_write(avr, p->r_spdr, avr_spi_write, p);
+       avr_register_io_read(avr, p->r_spdr, avr_spi_read, p);
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.h
new file mode 100644 (file)
index 0000000..c676adb
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+       avr_spi.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+       Modified 2020 by VintagePC <https://github.com/vintagepc> to support clock divisors
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_SPI_H__
+#define __AVR_SPI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+enum {
+       SPI_IRQ_INPUT = 0,
+       SPI_IRQ_OUTPUT,
+       SPI_IRQ_COUNT
+};
+
+// add port number to get the real IRQ
+#define AVR_IOCTL_SPI_GETIRQ(_name) AVR_IOCTL_DEF('s','p','i',(_name))
+
+typedef struct avr_spi_t {
+       avr_io_t        io;
+       char name;
+       avr_regbit_t    disabled;       // bit in the PRR
+
+       avr_io_addr_t   r_spdr;                 // data register
+       avr_io_addr_t   r_spcr;                 // control register
+       avr_io_addr_t   r_spsr;                 // status register
+
+       avr_regbit_t spe;               // spi enable
+       avr_regbit_t mstr;              // master/slave
+       avr_regbit_t spr[4];    // clock divider
+
+       avr_int_vector_t spi;   // spi interrupt
+
+       uint8_t         input_data_register;
+} avr_spi_t;
+
+void avr_spi_init(avr_t * avr, avr_spi_t * port);
+
+#define AVR_SPIX_DECLARE(_name, _prr, _prspi) \
+       .spi = { \
+               .name = '0' + _name,\
+               .disabled = AVR_IO_REGBIT(_prr, _prspi), \
+       \
+               .r_spdr = SPDR ## _name, \
+               .r_spcr = SPCR ## _name, \
+               .r_spsr = SPSR ## _name, \
+       \
+               .spe = AVR_IO_REGBIT(SPCR ## _name, SPE ## _name), \
+               .mstr = AVR_IO_REGBIT(SPCR ## _name, MSTR ## _name), \
+       \
+               .spr = { AVR_IO_REGBIT(SPCR ## _name, SPR0 ## _name), \
+                                       AVR_IO_REGBIT(SPCR ## _name, SPR1 ## _name), \
+                                       AVR_IO_REGBIT(SPSR ## _name, SPI2X ## _name) }, \
+               .spi = { \
+                       .enable = AVR_IO_REGBIT(SPCR ## _name, SPIE ## _name), \
+                       .raised = AVR_IO_REGBIT(SPSR ## _name, SPIF ## _name), \
+                       .vector = SPI_STC_vect, \
+               }, \
+       }
+
+
+#define AVR_SPI_DECLARE(_prr, _prspi) \
+       .spi = { \
+               .disabled = AVR_IO_REGBIT(_prr, _prspi), \
+       \
+               .r_spdr = SPDR, \
+               .r_spcr = SPCR, \
+               .r_spsr = SPSR, \
+       \
+               .spe = AVR_IO_REGBIT(SPCR, SPE), \
+               .mstr = AVR_IO_REGBIT(SPCR, MSTR), \
+       \
+               .spr = { AVR_IO_REGBIT(SPCR, SPR0), AVR_IO_REGBIT(SPCR, SPR1), AVR_IO_REGBIT(SPSR, SPI2X) }, \
+               .spi = { \
+                       .enable = AVR_IO_REGBIT(SPCR, SPIE), \
+                       .raised = AVR_IO_REGBIT(SPSR, SPIF), \
+                       .vector = SPI_STC_vect, \
+               }, \
+       }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__AVR_SPI_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.c
new file mode 100644 (file)
index 0000000..39a46bd
--- /dev/null
@@ -0,0 +1,985 @@
+/*
+       avr_timer.c
+
+       Handles the 8 bits and 16 bits AVR timer.
+       Handles
+       + CDC
+       + Fast PWM
+
+       Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "avr_timer.h"
+#include "avr_ioport.h"
+#include "sim_time.h"
+
+/*
+ * The timers are /always/ 16 bits here, if the higher byte register
+ * is specified it's just added.
+ */
+static uint16_t
+_timer_get_ocr(
+               avr_timer_t * p,
+               int compi)
+{
+       return p->io.avr->data[p->comp[compi].r_ocr] |
+                         (p->comp[compi].r_ocrh ?
+                                         (p->io.avr->data[p->comp[compi].r_ocrh] << 8) : 0);
+}
+
+static uint16_t
+_timer_get_comp_ocr(
+               struct avr_t * avr,
+               avr_timer_comp_p comp)
+{
+       int ocrh = comp->r_ocrh;
+       return avr->data[comp->r_ocr] |
+               (ocrh ? (avr->data[ocrh] << 8) : 0);
+}
+
+static uint16_t
+_timer_get_tcnt(
+               avr_timer_t * p)
+{
+       return p->io.avr->data[p->r_tcnt] |
+                               (p->r_tcnth ? (p->io.avr->data[p->r_tcnth] << 8) : 0);
+}
+
+static uint16_t
+_timer_get_icr(
+               avr_timer_t * p)
+{
+       return p->io.avr->data[p->r_icr] |
+                               (p->r_tcnth ? (p->io.avr->data[p->r_icrh] << 8) : 0);
+}
+static avr_cycle_count_t
+avr_timer_comp(
+               avr_timer_t *p,
+               avr_cycle_count_t when,
+               uint8_t comp,
+               uint8_t raise_interrupt)
+{
+       avr_t * avr = p->io.avr;
+       if (raise_interrupt) {
+          avr_raise_interrupt(avr, &p->comp[comp].interrupt);
+       }
+
+       // check output compare mode and set/clear pins
+       uint8_t mode = avr_regbit_get(avr, p->comp[comp].com);
+       avr_irq_t * irq = &p->io.irq[TIMER_IRQ_OUT_COMP + comp];
+
+               uint32_t flags = 0;
+               if (p->comp[comp].com_pin.reg)  // we got a physical pin
+                               flags |= AVR_IOPORT_OUTPUT;
+               AVR_LOG(avr, LOG_TRACE, "Timer comp: irq %p, mode %d @%d\n", irq, mode, when);
+       switch (mode) {
+               case avr_timer_com_normal: // Normal mode OCnA disconnected
+                       break;
+               case avr_timer_com_toggle: // Toggle OCnA on compare match
+                       if (p->comp[comp].com_pin.reg)  // we got a physical pin
+                               avr_raise_irq(irq,
+                                               flags |
+                                               (avr_regbit_get(avr, p->comp[comp].com_pin) ? 0 : 1));
+                       else // no pin, toggle the IRQ anyway
+                               avr_raise_irq(irq,
+                                               p->io.irq[TIMER_IRQ_OUT_COMP + comp].value ? 0 : 1);
+                       break;
+               case avr_timer_com_clear:
+                       avr_raise_irq(irq, flags | 0);
+                       break;
+               case avr_timer_com_set:
+                       avr_raise_irq(irq, flags | 1);
+                       break;
+       }
+
+       return p->tov_cycles ? 0 :
+                               p->comp[comp].comp_cycles ?
+                                               when + p->comp[comp].comp_cycles : 0;
+}
+
+static void
+avr_timer_comp_on_tov(
+               avr_timer_t *p,
+               avr_cycle_count_t when,
+               uint8_t comp)
+{
+       avr_t * avr = p->io.avr;
+
+       // check output compare mode and set/clear pins
+       uint8_t mode = avr_regbit_get(avr, p->comp[comp].com);
+       avr_irq_t * irq = &p->io.irq[TIMER_IRQ_OUT_COMP + comp];
+
+       // only PWM modes have special behaviour on overflow
+       if((p->wgm_op_mode_kind != avr_timer_wgm_pwm) &&
+          (p->wgm_op_mode_kind != avr_timer_wgm_fast_pwm))
+               return;
+
+       switch (mode) {
+               case avr_timer_com_normal: // Normal mode
+                       break;
+               case avr_timer_com_toggle: // toggle on compare match => on tov do nothing
+                       break;
+               case avr_timer_com_clear: // clear on compare match => set on tov
+                       avr_raise_irq(irq, 1);
+                       break;
+               case avr_timer_com_set: // set on compare match => clear on tov
+                       avr_raise_irq(irq, 0);
+                       break;
+       }
+}
+
+static avr_cycle_count_t
+avr_timer_compa(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       return avr_timer_comp((avr_timer_t*)param, when, AVR_TIMER_COMPA, 1);
+}
+
+static avr_cycle_count_t
+avr_timer_compb(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       return avr_timer_comp((avr_timer_t*)param, when, AVR_TIMER_COMPB, 1);
+}
+
+static avr_cycle_count_t
+avr_timer_compc(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       return avr_timer_comp((avr_timer_t*)param, when, AVR_TIMER_COMPC, 1);
+}
+
+static void
+avr_timer_irq_ext_clock(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+       avr_t * avr = p->io.avr;
+
+       if ((p->ext_clock_flags & AVR_TIMER_EXTCLK_FLAG_VIRT) || !p->tov_top)
+               return;                 // we are clocked internally (actually should never come here)
+
+       int bing = 0;
+       if (p->ext_clock_flags & AVR_TIMER_EXTCLK_FLAG_EDGE) { // clock on rising edge
+               if (!irq->value && value)
+                       bing++;
+       } else {        // clock on falling edge
+               if (irq->value && !value)
+                       bing++;
+       }
+       if (!bing)
+               return;
+
+       //AVR_LOG(avr, LOG_TRACE, "%s Timer%c tick, tcnt=%i\n", __func__, p->name, p->tov_base);
+
+       p->ext_clock_flags |= AVR_TIMER_EXTCLK_FLAG_STARTED;
+
+       static const avr_cycle_timer_t dispatch[AVR_TIMER_COMP_COUNT] =
+               { avr_timer_compa, avr_timer_compb, avr_timer_compc };
+
+       int overflow = 0;
+       /**
+         *
+         * Datasheet excerpt (Compare Match Output Unit):
+         * "The 16-bit comparator continuously compares TCNT1 with the Output Compare Regis-
+               ter (OCR1x). If TCNT equals OCR1x the comparator signals a match. A match will set
+               the Output Compare Flag (OCF1x) at the next timer clock cycle. If enabled (OCIE1x =
+               1), the Output Compare Flag generates an output compare interrupt."
+               Thus, comparators should go before incementing the counter to use counter value
+               from the previous cycle.
+       */
+       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+               if (p->wgm_op_mode_kind != avr_timer_wgm_ctc) {
+                       if ((p->mode.top == avr_timer_wgm_reg_ocra) && (compi == 0))
+                               continue; // ocra used to define TOP
+               }
+               if (p->comp[compi].comp_cycles && (p->tov_base == p->comp[compi].comp_cycles)) {
+                               dispatch[compi](avr, avr->cycle, param);
+                       if (p->wgm_op_mode_kind == avr_timer_wgm_ctc)
+                               p->tov_base = 0;
+               }
+       }
+
+       switch (p->wgm_op_mode_kind) {
+               case avr_timer_wgm_fc_pwm:      // in the avr_timer_write_ocr comment "OCR is not used here" - why?
+               case avr_timer_wgm_pwm:
+                       if ((p->ext_clock_flags & AVR_TIMER_EXTCLK_FLAG_REVDIR) != 0) {
+                               --p->tov_base;
+                               if (p->tov_base == 0) {
+                                       // overflow occured
+                                       p->ext_clock_flags &= ~AVR_TIMER_EXTCLK_FLAG_REVDIR; // restore forward count direction
+                                       overflow = 1;
+                               }
+                       }
+                       else {
+                               if (++p->tov_base >= p->tov_top) {
+                                       p->ext_clock_flags |= AVR_TIMER_EXTCLK_FLAG_REVDIR; // prepare to count down
+                               }
+                       }
+                       break;
+               case avr_timer_wgm_fast_pwm:
+                       if (++p->tov_base == p->tov_top) {
+                               overflow = 1;
+                               if (p->mode.top == avr_timer_wgm_reg_icr)
+                                       avr_raise_interrupt(avr, &p->icr);
+                               else if (p->mode.top == avr_timer_wgm_reg_ocra)
+                                       avr_raise_interrupt(avr, &p->comp[0].interrupt);
+                       }
+                       else if (p->tov_base > p->tov_top) {
+                               p->tov_base = 0;
+                       }
+                       break;
+               case avr_timer_wgm_ctc:
+                       {
+                               int max = (1 << p->wgm_op[0].size)-1;
+                               if (++p->tov_base > max) {
+                                       // overflow occured
+                                       p->tov_base = 0;
+                                       overflow = 1;
+                               }
+                       }
+                       break;
+               default:
+                       if (++p->tov_base > p->tov_top) {
+                               // overflow occured
+                               p->tov_base = 0;
+                               overflow = 1;
+                       }
+                       break;
+       }
+
+       if (overflow) {
+               for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+                       if (p->comp[compi].comp_cycles) {
+                               if (p->mode.top == avr_timer_wgm_reg_ocra && compi == 0)
+                                       continue;
+                               avr_timer_comp_on_tov(p, 0, compi);
+                       }
+               }
+               avr_raise_interrupt(avr, &p->overflow);
+       }
+
+}
+
+// timer overflow
+static avr_cycle_count_t
+avr_timer_tov(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+       int start = p->tov_base == 0;
+
+       avr_cycle_count_t next = when;
+       if (((p->ext_clock_flags & (AVR_TIMER_EXTCLK_FLAG_AS2 | AVR_TIMER_EXTCLK_FLAG_TN)) != 0)
+                       && (p->tov_cycles_fract != 0.0f)) {
+               p->phase_accumulator += p->tov_cycles_fract;
+               if (p->phase_accumulator >= 1.0f) {
+                       ++next;
+                       p->phase_accumulator -= 1.0f;
+               } else if (p->phase_accumulator <= -1.0f) {
+                       --next;
+                       p->phase_accumulator += 1.0f;
+               }
+       }
+
+       if (!start)
+               avr_raise_interrupt(avr, &p->overflow);
+       p->tov_base = when;
+
+       static const avr_cycle_timer_t dispatch[AVR_TIMER_COMP_COUNT] =
+               { avr_timer_compa, avr_timer_compb, avr_timer_compc };
+
+       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+               if (p->comp[compi].comp_cycles) {
+                       if (p->comp[compi].comp_cycles < p->tov_cycles && p->comp[compi].comp_cycles >= (avr->cycle - when)) {
+                               avr_timer_comp_on_tov(p, when, compi);
+                               avr_cycle_timer_register(avr,
+                                       p->comp[compi].comp_cycles - (avr->cycle - next),
+                                       dispatch[compi], p);
+                       } else if (p->tov_cycles == p->comp[compi].comp_cycles && !start)
+                               dispatch[compi](avr, when, param);
+               }
+       }
+
+       return next + p->tov_cycles;
+}
+
+static uint16_t
+_avr_timer_get_current_tcnt(
+               avr_timer_t * p)
+{
+       avr_t * avr = p->io.avr;
+       if (!(p->ext_clock_flags & (AVR_TIMER_EXTCLK_FLAG_TN | AVR_TIMER_EXTCLK_FLAG_AS2)) ||
+                       (p->ext_clock_flags & AVR_TIMER_EXTCLK_FLAG_VIRT)
+                       ) {
+               if (p->tov_cycles) {
+                       uint64_t when = avr->cycle - p->tov_base;
+
+                       return (when * (((uint32_t)p->tov_top)+1)) / p->tov_cycles;
+               }
+       }
+       else {
+               if (p->tov_top)
+                       return p->tov_base;
+       }
+       return 0;
+}
+
+static uint8_t
+avr_timer_tcnt_read(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+       // made to trigger potential watchpoints
+
+       uint16_t tcnt = _avr_timer_get_current_tcnt(p);
+
+       avr->data[p->r_tcnt] = tcnt;
+       if (p->r_tcnth)
+               avr->data[p->r_tcnth] = tcnt >> 8;
+
+       return avr_core_watch_read(avr, addr);
+}
+
+static inline void
+avr_timer_cancel_all_cycle_timers(
+               struct avr_t * avr,
+               avr_timer_t *timer,
+               const uint8_t clear_timers)
+{
+       if(clear_timers) {
+               for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++)
+                       timer->comp[compi].comp_cycles = 0;
+               timer->tov_cycles = 0;
+       }
+
+
+       avr_cycle_timer_cancel(avr, avr_timer_tov, timer);
+       avr_cycle_timer_cancel(avr, avr_timer_compa, timer);
+       avr_cycle_timer_cancel(avr, avr_timer_compb, timer);
+       avr_cycle_timer_cancel(avr, avr_timer_compc, timer);
+}
+
+static void
+avr_timer_tcnt_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+       avr_core_watch_write(avr, addr, v);
+       uint16_t tcnt = _timer_get_tcnt(p);
+
+       if (!p->tov_top)
+               return;
+
+       if (tcnt >= p->tov_top)
+               tcnt = 0;
+
+       if (!(p->ext_clock_flags & (AVR_TIMER_EXTCLK_FLAG_TN | AVR_TIMER_EXTCLK_FLAG_AS2)) ||
+                       (p->ext_clock_flags & AVR_TIMER_EXTCLK_FLAG_VIRT)
+                       ) {
+               // internal or virtual clock
+
+               // this involves some magicking
+               // cancel the current timers, recalculate the "base" we should be at, reset the
+               // timer base as it should, and re-schedule the timers using that base.
+
+               avr_timer_cancel_all_cycle_timers(avr, p, 0);
+
+               uint64_t cycles = (tcnt * p->tov_cycles) / p->tov_top;
+
+               //      printf("%s-%c %d/%d -- cycles %d/%d\n", __FUNCTION__, p->name, tcnt, p->tov_top, (uint32_t)cycles, (uint32_t)p->tov_cycles);
+
+               // this reset the timers bases to the new base
+               if (p->tov_cycles > 1) {
+                       avr_cycle_timer_register(avr, p->tov_cycles - cycles, avr_timer_tov, p);
+                       p->tov_base = 0;
+                       avr_timer_tov(avr, avr->cycle - cycles, p);
+               }
+
+               //      tcnt = ((avr->cycle - p->tov_base) * p->tov_top) / p->tov_cycles;
+               //      printf("%s-%c new tnt derive to %d\n", __FUNCTION__, p->name, tcnt);
+       }
+       else {
+               // clocked externally
+               p->tov_base = tcnt;
+       }
+}
+
+static void
+avr_timer_configure(
+               avr_timer_t * p,
+               uint32_t prescaler,
+               uint32_t top,
+               uint8_t reset)
+{
+       p->tov_top = top;
+
+       avr_t * avr = p->io.avr;
+       float resulting_clock = 0.0f; // used only for trace
+       float tov_cycles_exact = 0;
+
+       uint8_t as2 = p->ext_clock_flags & AVR_TIMER_EXTCLK_FLAG_AS2;
+       uint8_t use_ext_clock = as2 || (p->ext_clock_flags & AVR_TIMER_EXTCLK_FLAG_TN);
+       uint8_t virt_ext_clock = use_ext_clock && (p->ext_clock_flags & AVR_TIMER_EXTCLK_FLAG_VIRT);
+
+       if (!use_ext_clock) {
+               if (prescaler != 0)
+                       resulting_clock = (float)avr->frequency / prescaler;
+               p->tov_cycles = prescaler * (top+1);
+               p->tov_cycles_fract = 0.0f;
+               tov_cycles_exact = p->tov_cycles;
+       } else {
+               if (!virt_ext_clock) {
+                       p->tov_cycles = 0;
+                       p->tov_cycles_fract = 0.0f;
+               } else {
+                       if (prescaler != 0)
+                               resulting_clock = p->ext_clock / prescaler;
+                       tov_cycles_exact = (float)avr->frequency / p->ext_clock * prescaler * (top+1);
+                       // p->tov_cycles = round(tov_cycles_exact); -- don't want libm!
+                       p->tov_cycles = tov_cycles_exact + .5f; // Round to integer
+                       p->tov_cycles_fract = tov_cycles_exact - p->tov_cycles;
+               }
+       }
+
+       if (p->trace) {
+               if (!use_ext_clock || virt_ext_clock) {
+                       // clocked internally
+                       AVR_LOG(avr, LOG_TRACE, "TIMER: %s-%c TOP %.2fHz = %d cycles = %dusec\n", // TOP there means Timer Overflows Persec ?
+                                       __FUNCTION__, p->name, ((float)avr->frequency / tov_cycles_exact),
+                                       (int)p->tov_cycles, (int)avr_cycles_to_usec(avr, p->tov_cycles));
+               } else {
+                       // clocked externally from the Tn pin
+                       AVR_LOG(avr, LOG_TRACE, "TIMER: %s-%c use ext clock, TOP=%d\n",
+                                       __FUNCTION__, p->name, (int)p->tov_top
+                                       );
+               }
+       }
+
+       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+               if (!p->comp[compi].r_ocr)
+                       continue;
+               uint32_t ocr = _timer_get_ocr(p, compi);
+               //uint32_t comp_cycles = clock * (ocr + 1);
+               uint32_t comp_cycles;
+               if (virt_ext_clock)
+                       comp_cycles = (uint32_t)((float)avr->frequency / p->ext_clock * prescaler * (ocr+1));
+               else
+                       comp_cycles = prescaler * (ocr + 1);
+
+               p->comp[compi].comp_cycles = 0;
+
+               if (p->trace & (avr_timer_trace_compa << compi)) {
+                       if (!use_ext_clock || virt_ext_clock) {
+                               printf("%s-%c clock %f top %d OCR%c %d\n", __FUNCTION__, p->name,
+                                       resulting_clock, top, 'A'+compi, ocr);
+                       } else {
+                               AVR_LOG(avr, LOG_TRACE, "%s timer%c clock via ext pin, TOP=%d OCR%c=%d\n",
+                                               __FUNCTION__, p->name, top, 'A'+compi, ocr);
+                       }
+               }
+               if (ocr <= top) {
+                       p->comp[compi].comp_cycles = comp_cycles;
+
+                       if (p->trace & (avr_timer_trace_compa << compi)) printf(
+                                       "TIMER: %s-%c %c %.2fHz = %d cycles\n",
+                                       __FUNCTION__, p->name,
+                                       'A'+compi, resulting_clock / (ocr+1),
+                                       (int)comp_cycles);
+               }
+       }
+
+       if (!use_ext_clock || virt_ext_clock) {
+               if (p->tov_cycles > 1) {
+                       if (reset) {
+                               avr_cycle_timer_register(avr, p->tov_cycles, avr_timer_tov, p);
+                               // calling it once, with when == 0 tells it to arm the A/B/C timers if needed
+                               p->tov_base = 0;
+                               avr_timer_tov(avr, avr->cycle, p);
+                               p->phase_accumulator = 0.0f;
+                       } else {
+                               uint64_t orig_tov_base = p->tov_base;
+                               avr_cycle_timer_register(avr, p->tov_cycles - (avr->cycle - orig_tov_base), avr_timer_tov, p);
+                               // calling it once, with when == 0 tells it to arm the A/B/C timers if needed
+                               p->tov_base = 0;
+                               avr_timer_tov(avr, orig_tov_base, p);
+                       }
+               }
+       } else {
+               if (reset)
+                       p->tov_base = 0;
+       }
+
+       if (reset) {
+               avr_ioport_getirq_t req = {
+                       .bit = p->ext_clock_pin
+               };
+               if (avr_ioctl(p->io.avr, AVR_IOCTL_IOPORT_GETIRQ_REGBIT, &req) > 0) {
+                       // got an IRQ for the Tn input clock pin
+                       if (use_ext_clock && !virt_ext_clock) {
+                               if (p->trace)
+                                       AVR_LOG(p->io.avr, LOG_TRACE, "%s: timer%c connecting T%c pin IRQ %d\n", __FUNCTION__, p->name, p->name, req.irq[0]->irq);
+                               avr_irq_register_notify(req.irq[0], avr_timer_irq_ext_clock, p);
+                       } else {
+                               if (p->trace)
+                                       AVR_LOG(p->io.avr, LOG_TRACE, "%s: timer%c disconnecting T%c pin IRQ %d\n", __FUNCTION__, p->name, p->name, req.irq[0]->irq);
+                               avr_irq_unregister_notify(req.irq[0], avr_timer_irq_ext_clock, p);
+                       }
+               }
+       }
+
+}
+
+static void
+avr_timer_reconfigure(
+               avr_timer_t * p, uint8_t reset)
+{
+       avr_t * avr = p->io.avr;
+
+       // cancel everything
+       avr_timer_cancel_all_cycle_timers(avr, p, 1);
+
+       switch (p->wgm_op_mode_kind) {
+               case avr_timer_wgm_normal:
+                       avr_timer_configure(p, p->cs_div_value, p->wgm_op_mode_size, reset);
+                       break;
+               case avr_timer_wgm_fc_pwm:
+                       avr_timer_configure(p, p->cs_div_value, p->wgm_op_mode_size, reset);
+                       break;
+               case avr_timer_wgm_ctc: {
+                       avr_timer_configure(p, p->cs_div_value, _timer_get_ocr(p, AVR_TIMER_COMPA), reset);
+               }       break;
+               case avr_timer_wgm_pwm: {
+                       uint16_t top = (p->mode.top == avr_timer_wgm_reg_ocra) ?
+                               _timer_get_ocr(p, AVR_TIMER_COMPA) : _timer_get_icr(p);
+                       avr_timer_configure(p, p->cs_div_value, top, reset);
+               }       break;
+               case avr_timer_wgm_fast_pwm: {
+                       uint16_t top =
+                               (p->mode.top == avr_timer_wgm_reg_icr) ? _timer_get_icr(p) :
+                               p->wgm_op_mode_size;
+                       avr_timer_configure(p, p->cs_div_value, top, reset);
+               }       break;
+               case avr_timer_wgm_none:
+                       avr_timer_configure(p, p->cs_div_value, p->wgm_op_mode_size, reset);
+                       break;
+               default: {
+                       uint8_t mode = avr_regbit_get_array(avr, p->wgm, ARRAY_SIZE(p->wgm));
+                       AVR_LOG(avr, LOG_WARNING, "TIMER: %s-%c unsupported timer mode wgm=%d (%d)\n",
+                                       __FUNCTION__, p->name, mode, p->mode.kind);
+               }
+       }
+}
+
+static void
+avr_timer_write_ocr(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_timer_comp_p comp = (avr_timer_comp_p)param;
+       avr_timer_t *timer = comp->timer;
+       uint16_t oldv;
+
+       /* check to see if the OCR values actually changed */
+       oldv = _timer_get_comp_ocr(avr, comp);
+       avr_core_watch_write(avr, addr, v);
+
+       switch (timer->wgm_op_mode_kind) {
+               case avr_timer_wgm_normal:
+                       avr_timer_reconfigure(timer, 0);
+                       break;
+               case avr_timer_wgm_fc_pwm:      // OCR is not used here
+                       avr_timer_reconfigure(timer, 0);
+                       break;
+               case avr_timer_wgm_ctc:
+                       avr_timer_reconfigure(timer, 0);
+                       break;
+               case avr_timer_wgm_pwm:
+                       if (timer->mode.top != avr_timer_wgm_reg_ocra) {
+                               avr_raise_irq(timer->io.irq + TIMER_IRQ_OUT_PWM0, _timer_get_ocr(timer, AVR_TIMER_COMPA));
+                       } else {
+                               avr_timer_reconfigure(timer, 0); // if OCRA is the top, reconfigure needed
+                       }
+                       avr_raise_irq(timer->io.irq + TIMER_IRQ_OUT_PWM1, _timer_get_ocr(timer, AVR_TIMER_COMPB));
+                       if (sizeof(timer->comp)>2)
+                               avr_raise_irq(timer->io.irq + TIMER_IRQ_OUT_PWM2, _timer_get_ocr(timer, AVR_TIMER_COMPC));
+                       break;
+               case avr_timer_wgm_fast_pwm:
+                       if (oldv != _timer_get_comp_ocr(avr, comp))
+                               avr_timer_reconfigure(timer, 0);
+                       avr_raise_irq(timer->io.irq + TIMER_IRQ_OUT_PWM0,
+                                       _timer_get_ocr(timer, AVR_TIMER_COMPA));
+                       avr_raise_irq(timer->io.irq + TIMER_IRQ_OUT_PWM1,
+                                       _timer_get_ocr(timer, AVR_TIMER_COMPB));
+                       if (sizeof(timer->comp)>2)
+                               avr_raise_irq(timer->io.irq + TIMER_IRQ_OUT_PWM2,
+                                               _timer_get_ocr(timer, AVR_TIMER_COMPC));
+                       break;
+               default:
+                       AVR_LOG(avr, LOG_WARNING, "TIMER: %s-%c mode %d UNSUPPORTED\n",
+                                       __FUNCTION__, timer->name, timer->mode.kind);
+                       avr_timer_reconfigure(timer, 0);
+                       break;
+       }
+}
+
+static void
+avr_timer_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+
+       uint8_t as2 = avr_regbit_get(avr, p->as2);
+       uint8_t cs = avr_regbit_get_array(avr, p->cs, ARRAY_SIZE(p->cs));
+       uint8_t mode = avr_regbit_get_array(avr, p->wgm, ARRAY_SIZE(p->wgm));
+
+       avr_core_watch_write(avr, addr, v);
+
+       uint8_t new_as2 = avr_regbit_get(avr, p->as2);
+       uint8_t new_cs = avr_regbit_get_array(avr, p->cs, ARRAY_SIZE(p->cs));
+       uint8_t new_mode = avr_regbit_get_array(avr, p->wgm, ARRAY_SIZE(p->wgm));
+
+       // only reconfigure the timer if "relevant" bits have changed
+       // this prevent the timer reset when changing the edge detector
+       // or other minor bits
+       if (new_cs != cs || new_mode != mode || new_as2 != as2) {
+       /* cs */
+               if (new_cs == 0) {
+                       p->cs_div_value = 0;            // reset prescaler
+                       // cancel everything
+                       avr_timer_cancel_all_cycle_timers(avr, p, 1);
+
+                       AVR_LOG(avr, LOG_TRACE, "TIMER: %s-%c clock turned off\n",
+                                       __func__, p->name);
+                       return;
+               }
+
+               p->ext_clock_flags &= ~(AVR_TIMER_EXTCLK_FLAG_TN | AVR_TIMER_EXTCLK_FLAG_EDGE
+                                                               | AVR_TIMER_EXTCLK_FLAG_AS2 | AVR_TIMER_EXTCLK_FLAG_STARTED);
+               if (p->ext_clock_pin.reg
+                               && (p->cs_div[new_cs] == AVR_TIMER_EXTCLK_CHOOSE)) {
+                       // Special case: external clock source chosen, prescale divider irrelevant.
+                       p->cs_div_value = 1;
+                       p->ext_clock_flags |= AVR_TIMER_EXTCLK_FLAG_TN | (new_cs & AVR_TIMER_EXTCLK_FLAG_EDGE);
+               } else {
+                       p->cs_div_value = 1 << p->cs_div[new_cs];
+                       if (new_as2) {
+                               //p->cs_div_value = (uint32_t)((uint64_t)avr->frequency * (1 << p->cs_div[new_cs]) / 32768);
+                               p->ext_clock_flags |= AVR_TIMER_EXTCLK_FLAG_AS2 | AVR_TIMER_EXTCLK_FLAG_EDGE;
+                       }
+               }
+
+       /* mode */
+               p->mode = p->wgm_op[new_mode];
+               p->wgm_op_mode_kind = p->mode.kind;
+               p->wgm_op_mode_size = (1 << p->mode.size) - 1;
+
+               avr_timer_reconfigure(p, 1);
+       }
+}
+
+/*
+ * write to the "force output compare" bits
+ */
+static void avr_timer_write_foc(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+
+        /* These are strobe writes, so just decode them, don't store them */
+
+       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+               if ((addr == p->comp[compi].foc.reg) &&
+                               (v & (1 << p->comp[compi].foc.bit))) {
+                       avr_timer_comp(p, avr->cycle, compi, 0);
+               }
+       }
+}
+
+/*
+ * write to the TIFR register. Watch for code that writes "1" to clear
+ * pending interrupts.
+ */
+static void
+avr_timer_write_pending(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+
+       // All bits in this register are assumed to be write-1-to-clear.
+
+       if (addr == p->overflow.raised.reg &&
+           avr_regbit_from_value(avr, p->overflow.raised, v)) {
+               avr_clear_interrupt(avr, &p->overflow);
+       }
+       if (addr == p->icr.raised.reg &&
+           avr_regbit_from_value(avr, p->icr.raised, v)) {
+               avr_clear_interrupt(avr, &p->icr);
+       }
+
+       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+               if (addr == p->comp[compi].interrupt.raised.reg &&
+                   avr_regbit_from_value(avr, p->comp[compi].interrupt.raised,
+                                         v)) {
+                       avr_clear_interrupt(avr, &p->comp[compi].interrupt);
+               }
+       }
+}
+
+static void
+avr_timer_irq_icp(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+       avr_t * avr = p->io.avr;
+
+       // input capture disabled when ICR is used as top
+       if (p->mode.top == avr_timer_wgm_reg_icr)
+               return;
+       int bing = 0;
+       if (avr_regbit_get(avr, p->ices)) { // rising edge
+               if (!irq->value && value)
+                       bing++;
+       } else {        // default, falling edge
+               if (irq->value && !value)
+                       bing++;
+       }
+       if (!bing)
+               return;
+       // get current TCNT, copy it to ICR, and raise interrupt
+       uint16_t tcnt = _avr_timer_get_current_tcnt(p);
+       avr->data[p->r_icr] = tcnt;
+       if (p->r_icrh)
+               avr->data[p->r_icrh] = tcnt >> 8;
+       avr_raise_interrupt(avr, &p->icr);
+}
+
+static int
+avr_timer_ioctl(
+               avr_io_t * port,
+               uint32_t ctl,
+               void * io_param)
+{
+       avr_timer_t * p = (avr_timer_t *)port;
+       int res = -1;
+
+       if (ctl == AVR_IOCTL_TIMER_SET_TRACE(p->name)) {
+               /* Allow setting individual trace flags */
+               p->trace = *((uint32_t*)io_param);
+               res = 0;
+       } else if (ctl == AVR_IOCTL_TIMER_SET_FREQCLK(p->name)) {
+               float new_freq = *((float*)io_param);
+               if (new_freq >= 0.0f) {
+                       if (p->as2.reg) {
+                               if (new_freq <= port->avr->frequency/4) {
+                                       p->ext_clock = new_freq;
+                                       res = 0;
+                               }
+                       } else if (p->ext_clock_pin.reg) {
+                               if (new_freq <= port->avr->frequency/2) {
+                                       p->ext_clock = new_freq;
+                                       res = 0;
+                               }
+                       }
+               }
+       } else if (ctl == AVR_IOCTL_TIMER_SET_VIRTCLK(p->name)) {
+               uint8_t new_val = *((uint8_t*)io_param);
+               if (!new_val) {
+                       avr_ioport_getirq_t req_timer_clock_pin = {
+                               .bit = p->ext_clock_pin
+                       };
+                       if (avr_ioctl(p->io.avr, AVR_IOCTL_IOPORT_GETIRQ_REGBIT, &req_timer_clock_pin) > 0) {
+                               p->ext_clock_flags &= ~AVR_TIMER_EXTCLK_FLAG_VIRT;
+                               res = 0;
+                       }
+               } else {
+                       p->ext_clock_flags |= AVR_TIMER_EXTCLK_FLAG_VIRT;
+                       res = 0;
+               }
+       }
+       if (res >= 0)
+               avr_timer_reconfigure(p, 0); // virtual clock: attempt to follow frequency change preserving the phase
+       return res;
+}
+
+static void
+avr_timer_reset(
+               avr_io_t * port)
+{
+       avr_timer_t * p = (avr_timer_t *)port;
+       avr_timer_cancel_all_cycle_timers(p->io.avr, p, 0);
+
+       // check to see if the comparators have a pin output. If they do,
+       // (try) to get the ioport corresponding IRQ and connect them
+       // they will automagically be triggered when the comparator raises
+       // it's own IRQ
+       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+               p->comp[compi].comp_cycles = 0;
+
+               avr_ioport_getirq_t req = {
+                       .bit = p->comp[compi].com_pin
+               };
+               if (avr_ioctl(port->avr, AVR_IOCTL_IOPORT_GETIRQ_REGBIT, &req) > 0) {
+                       // cool, got an IRQ
+                       //printf("%s-%c COMP%c Connecting PIN IRQ %d\n",
+                       //      __func__, p->name, 'A'+compi, req.irq[0]->irq);
+                       avr_connect_irq(&port->irq[TIMER_IRQ_OUT_COMP + compi], req.irq[0]);
+               }
+       }
+
+       avr_irq_register_notify(port->irq + TIMER_IRQ_IN_ICP, avr_timer_irq_icp, p);
+
+       avr_ioport_getirq_t req = {
+               .bit = p->icp
+       };
+       if (avr_ioctl(port->avr, AVR_IOCTL_IOPORT_GETIRQ_REGBIT, &req) > 0) {
+               // cool, got an IRQ for the input capture pin
+               //printf("%s-%c ICP Connecting PIN IRQ %d\n", __func__, p->name, req.irq[0]->irq);
+               avr_connect_irq(req.irq[0], port->irq + TIMER_IRQ_IN_ICP);
+       }
+       p->ext_clock_flags &= ~(AVR_TIMER_EXTCLK_FLAG_STARTED | AVR_TIMER_EXTCLK_FLAG_TN |
+                                                       AVR_TIMER_EXTCLK_FLAG_AS2 | AVR_TIMER_EXTCLK_FLAG_REVDIR);
+
+}
+
+static const char * irq_names[TIMER_IRQ_COUNT] = {
+       [TIMER_IRQ_OUT_PWM0] = "8>pwm0",
+       [TIMER_IRQ_OUT_PWM1] = "8>pwm1",
+       [TIMER_IRQ_OUT_PWM2] = "8>pwm2",
+       [TIMER_IRQ_IN_ICP] = "<icp",
+       [TIMER_IRQ_OUT_COMP + 0] = ">compa",
+       [TIMER_IRQ_OUT_COMP + 1] = ">compb",
+       [TIMER_IRQ_OUT_COMP + 2] = ">compc",
+};
+
+static avr_io_t        _io = {
+       .kind = "timer",
+       .irq_names = irq_names,
+       .reset = avr_timer_reset,
+       .ioctl = avr_timer_ioctl,
+};
+
+void
+avr_timer_init(
+               avr_t * avr,
+               avr_timer_t * p)
+{
+       p->io = _io;
+
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->overflow);
+       avr_register_vector(avr, &p->icr);
+
+       // allocate this module's IRQ
+       avr_io_setirqs(&p->io, AVR_IOCTL_TIMER_GETIRQ(p->name), TIMER_IRQ_COUNT, NULL);
+
+       // marking IRQs as "filtered" means they don't propagate if the
+       // new value raised is the same as the last one.. in the case of the
+       // pwm value it makes sense not to bother.
+       p->io.irq[TIMER_IRQ_OUT_PWM0].flags |= IRQ_FLAG_FILTERED;
+       p->io.irq[TIMER_IRQ_OUT_PWM1].flags |= IRQ_FLAG_FILTERED;
+       p->io.irq[TIMER_IRQ_OUT_PWM2].flags |= IRQ_FLAG_FILTERED;
+
+       if (p->wgm[0].reg) // these are not present on older AVRs
+               avr_register_io_write(avr, p->wgm[0].reg, avr_timer_write, p);
+       if (p->wgm[1].reg &&
+                       (p->wgm[1].reg != p->wgm[0].reg))
+               avr_register_io_write(avr, p->wgm[1].reg, avr_timer_write, p);
+       if (p->wgm[2].reg &&
+                       (p->wgm[2].reg != p->wgm[0].reg) &&
+                       (p->wgm[2].reg != p->wgm[1].reg))
+               avr_register_io_write(avr, p->wgm[2].reg, avr_timer_write, p);
+       if (p->wgm[3].reg &&
+                       (p->wgm[3].reg != p->wgm[0].reg) &&
+                       (p->wgm[3].reg != p->wgm[1].reg) &&
+                       (p->wgm[3].reg != p->wgm[2].reg))
+               avr_register_io_write(avr, p->wgm[3].reg, avr_timer_write, p);
+
+       avr_register_io_write(avr, p->cs[0].reg, avr_timer_write, p);
+       if (p->cs[1].reg &&
+                       (p->cs[1].reg != p->cs[0].reg))
+               avr_register_io_write(avr, p->cs[1].reg, avr_timer_write, p);
+       if (p->cs[2].reg &&
+                       (p->cs[2].reg != p->cs[0].reg) && (p->cs[2].reg != p->cs[1].reg))
+               avr_register_io_write(avr, p->cs[2].reg, avr_timer_write, p);
+       if (p->cs[3].reg &&
+                       (p->cs[3].reg != p->cs[0].reg) &&
+                       (p->cs[3].reg != p->cs[1].reg) &&
+                       (p->cs[3].reg != p->cs[2].reg))
+               avr_register_io_write(avr, p->cs[3].reg, avr_timer_write, p);
+
+       if (p->as2.reg) // as2 signifies timer/counter 2... therefore must check for register.
+               avr_register_io_write(avr, p->as2.reg, avr_timer_write, p);
+
+       // this assumes all the "pending" interrupt bits are in the same
+       // register. Might not be true on all devices ?
+       avr_register_io_write(avr, p->overflow.raised.reg, avr_timer_write_pending, p);
+
+       /*
+        * Even if the timer is 16 bits, we don't care to have watches on the
+        * high bytes because the datasheet says that the low address is always
+        * the trigger.
+        */
+       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+               p->comp[compi].timer = p;
+
+               avr_register_vector(avr, &p->comp[compi].interrupt);
+
+               if (p->comp[compi].r_ocr) // not all timers have all comparators
+                       avr_register_io_write(avr, p->comp[compi].r_ocr, avr_timer_write_ocr, &p->comp[compi]);
+               if (p->comp[compi].foc.reg)
+                       avr_register_io_write(avr, p->comp[compi].foc.reg, avr_timer_write_foc, p);
+       }
+       avr_register_io_write(avr, p->r_tcnt, avr_timer_tcnt_write, p);
+       avr_register_io_read(avr, p->r_tcnt, avr_timer_tcnt_read, p);
+
+       if (p->as2.reg) {
+               p->ext_clock_flags = AVR_TIMER_EXTCLK_FLAG_VIRT;
+               p->ext_clock = 32768.0f;
+       } else {
+               p->ext_clock_flags = 0;
+               p->ext_clock = 0.0f;
+       }
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.h
new file mode 100644 (file)
index 0000000..837936c
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+       avr_timer.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_TIMER_H__
+#define __AVR_TIMER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+enum {
+       AVR_TIMER_COMPA = 0,
+       AVR_TIMER_COMPB,
+       AVR_TIMER_COMPC,
+
+       AVR_TIMER_COMP_COUNT
+};
+
+enum {
+       TIMER_IRQ_OUT_PWM0 = 0,
+       TIMER_IRQ_OUT_PWM1,
+       TIMER_IRQ_OUT_PWM2,
+       TIMER_IRQ_IN_ICP,       // input capture
+       TIMER_IRQ_OUT_COMP,     // comparator pins output IRQ
+
+       TIMER_IRQ_COUNT = TIMER_IRQ_OUT_COMP + AVR_TIMER_COMP_COUNT
+};
+
+// Get the internal IRQ corresponding to the INT
+#define AVR_IOCTL_TIMER_GETIRQ(_name) AVR_IOCTL_DEF('t','m','r',(_name))
+
+// add timer number/name (character) to set tracing flags
+#define AVR_IOCTL_TIMER_SET_TRACE(_number) AVR_IOCTL_DEF('t','m','t',(_number))
+// enforce using virtual clock generator when external clock is chosen by firmware
+#define AVR_IOCTL_TIMER_SET_VIRTCLK(_number) AVR_IOCTL_DEF('t','m','v',(_number))
+// set frequency of the virtual clock generator
+#define AVR_IOCTL_TIMER_SET_FREQCLK(_number) AVR_IOCTL_DEF('t','m','f',(_number))
+
+// Waveform generation modes
+enum {
+       avr_timer_wgm_none = 0, // invalid mode
+       avr_timer_wgm_normal,
+       avr_timer_wgm_ctc,
+       avr_timer_wgm_pwm,
+       avr_timer_wgm_fast_pwm,
+       avr_timer_wgm_fc_pwm,
+};
+
+// Compare output modes
+enum {
+       avr_timer_com_normal = 0,// Normal mode, OCnx disconnected
+       avr_timer_com_toggle,   // Toggle OCnx on compare match
+       avr_timer_com_clear,    // clear OCnx on compare match
+       avr_timer_com_set,      // set OCnx on compare match
+
+};
+
+enum {
+       avr_timer_wgm_reg_constant = 0,
+       avr_timer_wgm_reg_ocra,
+       avr_timer_wgm_reg_icr,
+};
+
+typedef struct avr_timer_wgm_t {
+       uint32_t top: 8, bottom: 8, size : 8, kind : 8;
+} avr_timer_wgm_t;
+
+#define AVR_TIMER_EXTCLK_CHOOSE 0x80           // marker value for cs_div specifying ext clock selection
+#define AVR_TIMER_EXTCLK_FLAG_TN 0x80          // Tn external clock chosen
+#define AVR_TIMER_EXTCLK_FLAG_STARTED 0x40     // peripheral started
+#define AVR_TIMER_EXTCLK_FLAG_REVDIR 0x20      // reverse counting (decrement)
+#define AVR_TIMER_EXTCLK_FLAG_AS2 0x10         // asynchronous external clock chosen
+#define AVR_TIMER_EXTCLK_FLAG_VIRT 0x08                // don't use the input pin, generate clock internally
+#define AVR_TIMER_EXTCLK_FLAG_EDGE 0x01                // use the rising edge
+
+#define AVR_TIMER_WGM_NORMAL8() { .kind = avr_timer_wgm_normal, .size=8 }
+#define AVR_TIMER_WGM_NORMAL16() { .kind = avr_timer_wgm_normal, .size=16 }
+#define AVR_TIMER_WGM_CTC() { .kind = avr_timer_wgm_ctc, .top = avr_timer_wgm_reg_ocra }
+#define AVR_TIMER_WGM_ICCTC() { .kind = avr_timer_wgm_ctc, .top = avr_timer_wgm_reg_icr }
+#define AVR_TIMER_WGM_FASTPWM8() { .kind = avr_timer_wgm_fast_pwm, .size=8 }
+#define AVR_TIMER_WGM_FASTPWM9() { .kind = avr_timer_wgm_fast_pwm, .size=9 }
+#define AVR_TIMER_WGM_FASTPWM10() { .kind = avr_timer_wgm_fast_pwm, .size=10 }
+#define AVR_TIMER_WGM_FCPWM8() { .kind = avr_timer_wgm_fc_pwm, .size=8 }
+#define AVR_TIMER_WGM_FCPWM9() { .kind = avr_timer_wgm_fc_pwm, .size=9 }
+#define AVR_TIMER_WGM_FCPWM10() { .kind = avr_timer_wgm_fc_pwm, .size=10 }
+#define AVR_TIMER_WGM_OCPWM() { .kind = avr_timer_wgm_pwm, .top = avr_timer_wgm_reg_ocra }
+#define AVR_TIMER_WGM_ICPWM() { .kind = avr_timer_wgm_pwm, .top = avr_timer_wgm_reg_icr }
+#define AVR_TIMER_WGM_ICFASTPWM() { .kind = avr_timer_wgm_fast_pwm, .top = avr_timer_wgm_reg_icr }
+
+typedef struct avr_timer_comp_t {
+               avr_int_vector_t        interrupt;              // interrupt vector
+               struct avr_timer_t      *timer;                 // parent timer
+               avr_io_addr_t           r_ocr;                  // comparator register low byte
+               avr_io_addr_t           r_ocrh;                 // comparator register hi byte
+               avr_regbit_t            com;                    // comparator output mode registers
+               avr_regbit_t            com_pin;                // where comparator output is connected
+               uint64_t                        comp_cycles;
+                avr_regbit_t            foc;                    // "force compare match" strobe
+} avr_timer_comp_t, *avr_timer_comp_p;
+
+enum {
+       avr_timer_trace_ocr             = (1 << 0),
+       avr_timer_trace_tcnt    = (1 << 1),
+
+       avr_timer_trace_compa   = (1 << 8),
+       avr_timer_trace_compb   = (1 << 9),
+       avr_timer_trace_compc   = (1 << 10),
+};
+
+typedef struct avr_timer_t {
+       avr_io_t                io;
+       char                    name;
+       uint32_t                trace;          // debug trace
+
+       avr_regbit_t    disabled;       // bit in the PRR
+
+       avr_io_addr_t   r_tcnt, r_icr;
+       avr_io_addr_t   r_tcnth, r_icrh;
+
+       avr_regbit_t    wgm[4];
+       avr_timer_wgm_t wgm_op[16];
+       avr_timer_wgm_t mode;
+       int                             wgm_op_mode_kind;
+       uint32_t                wgm_op_mode_size;
+
+       avr_regbit_t    as2;            // asynchronous clock 32khz
+       avr_regbit_t    cs[4];          // specify control register bits choosing clock sourcre
+       uint8_t                 cs_div[16];     // translate control register value to clock prescaler (orders of 2 exponent)
+       uint32_t                cs_div_value;
+
+       avr_regbit_t    ext_clock_pin;  // external clock input pin, to link IRQs
+       uint8_t                 ext_clock_flags;        // holds AVR_TIMER_EXTCLK_FLAG_ON, AVR_TIMER_EXTCLK_FLAG_EDGE and other ext. clock mode flags
+       float                   ext_clock;      // external clock frequency, e.g. 32768Hz
+
+       avr_regbit_t    icp;            // input capture pin, to link IRQs
+       avr_regbit_t    ices;           // input capture edge select
+
+       avr_timer_comp_t comp[AVR_TIMER_COMP_COUNT];
+
+       avr_int_vector_t overflow;      // overflow
+       avr_int_vector_t icr;   // input capture
+
+       uint64_t                tov_cycles;     // number of cycles from zero to overflow
+       float                   tov_cycles_fract; // fractional part for external clock with non int ratio to F_CPU
+       float                   phase_accumulator;
+       uint64_t                tov_base;       // MCU cycle when the last overflow occured; when clocked externally holds external clock count
+       uint16_t                tov_top;        // current top value to calculate tnct
+} avr_timer_t;
+
+void avr_timer_init(avr_t * avr, avr_timer_t * port);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__AVR_TIMER_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.c
new file mode 100644 (file)
index 0000000..521c03c
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+       avr_twi.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include "avr_twi.h"
+
+/*
+ * This block respectfully nicked straight out from the Atmel sample
+ * code for AVR315. Typos and all.
+ * There is no copyright notice on the original file.
+ */
+/****************************************************************************
+  TWI State codes
+****************************************************************************/
+// General TWI Master status codes
+#define TWI_START                  0x08  // START has been transmitted
+#define TWI_REP_START              0x10  // Repeated START has been transmitted
+#define TWI_ARB_LOST               0x38  // Arbitration lost
+
+// TWI Master Transmitter status codes
+#define TWI_MTX_ADR_ACK            0x18  // SLA+W has been transmitted and ACK received
+#define TWI_MTX_ADR_NACK           0x20  // SLA+W has been transmitted and NACK received
+#define TWI_MTX_DATA_ACK           0x28  // Data byte has been transmitted and ACK received
+#define TWI_MTX_DATA_NACK          0x30  // Data byte has been transmitted and NACK received
+
+// TWI Master Receiver status codes
+#define TWI_MRX_ADR_ACK            0x40  // SLA+R has been transmitted and ACK received
+#define TWI_MRX_ADR_NACK           0x48  // SLA+R has been transmitted and NACK received
+#define TWI_MRX_DATA_ACK           0x50  // Data byte has been received and ACK transmitted
+#define TWI_MRX_DATA_NACK          0x58  // Data byte has been received and NACK transmitted
+
+// TWI Slave Transmitter status codes
+#define TWI_STX_ADR_ACK            0xA8  // Own SLA+R has been received; ACK has been returned
+#define TWI_STX_ADR_ACK_M_ARB_LOST 0xB0  // Arbitration lost in SLA+R/W as Master; own SLA+R has been received; ACK has been returned
+#define TWI_STX_DATA_ACK           0xB8  // Data byte in TWDR has been transmitted; ACK has been received
+#define TWI_STX_DATA_NACK          0xC0  // Data byte in TWDR has been transmitted; NOT ACK has been received
+#define TWI_STX_DATA_ACK_LAST_BYTE 0xC8  // Last data byte in TWDR has been transmitted (TWEA = ï¿½0�); ACK has been received
+
+// TWI Slave Receiver status codes
+#define TWI_SRX_ADR_ACK            0x60  // Own SLA+W has been received ACK has been returned
+#define TWI_SRX_ADR_ACK_M_ARB_LOST 0x68  // Arbitration lost in SLA+R/W as Master; own SLA+W has been received; ACK has been returned
+#define TWI_SRX_GEN_ACK            0x70  // General call address has been received; ACK has been returned
+#define TWI_SRX_GEN_ACK_M_ARB_LOST 0x78  // Arbitration lost in SLA+R/W as Master; General call address has been received; ACK has been returned
+#define TWI_SRX_ADR_DATA_ACK       0x80  // Previously addressed with own SLA+W; data has been received; ACK has been returned
+#define TWI_SRX_ADR_DATA_NACK      0x88  // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned
+#define TWI_SRX_GEN_DATA_ACK       0x90  // Previously addressed with general call; data has been received; ACK has been returned
+#define TWI_SRX_GEN_DATA_NACK      0x98  // Previously addressed with general call; data has been received; NOT ACK has been returned
+#define TWI_SRX_STOP_RESTART       0xA0  // A STOP condition or repeated START condition has been received while still addressed as Slave
+
+// TWI Miscellaneous status codes
+#define TWI_NO_STATE               0xF8  // No relevant state information available; TWINT = ï¿½0�
+#define TWI_BUS_ERROR              0x00  // Bus error due to an illegal START or STOP condition
+
+#define AVR_TWI_DEBUG 1
+
+static inline void
+_avr_twi_status_set(
+               avr_twi_t * p,
+               uint8_t v,
+               int interrupt)
+{
+       avr_regbit_setto_raw(p->io.avr, p->twsr, v);
+#if AVR_TWI_DEBUG
+       AVR_TRACE(p->io.avr, "%s %02x\n", __func__, v);
+#endif
+       avr_raise_irq(p->io.irq + TWI_IRQ_STATUS, v);
+       if (interrupt)
+               avr_raise_interrupt(p->io.avr, &p->twi);
+}
+
+static __attribute__ ((unused)) inline uint8_t
+_avr_twi_status_get(
+               avr_twi_t * p)
+{
+       return avr_regbit_get_raw(p->io.avr, p->twsr);
+}
+
+static avr_cycle_count_t
+avr_twi_set_state_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_twi_t * p = (avr_twi_t *)param;
+       _avr_twi_status_set(p, p->next_twstate, 1);
+       p->next_twstate = 0;
+       return 0;
+}
+
+// Quick exponent helper for integer values > 0.
+static uint32_t _avr_twi_quick_exp(uint8_t base, uint8_t exp)
+{
+       uint32_t result = 1;
+       for (uint8_t i=exp; i>0; i--)
+               result *= base;
+       return result;
+}
+
+/*
+ * This is supposed to trigger a timer whose duration is a multiple
+ * of 'twi' clock cycles, which should be derived from the prescaler
+ * (100khz, 400khz etc).
+ * Right now it cheats and uses one twi cycle == one usec.
+ */
+
+static void
+_avr_twi_delay_state(
+               avr_twi_t * p,
+               int twi_cycles,
+               uint8_t state)
+{
+       p->next_twstate = state;
+       uint8_t prescale = avr_regbit_get(p->io.avr, p->twps);
+       uint16_t bitrate =  p->io.avr->data[p->r_twbr];
+       uint32_t clockdiv = 16u+((bitrate<<1u)*_avr_twi_quick_exp(4,prescale));
+       //One TWI cycle is "clockdiv" AVR Cycles. So we can wait in these directly.
+       // printf("Waiting %d cycles\n",clockdiv*twi_cycles);
+       avr_cycle_timer_register(
+                       p->io.avr, twi_cycles*clockdiv, avr_twi_set_state_timer, p);
+}
+
+static void
+avr_twi_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_twi_t * p = (avr_twi_t *)param;
+
+       uint8_t twen = avr_regbit_get(avr, p->twen);
+       uint8_t twsta = avr_regbit_get(avr, p->twsta);
+       uint8_t twsto = avr_regbit_get(avr, p->twsto);
+       uint8_t twint = avr_regbit_get(avr, p->twi.raised);
+
+       avr_core_watch_write(avr, addr, v);
+#if AVR_TWI_DEBUG
+       AVR_TRACE(avr, "%s %02x START:%d STOP:%d ACK:%d INT:%d TWSR:%02x (state %02x)\n",
+                       __func__, v,
+                       avr_regbit_get(avr, p->twsta),
+                       avr_regbit_get(avr, p->twsto),
+                       avr_regbit_get(avr, p->twea),
+                       avr_regbit_get(avr, p->twi.raised),
+                       avr_regbit_get_raw(p->io.avr, p->twsr), p->state);
+#endif
+       if (twen != avr_regbit_get(avr, p->twen)) {
+               twen = !twen;
+               if (!twen) { // if we were running, now now are not
+                       avr_regbit_clear(avr, p->twea);
+                       avr_regbit_clear(avr, p->twsta);
+                       avr_regbit_clear(avr, p->twsto);
+                       avr_clear_interrupt(avr, &p->twi);
+                       avr_core_watch_write(avr, p->r_twdr, 0xff);
+                       _avr_twi_status_set(p, TWI_NO_STATE, 0);
+                       p->state = 0;
+                       p->peer_addr = 0;
+               }
+               AVR_TRACE(avr, "TWEN: %d\n", twen);
+               if (avr->data[p->r_twar]) {
+                       AVR_TRACE(avr, "TWEN Slave: %02x&%02x\n", avr->data[p->r_twar] >> 1, avr->data[p->r_twamr] >> 1);
+                       p->state |= TWI_COND_SLAVE;
+               }
+       }
+       if (!twen)
+               return;
+
+       uint8_t cleared = avr_regbit_get(avr, p->twi.raised);
+
+       /*int cleared = */
+       avr_clear_interrupt_if(avr, &p->twi, twint);
+//     AVR_TRACE(avr, "cleared %d\n", cleared);
+
+       if (!twsto && avr_regbit_get(avr, p->twsto)) {
+               // generate a stop condition
+#if AVR_TWI_DEBUG
+               AVR_TRACE(avr, "<<<<< I2C stop\n");
+#endif
+               if (p->state) { // doing stuff
+                       if (p->state & TWI_COND_START) {
+                               avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT,
+                                               avr_twi_irq_msg(TWI_COND_STOP, p->peer_addr, 1));
+                       }
+               }
+               /* clear stop condition regardless of status */
+               avr_regbit_clear(avr, p->twsto);
+               _avr_twi_status_set(p, TWI_NO_STATE, 0);
+               p->state = 0;
+       }
+       if (!twsta && avr_regbit_get(avr, p->twsta)) {
+#if AVR_TWI_DEBUG
+               AVR_TRACE(avr, ">>>>> I2C %sstart\n", p->state & TWI_COND_START ? "RE" : "");
+#endif
+               // generate a start condition
+               if (p->state & TWI_COND_START)
+                       _avr_twi_delay_state(p, 0, TWI_REP_START);
+               else
+                       _avr_twi_delay_state(p, 0, TWI_START);
+               p->peer_addr = 0;
+               p->state = TWI_COND_START;
+       }
+
+       int data = cleared &&
+                       !avr_regbit_get(avr, p->twsta) &&
+                       !avr_regbit_get(avr, p->twsto);
+
+       if (!data)
+               return;
+
+       int do_read = p->peer_addr & 1;
+       int do_ack = avr_regbit_get(avr, p->twea) != 0;
+
+       if (p->state & TWI_COND_SLAVE) {
+               // writing or reading a byte
+               if (p->state & TWI_COND_ADDR) {
+#if AVR_TWI_DEBUG
+                       if (do_read)
+                               AVR_TRACE(avr, "I2C slave READ byte\n");
+                       else
+                               AVR_TRACE(avr, "I2C slave WRITE byte\n");
+#endif
+                       if (do_read) {
+                               if (p->state & TWI_COND_WRITE)  {
+                                       avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT,
+                                               avr_twi_irq_msg(TWI_COND_READ | TWI_COND_ACK, p->peer_addr, avr->data[p->r_twdr]));
+                               }
+#if AVR_TWI_DEBUG
+                               else
+                                       AVR_TRACE(avr, "I2C latch is not ready, do nothing\n");
+#endif
+                       } else {
+                               avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT,
+                                       avr_twi_irq_msg(TWI_COND_ACK, p->peer_addr, 0));
+                       }
+                       avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT,
+                                       avr_twi_irq_msg(TWI_COND_ADDR + (do_ack ? TWI_COND_ACK : 0), p->peer_addr, avr->data[p->r_twdr]));
+               } else {        // address, acknowledge it
+                       p->state |= TWI_COND_ADDR;
+                       avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT,
+                                       avr_twi_irq_msg(
+                                               TWI_COND_ADDR |
+                                                       (do_ack ? TWI_COND_ACK : 0) |
+                                                       (p->state & TWI_COND_WRITE ? TWI_COND_READ : 0),
+                                               p->peer_addr, avr->data[p->r_twdr]));
+               }
+       } else {
+
+               // writing or reading a byte
+               if (p->state & TWI_COND_ADDR) {
+#if AVR_TWI_DEBUG
+                       if (do_read)
+                               AVR_TRACE(avr, "I2C READ byte from %02x\n", p->peer_addr);
+                       else
+                               AVR_TRACE(avr, "I2C WRITE byte %02x to %02x\n", avr->data[p->r_twdr], p->peer_addr);
+#endif
+                       // a normal data byte
+                       uint8_t msgv = do_read ? TWI_COND_READ : TWI_COND_WRITE;
+
+                       if (do_ack)
+                               msgv |= TWI_COND_ACK;
+
+                       p->state &= ~TWI_COND_ACK;      // clear ACK bit
+
+                       AVR_TRACE(avr, "state %02x want %02x\n", p->state, msgv);
+                       // if the latch is ready... as set by writing/reading the TWDR
+                       if (p->state & msgv) {
+
+                               // we send an IRQ and we /expect/ a slave to reply
+                               // immediately via an IRQ to set the COND_ACK bit
+                               // otherwise it's assumed it's been nacked...
+                               avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT,
+                                               avr_twi_irq_msg(msgv, p->peer_addr, avr->data[p->r_twdr]));
+
+                               if (do_read) { // read ?
+                                       _avr_twi_delay_state(p, 9,
+                                                       msgv & TWI_COND_ACK ?
+                                                                       TWI_MRX_DATA_ACK : TWI_MRX_DATA_NACK);
+                               } else {
+                                       _avr_twi_delay_state(p, 9,
+                                                       p->state & TWI_COND_ACK ?
+                                                                       TWI_MTX_DATA_ACK : TWI_MTX_DATA_NACK);
+                               }
+                       }
+#if AVR_TWI_DEBUG
+                       else
+                               AVR_TRACE(avr, "I2C latch is not ready, do nothing\n");
+#endif
+               } else if (p->state) {
+#if AVR_TWI_DEBUG
+                       AVR_TRACE(avr, "I2C Master address %02x\n", avr->data[p->r_twdr]);
+#endif
+                       // send the address
+                       p->state |= TWI_COND_ADDR;
+                       p->peer_addr = avr->data[p->r_twdr];
+                       p->state &= ~TWI_COND_ACK;      // clear ACK bit
+
+                       // we send an IRQ and we /expect/ a slave to reply
+                       // immediately via an IRQ tp set the COND_ACK bit
+                       // otherwise it's assumed it's been nacked...
+                       avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT,
+                                       avr_twi_irq_msg(TWI_COND_START, p->peer_addr, 0));
+
+                       if (p->peer_addr & 1) { // read ?
+                               p->state |= TWI_COND_READ;      // always allow read to start with
+                               _avr_twi_delay_state(p, 9,
+                                               p->state & TWI_COND_ACK ?
+                                                               TWI_MRX_ADR_ACK : TWI_MRX_ADR_NACK);
+                       } else {
+                               if(p->state & TWI_COND_ADDR){
+                                       _avr_twi_delay_state(p, 0,
+                                                       p->state & TWI_COND_ACK ?
+                                                                       TWI_MTX_ADR_ACK : TWI_MTX_ADR_NACK);
+                               }else{
+                                       _avr_twi_delay_state(p, 9,
+                                                       p->state & TWI_COND_ACK ?
+                                                                       TWI_MTX_DATA_ACK : TWI_MTX_DATA_NACK);
+                               }
+                       }
+               }
+               p->state &= ~TWI_COND_WRITE;
+       }
+}
+
+/*
+ * Write data to the latch, tell the system we have something
+ * to send next
+ */
+static void
+avr_twi_write_data(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_twi_t * p = (avr_twi_t *)param;
+
+       avr_core_watch_write(avr, addr, v);
+       // tell system we have something in the write latch
+       p->state |= TWI_COND_WRITE;
+}
+
+/*
+ * Read data from the latch, tell the system can receive a new byte
+ */
+static uint8_t
+avr_twi_read_data(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               void * param)
+{
+       avr_twi_t * p = (avr_twi_t *)param;
+
+       // tell system we can receive another byte
+       p->state |= TWI_COND_READ;
+       return avr->data[p->r_twdr];
+}
+
+/*
+ * prevent code from rewriting out status bits, since we actually use them!
+ */
+static void
+avr_twi_write_status(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_twi_t * p = (avr_twi_t *)param;
+       uint8_t sr = avr_regbit_get(avr, p->twsr);
+       uint8_t c = avr_regbit_get(avr, p->twps);
+
+       avr_core_watch_write(avr, addr, v);
+       avr_regbit_setto(avr, p->twsr, sr);     // force restore
+
+       if (c != avr_regbit_get(avr, p->twps)) {
+               // prescaler bits changed...
+       }
+}
+
+static void
+avr_twi_irq_input(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       avr_twi_t * p = (avr_twi_t *)param;
+       avr_t * avr = p->io.avr;
+
+       // check to see if we are enabled
+       if (!avr_regbit_get(avr, p->twen))
+               return;
+       avr_twi_msg_irq_t msg;
+       msg.u.v = value;
+
+       AVR_TRACE(avr, "%s %08x\n", __func__, value);
+
+       // receiving an attempt at waking a slave
+       if (msg.u.twi.msg & TWI_COND_START) {
+               p->state = 0;
+               p->peer_addr = 0;
+               if (msg.u.twi.msg & TWI_COND_ADDR) {
+                       uint8_t mask = ~avr->data[p->r_twamr] >> 1;
+                       AVR_TRACE(avr, "I2C slave start %2x (want %02x&%02x)\n",
+                               msg.u.twi.addr, avr->data[p->r_twar] >> 1, mask);
+                       p->peer_addr = msg.u.twi.addr & mask;
+                       if (p->peer_addr == ((avr->data[p->r_twar] >> 1) & mask)) {
+                               // address match, we're talking
+                               p->state = TWI_COND_SLAVE;
+                               // INVERSE logic here
+                               if (!(msg.u.twi.msg & TWI_COND_WRITE))
+                                       p->peer_addr |= 1;
+                               _avr_twi_delay_state(p, 9,
+                                       msg.u.twi.msg & TWI_COND_WRITE ?
+                                               TWI_SRX_ADR_ACK : TWI_STX_ADR_ACK );
+                       }
+               } else {
+                       // "general call" address
+                       AVR_TRACE(avr, "I2C slave start without address?\n");
+                       if (avr->data[p->r_twar] & 1) {
+                               // TODO
+                       }
+               }
+       }
+       if (msg.u.twi.msg & TWI_COND_STOP) {
+               _avr_twi_delay_state(p, 9,
+                       msg.u.twi.msg & TWI_COND_WRITE ?
+                               TWI_SRX_ADR_ACK : TWI_STX_ADR_ACK );
+       }
+       // receiving an acknowledge bit
+       if (msg.u.twi.msg & TWI_COND_ACK) {
+#if AVR_TWI_DEBUG
+               AVR_TRACE(avr, "I2C received ACK:%d\n", msg.u.twi.data & 1);
+#endif
+               if (msg.u.twi.data & 1)
+                       p->state |= TWI_COND_ACK;
+               else
+                       p->state &= ~TWI_COND_ACK;
+       }
+       if (p->state & TWI_COND_SLAVE) {
+               if (msg.u.twi.msg & TWI_COND_WRITE) {
+                       avr->data[p->r_twdr] = msg.u.twi.data;
+                       _avr_twi_delay_state(p, 9, TWI_SRX_ADR_DATA_ACK );
+               }
+       } else {
+               // receive a data byte from a slave
+               if (msg.u.twi.msg & TWI_COND_READ) {
+#if AVR_TWI_DEBUG
+                       AVR_TRACE(avr, "I2C received %02x\n", msg.u.twi.data);
+#endif
+                       avr->data[p->r_twdr] = msg.u.twi.data;
+               }
+       }
+}
+
+void avr_twi_reset(struct avr_io_t *io)
+{
+       avr_twi_t * p = (avr_twi_t *)io;
+       avr_irq_register_notify(p->io.irq + TWI_IRQ_INPUT, avr_twi_irq_input, p);
+       p->state = p->peer_addr = 0;
+       avr_regbit_setto_raw(p->io.avr, p->twsr, TWI_NO_STATE);
+}
+
+static const char * irq_names[TWI_IRQ_COUNT] = {
+       [TWI_IRQ_INPUT] = "8<input",
+       [TWI_IRQ_OUTPUT] = "32>output",
+       [TWI_IRQ_STATUS] = "8>status",
+};
+
+static avr_io_t        _io = {
+       .kind = "twi",
+       .reset = avr_twi_reset,
+       .irq_names = irq_names,
+};
+
+void avr_twi_init(avr_t * avr, avr_twi_t * p)
+{
+       p->io = _io;
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->twi);
+
+       //printf("%s TWI%c init\n", __FUNCTION__, p->name);
+
+       // allocate this module's IRQ
+       avr_io_setirqs(&p->io, AVR_IOCTL_TWI_GETIRQ(p->name), TWI_IRQ_COUNT, NULL);
+
+       avr_register_io_write(avr, p->twen.reg, avr_twi_write, p);
+       avr_register_io_write(avr, p->r_twdr, avr_twi_write_data, p);
+       avr_register_io_read(avr, p->r_twdr, avr_twi_read_data, p);
+       avr_register_io_write(avr, p->twsr.reg, avr_twi_write_status, p);
+}
+
+uint32_t
+avr_twi_irq_msg(
+               uint8_t msg,
+               uint8_t addr,
+               uint8_t data)
+{
+       avr_twi_msg_irq_t _msg = {
+                       .u.twi.msg = msg,
+                       .u.twi.addr = addr,
+                       .u.twi.data = data,
+       };
+       return _msg.u.v;
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.h
new file mode 100644 (file)
index 0000000..8b37ba3
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+       avr_twi.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_TWI_H__
+#define __AVR_TWI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+//#include "sim_twi.h"
+
+enum {
+       TWI_IRQ_INPUT = 0,
+       TWI_IRQ_OUTPUT,
+       TWI_IRQ_STATUS,
+       TWI_IRQ_COUNT
+};
+
+enum {
+       TWI_COND_START = (1 << 0),
+       TWI_COND_STOP = (1 << 1),
+       TWI_COND_ADDR = (1 << 2),
+       TWI_COND_ACK = (1 << 3),
+       TWI_COND_WRITE = (1 << 4),
+       TWI_COND_READ = (1 << 5),
+       // internal state, do not use in irq messages
+       TWI_COND_SLAVE  = (1 << 6),
+};
+
+typedef struct avr_twi_msg_t {
+       uint32_t unused : 8,
+               msg : 8,
+               addr : 8,
+               data : 8;
+} avr_twi_msg_t;
+
+typedef struct avr_twi_msg_irq_t {
+       union {
+               uint32_t v;
+               avr_twi_msg_t twi;
+       } u;
+} avr_twi_msg_irq_t;
+
+// add port number to get the real IRQ
+#define AVR_IOCTL_TWI_GETIRQ(_name) AVR_IOCTL_DEF('t','w','i',(_name))
+
+typedef struct avr_twi_t {
+       avr_io_t        io;
+       char name;
+
+       avr_regbit_t    disabled;       // bit in the PRR
+
+       avr_io_addr_t   r_twbr;                 // bit rate register
+       avr_io_addr_t   r_twcr;                 // control register
+       avr_io_addr_t   r_twsr;                 // status register
+       avr_io_addr_t   r_twar;                 // address register (slave)
+       avr_io_addr_t   r_twamr;                // address mask register
+       avr_io_addr_t   r_twdr;                 // data register
+       
+       avr_regbit_t twen;              // twi enable bit
+       avr_regbit_t twea;              // enable acknowledge bit
+       avr_regbit_t twsta;             // start condition
+       avr_regbit_t twsto;             // stop condition
+       avr_regbit_t twwc;              // write collision
+       
+       avr_regbit_t twsr;              // status registers, (5 bits)
+       avr_regbit_t twps;              // prescaler bits (2 bits)
+       
+       avr_int_vector_t twi;   // twi interrupt
+
+       uint8_t state;
+       uint8_t peer_addr;
+       uint8_t next_twstate;
+} avr_twi_t;
+
+void
+avr_twi_init(
+               avr_t * avr,
+               avr_twi_t * port);
+
+/*
+ * Create a message value for twi including the 'msg' bitfield,
+ * 'addr' and data. This value is what is sent as the IRQ value
+ */
+uint32_t
+avr_twi_irq_msg(
+               uint8_t msg,
+               uint8_t addr,
+               uint8_t data);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__AVR_TWI_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.c
new file mode 100644 (file)
index 0000000..25b6fe6
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+       avr_uart.c
+
+       Handles UART access
+       Right now just handle "write" to the serial port at any speed
+       and printf to the console when '\n' is written.
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef NO_COLOR
+       #define FONT_GREEN              
+       #define FONT_DEFAULT    
+#else
+       #define FONT_GREEN              "\e[32m"
+       #define FONT_DEFAULT    "\e[0m"
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "avr_uart.h"
+#include "sim_hex.h"
+#include "sim_time.h"
+#include "sim_gdb.h"
+
+//#define TRACE(_w) _w
+#ifndef TRACE
+#define TRACE(_w)
+#endif
+
+DEFINE_FIFO(uint16_t, uart_fifo);
+
+static inline void
+avr_uart_clear_interrupt(
+               avr_t * avr,
+               avr_int_vector_t * vector)
+{
+       if (!vector->vector)
+               return;
+       // clear the interrupt flag even it's 'sticky'
+       if (avr_regbit_get(avr, vector->raised))
+               avr_clear_interrupt_if(avr, vector, 0);
+       if (avr_regbit_get(avr, vector->raised))
+               avr_regbit_clear(avr, vector->raised);
+}
+
+static avr_cycle_count_t
+avr_uart_txc_raise(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+       if (p->tx_cnt) {
+               // Even if the interrupt is disabled, still raise the TXC flag
+               if (p->tx_cnt == 1)
+                       avr_raise_interrupt(avr, &p->txc);
+               p->tx_cnt--;
+       }
+       if (p->udrc.vector) {// UDRE is disabled in the LIN mode
+               if (p->tx_cnt) {
+                       if (avr_regbit_get(avr, p->udrc.raised)) {
+                               avr_uart_clear_interrupt(avr, &p->udrc);
+                       }
+               } else {
+                       if (avr_regbit_get(avr, p->txen)) {
+                               // Even if the interrupt is disabled, still raise the UDRE flag
+                               avr_raise_interrupt(avr, &p->udrc);
+                               if (!avr_regbit_get(avr, p->udrc.enable)) {
+                                       return 0; //polling mode: stop TX pump
+                               } else // udrc (alias udre) should be rased repeatedly while output buffer is empty
+                                       return when + p->cycles_per_byte;
+                       } else
+                               return 0; // transfer disabled: stop TX pump
+               }
+       }
+       if (p->tx_cnt)
+               return when + p->cycles_per_byte;
+       return 0; // stop TX pump
+}
+
+static avr_cycle_count_t
+avr_uart_rxc_raise(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+       if (avr_regbit_get(avr, p->rxen)) {
+               // rxc should be rased continiosly untill input buffer is empty
+               if (!uart_fifo_isempty(&p->input)) {
+                       if (!avr_regbit_get(avr, p->rxc.raised)) {
+                               p->rxc_raise_time = when;
+                               p->rx_cnt = 0;
+                       }
+                       avr_raise_interrupt(avr, &p->rxc);
+                       return when + p->cycles_per_byte;
+               }
+       }
+       return 0;
+}
+
+static uint8_t
+avr_uart_status_read(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+
+       if (addr == p->fe.reg) {
+               if (!uart_fifo_isempty(&p->input)) {
+                       uint16_t d = uart_fifo_read_at(&p->input, 0);
+
+                       uint8_t st = avr->data[addr];
+
+                       st &= ~(p->fe.mask << p->fe.bit);
+                       if (d & UART_INPUT_FE) {
+                               st |= p->fe.mask << p->fe.bit;
+                       }
+
+                       avr->data[addr] = st;
+               }
+       }
+
+       uint8_t v = avr_core_watch_read(avr, addr);
+
+       if (addr == p->rxc.raised.reg) {
+               //static uint8_t old = 0xff; if (v != old) printf("UCSRA read %02x\n", v); old = v;
+               //
+               // if RX is enabled, and there is nothing to read, and
+               // the AVR core is reading this register, it's probably
+               // to poll the RXC TXC flag and spinloop
+               // so here we introduce a usleep to make it a bit lighter
+               // on CPU and let data arrive
+               //
+               uint8_t ri = !avr_regbit_get(avr, p->rxen) || !avr_regbit_get(avr, p->rxc.raised);
+               uint8_t ti = !avr_regbit_get(avr, p->txen) || !avr_regbit_get(avr, p->txc.raised);
+
+               if (p->flags & AVR_UART_FLAG_POLL_SLEEP) {
+
+                       if (ri && ti)
+                               usleep(1);
+               }
+               // if reception is idle and the fifo is empty, tell whomever there is room
+               if (avr_regbit_get(avr, p->rxen) && uart_fifo_isempty(&p->input)) {
+                       avr_raise_irq(p->io.irq + UART_IRQ_OUT_XOFF, 0);
+                       avr_raise_irq(p->io.irq + UART_IRQ_OUT_XON, 1);
+               }
+       }
+
+       return v;
+}
+
+static uint8_t
+avr_uart_read(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+       uint8_t v = 0;
+
+       if (!avr_regbit_get(avr, p->rxen) ||
+                       !avr_regbit_get(avr, p->rxc.raised) // rxc flag not raised - nothing to read!
+                       ) {
+               AVR_LOG(avr, LOG_TRACE, "UART%c: attempt to read empty rx buffer\n", p->name);
+               avr->data[addr] = 0;
+               // made to trigger potential watchpoints
+               avr_core_watch_read(avr, addr);
+               //return 0;
+               goto avr_uart_read_check;
+       }
+       if (!uart_fifo_isempty(&p->input)) { // probably redundant check
+               v = (uint8_t)uart_fifo_read(&p->input) & 0xFF;
+               p->rx_cnt++;
+               if ((p->rx_cnt > 1) && // UART actually has 2-character rx buffer
+                               ((avr->cycle-p->rxc_raise_time)/p->rx_cnt < p->cycles_per_byte)) {
+                       // prevent the firmware from reading input characters with non-realistic high speed
+                       avr_uart_clear_interrupt(avr, &p->rxc);
+                       p->rx_cnt = 0;
+               }
+       } else {
+               AVR_LOG(avr, LOG_TRACE, "UART%c: BUG: rxc raised with empty rx buffer\n", p->name);
+       }
+
+//     TRACE(printf("UART read %02x %s\n", v, uart_fifo_isempty(&p->input) ? "EMPTY!" : "");)
+       avr->data[addr] = v;
+       // made to trigger potential watchpoints
+       v = avr_core_watch_read(avr, addr);
+
+avr_uart_read_check:
+       if (uart_fifo_isempty(&p->input)) {
+               avr_cycle_timer_cancel(avr, avr_uart_rxc_raise, p);
+               avr_uart_clear_interrupt(avr, &p->rxc);
+               avr_raise_irq(p->io.irq + UART_IRQ_OUT_XOFF, 0);
+               avr_raise_irq(p->io.irq + UART_IRQ_OUT_XON, 1);
+       }
+       if (!uart_fifo_isfull(&p->input)) {
+               avr_regbit_clear(avr, p->dor);
+       }
+
+       return v;
+}
+
+static void
+avr_uart_baud_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+       avr_core_watch_write(avr, addr, v);
+       uint32_t val = avr_regbit_get(avr,p->ubrrl) | (avr_regbit_get(avr,p->ubrrh) << 8);
+
+       const int databits[] = { 5,6,7,8,  /* 'reserved', assume 8 */8,8,8, 9 };
+       int db = databits[avr_regbit_get(avr, p->ucsz) | (avr_regbit_get(avr, p->ucsz2) << 2)];
+       int sb = 1 + avr_regbit_get(avr, p->usbs);
+       int word_size = 1 /* start */ + db /* data bits */ + 1 /* parity */ + sb /* stops */;
+       int cycles_per_bit = (val+1)*8;
+       if (!avr_regbit_get(avr, p->u2x))
+               cycles_per_bit *= 2;
+       double baud = ((double)avr->frequency) / cycles_per_bit; // can be less than 1
+       p->cycles_per_byte = cycles_per_bit * word_size;
+
+       AVR_LOG(avr, LOG_TRACE, "UART: %c configured to %04x = %.4f bps (x%d), %d data %d stop\n",
+                       p->name, val, baud, avr_regbit_get(avr, p->u2x)?2:1, db, sb);
+       AVR_LOG(avr, LOG_TRACE, "UART: Roughly %d usec per byte\n",
+                       avr_cycles_to_usec(avr, p->cycles_per_byte));
+}
+
+static void
+avr_uart_udr_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+
+       // The byte to be sent should NOT be written there,
+       // the value written could never be read back.
+       //avr_core_watch_write(avr, addr, v);
+       if (avr->gdb) {
+               avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_WRITE);
+       }
+
+       //avr_cycle_timer_cancel(avr, avr_uart_txc_raise, p); // synchronize tx pump
+       if (p->udrc.vector && avr_regbit_get(avr, p->udrc.raised)) {
+               avr_uart_clear_interrupt(avr, &p->udrc);
+       }
+
+       if (p->flags & AVR_UART_FLAG_STDIO) {
+               const int maxsize = 256;
+               if (!p->stdio_out)
+                       p->stdio_out = malloc(maxsize);
+               p->stdio_out[p->stdio_len++] = v < ' ' ? '.' : v;
+               p->stdio_out[p->stdio_len] = 0;
+               if (v == '\n' || p->stdio_len == maxsize) {
+                       p->stdio_len = 0;
+                       AVR_LOG(avr, LOG_OUTPUT,
+                                       FONT_GREEN "%s\n" FONT_DEFAULT, p->stdio_out);
+               }
+       }
+       TRACE(printf("UDR%c(%02x) = %02x\n", p->name, addr, v);)
+       // tell other modules we are "outputting" a byte
+       if (avr_regbit_get(avr, p->txen)) {
+               avr_raise_irq(p->io.irq + UART_IRQ_OUTPUT, v);
+               p->tx_cnt++;
+               if (p->tx_cnt > 2) // AVR actually has 1-character UART tx buffer, plus shift register
+                       AVR_LOG(avr, LOG_TRACE,
+                                       "UART%c: tx buffer overflow %d\n",
+                                       p->name, (int)p->tx_cnt);
+               if (avr_cycle_timer_status(avr, avr_uart_txc_raise, p) == 0)
+                       avr_cycle_timer_register(avr, p->cycles_per_byte,
+                                       avr_uart_txc_raise, p); // start the tx pump
+       }
+}
+
+
+static void
+avr_uart_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+
+       uint8_t masked_v = v;
+       uint8_t clear_txc = 0;
+       uint8_t clear_rxc = 0;
+
+       // exclude these locations from direct write:
+       if (p->udrc.raised.reg == addr) {
+               masked_v &= ~(p->udrc.raised.mask << p->udrc.raised.bit);
+               masked_v |= avr_regbit_get_raw(avr, p->udrc.raised);
+       }
+       if (p->txc.raised.reg == addr) {
+               uint8_t mask = p->txc.raised.mask << p->txc.raised.bit;
+               masked_v &= ~(mask);
+               masked_v |= avr_regbit_get_raw(avr, p->txc.raised);
+               // it can be cleared by writing a one to its bit location
+               if (v & mask)
+                       clear_txc = 1;
+       }
+       if (p->rxc.raised.reg == addr) {
+               uint8_t mask = p->rxc.raised.mask << p->rxc.raised.bit;
+               masked_v &= ~(mask);
+               masked_v |= avr_regbit_get_raw(avr, p->rxc.raised);
+               if (!p->udrc.vector) {
+                       // In the LIN mode it can be cleared by writing a one to its bit location
+                       if (v & mask)
+                               clear_rxc = 1;
+               }
+       }
+       // mainly to prevent application to confuse itself
+       // by writing something there and reading it back:
+       if (p->fe.reg == addr) {
+               masked_v &= ~(p->fe.mask << p->fe.bit);
+               masked_v |= avr_regbit_get_raw(avr, p->fe);
+       }
+       if (p->dor.reg == addr) {
+               masked_v &= ~(p->dor.mask << p->dor.bit);
+               //masked_v |= avr_regbit_get_raw(avr, p->dor);
+       }
+       if (p->upe.reg == addr) {
+               masked_v &= ~(p->upe.mask << p->upe.bit);
+               masked_v |= avr_regbit_get_raw(avr, p->upe);
+       }
+       if (p->rxb8.reg == addr) {
+               masked_v &= ~(p->rxb8.mask << p->rxb8.bit);
+               masked_v |= avr_regbit_get_raw(avr, p->rxb8);
+       }
+
+       uint8_t txen = avr_regbit_get(avr, p->txen);
+       uint8_t rxen = avr_regbit_get(avr, p->rxen);
+       uint8_t udrce = avr_regbit_get(avr, p->udrc.enable);
+       // Now write whatever bits could be writen directly.
+       // It is necessary anyway, to trigger potential watchpoints.
+       avr_core_watch_write(avr, addr, masked_v);
+       uint8_t new_txen = avr_regbit_get(avr, p->txen);
+       uint8_t new_rxen = avr_regbit_get(avr, p->rxen);
+       uint8_t new_udrce = avr_regbit_get(avr, p->udrc.enable);
+       if (p->udrc.vector && (!udrce && new_udrce) && new_txen) {
+               // If enabling the UDRC (alias is UDRE) interrupt, raise it immediately if FIFO is empty.
+               // If the FIFO is not empty (clear timer is flying) we don't
+               // need to raise the interrupt, it will happen when the timer
+               // is fired.
+               if (avr_cycle_timer_status(avr, avr_uart_txc_raise, p) == 0)
+                       avr_raise_interrupt(avr, &p->udrc);
+       }
+       if (clear_txc)
+               avr_uart_clear_interrupt(avr, &p->txc);
+       if (clear_rxc)
+               avr_uart_clear_interrupt(avr, &p->rxc);
+
+       ///TODO: handle the RxD & TxD pins function override
+
+       if (new_rxen != rxen) {
+               if (new_rxen) {
+                       if (uart_fifo_isempty(&p->input)) {
+                               // if reception is enabled and the fifo is empty, tell whomever there is room
+                               avr_raise_irq(p->io.irq + UART_IRQ_OUT_XOFF, 0);
+                               avr_raise_irq(p->io.irq + UART_IRQ_OUT_XON, 1);
+                       }
+               } else {
+                       avr_raise_irq(p->io.irq + UART_IRQ_OUT_XOFF, 1);
+                       avr_cycle_timer_cancel(avr, avr_uart_rxc_raise, p);
+                       // flush the Receive Buffer
+                       uart_fifo_reset(&p->input);
+                       // clear the rxc interrupt flag
+                       avr_uart_clear_interrupt(avr, &p->rxc);
+               }
+       }
+       if (new_txen != txen) {
+               if (p->udrc.vector && !new_txen) {
+                       avr_uart_clear_interrupt(avr, &p->udrc);
+               } else {
+                       avr_regbit_set(avr, p->udrc.raised);
+               }
+       }
+}
+
+static void
+avr_uart_irq_input(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+       avr_t * avr = p->io.avr;
+
+       // check to see if receiver is enabled
+       if (!avr_regbit_get(avr, p->rxen))
+               return;
+
+       // reserved/not implemented:
+       //avr_regbit_clear(avr, p->fe);
+       //avr_regbit_clear(avr, p->upe);
+       //avr_regbit_clear(avr, p->rxb8);
+
+       if (uart_fifo_isempty(&p->input) &&
+                       (avr_cycle_timer_status(avr, avr_uart_rxc_raise, p) == 0)
+                       ) {
+               avr_cycle_timer_register(avr, p->cycles_per_byte, avr_uart_rxc_raise, p); // start the rx pump
+               p->rx_cnt = 0;
+               avr_regbit_clear(avr, p->dor);
+       } else if (uart_fifo_isfull(&p->input)) {
+               avr_regbit_setto(avr, p->dor, 1);
+       }
+       if (!avr_regbit_get(avr, p->dor)) { // otherwise newly received character must be rejected
+               uart_fifo_write(&p->input, value); // add to fifo
+       } else {
+               AVR_LOG(avr, LOG_ERROR, "UART%c: %s: RX buffer overrun, lost char=%c=0x%02X\n", p->name, __func__,
+                               (char)value, (uint8_t)value );
+       }
+
+       TRACE(printf("UART IRQ in %02x (%d/%d) %s\n", value, p->input.read, p->input.write, uart_fifo_isfull(&p->input) ? "FULL!!" : "");)
+
+       if (uart_fifo_isfull(&p->input))
+               avr_raise_irq(p->io.irq + UART_IRQ_OUT_XOFF, 1);
+}
+
+
+void
+avr_uart_reset(
+               struct avr_io_t *io)
+{
+       avr_uart_t * p = (avr_uart_t *)io;
+       avr_t * avr = p->io.avr;
+       if (p->udrc.vector) {
+               avr_regbit_set(avr, p->udrc.raised);
+               avr_regbit_clear(avr, p->dor);
+       }
+       avr_uart_clear_interrupt(avr, &p->txc);
+       avr_uart_clear_interrupt(avr, &p->rxc);
+       avr_irq_register_notify(p->io.irq + UART_IRQ_INPUT, avr_uart_irq_input, p);
+       avr_cycle_timer_cancel(avr, avr_uart_rxc_raise, p);
+       avr_cycle_timer_cancel(avr, avr_uart_txc_raise, p);
+       uart_fifo_reset(&p->input);
+       p->tx_cnt =  0;
+
+       avr_regbit_set(avr, p->ucsz);
+       avr_regbit_clear(avr, p->ucsz2);
+
+       // DEBUG allow printf without fiddling with enabling the uart
+       avr_regbit_set(avr, p->txen);
+       p->cycles_per_byte = avr_usec_to_cycles(avr, 100);
+}
+
+static int
+avr_uart_ioctl(
+               struct avr_io_t * port,
+               uint32_t ctl,
+               void * io_param)
+{
+       avr_uart_t * p = (avr_uart_t *)port;
+       int res = -1;
+
+       if (!io_param)
+               return res;
+
+       if (ctl == AVR_IOCTL_UART_SET_FLAGS(p->name)) {
+               p->flags = *(uint32_t*)io_param;
+               res = 0;
+       }
+       if (ctl == AVR_IOCTL_UART_GET_FLAGS(p->name)) {
+               *(uint32_t*)io_param = p->flags;
+               res = 0;
+       }
+
+       return res;
+}
+
+static const char * irq_names[UART_IRQ_COUNT] = {
+       [UART_IRQ_INPUT] = "8<in",
+       [UART_IRQ_OUTPUT] = "8>out",
+       [UART_IRQ_OUT_XON] = ">xon",
+       [UART_IRQ_OUT_XOFF] = ">xoff",
+};
+
+static avr_io_t        _io = {
+       .kind = "uart",
+       .reset = avr_uart_reset,
+       .ioctl = avr_uart_ioctl,
+       .irq_names = irq_names,
+};
+
+void
+avr_uart_init(
+               avr_t * avr,
+               avr_uart_t * p)
+{
+       p->io = _io;
+
+//     printf("%s UART%c UDR=%02x\n", __FUNCTION__, p->name, p->r_udr);
+
+       p->flags = AVR_UART_FLAG_POLL_SLEEP|AVR_UART_FLAG_STDIO;
+
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->rxc);
+       avr_register_vector(avr, &p->txc);
+       avr_register_vector(avr, &p->udrc);
+
+       // allocate this module's IRQ
+       avr_io_setirqs(&p->io, AVR_IOCTL_UART_GETIRQ(p->name), UART_IRQ_COUNT, NULL);
+       // Only call callbacks when the value change...
+       p->io.irq[UART_IRQ_OUT_XOFF].flags |= IRQ_FLAG_FILTERED;
+
+       avr_register_io_write(avr, p->r_udr, avr_uart_udr_write, p);
+       avr_register_io_read(avr, p->r_udr, avr_uart_read, p);
+
+       // status bits
+       // monitor code that reads the rxc flag, and delay it a bit
+       avr_register_io_read(avr, p->rxc.raised.reg, avr_uart_status_read, p);
+       if (p->fe.reg != p->rxc.raised.reg)
+               avr_register_io_read(avr, p->fe.reg, avr_uart_status_read, p);
+
+       if (p->udrc.vector)
+               avr_register_io_write(avr, p->udrc.enable.reg, avr_uart_write, p);
+       if (p->r_ucsra)
+               avr_register_io_write(avr, p->r_ucsra, avr_uart_write, p);
+       if (p->ubrrl.reg)
+               avr_register_io_write(avr, p->ubrrl.reg, avr_uart_baud_write, p);
+       avr_register_io_write(avr, p->rxen.reg, avr_uart_write, p);
+}
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.h
new file mode 100644 (file)
index 0000000..107359e
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+       avr_uart.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_UART_H__
+#define __AVR_UART_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+#include "fifo_declare.h"
+
+DECLARE_FIFO(uint16_t, uart_fifo, 64);
+
+/*
+ * The method of "connecting" the the UART from external code is to use 4 IRQS.
+ * The easy one is UART->YOU, where you will be called with the byte every time
+ * the AVR firmware sends one. Do whatever you like with it.
+ *
+ * The slightly more tricky one is the INPUT part. Since the AVR is quite a bit
+ * slower than your code most likely, there is a way for the AVR UART to tell
+ * you to "pause" sending it bytes when its own input buffer is full.
+ * So, the UART will send XON to you when its fifo is empty, XON means you can
+ * send as many bytes as you have until XOFF is sent. Note that these are two
+ * IRQs because you /will/ be called with XOFF when sending a byte in INPUT...
+ * So it's a reentrant process.
+ *
+ * When XOFF has been called, do not send any new bytes, they would be dropped.
+ * Instead wait for XON again and continue.
+ * See examples/parts/uart_udp.c for a full implementation
+ *
+ * Pseudo code:
+ *
+ * volatile int off = 0;
+ * void irq_xon()
+ * {
+ *     off = 0;
+ *     while (!off && bytes_left)
+ *     avr_raise_irq(UART_IRQ_INPUT, a_byte);
+ * }
+ * void irq_xoff()
+ * {
+ *     off = 1;
+ * }
+ *
+ */
+enum {
+       UART_IRQ_INPUT = 0,
+       UART_IRQ_OUTPUT,
+       UART_IRQ_OUT_XON,               // signaled (continuously) when input fifo is not full
+       UART_IRQ_OUT_XOFF,              // signaled when input fifo IS full
+       UART_IRQ_COUNT
+};
+
+enum {
+       UART_INPUT_FE = 0x8000          // framing error
+};
+
+// add port number to get the real IRQ
+#define AVR_IOCTL_UART_GETIRQ(_name) AVR_IOCTL_DEF('u','a','r',(_name))
+
+enum {
+       // the uart code monitors for firmware that poll on
+       // reception registers, and can do an atomic usleep()
+       // if it's detected, this helps regulating CPU
+       AVR_UART_FLAG_POOL_SLEEP = (1 << 0),
+       AVR_UART_FLAG_POLL_SLEEP = (1 << 0),            // to replace pool_sleep
+       AVR_UART_FLAG_STDIO = (1 << 1),                         // print lines on the console
+};
+
+typedef struct avr_uart_t {
+       avr_io_t        io;
+       char name;
+       avr_regbit_t    disabled;       // bit in the PRR
+       
+       avr_io_addr_t r_udr;
+       avr_io_addr_t r_ucsra;
+       avr_io_addr_t r_ucsrb;
+       avr_io_addr_t r_ucsrc;
+
+       avr_regbit_t    rxen;           // receive enabled
+       avr_regbit_t    txen;           // transmit enable
+       avr_regbit_t    u2x;            // double UART speed
+       avr_regbit_t    usbs;           // stop bits
+       avr_regbit_t    ucsz;           // data bits
+       avr_regbit_t    ucsz2;          // data bits, continued
+
+       // read-only bits (just to mask it out)
+       avr_regbit_t    fe;                     // frame error bit
+       avr_regbit_t    dor;            // data overrun bit
+       avr_regbit_t    upe;            // parity error bit
+       avr_regbit_t    rxb8;           // receive data bit 8
+
+       avr_regbit_t    ubrrl;
+       avr_regbit_t    ubrrh;
+
+       avr_int_vector_t rxc;
+       avr_int_vector_t txc;
+       avr_int_vector_t udrc;  
+
+       uart_fifo_t     input;
+       uint8_t         tx_cnt;                 // number of unsent characters in the output buffer
+       uint32_t        rx_cnt;                 // number of characters read by app since rxc_raise_time
+
+       uint32_t                flags;
+       avr_cycle_count_t cycles_per_byte;
+       avr_cycle_count_t rxc_raise_time; // the cpu cycle when rxc flag was raised last time
+
+       uint8_t *               stdio_out;
+       int                             stdio_len;      // current size in the stdio output
+} avr_uart_t;
+
+/* takes a uint32_t* as parameter */
+#define AVR_IOCTL_UART_SET_FLAGS(_name)        AVR_IOCTL_DEF('u','a','s',(_name))
+#define AVR_IOCTL_UART_GET_FLAGS(_name)        AVR_IOCTL_DEF('u','a','g',(_name))
+
+void avr_uart_init(avr_t * avr, avr_uart_t * port);
+
+#define AVR_UARTX_DECLARE(_name, _prr, _prusart) \
+       .uart ## _name = { \
+               .name = '0' + _name, \
+               .disabled = AVR_IO_REGBIT(_prr, _prusart), \
+       \
+               .r_udr = UDR ## _name, \
+       \
+               .fe = AVR_IO_REGBIT(UCSR ## _name ## A, FE ## _name), \
+               .dor = AVR_IO_REGBIT(UCSR ## _name ## A, DOR ## _name), \
+               .upe = AVR_IO_REGBIT(UCSR ## _name ## A, UPE ## _name), \
+               .u2x = AVR_IO_REGBIT(UCSR ## _name ## A, U2X ## _name), \
+               .txen = AVR_IO_REGBIT(UCSR ## _name ## B, TXEN ## _name), \
+               .rxen = AVR_IO_REGBIT(UCSR ## _name ## B, RXEN ## _name), \
+               .rxb8 = AVR_IO_REGBIT(UCSR ## _name ## B, RXB8 ## _name), \
+               .usbs = AVR_IO_REGBIT(UCSR ## _name ## C, USBS ## _name), \
+               .ucsz = AVR_IO_REGBITS(UCSR ## _name ## C, UCSZ ## _name ## 0, 0x3), \
+               .ucsz2 = AVR_IO_REGBIT(UCSR ## _name ## B, UCSZ ## _name ## 2), \
+               .ubrrl = AVR_IO_REGBITS(UBRR ## _name ## L, 0,0xFF), \
+               .ubrrh = AVR_IO_REGBITS(UBRR ## _name ## H, 0,0xF), \
+               \
+               .r_ucsra = UCSR ## _name ## A, \
+               .r_ucsrb = UCSR ## _name ## B, \
+               .r_ucsrc = UCSR ## _name ## C, \
+       \
+               .rxc = { \
+                       .enable = AVR_IO_REGBIT(UCSR ## _name ## B, RXCIE ## _name), \
+                       .raised = AVR_IO_REGBIT(UCSR ## _name ## A, RXC ## _name), \
+                       .vector = USART ## _name ## _RX_vect, \
+                       .raise_sticky = 1, \
+               }, \
+               .txc = { \
+                       .enable = AVR_IO_REGBIT(UCSR ## _name ## B, TXCIE ## _name), \
+                       .raised = AVR_IO_REGBIT(UCSR ## _name ## A, TXC ## _name), \
+                       .vector = USART ## _name ## _TX_vect, \
+               }, \
+               .udrc = { \
+                       .enable = AVR_IO_REGBIT(UCSR ## _name ## B, UDRIE ## _name), \
+                       .raised = AVR_IO_REGBIT(UCSR ## _name ## A, UDRE ## _name), \
+                       .vector = USART ## _name ## _UDRE_vect, \
+                       .raise_sticky = 1, \
+               }, \
+       }
+
+// This macro is for older single-interface devices where variable names are bit divergent
+#define AVR_UART_DECLARE(_prr, _prusart, _upe_name, _rname_ix, _intr_c) \
+       .uart = { \
+               .name = '0', \
+               .disabled = AVR_IO_REGBIT(_prr, _prusart), \
+               .r_udr = UDR ## _rname_ix, \
+       \
+               .fe = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, FE ## _rname_ix), \
+               .dor = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, DOR ## _rname_ix), \
+               .upe = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, _upe_name ## _rname_ix), \
+               .u2x = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, U2X ## _rname_ix), \
+               .txen = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, TXEN ## _rname_ix), \
+               .rxen = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, RXEN ## _rname_ix), \
+               .rxb8 = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, RXB8 ## _rname_ix), \
+               .usbs = AVR_IO_REGBIT(UCSR ## _rname_ix ## C, USBS ## _rname_ix), \
+               .ucsz = AVR_IO_REGBITS(UCSR ## _rname_ix ## C, UCSZ ## _rname_ix ## 0, 0x3), \
+               .ucsz2 = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, UCSZ ## _rname_ix ## 2), \
+               .ubrrl = AVR_IO_REGBITS(UBRR ## _rname_ix ## L, 0,0xFF), \
+               .ubrrh = AVR_IO_REGBITS(UBRR ## _rname_ix ## H, 0,0xF), \
+       \
+               .r_ucsra = UCSR ## _rname_ix ## A, \
+               .r_ucsrb = UCSR ## _rname_ix ## B, \
+               .r_ucsrc = UCSR ## _rname_ix ## C, \
+       \
+               .rxc = { \
+                       .enable = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, RXCIE ## _rname_ix), \
+                       .raised = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, RXC ## _rname_ix), \
+                       .vector = USART_RX ## _intr_c ## _vect, \
+                       .raise_sticky = 1, \
+               }, \
+               .txc = { \
+                       .enable = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, TXCIE ## _rname_ix), \
+                       .raised = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, TXC ## _rname_ix), \
+                       .vector = USART_TX ## _intr_c ## _vect, \
+               }, \
+               .udrc = { \
+                       .enable = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, UDRIE ## _rname_ix), \
+                       .raised = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, UDRE ## _rname_ix), \
+                       .vector = USART_UDRE_vect, \
+                       .raise_sticky = 1, \
+               }, \
+       }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__AVR_UART_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.c
new file mode 100644 (file)
index 0000000..4acbcfd
--- /dev/null
@@ -0,0 +1,801 @@
+/* vim: set sts=4:sw=4:ts=4:noexpandtab
+       avr_usb.c
+
+       Copyright 2012 Torbjorn Tyridal <ttyridal@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* TODO correct reset values */
+/* TODO generate sofi every 1ms (when connected) */
+/* TODO otg support? */
+/* TODO drop bitfields? */
+/* TODO thread safe ioctls */
+/* TODO dual-bank endpoint buffers */
+/* TODO actually pay attention to endpoint memory allocation ? buggy endpoint configuration doesn't matter in the simulator now. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "avr_usb.h"
+
+enum usb_regs
+{
+       usbcon = 0,
+       udcon = 8,
+       udint = 9,
+       udien = 10,
+       udaddr = 11,
+       udfnuml = 12,
+       udfnumh = 13,
+       udmfn = 14,
+//     _res=15,
+       ueintx = 16,
+       uenum = 17,
+       uerst = 18,
+       ueconx = 19,
+       uecfg0x = 20,
+       uecfg1x = 21,
+       uesta0x = 22,
+       uesta1x = 23,
+       ueienx = 24,
+       uedatx = 25,
+       uebclx = 26,
+//     _res2=27,
+       ueint = 28,
+       otgtcon = 29,
+};
+
+union _ueintx {
+       struct {
+               uint8_t txini :1;
+               uint8_t stalledi :1;
+               uint8_t rxouti :1;
+               uint8_t rxstpi :1;
+               uint8_t nakouti :1;
+               uint8_t rwal :1;
+               uint8_t nakini :1;
+               uint8_t fifocon :1;
+       };
+       uint8_t v;
+};
+
+struct _epstate {
+       union _ueintx ueintx;
+       uint8_t dummy1;
+       uint8_t dummy2;
+       union {
+               struct {
+                       uint8_t epen :1;
+                       uint8_t res :2;
+                       uint8_t rstdt :1;
+                       uint8_t stallrqc :1;
+                       uint8_t stallrq :1;
+               };
+               uint8_t v;
+       } ueconx;
+       union {
+               struct {
+                       uint8_t epdir :1;
+                       uint8_t res :5;
+                       uint8_t eptype :2;
+               };
+               uint8_t v;
+       } uecfg0x;
+       union {
+               struct {
+                       uint8_t res :1;
+                       uint8_t alloc :1;
+                       uint8_t epbk1 :2;
+                       uint8_t epsize :3;
+                       uint8_t res2 :1;
+               };
+               uint8_t v;
+       } uecfg1x;
+       union {
+               struct {
+                       uint8_t nbusybk :2;
+                       uint8_t dtseq :2;
+                       uint8_t res :1;
+                       uint8_t underfi :1;
+                       uint8_t overfi :1;
+                       uint8_t cfgok :1;
+               };
+               uint8_t v;
+       } uesta0x;
+       union {
+               struct {
+                       uint8_t curbk :2;
+                       uint8_t ctrldir :1;
+                       uint8_t res :5;
+               };
+               uint8_t v;
+       } uesta1x;
+       union {
+               struct {
+                       uint8_t txine :1;
+                       uint8_t stallede :1;
+                       uint8_t rxoute :1;
+                       uint8_t rxstpe :1;
+                       uint8_t nakoute :1;
+                       uint8_t res :1;
+                       uint8_t nakine :1;
+                       uint8_t flerre :1;
+               };
+               uint8_t v;
+       } ueienx;
+
+       struct {
+               uint8_t bytes[64];
+               uint8_t tail;
+       } bank[2];
+       uint8_t current_bank;
+       int setup_is_read;
+};
+
+struct usb_internal_state {
+       struct _epstate ep_state[5];
+       avr_int_vector_t com_vect;
+       avr_int_vector_t gen_vect;
+};
+
+const uint8_t num_endpoints = 5;//sizeof (struct usb_internal_state.ep_state) / sizeof (struct usb_internal_state.ep_state[0]);
+
+static uint8_t
+current_ep_to_cpu(
+               avr_usb_t * p)
+{
+       return p->io.avr->data[p->r_usbcon + uenum];
+}
+
+static struct _epstate *
+get_epstate(
+               avr_usb_t * p,
+               uint8_t ep)
+{
+       assert(ep < num_endpoints);
+       return &p->state->ep_state[ep];
+}
+
+
+enum epints {
+       txini = 0,
+       stalledi = 1,
+       rxouti = 2,
+       rxstpi = 3,
+       nakouti = 4,
+       nakini = 6,
+       overfi = 10,
+       underfi = 11,
+};
+
+static void
+raise_ep_interrupt(
+        struct avr_t * avr,
+        avr_usb_t * p,
+        uint8_t ep,
+        enum epints irq)
+{
+       struct _epstate * epstate = get_epstate(p, ep);
+       assert(ep < num_endpoints);
+       avr->data[p->r_usbcon + ueint] |= 1 << ep;
+       switch (irq) {
+               case txini:
+               case stalledi:
+               case rxouti:
+               case nakouti:
+               case nakini:
+                       epstate->ueintx.v |= 1 << irq;
+                       if (epstate->ueienx.v & (1 << irq))
+                               avr_raise_interrupt(avr, &p->state->com_vect);
+                       break;
+               case rxstpi:
+                       epstate->ueintx.v |= 1 << irq;
+                       if (epstate->ueienx.v & (1 << irq))
+                               avr_raise_interrupt(avr, &p->state->com_vect);
+                       break;
+               case overfi:
+                       epstate->uesta0x.overfi = 1;
+                       if (epstate->ueienx.flerre)
+                               avr_raise_interrupt(avr, &p->state->com_vect);
+                       break;
+               case underfi:
+                       epstate->uesta0x.underfi = 1;
+                       if (epstate->ueienx.flerre)
+                               avr_raise_interrupt(avr, &p->state->com_vect);
+                       break;
+               default:
+                       assert(0);
+       }
+}
+
+enum usbints {
+       suspi = 0, sofi = 2, eorsti = 3, wakeupi = 4, eorsmi = 5, uprsmi = 6
+};
+static void
+raise_usb_interrupt(
+               avr_usb_t * p,
+               enum usbints irq)
+{
+       uint8_t * Rudien = &p->io.avr->data[p->r_usbcon + udien];
+       uint8_t * Rudint = &p->io.avr->data[p->r_usbcon + udint];
+
+       switch (irq) {
+               case uprsmi:
+               case eorsmi:
+               case wakeupi:
+               case eorsti:
+               case sofi:
+               case suspi:
+                       *Rudint |= 1 << irq;
+                       if (*Rudien & (1 << irq))
+                               avr_raise_interrupt(p->io.avr, &p->state->gen_vect);
+                       break;
+               default:
+                       assert(0);
+       }
+
+}
+
+static void
+reset_endpoints(
+               struct avr_t * avr,
+               avr_usb_t * p)
+{
+       memset(&p->state->ep_state[1], 0,
+               sizeof p->state->ep_state - sizeof p->state->ep_state[0]);
+}
+
+static int
+ep_fifo_empty(
+               struct _epstate * epstate)
+{
+       return epstate->bank[epstate->current_bank].tail == 0;
+}
+
+static int
+ep_fifo_full(
+               struct _epstate * epstate)
+{
+       return epstate->bank[epstate->current_bank].tail >=
+                                       (8 << epstate->uecfg1x.epsize);
+}
+
+static uint8_t
+ep_fifo_size(
+               struct _epstate * epstate)
+{
+       assert(epstate->ueconx.epen);
+       return (8 << epstate->uecfg1x.epsize);
+}
+
+static uint8_t
+ep_fifo_count(
+               struct _epstate * epstate)
+{
+       return epstate->bank[epstate->current_bank].tail;
+}
+
+static int
+ep_fifo_cpu_readbyte(
+               struct _epstate * epstate)
+{
+       uint8_t i, j;
+       uint8_t v = epstate->bank[epstate->current_bank].bytes[0];
+
+       if (!epstate->ueconx.epen) {
+               printf("WARNING! Adding bytes to non configured endpoint\n");
+               return -1;
+       }
+
+       if (ep_fifo_empty(epstate))
+               return -2;
+
+       for (i = 0, j = ep_fifo_count(epstate) - 1; i < j; i++)
+               epstate->bank[epstate->current_bank].bytes[i] =
+                       epstate->bank[epstate->current_bank].bytes[i + 1];
+       epstate->bank[epstate->current_bank].tail--;
+       return v;
+}
+
+static int
+ep_fifo_cpu_writebyte(
+               struct _epstate * epstate,
+               uint8_t v)
+{
+       if (!epstate->ueconx.epen) {
+               printf("WARNING! Adding bytes to non configured endpoint\n");
+               return -1;
+       }
+       if (ep_fifo_full(epstate))
+               return -2;
+
+       epstate->bank[epstate->current_bank].bytes[epstate->bank[epstate->current_bank].tail++] = v;
+       return 0;
+}
+
+static int
+ep_fifo_usb_read(
+               struct _epstate * epstate,
+               uint8_t * buf)
+{
+       if (!epstate->ueconx.epen) {
+               printf("WARNING! Reading from non configured endpoint\n");
+               return -1;
+       }
+       if (epstate->ueintx.txini) {
+               return AVR_IOCTL_USB_NAK;
+       }
+       if (epstate->ueintx.fifocon && epstate->uecfg0x.eptype != 0) {
+               return AVR_IOCTL_USB_NAK;
+       }
+
+       int ret = epstate->bank[epstate->current_bank].tail;
+       memcpy(buf, epstate->bank[epstate->current_bank].bytes,
+               epstate->bank[epstate->current_bank].tail);
+       epstate->bank[epstate->current_bank].tail = 0;
+       return ret;
+}
+
+static int
+ep_fifo_usb_write(
+        struct _epstate * epstate,
+        uint8_t * buf,
+        uint8_t len)
+{
+       if (!epstate->ueconx.epen) {
+               printf("WARNING! Adding bytes to non configured endpoint\n");
+               return -1;
+       }
+
+       if (epstate->ueintx.rxouti) {
+               return AVR_IOCTL_USB_NAK;
+       }
+       if (epstate->ueintx.fifocon && epstate->uecfg0x.eptype != 0) {
+               return AVR_IOCTL_USB_NAK;
+       }
+
+       if (len > ep_fifo_size(epstate)) {
+               printf("EP OVERFI\n");
+               len = sizeof epstate->bank[epstate->current_bank].bytes;
+       }
+       memcpy(epstate->bank[epstate->current_bank].bytes, buf, len);
+       epstate->bank[epstate->current_bank].tail = len;
+
+       return 0;
+}
+
+static uint8_t
+avr_usb_ep_read_bytecount(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       return ep_fifo_count(get_epstate(p, current_ep_to_cpu(p)));
+}
+
+static void
+avr_usb_udaddr_write(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       if (v & 0x80)
+               AVR_LOG(avr, LOG_TRACE, "USB: Activate address %d\n", v & 0x7f);
+       avr_core_watch_write(avr, addr, v);
+}
+
+static void
+avr_usb_udcon_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       avr_usb_t * p = (avr_usb_t *)param;
+
+       if(avr->data[addr]&1 && !(v&1))
+               avr_raise_irq(p->io.irq + USB_IRQ_ATTACH, !(v&1));
+       avr_core_watch_write(avr, addr, v);
+}
+
+static void
+avr_usb_uenum_write(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       assert(v < num_endpoints);
+       avr_core_watch_write(avr, addr, v);
+}
+
+static uint8_t
+avr_usb_ep_read_ueintx(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       uint8_t ep = current_ep_to_cpu(p);
+
+       if (p->state->ep_state[ep].uecfg0x.epdir)
+               p->state->ep_state[ep].ueintx.rwal = !ep_fifo_full(get_epstate(p, ep));
+       else
+               p->state->ep_state[ep].ueintx.rwal = !ep_fifo_empty(get_epstate(p, ep));
+
+       return p->state->ep_state[ep].ueintx.v;
+}
+
+static void
+avr_usb_ep_write_ueintx(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       uint8_t ep = current_ep_to_cpu(p);
+
+       union _ueintx * newstate = (union _ueintx*) &v;
+       union _ueintx * curstate = &p->state->ep_state[ep].ueintx;
+
+       if (curstate->rxouti & !newstate->rxouti)
+               curstate->rxouti = 0;
+       if (curstate->txini & !newstate->txini)
+               curstate->txini = 0;
+       if (curstate->rxstpi & !newstate->rxstpi) {
+               curstate->txini = 1;
+               curstate->rxouti = 0;
+               curstate->rxstpi = 0;
+       }
+       if (curstate->fifocon & !newstate->fifocon)
+               curstate->fifocon = 0;
+       if (curstate->nakini & !newstate->nakini)
+               curstate->nakini = 0;
+       if (curstate->nakouti & !newstate->nakouti)
+               curstate->nakouti = 0;
+       if (curstate->stalledi & !newstate->stalledi)
+               curstate->stalledi = 0;
+       if (curstate->rwal & !newstate->rwal)
+               AVR_LOG(avr, LOG_WARNING, "USB: Pointless change of ueintx.rwal\n");
+
+       if ((curstate->v & 0xdf) == 0)
+               avr->data[p->r_usbcon + ueint] &= 0xff ^ (1 << ep); // mark ep0 interrupt
+}
+
+static uint8_t
+avr_usb_ep_read(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       uint8_t laddr = addr - p->r_usbcon;
+       uint8_t v;
+       struct _epstate * epstate = get_epstate(p, current_ep_to_cpu(p));
+
+       switch(laddr) {
+               case ueconx:  v = epstate->ueconx.v; break;
+               case uecfg0x: v = epstate->uecfg0x.v; break;
+               case uecfg1x: v = epstate->uecfg1x.v; break;
+               case uesta0x: v = epstate->uesta0x.v; break;
+               case uesta1x: v = epstate->uesta1x.v; break;
+               case ueienx:  v = epstate->ueienx.v; break;
+               default:assert(0);
+       }
+       return v;
+}
+
+static void
+avr_usb_ep_write(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       struct _epstate * epstate = get_epstate(p, current_ep_to_cpu(p));
+       uint8_t laddr = addr - p->r_usbcon;
+
+       switch (laddr) {
+               case ueconx:
+                       if (v & 1 << 4)
+                               epstate->ueconx.stallrq = 0;
+                       if (v & 1 << 5)
+                               epstate->ueconx.stallrq = 1;
+                       epstate->ueconx.epen = (v & 1) != 0;
+                       break;
+               case uecfg0x:
+                       epstate->uecfg0x.v = v;
+                       epstate->uesta0x.cfgok = 0;
+                       break;
+               case uecfg1x:
+                       epstate->uecfg1x.v = v;
+                       epstate->uesta0x.cfgok = epstate->uecfg1x.alloc;
+                       if (epstate->uecfg0x.eptype == 0)
+                               epstate->ueintx.txini = 1;
+                       else if (epstate->uecfg0x.epdir) {
+                               epstate->ueintx.txini = 1;
+                               epstate->ueintx.rwal = 1;
+                               epstate->ueintx.fifocon = 1;
+                       } else
+                               epstate->ueintx.rxouti = 0;
+                       avr_core_watch_write(avr, p->r_usbcon + uesta0x,
+                               epstate->uesta0x.v);
+                       break;
+               case uesta0x:
+                       v = (epstate->uesta0x.v & 0x9f) + (v & (0x60 & epstate->uesta0x.v));
+                       epstate->uesta0x.v = v;
+                       break;
+               case ueienx:
+                       epstate->ueienx.v = v;
+                       break;
+               default:
+                       assert(0);
+       }
+}
+
+static uint8_t
+avr_usb_ep_read_data(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       int ret = ep_fifo_cpu_readbyte(get_epstate(p, current_ep_to_cpu(p)));
+
+       if (ret < 0) {
+               if (ret == -2)
+                       raise_ep_interrupt(avr, p, current_ep_to_cpu(p), underfi);
+               return 0;
+       } else
+               return (uint8_t) ret;
+}
+
+static void
+avr_usb_ep_write_data(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       int ret = ep_fifo_cpu_writebyte(get_epstate(p, current_ep_to_cpu(p)), v);
+       if (ret == 0)
+               return;
+
+       if (ret == -2)
+               raise_ep_interrupt(avr, p, current_ep_to_cpu(p), overfi);
+}
+
+static void
+avr_usb_pll_write(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       v |= (v >> 1) & 1;
+       avr_core_watch_write(avr, addr, v);
+}
+
+
+avr_cycle_count_t
+sof_generator(
+        struct avr_t * avr,
+        avr_cycle_count_t when,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       //stop sof generation if detached
+       if (avr->data[p->r_usbcon + udcon] & 1)
+               return 0;
+       else {
+               raise_usb_interrupt(p, sofi);
+               return when;
+       }
+}
+
+static int
+avr_usb_ioctl(
+               struct avr_io_t * io,
+               uint32_t ctl,
+               void * io_param)
+{
+       avr_usb_t * p = (avr_usb_t *) io;
+       struct avr_io_usb * d = (struct avr_io_usb*) io_param;
+       struct _epstate * epstate = 0;
+       int ret;
+       uint8_t ep;
+
+       switch (ctl) {
+               case AVR_IOCTL_USB_READ:
+                       ep = d->pipe & 0x7f;
+                       epstate = get_epstate(p, ep);
+
+                       if (epstate->ueconx.stallrq) {
+                               raise_ep_interrupt(io->avr, p, 0, stalledi);
+                               return AVR_IOCTL_USB_STALL;
+                       }
+                       if (ep && !epstate->uecfg0x.epdir)
+                               AVR_LOG(io->avr, LOG_WARNING, "USB: Reading from IN endpoint from host??\n");
+
+                       ret = ep_fifo_usb_read(epstate, d->buf);
+                       if (ret < 0) {
+                               // is this correct? It makes the cdc example work.
+                               // Linux stops polling the data ep if we send naks,but
+                               // according to usb spec nak'ing should be ok.
+                               if (epstate->uecfg0x.eptype == 2) {
+                                       d->sz = 0;
+                                       return 0;
+                               } else
+                                       return ret;
+                       }
+                       d->sz = ret;
+                       ret = 0;
+                       epstate->ueintx.fifocon = 1;
+                       raise_ep_interrupt(io->avr, p, ep, txini);
+                       return ret;
+               case AVR_IOCTL_USB_WRITE:
+                       ep = d->pipe & 0x7f;
+                       epstate = get_epstate(p, ep);
+
+                       if (ep && epstate->uecfg0x.epdir)
+                               AVR_LOG(io->avr, LOG_WARNING, "USB: Writing to IN endpoint from host??\n");
+
+                       if (epstate->ueconx.stallrq) {
+                               raise_ep_interrupt(io->avr, p, 0, stalledi);
+                               return AVR_IOCTL_USB_STALL;
+                       }
+
+                       ret = ep_fifo_usb_write(epstate, d->buf, d->sz);
+                       if (ret < 0)
+                               return ret;
+
+                       epstate->ueintx.fifocon = 1;
+                       raise_ep_interrupt(io->avr, p, ep, rxouti);
+                       return 0;
+               case AVR_IOCTL_USB_SETUP:
+                       ep = d->pipe & 0x7f;
+                       epstate = get_epstate(p, ep);
+
+                       epstate->ueconx.stallrq = 0;
+                       // teensy actually depends on this (fails to ack rxouti on usb
+                       // control read status stage) even if the datasheet clearly states
+                       // that one should do so.
+                       epstate->ueintx.rxouti = 0;
+
+                       ret = ep_fifo_usb_write(epstate, d->buf, d->sz);
+                       if (ret < 0)
+                               return ret;
+                       raise_ep_interrupt(io->avr, p, ep, rxstpi);
+
+                       return 0;
+               case AVR_IOCTL_USB_RESET:
+                       AVR_LOG(io->avr, LOG_TRACE, "USB: __USB_RESET__\n");
+                       reset_endpoints(io->avr, p);
+                       raise_usb_interrupt(p, eorsti);
+                       if (0)
+                               avr_cycle_timer_register_usec(io->avr, 1000, sof_generator, p);
+                       return 0;
+               default:
+                       return -1;
+       }
+}
+
+void
+avr_usb_reset(
+               struct avr_io_t *io)
+{
+       avr_usb_t * p = (avr_usb_t *) io;
+       uint8_t i;
+
+       memset(p->state->ep_state, 0, sizeof p->state->ep_state);
+
+       for (i = 0; i < otgtcon; i++)
+               p->io.avr->data[p->r_usbcon + i] = 0;
+
+       p->io.avr->data[p->r_usbcon] = 0x20;
+       p->io.avr->data[p->r_usbcon + udcon] = 1;
+
+       AVR_LOG(io->avr, LOG_TRACE, "USB: %s\n", __FUNCTION__);
+}
+
+static const char * irq_names[USB_IRQ_COUNT] = {
+       [USB_IRQ_ATTACH] = ">attach",
+};
+
+static void
+avr_usb_dealloc(
+               struct avr_io_t * port)
+{
+       avr_usb_t * p = (avr_usb_t *) port;
+       free(p->state);
+}
+
+static avr_io_t        _io = {
+       .kind = "usb",
+       .reset = avr_usb_reset,
+       .irq_names = irq_names,
+       .ioctl = avr_usb_ioctl,
+       .dealloc = avr_usb_dealloc,
+};
+
+static void
+register_io_ep_readwrite(
+               avr_t * avr,
+               avr_usb_t * p,
+               uint8_t laddr)
+{
+       avr_register_io_write(avr, p->r_usbcon + laddr, avr_usb_ep_write, p);
+       avr_register_io_read(avr, p->r_usbcon + laddr, avr_usb_ep_read, p);
+}
+
+static void
+register_vectors(
+               avr_t * avr,
+               avr_usb_t * p)
+{
+       // usb interrupts are multiplexed into just two vectors.
+       // we therefore need fake bits for enable & raise
+
+       // use usbe as fake enable bit
+       p->state->com_vect.enable = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon, 7);
+       p->state->gen_vect.enable = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon, 7);
+
+//     // use reserved/unused bits in usbsta as fake raised bits
+//     p->state->com_vect.raised = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon+1,7);
+//     p->state->gen_vect.raised = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon+1,6);
+
+       p->state->com_vect.vector = p->usb_com_vect;
+       p->state->gen_vect.vector = p->usb_gen_vect;
+
+       avr_register_vector(avr, &p->state->com_vect);
+       avr_register_vector(avr, &p->state->gen_vect);
+}
+
+void avr_usb_init(avr_t * avr, avr_usb_t * p)
+{
+       p->io = _io;
+
+       p->state = calloc(1, sizeof *p->state);
+
+       avr_register_io(avr, &p->io);
+       register_vectors(avr, p);
+       // allocate this module's IRQ
+       avr_io_setirqs(&p->io, AVR_IOCTL_USB_GETIRQ(), USB_IRQ_COUNT, NULL);
+
+       avr_register_io_write(avr, p->r_usbcon + udaddr, avr_usb_udaddr_write, p);
+       avr_register_io_write(avr, p->r_usbcon + udcon, avr_usb_udcon_write, p);
+       avr_register_io_write(avr, p->r_usbcon + uenum, avr_usb_uenum_write, p);
+
+       avr_register_io_read(avr, p->r_usbcon + uedatx, avr_usb_ep_read_data, p);
+       avr_register_io_write(avr, p->r_usbcon + uedatx, avr_usb_ep_write_data, p);
+       avr_register_io_read(avr, p->r_usbcon + uebclx, avr_usb_ep_read_bytecount, p); //ro
+
+       avr_register_io_read(avr, p->r_usbcon + ueintx, avr_usb_ep_read_ueintx, p);
+       avr_register_io_write(avr, p->r_usbcon + ueintx, avr_usb_ep_write_ueintx, p);
+
+       register_io_ep_readwrite(avr, p, ueconx);
+       register_io_ep_readwrite(avr, p, uecfg0x);
+       register_io_ep_readwrite(avr, p, uecfg1x);
+       register_io_ep_readwrite(avr, p, uesta0x);
+       register_io_ep_readwrite(avr, p, uesta1x);
+       register_io_ep_readwrite(avr, p, ueienx);
+
+       avr_register_io_write(avr, p->r_pllcsr, avr_usb_pll_write, p);
+}
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.h
new file mode 100644 (file)
index 0000000..17e5c0d
--- /dev/null
@@ -0,0 +1,74 @@
+/* vim: set sts=4:sw=4:ts=4:noexpandtab
+       avr_usb.h
+
+       Copyright 2012 Torbjorn Tyridal <ttyridal@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AVR_USB_H__
+#define __AVR_USB_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+enum {
+       USB_IRQ_ATTACH = 0,
+       USB_IRQ_COUNT
+};
+
+// add port number to get the real IRQ
+#define AVR_IOCTL_USB_WRITE AVR_IOCTL_DEF('u','s','b','w')
+#define AVR_IOCTL_USB_READ AVR_IOCTL_DEF('u','s','b','r')
+#define AVR_IOCTL_USB_SETUP AVR_IOCTL_DEF('u','s','b','s')
+#define AVR_IOCTL_USB_RESET AVR_IOCTL_DEF('u','s','b','R')
+#define AVR_IOCTL_USB_VBUS AVR_IOCTL_DEF('u','s','b','V')
+#define AVR_IOCTL_USB_GETIRQ() AVR_IOCTL_DEF('u','s','b',' ')
+
+struct avr_io_usb {
+       uint8_t pipe;   //[in]
+       uint32_t  sz;           //[in/out]
+       uint8_t * buf;  //[in/out]
+};
+#define AVR_IOCTL_USB_NAK -2
+#define AVR_IOCTL_USB_STALL -3
+#define AVR_IOCTL_USB_OK 0
+
+typedef struct avr_usb_t {
+       avr_io_t        io;
+       char name;
+       avr_regbit_t    disabled;       // bit in the PRR
+       avr_regbit_t    usbrf;          // bit in the MCUSR
+       avr_io_addr_t   r_usbcon;       // every usb reg is an offset of this.
+       avr_io_addr_t   r_pllcsr;
+
+
+       uint8_t usb_com_vect;
+       uint8_t usb_gen_vect;
+
+       struct usb_internal_state * state;
+} avr_usb_t;
+
+void avr_usb_init(avr_t * avr, avr_usb_t * port);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__AVR_USB_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.c
new file mode 100644 (file)
index 0000000..b7a5d01
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+       avr_watchdog.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "avr_watchdog.h"
+
+static void avr_watchdog_run_callback_software_reset(avr_t * avr)
+{
+       avr_reset(avr);
+}
+
+static avr_cycle_count_t avr_watchdog_timer(
+               struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_watchdog_t * p = (avr_watchdog_t *)param;
+
+       if (avr_regbit_get(avr, p->watchdog.enable)) {
+               AVR_LOG(avr, LOG_TRACE, "WATCHDOG: timer fired.\n");
+               avr_raise_interrupt(avr, &p->watchdog);
+               return when + p->cycle_count;
+       } else if (avr_regbit_get(avr, p->wde)) {
+               AVR_LOG(avr, LOG_TRACE,
+                               "WATCHDOG: timer fired without interrupt. Resetting\n");
+
+               p->reset_context.avr_run = avr->run;
+               p->reset_context.wdrf = 1;
+
+               /* Ideally we would perform a reset here via 'avr_reset'
+                * However, returning after reset would result in an unconsistent state.
+                * It seems our best (and cleanest) solution is to set a temporary call 
+                * back which can safely perform the reset for us...  During reset,
+                * the previous callback can be restored and safely resume.
+                */
+               avr->run = avr_watchdog_run_callback_software_reset;
+       }
+
+       return 0;
+}
+
+static avr_cycle_count_t avr_wdce_clear(
+               struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_watchdog_t * p = (avr_watchdog_t *)param;
+       avr_regbit_clear(p->io.avr, p->wdce);
+       return 0;
+}
+
+static void avr_watchdog_set_cycle_count_and_timer(
+       avr_t * avr, 
+       avr_watchdog_t * p, 
+       uint8_t was_enabled, 
+       int8_t old_wdp)
+{
+       // If nothing else, always ensure we have a valid cycle count...
+       uint8_t wdp = avr_regbit_get_array(avr, p->wdp, 4);
+
+       p->cycle_count = 2048 << wdp;
+       p->cycle_count = (p->cycle_count * avr->frequency) / 128000;
+
+       uint8_t wde = avr_regbit_get(avr, p->wde);
+       uint8_t wdie = avr_regbit_get(avr, p->watchdog.enable);
+
+       uint8_t enable_changed = (was_enabled != (wde || wdie));
+
+       uint8_t wdp_changed = ((old_wdp >= 0) ? (wdp != old_wdp) : 0);
+       
+       if (!enable_changed && !wdp_changed)
+               return;
+
+       static char *message[2][2] = {
+                       { 0, "reset" }, { "enabled", "enabled and set" } };
+
+       if (wde || wdie) {
+               AVR_LOG(avr, LOG_TRACE,
+                               "WATCHDOG: %s to %d cycles @ 128kz (* %d) = %d CPU cycles.\n",
+                               message[enable_changed][wdp_changed], 2048 << wdp,
+                               1 << wdp, (int)p->cycle_count);
+
+               avr_cycle_timer_register(avr, p->cycle_count, avr_watchdog_timer, p);
+       } else if (enable_changed) {
+               AVR_LOG(avr, LOG_TRACE, "WATCHDOG: disabled\n");
+               avr_cycle_timer_cancel(avr, avr_watchdog_timer, p);
+       }
+}
+
+static void avr_watchdog_write(
+               avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       avr_watchdog_t * p = (avr_watchdog_t *)param;
+
+       uint8_t old_wde = avr_regbit_get(avr, p->wde);
+       uint8_t old_wdie = avr_regbit_get(avr, p->watchdog.enable);
+       uint8_t old_wdce = avr_regbit_get(avr, p->wdce);
+       
+       uint8_t was_enabled = (old_wde || old_wdie);
+
+       uint8_t old_v = avr->data[addr]; // allow gdb to see write...
+       avr_core_watch_write(avr, addr, v);
+
+       if (old_wdce) {
+               uint8_t old_wdp = avr_regbit_get_array(avr, p->wdp, 4);
+
+               // wdrf (watchdog reset flag) must be cleared before wde can be cleared.
+               if (avr_regbit_get(avr, p->wdrf))
+                       avr_regbit_set(avr, p->wde);
+
+               avr_watchdog_set_cycle_count_and_timer(avr, p, was_enabled, old_wdp);
+       } else {
+               /* easier to change only what we need rather than check and reset 
+                * locked/read-only bits.
+                */
+               avr->data[addr] = old_v;
+               
+               uint8_t wdce_v = avr_regbit_from_value(avr, p->wdce, v);
+               uint8_t wde_v = avr_regbit_from_value(avr, p->wde, v);
+
+               if (wdce_v && wde_v) {
+                       avr_regbit_set(avr, p->wdce);
+
+                       avr_cycle_timer_register(avr, 4, avr_wdce_clear, p);
+               } else {
+                       if (wde_v) // wde can be set but not cleared
+                               avr_regbit_set(avr, p->wde);
+
+                       avr_regbit_setto_raw(avr, p->watchdog.enable, v);
+
+                       avr_watchdog_set_cycle_count_and_timer(avr, p, was_enabled, -1);
+               }
+       }
+}
+
+/*
+ * called by the core when a WTD instruction is found
+ */
+static int avr_watchdog_ioctl(
+               struct avr_io_t * port, uint32_t ctl, void * io_param)
+{
+       avr_watchdog_t * p = (avr_watchdog_t *)port;
+       int res = -1;
+
+       if (ctl == AVR_IOCTL_WATCHDOG_RESET) {
+               if (avr_regbit_get(p->io.avr, p->wde) ||
+                               avr_regbit_get(p->io.avr, p->watchdog.enable))
+                       avr_cycle_timer_register(p->io.avr, p->cycle_count,
+                                       avr_watchdog_timer, p);
+               res = 0;
+       }
+
+       return res;
+}
+
+static void avr_watchdog_irq_notify(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       avr_watchdog_t * p = (avr_watchdog_t *)param;
+       avr_t * avr = p->io.avr;
+
+       /* interrupt handling calls this twice...
+        * first when raised (during queuing), value = 1
+        * again when cleared (after servicing), value = 0
+        */
+
+       if (!value && avr_regbit_get(avr, p->watchdog.raised) && avr_regbit_get(avr, p->wde)) {
+               avr_regbit_clear(avr, p->watchdog.enable);
+       }
+}
+               
+static void avr_watchdog_reset(avr_io_t * port)
+{
+       avr_watchdog_t * p = (avr_watchdog_t *)port;
+       avr_t * avr = p->io.avr;
+
+       if (p->reset_context.wdrf) {
+               p->reset_context.wdrf = 0;
+               /*
+                * if watchdog reset kicked, then watchdog gets restarted at
+                * fastest interval
+                */
+               avr->run = p->reset_context.avr_run;
+
+               avr_regbit_set(avr, p->wde);
+               avr_regbit_set(avr, p->wdrf);
+               avr_regbit_set_array_from_value(avr, p->wdp, 4, 0);
+               
+               avr_watchdog_set_cycle_count_and_timer(avr, p, 0, 0);
+       }
+       /* TODO could now use the two pending/running IRQs to do the same
+        * as before */
+       avr_irq_register_notify(p->watchdog.irq, avr_watchdog_irq_notify, p);
+}
+
+static avr_io_t        _io = {
+       .kind = "watchdog",
+       .reset = avr_watchdog_reset,
+       .ioctl = avr_watchdog_ioctl,
+};
+
+void avr_watchdog_init(avr_t * avr, avr_watchdog_t * p)
+{
+       p->io = _io;
+
+       avr_register_io(avr, &p->io);
+       avr_register_vector(avr, &p->watchdog);
+
+       avr_register_io_write(avr, p->wdce.reg, avr_watchdog_write, p);
+
+       p->reset_context.wdrf = 0;
+}
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.h
new file mode 100644 (file)
index 0000000..da86371
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+       avr_watchdog.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __AVR_WATCHDOG_H___
+#define __AVR_WATCHDOG_H___
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+typedef struct avr_watchdog_t {
+       avr_io_t        io;
+
+       avr_regbit_t    wdrf;           // watchdog reset flag (in MCU Status Register)
+
+       avr_regbit_t    wdce;           // watchdog change enable
+       avr_regbit_t    wde;            // watchdog enabled
+       avr_regbit_t    wdp[4];         // watchdog Timer Prescaler
+
+       avr_int_vector_t watchdog;      // watchdog interrupt
+
+       avr_cycle_count_t       cycle_count;
+
+       struct {
+               uint8_t         wdrf;           // saved watchdog reset flag
+               avr_run_t       avr_run;        // restored during reset
+       } reset_context;
+} avr_watchdog_t;
+
+/* takes no parameter */
+#define AVR_IOCTL_WATCHDOG_RESET       AVR_IOCTL_DEF('w','d','t','r')
+
+void avr_watchdog_init(avr_t * avr, avr_watchdog_t * p);
+
+
+/*
+ * This helps declare a watchdog block into a core.
+ * No guarantee it will work with all, but it works
+ * with the one we have right now
+ */
+#define AVR_WATCHDOG_DECLARE(_WDSR, _vec) \
+       .watchdog = {\
+               .wdrf = AVR_IO_REGBIT(MCUSR, WDRF),\
+               .wdce = AVR_IO_REGBIT(_WDSR, WDCE),\
+               .wde = AVR_IO_REGBIT(_WDSR, WDE),\
+               .wdp = { AVR_IO_REGBIT(_WDSR, WDP0),AVR_IO_REGBIT(_WDSR, WDP1),\
+                               AVR_IO_REGBIT(_WDSR, WDP2),AVR_IO_REGBIT(_WDSR, WDP3) },\
+               .watchdog = {\
+                       .enable = AVR_IO_REGBIT(_WDSR, WDIE),\
+                       .raised = AVR_IO_REGBIT(_WDSR, WDIF),\
+                       .vector = _vec,\
+               },\
+       }
+
+/* no WDP3, WDIE, WDIF in atmega128 */
+/* MCUSR is called MCUCSR in atmega128 */
+#define AVR_WATCHDOG_DECLARE_128(_WDSR, _vec) \
+       .watchdog = {\
+               .wdrf = AVR_IO_REGBIT(MCUCSR, WDRF),\
+               .wdce = AVR_IO_REGBIT(_WDSR, WDCE),\
+               .wde = AVR_IO_REGBIT(_WDSR, WDE),\
+               .wdp = { AVR_IO_REGBIT(_WDSR, WDP0),AVR_IO_REGBIT(_WDSR, WDP1),\
+                               AVR_IO_REGBIT(_WDSR, WDP2) },\
+               .watchdog = {\
+                       .enable = AVR_IO_REGBIT(_WDSR, 6),\
+                       .raised = AVR_IO_REGBIT(_WDSR, 7),\
+                       .vector = _vec,\
+               },\
+       }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __AVR_WATCHDOG_H___ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/fifo_declare.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/fifo_declare.h
new file mode 100644 (file)
index 0000000..8a3b2fb
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+       fido_declare.h
+       Copyright (C) 2003-2012 Michel Pollet <buserror@gmail.com>
+
+       This library is free software; you can redistribute it and/or
+       modify it under the terms of the GNU Lesser General Public
+       License as published by the Free Software Foundation; either
+       version 2.1 of the License, or (at your option) any later version.
+
+       This library is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+       Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+       License along with this library; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/*
+ * FIFO helpers, aka circular buffers
+ *
+ * these macros define accessories for FIFOs of any name, type and
+ * any (power of two) size
+ */
+
+#ifndef __FIFO_DECLARE__
+#define __FIFO_DECLARE__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+       doing a :
+       DECLARE_FIFO(uint8_t, myfifo, 128);
+
+       will declare :
+       enum : myfifo_overflow_f
+       type : myfifo_t
+       functions:
+               // write a byte into the fifo, return 1 if there was room, 0 if there wasn't
+               int myfifo_write(myfifo_t *c, uint8_t b);
+               // reads a byte from the fifo, return 0 if empty. Use myfifo_isempty() to check beforehand
+               uint8_t myfifo_read(myfifo_t *c);
+               int myfifo_isfull(myfifo_t *c);
+               int myfifo_isempty(myfifo_t *c);
+               // returns number of items to read now
+               uint16_t myfifo_get_read_size(myfifo_t *c);
+               // read item at offset o from read cursor, no cursor advance
+               uint8_t myfifo_read_at(myfifo_t *c, uint16_t o);
+               // write b at offset o compared to current write cursor, no cursor advance
+               void myfifo_write_at(myfifo_t *c, uint16_t o, uint8_t b);
+
+       In your .c you need to 'implement' the fifo:
+       DEFINE_FIFO(uint8_t, myfifo)
+
+       To use the fifo, you must declare at least one :
+       myfifo_t fifo = FIFO_NULL;
+
+       while (!myfifo_isfull(&fifo))
+               myfifo_write(&fifo, 0xaa);
+       ....
+       while (!myfifo_isempty(&fifo))
+               b = myfifo_read(&fifo);
+ */
+
+#include <stdint.h>
+
+#if __AVR__
+#define FIFO_CURSOR_TYPE       uint8_t
+#define FIFO_BOOL_TYPE char
+#define FIFO_INLINE
+#define FIFO_SYNC
+#endif
+
+#ifndef        FIFO_CURSOR_TYPE
+#define FIFO_CURSOR_TYPE       uint16_t
+#endif
+#ifndef        FIFO_BOOL_TYPE
+#define FIFO_BOOL_TYPE int
+#endif
+#ifndef        FIFO_INLINE
+#define FIFO_INLINE    inline
+#endif
+
+/* We should not need volatile */
+#ifndef FIFO_VOLATILE
+#define FIFO_VOLATILE
+#endif
+#ifndef FIFO_SYNC
+#define FIFO_SYNC __sync_synchronize()
+#endif
+
+#ifndef FIFO_ZERO_INIT
+#define FIFO_ZERO_INIT {0}
+#endif
+#define FIFO_NULL { FIFO_ZERO_INIT, 0, 0, 0 }
+
+/* New compilers don't like unused static functions. However,
+ * we do like 'static inlines' for these small accessors,
+ * so we mark them as 'unused'. It stops it complaining */
+#ifdef __GNUC__
+#define FIFO_DECL static __attribute__ ((unused))
+#else
+#define FIFO_DECL static
+#endif
+
+#define DECLARE_FIFO(__type, __name, __size) \
+enum { __name##_overflow_f = (1 << 0) }; \
+enum { __name##_fifo_size = (__size) }; \
+typedef struct __name##_t {                    \
+       __type          buffer[__name##_fifo_size];             \
+       FIFO_VOLATILE FIFO_CURSOR_TYPE  read;           \
+       FIFO_VOLATILE FIFO_CURSOR_TYPE  write;          \
+       FIFO_VOLATILE uint8_t   flags;          \
+} __name##_t
+
+#define DEFINE_FIFO(__type, __name) \
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\
+{\
+       FIFO_CURSOR_TYPE now = c->write;\
+       FIFO_CURSOR_TYPE next = (now + 1) & (__name##_fifo_size-1);\
+       if (c->read != next) {  \
+               c->buffer[now] = b;\
+               FIFO_SYNC; \
+               c->write = next;\
+               return 1;\
+       }\
+       return 0;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\
+{\
+       FIFO_CURSOR_TYPE next = (c->write + 1) & (__name##_fifo_size-1);\
+       return c->read == next;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\
+{\
+       return c->read == c->write;\
+}\
+FIFO_DECL FIFO_INLINE __type __name##_read(__name##_t * c)\
+{\
+       __type res = FIFO_ZERO_INIT; \
+       FIFO_CURSOR_TYPE read = c->read;\
+       if (read == c->write)\
+               return res;\
+       res = c->buffer[read];\
+       FIFO_SYNC; \
+       c->read = (read + 1) & (__name##_fifo_size-1);\
+       return res;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\
+{\
+       return ((c->write + __name##_fifo_size) - c->read) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_write_size(__name##_t *c)\
+{\
+       return (__name##_fifo_size-1) - __name##_get_read_size(c);\
+}\
+FIFO_DECL FIFO_INLINE void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       FIFO_SYNC; \
+       c->read = (c->read + o) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       return c->buffer[(c->read + o) & (__name##_fifo_size-1)];\
+}\
+FIFO_DECL FIFO_INLINE void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\
+{\
+       c->buffer[(c->write + o) & (__name##_fifo_size-1)] = b;\
+}\
+FIFO_DECL FIFO_INLINE void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       FIFO_SYNC; \
+       c->write = (c->write + o) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE void __name##_reset(__name##_t *c)\
+{\
+       FIFO_SYNC; \
+       c->read = c->write = c->flags = 0;\
+}\
+struct __name##_t
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/run_avr.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/run_avr.c
new file mode 100644 (file)
index 0000000..dfa6fce
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+       run_avr.c
+
+       Copyright 2008, 2010 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string.h>
+#include <signal.h>
+#include "sim_avr.h"
+#include "sim_elf.h"
+#include "sim_core.h"
+#include "sim_gdb.h"
+#include "sim_hex.h"
+#include "sim_vcd_file.h"
+
+#include "sim_core_decl.h"
+
+static void
+display_usage(
+       const char * app)
+{
+       printf("Usage: %s [...] <firmware>\n", app);
+       printf(
+        "       [--help|-h|-?]      Display this usage message and exit\n"
+        "       [--list-cores]      List all supported AVR cores and exit\n"
+        "       [-v]                Raise verbosity level\n"
+        "                           (can be passed more than once)\n"
+        "       [--freq|-f <freq>]  Sets the frequency for an .hex firmware\n"
+        "       [--mcu|-m <device>] Sets the MCU type for an .hex firmware\n"
+        "       [--gdb|-g [<port>]] Listen for gdb connection on <port> "
+        "(default 1234)\n"
+#ifdef CONFIG_SIMAVR_TRACE
+        "       [--trace, -t]       Run full scale decoder trace\n"
+#else
+        "       [--trace, -t]       Run full scale decoder trace (Off)\n"
+#endif //CONFIG_SIMAVR_TRACE
+        "       [-ti <vector>]      Add traces for IRQ vector <vector>\n"
+        "       [--input|-i <file>] A VCD file to use as input signals\n"
+        "       [--output|-o <file>] A VCD file to save the traced signals\n"
+        "       [--add-trace|-at <name=kind@addr/mask>]\n"
+        "                           Add signal to be included in VCD output\n"
+        "       [-ff <.hex file>]   Load next .hex file as flash\n"
+        "       [-ee <.hex file>]   Load next .hex file as eeprom\n"
+        "       <firmware>          A .hex or an ELF file. ELF files are\n"
+        "                           preferred, and can include "
+        "debugging syms\n");
+       exit(1);
+}
+
+static void
+list_cores()
+{
+       printf( "Supported AVR cores:\n");
+       for (int i = 0; avr_kind[i]; i++) {
+               printf("       ");
+               for (int ti = 0; ti < 4 && avr_kind[i]->names[ti]; ti++)
+                       printf("%s ", avr_kind[i]->names[ti]);
+               printf("\n");
+       }
+       exit(1);
+}
+
+static avr_t * avr = NULL;
+
+static void
+sig_int(
+               int sign)
+{
+       printf("signal caught, simavr terminating\n");
+       if (avr)
+               avr_terminate(avr);
+       exit(0);
+}
+
+int
+main(
+               int argc,
+               char *argv[])
+{
+#ifdef CONFIG_SIMAVR_TRACE
+       int trace = 0;
+#endif //CONFIG_SIMAVR_TRACE
+       elf_firmware_t f = {{0}};
+       uint32_t f_cpu = 0;
+       int gdb = 0;
+       int log = 1;
+       int port = 1234;
+       char name[24] = "";
+       uint32_t loadBase = AVR_SEGMENT_OFFSET_FLASH;
+       int trace_vectors[8] = {0};
+       int trace_vectors_count = 0;
+       const char *vcd_input = NULL;
+
+       if (argc == 1)
+               display_usage(basename(argv[0]));
+
+       for (int pi = 1; pi < argc; pi++) {
+               if (!strcmp(argv[pi], "--list-cores")) {
+                       list_cores();
+               } else if (!strcmp(argv[pi], "-h") || !strcmp(argv[pi], "--help")) {
+                       display_usage(basename(argv[0]));
+               } else if (!strcmp(argv[pi], "-m") || !strcmp(argv[pi], "--mcu")) {
+                       if (pi < argc-1) {
+                               snprintf(name, sizeof(name), "%s", argv[++pi]);
+                               strcpy(f.mmcu, name);
+                       } else {
+                               display_usage(basename(argv[0]));
+                       }
+               } else if (!strcmp(argv[pi], "-f") || !strcmp(argv[pi], "--freq")) {
+                       if (pi < argc-1) {
+                               f_cpu = atoi(argv[++pi]);
+                               f.frequency = f_cpu;
+                       } else {
+                               display_usage(basename(argv[0]));
+                       }
+               } else if (!strcmp(argv[pi], "-i") || !strcmp(argv[pi], "--input")) {
+                       if (pi < argc-1)
+                               vcd_input = argv[++pi];
+                       else
+                               display_usage(basename(argv[0]));
+               } else if (!strcmp(argv[pi], "-o") ||
+                                  !strcmp(argv[pi], "--output")) {
+                       if (pi + 1 >= argc) {
+                               fprintf(stderr, "%s: missing mandatory argument for %s.\n", argv[0], argv[pi]);
+                               exit(1);
+                       }
+                       snprintf(f.tracename, sizeof(f.tracename), "%s", argv[++pi]);
+               } else if (!strcmp(argv[pi], "-t") ||
+                                  !strcmp(argv[pi], "--trace")) {
+#ifdef CONFIG_SIMAVR_TRACE
+                       trace++;
+#else
+                       fprintf(stderr,
+                                       "%s: tracing option '%s' requires "
+                                       "compilation option CONFIG_SIMAVR_TRACE.\n",
+                                       argv[0], argv[pi]);
+#endif //CONFIG_SIMAVR_TRACE
+               } else if (!strcmp(argv[pi], "-at") ||
+                                  !strcmp(argv[pi], "--add-trace")) {
+                       if (pi + 1 >= argc) {
+                               fprintf(stderr, "%s: missing mandatory argument for %s.\n", argv[0], argv[pi]);
+                               exit(1);
+                       }
+                       ++pi;
+                       struct {
+                               char     kind[64];
+                               uint8_t  mask;
+                               uint16_t addr;
+                               char     name[64];
+                       } trace;
+                       const int n_args = sscanf(
+                               argv[pi],
+                               "%63[^=]=%63[^@]@0x%hx/0x%hhx",
+                               &trace.name[0],
+                               &trace.kind[0],
+                               &trace.addr,
+                               &trace.mask
+                       );
+                       if (n_args != 4) {
+                               --pi;
+                               fprintf(stderr, "%s: format for %s is name=kind@addr/mask.\n", argv[0], argv[pi]);
+                               exit(1);
+                       }
+
+                       /****/ if (!strcmp(trace.kind, "portpin")) {
+                               f.trace[f.tracecount].kind = AVR_MMCU_TAG_VCD_PORTPIN;
+                       } else if (!strcmp(trace.kind, "irq")) {
+                               f.trace[f.tracecount].kind = AVR_MMCU_TAG_VCD_IRQ;
+                       } else if (!strcmp(trace.kind, "trace")) {
+                               f.trace[f.tracecount].kind = AVR_MMCU_TAG_VCD_TRACE;
+                       } else {
+                               fprintf(
+                                       stderr,
+                                       "%s: unknown trace kind '%s', not one of 'portpin', 'irq', or 'trace'.\n",
+                                       argv[0],
+                                       trace.kind
+                               );
+                               exit(1);
+                       }
+                       f.trace[f.tracecount].mask = trace.mask;
+                       f.trace[f.tracecount].addr = trace.addr;
+                       strncpy(f.trace[f.tracecount].name, trace.name, sizeof(f.trace[f.tracecount].name));
+
+                       printf(
+                               "Adding %s trace on address 0x%04x, mask 0x%02x ('%s')\n",
+                                 f.trace[f.tracecount].kind == AVR_MMCU_TAG_VCD_PORTPIN ? "portpin"
+                               : f.trace[f.tracecount].kind == AVR_MMCU_TAG_VCD_IRQ     ? "irq"
+                               : f.trace[f.tracecount].kind == AVR_MMCU_TAG_VCD_TRACE   ? "trace"
+                               : "unknown",
+                               f.trace[f.tracecount].addr,
+                               f.trace[f.tracecount].mask,
+                               f.trace[f.tracecount].name
+                       );
+
+                       ++f.tracecount;
+               } else if (!strcmp(argv[pi], "-ti")) {
+                       if (pi < argc-1)
+                               trace_vectors[trace_vectors_count++] = atoi(argv[++pi]);
+               } else if (!strcmp(argv[pi], "-g") ||
+                                  !strcmp(argv[pi], "--gdb")) {
+                       gdb++;
+                       if (pi < (argc-2) && argv[pi+1][0] != '-' )
+                               port = atoi(argv[++pi]);
+               } else if (!strcmp(argv[pi], "-v")) {
+                       log++;
+               } else if (!strcmp(argv[pi], "-ee")) {
+                       loadBase = AVR_SEGMENT_OFFSET_EEPROM;
+               } else if (!strcmp(argv[pi], "-ff")) {
+                       loadBase = AVR_SEGMENT_OFFSET_FLASH;
+               } else if (argv[pi][0] != '-') {
+                       sim_setup_firmware(argv[pi], loadBase, &f, argv[0]);
+               }
+       }
+
+       // Frequency and MCU type were set early so they can be checked when
+       // loading a hex file. Set them again because they can also be set
+       // in an ELF firmware file.
+
+       if (strlen(name))
+               strcpy(f.mmcu, name);
+       if (f_cpu)
+               f.frequency = f_cpu;
+
+       avr = avr_make_mcu_by_name(f.mmcu);
+       if (!avr) {
+               fprintf(stderr, "%s: AVR '%s' not known\n", argv[0], f.mmcu);
+               exit(1);
+       }
+       avr_init(avr);
+       avr->log = (log > LOG_TRACE ? LOG_TRACE : log);
+#ifdef CONFIG_SIMAVR_TRACE
+       avr->trace = trace;
+#endif //CONFIG_SIMAVR_TRACE
+
+       avr_load_firmware(avr, &f);
+       if (f.flashbase) {
+               printf("Attempted to load a bootloader at %04x\n", f.flashbase);
+               avr->pc = f.flashbase;
+       }
+       for (int ti = 0; ti < trace_vectors_count; ti++) {
+               for (int vi = 0; vi < avr->interrupts.vector_count; vi++)
+                       if (avr->interrupts.vector[vi]->vector == trace_vectors[ti])
+                               avr->interrupts.vector[vi]->trace = 1;
+       }
+       if (vcd_input) {
+               static avr_vcd_t input;
+               if (avr_vcd_init_input(avr, vcd_input, &input)) {
+                       fprintf(stderr, "%s: Warning: VCD input file %s failed\n", argv[0], vcd_input);
+               }
+       }
+
+       // even if not setup at startup, activate gdb if crashing
+       avr->gdb_port = port;
+       if (gdb) {
+               avr->state = cpu_Stopped;
+               avr_gdb_init(avr);
+       }
+
+       signal(SIGINT, sig_int);
+       signal(SIGTERM, sig_int);
+
+       for (;;) {
+               int state = avr_run(avr);
+               if (state == cpu_Done || state == cpu_Crashed)
+                       break;
+       }
+
+       avr_terminate(avr);
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.c
new file mode 100644 (file)
index 0000000..24cb244
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+       sim_avr.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "sim_avr.h"
+#include "sim_core.h"
+#include "sim_time.h"
+#include "sim_gdb.h"
+#include "avr_uart.h"
+#include "sim_vcd_file.h"
+#include "avr/avr_mcu_section.h"
+
+#define AVR_KIND_DECL
+#include "sim_core_decl.h"
+
+static void
+std_logger(
+               avr_t * avr,
+               const int level,
+               const char * format,
+               va_list ap);
+static avr_logger_p _avr_global_logger = std_logger;
+
+void
+avr_global_logger(
+               struct avr_t* avr,
+               const int level,
+               const char * format,
+               ... )
+{
+       va_list args;
+       va_start(args, format);
+       if (_avr_global_logger)
+               _avr_global_logger(avr, level, format, args);
+       va_end(args);
+}
+
+void
+avr_global_logger_set(
+               avr_logger_p logger)
+{
+       _avr_global_logger = logger ? logger : std_logger;
+}
+
+avr_logger_p
+avr_global_logger_get(void)
+{
+       return _avr_global_logger;
+}
+
+uint64_t
+avr_get_time_stamp(
+               avr_t * avr )
+{
+       uint64_t stamp;
+#ifndef CLOCK_MONOTONIC_RAW
+       /* CLOCK_MONOTONIC_RAW isn't portable, here is the POSIX alternative.
+        * Only downside is that it will drift if the system clock changes */
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       stamp = (((uint64_t)tv.tv_sec) * 1E9) + (tv.tv_usec * 1000);
+#else
+       struct timespec tp;
+       clock_gettime(CLOCK_MONOTONIC_RAW, &tp);
+       stamp = (tp.tv_sec * 1E9) + tp.tv_nsec;
+#endif
+       if (!avr->time_base)
+               avr->time_base = stamp;
+       return stamp - avr->time_base;
+}
+
+int
+avr_init(
+               avr_t * avr)
+{
+       avr->flash = malloc(avr->flashend + 4);
+       memset(avr->flash, 0xff, avr->flashend + 1);
+       *((uint16_t*)&avr->flash[avr->flashend + 1]) = AVR_OVERFLOW_OPCODE;
+       avr->codeend = avr->flashend;
+       avr->data = malloc(avr->ramend + 1);
+       memset(avr->data, 0, avr->ramend + 1);
+#ifdef CONFIG_SIMAVR_TRACE
+       avr->trace_data = calloc(1, sizeof(struct avr_trace_data_t));
+#endif
+
+       AVR_LOG(avr, LOG_TRACE, "%s init\n", avr->mmcu);
+
+       // cpu is in limbo before init is finished.
+       avr->state = cpu_Limbo;
+       avr->frequency = 1000000;       // can be overridden via avr_mcu_section
+       avr_cmd_init(avr);
+       avr_interrupt_init(avr);
+       if (avr->custom.init)
+               avr->custom.init(avr, avr->custom.data);
+       if (avr->init)
+               avr->init(avr);
+       // set default (non gdb) fast callbacks
+       avr->run = avr_callback_run_raw;
+       avr->sleep = avr_callback_sleep_raw;
+       // number of address bytes to push/pull on/off the stack
+       avr->address_size = avr->eind ? 3 : 2;
+       avr->log = 1;
+       avr_reset(avr);
+       avr_regbit_set(avr, avr->reset_flags.porf);             // by  default set to power-on reset
+       return 0;
+}
+
+void
+avr_terminate(
+               avr_t * avr)
+{
+       if (avr->custom.deinit)
+               avr->custom.deinit(avr, avr->custom.data);
+       if (avr->gdb) {
+               avr_deinit_gdb(avr);
+               avr->gdb = NULL;
+       }
+       if (avr->vcd) {
+               avr_vcd_close(avr->vcd);
+               avr->vcd = NULL;
+       }
+       avr_deallocate_ios(avr);
+
+       if (avr->flash) free(avr->flash);
+       if (avr->data) free(avr->data);
+       if (avr->io_console_buffer.buf) {
+               avr->io_console_buffer.len = 0;
+               avr->io_console_buffer.size = 0;
+               free(avr->io_console_buffer.buf);
+               avr->io_console_buffer.buf = NULL;
+       }
+       avr->flash = avr->data = NULL;
+}
+
+void
+avr_reset(
+               avr_t * avr)
+{
+       AVR_LOG(avr, LOG_TRACE, "%s reset\n", avr->mmcu);
+
+       avr->state = cpu_Running;
+       for(int i = 0x20; i <= avr->ioend; i++)
+               avr->data[i] = 0;
+       _avr_sp_set(avr, avr->ramend);
+       avr->pc = avr->reset_pc;        // Likely to be zero
+       for (int i = 0; i < 8; i++)
+               avr->sreg[i] = 0;
+       avr_interrupt_reset(avr);
+       avr_cycle_timer_reset(avr);
+       if (avr->reset)
+               avr->reset(avr);
+       avr_io_t * port = avr->io_port;
+       while (port) {
+               if (port->reset)
+                       port->reset(port);
+               port = port->next;
+       }
+       avr->cycle = 0; // Prevent crash
+}
+
+void
+avr_sadly_crashed(
+               avr_t *avr,
+               uint8_t signal)
+{
+       AVR_LOG(avr, LOG_ERROR, "%s\n", __FUNCTION__);
+       avr->state = cpu_Stopped;
+       if (avr->gdb_port) {
+               // enable gdb server, and wait
+               if (!avr->gdb)
+                       avr_gdb_init(avr);
+       }
+       if (!avr->gdb)
+               avr->state = cpu_Crashed;
+}
+
+void
+avr_set_command_register(
+               avr_t * avr,
+               avr_io_addr_t addr)
+{
+       avr_cmd_set_register(avr, addr);
+}
+
+static void
+_avr_io_console_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       if (v == '\r' && avr->io_console_buffer.buf) {
+               avr->io_console_buffer.buf[avr->io_console_buffer.len] = 0;
+               AVR_LOG(avr, LOG_OUTPUT, "O:" "%s" "" "\n",
+                       avr->io_console_buffer.buf);
+               avr->io_console_buffer.len = 0;
+               return;
+       }
+       if (avr->io_console_buffer.len + 1 >= avr->io_console_buffer.size) {
+               avr->io_console_buffer.size += 128;
+               avr->io_console_buffer.buf = (char*)realloc(
+                       avr->io_console_buffer.buf,
+                       avr->io_console_buffer.size);
+       }
+       if (v >= ' ')
+               avr->io_console_buffer.buf[avr->io_console_buffer.len++] = v;
+}
+
+void
+avr_set_console_register(
+               avr_t * avr,
+               avr_io_addr_t addr)
+{
+       if (addr)
+               avr_register_io_write(avr, addr, _avr_io_console_write, NULL);
+}
+
+void
+avr_loadcode(
+               avr_t * avr,
+               uint8_t * code,
+               uint32_t size,
+               avr_flashaddr_t address)
+{
+       if ((address + size) > avr->flashend+1) {
+               AVR_LOG(avr, LOG_ERROR, "avr_loadcode(): Attempted to load code of size %d but flash size is only %d.\n",
+                       size, avr->flashend + 1);
+               abort();
+       }
+       memcpy(avr->flash + address, code, size);
+}
+
+/**
+ * Accumulates sleep requests (and returns a sleep time of 0) until
+ * a minimum count of requested sleep microseconds are reached
+ * (low amounts cannot be handled accurately).
+ */
+uint32_t
+avr_pending_sleep_usec(
+               avr_t * avr,
+               avr_cycle_count_t howLong)
+{
+       avr->sleep_usec += avr_cycles_to_usec(avr, howLong);
+       uint32_t usec = avr->sleep_usec;
+       if (usec > 200) {
+               avr->sleep_usec = 0;
+               return usec;
+       }
+       return 0;
+}
+
+void
+avr_callback_sleep_gdb(
+               avr_t * avr,
+               avr_cycle_count_t howLong)
+{
+       uint32_t usec = avr_pending_sleep_usec(avr, howLong);
+       while (avr_gdb_processor(avr, usec))
+               ;
+}
+
+void
+avr_callback_run_gdb(
+               avr_t * avr)
+{
+       avr_gdb_processor(avr, avr->state == cpu_Stopped ? 50000 : 0);
+
+       if (avr->state == cpu_Stopped)
+               return ;
+
+       // if we are stepping one instruction, we "run" for one..
+       int step = avr->state == cpu_Step;
+       if (step)
+               avr->state = cpu_Running;
+
+       avr_flashaddr_t new_pc = avr->pc;
+
+       if (avr->state == cpu_Running) {
+               new_pc = avr_run_one(avr);
+#if CONFIG_SIMAVR_TRACE
+               avr_dump_state(avr);
+#endif
+       }
+
+       // run the cycle timers, get the suggested sleep time
+       // until the next timer is due
+       avr_cycle_count_t sleep = avr_cycle_timer_process(avr);
+
+       avr->pc = new_pc;
+
+       if (avr->state == cpu_Sleeping) {
+               if (!avr->sreg[S_I]) {
+                       if (avr->log)
+                               AVR_LOG(avr, LOG_TRACE, "simavr: sleeping with interrupts off, quitting gracefully\n");
+                       avr->state = cpu_Done;
+                       return;
+               }
+               /*
+                * try to sleep for as long as we can (?)
+                */
+               avr->sleep(avr, sleep);
+               avr->cycle += 1 + sleep;
+       }
+       // Interrupt servicing might change the PC too, during 'sleep'
+       if (avr->state == cpu_Running || avr->state == cpu_Sleeping)
+               avr_service_interrupts(avr);
+
+       // if we were stepping, use this state to inform remote gdb
+       if (step)
+               avr->state = cpu_StepDone;
+}
+
+/*
+To avoid simulated time and wall clock time to diverge over time
+this function tries to keep them in sync (roughly) by sleeping
+for the time required to match the expected sleep deadline
+in wall clock time.
+*/
+void
+avr_callback_sleep_raw(
+               avr_t *avr,
+               avr_cycle_count_t how_long)
+{
+       /* figure out how long we should wait to match the sleep deadline */
+       uint64_t deadline_ns = avr_cycles_to_nsec(avr, avr->cycle + how_long);
+       uint64_t runtime_ns = avr_get_time_stamp(avr);
+       if (runtime_ns >= deadline_ns)
+               return;
+       uint64_t sleep_us = (deadline_ns - runtime_ns) / 1000;
+       usleep(sleep_us);
+       return;
+}
+
+void
+avr_callback_run_raw(
+               avr_t * avr)
+{
+       avr_flashaddr_t new_pc = avr->pc;
+
+       if (avr->state == cpu_Running) {
+               new_pc = avr_run_one(avr);
+#if CONFIG_SIMAVR_TRACE
+               avr_dump_state(avr);
+#endif
+       }
+
+       // run the cycle timers, get the suggested sleep time
+       // until the next timer is due
+       avr_cycle_count_t sleep = avr_cycle_timer_process(avr);
+
+       avr->pc = new_pc;
+
+       if (avr->state == cpu_Sleeping) {
+               if (!avr->sreg[S_I]) {
+                       if (avr->log)
+                               AVR_LOG(avr, LOG_TRACE, "simavr: sleeping with interrupts off, quitting gracefully\n");
+                       avr->state = cpu_Done;
+                       return;
+               }
+               /*
+                * try to sleep for as long as we can (?)
+                */
+               avr->sleep(avr, sleep);
+               avr->cycle += 1 + sleep;
+       }
+       // Interrupt servicing might change the PC too, during 'sleep'
+       if (avr->state == cpu_Running || avr->state == cpu_Sleeping) {
+               /* Note: checking interrupt_state here is completely superfluous, however
+                       as interrupt_state tells us all we really need to know, here
+                       a simple check here may be cheaper than a call not needed. */
+               if (avr->interrupt_state)
+                       avr_service_interrupts(avr);
+       }
+}
+
+
+int
+avr_run(
+               avr_t * avr)
+{
+       avr->run(avr);
+       return avr->state;
+}
+
+avr_t *
+avr_core_allocate(
+               const avr_t * core,
+               uint32_t coreLen)
+{
+       uint8_t * b = malloc(coreLen);
+       memcpy(b, core, coreLen);
+       return (avr_t *)b;
+}
+
+avr_t *
+avr_make_mcu_by_name(
+               const char *name)
+{
+       avr_kind_t * maker = NULL;
+       for (int i = 0; avr_kind[i] && !maker; i++) {
+               for (int j = 0; avr_kind[i]->names[j]; j++)
+                       if (!strcmp(avr_kind[i]->names[j], name)) {
+                               maker = avr_kind[i];
+                               break;
+                       }
+       }
+       if (!maker) {
+               AVR_LOG(((avr_t*)0), LOG_ERROR, "%s: AVR '%s' not known\n", __FUNCTION__, name);
+               return NULL;
+       }
+
+       avr_t * avr = maker->make();
+       AVR_LOG(avr, LOG_TRACE, "Starting %s - flashend %04x ramend %04x e2end %04x\n",
+                       avr->mmcu, avr->flashend, avr->ramend, avr->e2end);
+       return avr;
+}
+
+static void
+std_logger(
+               avr_t * avr,
+               const int level,
+               const char * format,
+               va_list ap)
+{
+       if (!avr || avr->log >= level) {
+               vfprintf((level < LOG_ERROR) ?  stdout : stderr, format, ap);
+       }
+}
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.h
new file mode 100644 (file)
index 0000000..e9a97cd
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+       sim_avr.h
+
+       Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_AVR_H__
+#define __SIM_AVR_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __has_attribute
+       #define __has_attribute(x) 0
+#endif
+
+#if __has_attribute(fallthrough)
+       #define FALLTHROUGH __attribute__((fallthrough));
+#else
+       #define FALLTHROUGH
+#endif
+
+#include "sim_irq.h"
+#include "sim_interrupts.h"
+#include "sim_cmds.h"
+#include "sim_cycle_timers.h"
+
+typedef uint32_t avr_flashaddr_t;
+
+struct avr_t;
+typedef uint8_t (*avr_io_read_t)(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               void * param);
+typedef void (*avr_io_write_t)(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param);
+
+enum {
+       // SREG bit indexes
+       S_C = 0,S_Z,S_N,S_V,S_S,S_H,S_T,S_I,
+
+       // 16 bits register pairs
+       R_XL    = 0x1a, R_XH,R_YL,R_YH,R_ZL,R_ZH,
+       // stack pointer
+       R_SPL   = 32+0x3d, R_SPH,
+       // real SREG
+       R_SREG  = 32+0x3f,
+
+       // maximum number of IO registers, on normal AVRs
+       MAX_IOs = 280,  // Bigger AVRs need more than 256-32 (mega1280)
+};
+
+#define AVR_DATA_TO_IO(v) ((v) - 32)
+#define AVR_IO_TO_DATA(v) ((v) + 32)
+
+/**
+ * Logging macros and associated log levels.
+ * The current log level is kept in avr->log.
+ */
+enum {
+       LOG_NONE = 0,
+       LOG_OUTPUT,
+       LOG_ERROR,
+       LOG_WARNING,
+       LOG_TRACE,
+       LOG_DEBUG,
+};
+
+
+#ifndef AVR_LOG
+#define AVR_LOG(avr, level, ...) \
+       do { \
+               avr_global_logger(avr, level, __VA_ARGS__); \
+       } while(0)
+#endif
+#define AVR_TRACE(avr, ... ) \
+       AVR_LOG(avr, LOG_TRACE, __VA_ARGS__)
+
+/*
+ * Core states.
+ */
+enum {
+       cpu_Limbo = 0,  // before initialization is finished
+       cpu_Stopped,    // all is stopped, timers included
+
+       cpu_Running,    // we're free running
+
+       cpu_Sleeping,   // we're now sleeping until an interrupt
+
+       cpu_Step,               // run ONE instruction, then...
+       cpu_StepDone,   // tell gdb it's all OK, and give it registers
+       cpu_Done,       // avr software stopped gracefully
+       cpu_Crashed,    // avr software crashed (watchdog fired)
+};
+
+// this is only ever used if CONFIG_SIMAVR_TRACE is defined
+struct avr_trace_data_t {
+       struct avr_symbol_t ** codeline;
+
+       /* DEBUG ONLY
+        * this keeps track of "jumps" ie, call,jmp,ret,reti and so on
+        * allows dumping of a meaningful data even if the stack is
+        * munched and so on
+        */
+       #define OLD_PC_SIZE     32
+       struct {
+               uint32_t pc;
+               uint16_t sp;
+       } old[OLD_PC_SIZE]; // catches reset..
+       int                     old_pci;
+
+#if AVR_STACK_WATCH
+       #define STACK_FRAME_SIZE        32
+       // this records the call/ret pairs, to try to catch
+       // code that munches the stack -under- their own frame
+       struct {
+               uint32_t        pc;
+               uint16_t        sp;
+       } stack_frame[STACK_FRAME_SIZE];
+       int                     stack_frame_index;
+#endif
+
+       // DEBUG ONLY
+       // keeps track of which registers gets touched by instructions
+       // reset before each new instructions. Allows meaningful traces
+       uint32_t        touched[256 / 32];      // debug
+};
+
+typedef void (*avr_run_t)(
+               struct avr_t * avr);
+
+#define AVR_FUSE_LOW   0
+#define AVR_FUSE_HIGH  1
+#define AVR_FUSE_EXT   2
+
+#define REG_NAME_COUNT (256 + 32)       // Size of reg_names table.
+
+/*
+ * Main AVR instance. Some of these fields are set by the AVR "Core" definition files
+ * the rest is runtime data (as little as possible)
+ */
+typedef struct avr_t {
+       const char *            mmcu;   // name of the AVR
+       // these are filled by sim_core_declare from constants in /usr/lib/avr/include/avr/io*.h
+       uint16_t                        ioend;
+       uint16_t                        ramend;
+       uint32_t                        flashend;
+       uint32_t                        e2end;
+       uint8_t                         vector_size;
+       uint8_t                         signature[3];
+       uint8_t                         fuse[6];
+       uint8_t                         lockbits;
+       avr_io_addr_t           rampz;  // optional, only for ELPM/SPM on >64Kb cores
+       avr_io_addr_t           eind;   // optional, only for EIJMP/EICALL on >64Kb cores
+       uint8_t                         address_size;   // 2, or 3 for cores >128KB in flash
+       struct {
+               avr_regbit_t            porf;
+               avr_regbit_t            extrf;
+               avr_regbit_t            borf;
+               avr_regbit_t            wdrf;
+       } reset_flags;
+
+       // filled by the ELF data, this allow tracking of invalid jumps
+       uint32_t                        codeend;
+
+       int                                     state;          // stopped, running, sleeping
+       uint32_t                        frequency;      // frequency we are running at
+       // mostly used by the ADC for now
+       uint32_t                        vcc,avcc,aref; // (optional) voltages in millivolts
+
+       // cycles gets incremented when sleeping and when running; it corresponds
+       // not only to "cycles that runs" but also "cycles that might have run"
+       // like, sleeping.
+       avr_cycle_count_t       cycle;          // current cycle
+
+       // these next two allow the core to freely run between cycle timers and also allows
+       // for a maximum run cycle limit... run_cycle_count is set during cycle timer processing.
+       avr_cycle_count_t       run_cycle_count;        // cycles to run before next timer
+       avr_cycle_count_t       run_cycle_limit;        // maximum run cycle interval limit
+
+       /**
+        * Sleep requests are accumulated in sleep_usec until the minimum sleep value
+        * is reached, at which point sleep_usec is cleared and the sleep request
+        * is passed on to the operating system.
+        */
+       uint32_t                        sleep_usec;
+       uint64_t                        time_base;      // for avr_get_time_stamp()
+
+       // called at init time
+       void (*init)(struct avr_t * avr);
+       // called at reset time
+       void (*reset)(struct avr_t * avr);
+
+       struct {
+               // called at init time (for special purposes like using a
+               // memory mapped file as flash see: simduino)
+               void (*init)(struct avr_t * avr, void * data);
+               // called at termination time ( to clean special initializations)
+               void (*deinit)(struct avr_t * avr, void * data);
+               // value passed to init() and deinit()
+               void *data;
+       } custom;
+
+       /*!
+        * Default AVR core run function.
+        * Two modes are available, a "raw" run that goes as fast as
+        * it can, and a "gdb" mode that also watchouts for gdb events
+        * and is a little bit slower.
+        */
+       avr_run_t       run;
+
+       /*!
+        * Sleep default behaviour.
+        * In "raw" mode, it calls usleep, in gdb mode, it waits
+        * for howLong for gdb command on it's sockets.
+        */
+       void (*sleep)(struct avr_t * avr, avr_cycle_count_t howLong);
+
+       /*!
+        * Every IRQs will be stored in this pool. It is not
+        * mandatory (yet) but will allow listing IRQs and their connections
+        */
+       avr_irq_pool_t  irq_pool;
+
+       // Mirror of the SREG register, to facilitate the access to bits
+       // in the opcode decoder.
+       // This array is re-synthesized back/forth when SREG changes
+       uint8_t         sreg[8];
+
+       /* Interrupt state:
+               00: idle (no wait, no pending interrupts) or disabled
+               <0: wait till zero
+               >0: interrupt pending */
+       int8_t                  interrupt_state;        // interrupt state
+
+       /*
+        * ** current PC **
+        * Note that the PC is representing /bytes/ while the AVR value is
+        * assumed to be "words". This is in line with what GDB does...
+        * this is why you will see >>1 and <<1 in the decoder to handle jumps.
+        * It CAN be a little confusing, so concentrate, young grasshopper.
+        */
+       avr_flashaddr_t pc;
+       /*
+        * Reset PC, this is the value used to jump to at reset time, this
+        * allow support for bootloaders
+        */
+       avr_flashaddr_t reset_pc;
+
+       /*
+        * callback when specific IO registers are read/written.
+        * There is one drawback here, there is in way of knowing what is the
+        * "beginning of useful sram" on a core, so there is no way to deduce
+        * what is the maximum IO register for a core, and thus, we can't
+        * allocate this table dynamically.
+        * If you wanted to emulate the BIG AVRs, and XMegas, this would need
+        * work.
+        */
+       struct {
+               struct avr_irq_t * irq; // optional, used only if asked for with avr_iomem_getirq()
+               struct {
+                       void * param;
+                       avr_io_read_t c;
+               } r;
+               struct {
+                       void * param;
+                       avr_io_write_t c;
+               } w;
+       } io[MAX_IOs];
+
+       /*
+        * This block allows sharing of the IO write/read on addresses between
+        * multiple callbacks. In 99% of case it's not needed, however on the tiny*
+        * (tiny85 at last) some registers have bits that are used by different
+        * IO modules.
+        * If this case is detected, a special "dispatch" callback is installed that
+        * will handle this particular case, without impacting the performance of the
+        * other, normal cases...
+        */
+       int                             io_shared_io_count;
+       struct {
+               int used;
+               struct {
+                       void * param;
+                       void * c;
+               } io[4];
+       } io_shared_io[4];
+
+       // flash memory (initialized to 0xff, and code loaded into it)
+       uint8_t *               flash;
+       // this is the general purpose registers, IO registers, and SRAM
+       uint8_t *               data;
+
+       // queue of io modules
+       struct avr_io_t * io_port;
+
+       // Builtin and user-defined commands
+       avr_cmd_table_t commands;
+       // cycle timers tracking & delivery
+       avr_cycle_timer_pool_t  cycle_timers;
+       // interrupt vectors and delivery fifo
+       avr_int_table_t interrupts;
+
+       // DEBUG ONLY -- value ignored if CONFIG_SIMAVR_TRACE = 0
+       uint8_t trace : 1,
+                       log : 4; // log level, default to 1
+
+       // Only used if CONFIG_SIMAVR_TRACE is defined
+       struct avr_trace_data_t *trace_data;
+
+       // VALUE CHANGE DUMP file (waveforms)
+       // this is the VCD file that gets allocated if the
+       // firmware that is loaded explicitly asks for a trace
+       // to be generated, and allocates it's own symbols
+       // using AVR_MMCU_TAG_VCD_TRACE (see avr_mcu_section.h)
+       struct avr_vcd_t * vcd;
+
+       // gdb hooking structure. Only present when gdb server is active
+       struct avr_gdb_t * gdb;
+
+       // if non-zero, the gdb server will be started when the core
+       // crashed even if not activated at startup
+       // if zero, the simulator will just exit() in case of a crash
+       int             gdb_port;
+
+       // buffer for console debugging output from register
+       struct {
+               char *   buf;
+               uint32_t size;
+               uint32_t len;
+       } io_console_buffer;
+} avr_t;
+
+
+// this is a static constructor for each of the AVR devices
+typedef struct avr_kind_t {
+       const char * names[4];  // name aliases
+       avr_t * (*make)(void);
+} avr_kind_t;
+
+// a symbol loaded from the .elf file
+typedef struct avr_symbol_t {
+       uint32_t        addr;
+       uint32_t        size;
+       const char  symbol[0];
+} avr_symbol_t;
+
+// locate the maker for mcu "name" and allocates a new avr instance
+avr_t *
+avr_make_mcu_by_name(
+               const char *name);
+// initializes a new AVR instance. Will call the IO registers init(), and then reset()
+int
+avr_init(
+               avr_t * avr);
+// Used by the cores, allocated a mutable avr_t from the const global
+avr_t *
+avr_core_allocate(
+               const avr_t * core,
+               uint32_t coreLen);
+
+// resets the AVR, and the IO modules
+void
+avr_reset(
+               avr_t * avr);
+// run one cycle of the AVR, sleep if necessary
+int
+avr_run(
+               avr_t * avr);
+// finish any pending operations
+void
+avr_terminate(
+               avr_t * avr);
+
+// set an IO register to receive commands from the AVR firmware
+// it's optional, and uses the ELF tags
+void
+avr_set_command_register(
+               avr_t * avr,
+               avr_io_addr_t addr);
+
+// specify the "console register" -- output sent to this register
+// is printed on the simulator console, without using a UART
+void
+avr_set_console_register(
+               avr_t * avr,
+               avr_io_addr_t addr);
+
+// load code in the "flash"
+void
+avr_loadcode(
+               avr_t * avr,
+               uint8_t * code,
+               uint32_t size,
+               avr_flashaddr_t address);
+
+/*
+ * These are accessors for avr->data but allows watchpoints to be set for gdb
+ * IO modules use that to set values to registers, and the AVR core decoder uses
+ * that to register "public" read by instructions.
+ */
+void
+avr_core_watch_write(
+               avr_t *avr,
+               uint16_t addr,
+               uint8_t v);
+uint8_t
+avr_core_watch_read(
+               avr_t *avr,
+               uint16_t addr);
+
+// called when the core has detected a crash somehow.
+// this might activate gdb server
+void
+avr_sadly_crashed(
+               avr_t *avr,
+               uint8_t signal);
+
+/*
+ * Logs a message using the current logger
+ */
+void
+avr_global_logger(
+               struct avr_t* avr,
+               const int level,
+               const char * format,
+               ... );
+
+#ifndef AVR_CORE
+#include <stdarg.h>
+/*
+ * Type for custom logging functions
+ */
+typedef void (*avr_logger_p)(struct avr_t* avr, const int level, const char * format, va_list ap);
+
+/* Sets a global logging function in place of the default */
+void
+avr_global_logger_set(
+               avr_logger_p logger);
+/* Gets the current global logger function */
+avr_logger_p
+avr_global_logger_get(void);
+#endif
+
+/*
+ * These are callbacks for the two 'main' behaviour in simavr
+ */
+void avr_callback_sleep_gdb(avr_t * avr, avr_cycle_count_t howLong);
+void avr_callback_run_gdb(avr_t * avr);
+void avr_callback_sleep_raw(avr_t * avr, avr_cycle_count_t howLong);
+void avr_callback_run_raw(avr_t * avr);
+
+/**
+ * Accumulates sleep requests (and returns a sleep time of 0) until
+ * a minimum count of requested sleep microseconds are reached
+ * (low amounts cannot be handled accurately).
+ * This function is an utility function for the sleep callbacks
+ */
+uint32_t
+avr_pending_sleep_usec(
+               avr_t * avr,
+               avr_cycle_count_t howLong);
+/* Return the number of 'real time' spent since sim started, in uS */
+uint64_t
+avr_get_time_stamp(
+               avr_t * avr );
+
+#ifdef __cplusplus
+};
+#endif
+
+#include "sim_io.h"
+#include "sim_regbit.h"
+
+#ifdef __GNUC__
+
+# ifndef likely
+#  define likely(x) __builtin_expect(!!(x), 1)
+# endif
+
+# ifndef unlikely
+#  define unlikely(x) __builtin_expect(!!(x), 0)
+# endif
+
+#else /* ! __GNUC__ */
+
+# ifndef likely
+#  define likely(x) x
+# endif
+
+# ifndef unlikely
+#  define unlikely(x) x
+# endif
+
+#endif /* __GNUC__ */
+
+#endif /*__SIM_AVR_H__*/
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr_types.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr_types.h
new file mode 100644 (file)
index 0000000..b15bafc
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+       sim_avr_types.h
+
+       Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __SIM_AVR_TYPES_H___
+#define __SIM_AVR_TYPES_H___
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <inttypes.h>
+
+typedef uint64_t       avr_cycle_count_t;
+typedef uint16_t       avr_io_addr_t;
+
+/*
+ * this 'structure' is a packed representation of an IO register 'bit'
+ * (or consecutive bits). This allows a way to set/get/clear them.
+ * gcc is happy passing these as register value, so you don't need to
+ * use a pointer when passing them along to functions.
+ *
+ * 9 bits ought to be enough, as it's the maximum I've seen (atmega2560)
+ */
+typedef struct avr_regbit_t {
+       uint32_t reg : 9, bit : 3, mask : 8;
+} avr_regbit_t;
+
+// printf() conversion specifier for avr_cycle_count_t
+#define PRI_avr_cycle_count PRIu64
+
+struct avr_t;
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_AVR_TYPES_H___ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.c
new file mode 100644 (file)
index 0000000..587f259
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+       sim_cmds.c
+
+       Copyright 2014 Florian Albrechtskirchinger <falbrechtskirchinger@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include "sim_avr.h"
+#include "sim_cmds.h"
+#include "sim_vcd_file.h"
+#include "avr_uart.h"
+#include "avr/avr_mcu_section.h"
+
+#define LOG_PREFIX             "CMDS: "
+
+static void
+_avr_cmd_io_write(
+               avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       avr_cmd_table_t * commands = &avr->commands;
+       avr_cmd_t * command = commands->pending;
+
+       AVR_LOG(avr, LOG_TRACE, LOG_PREFIX "%s: 0x%02x\n", __FUNCTION__, v);
+
+       if (!command) {
+               if (v > MAX_AVR_COMMANDS) {
+                       AVR_LOG(avr, LOG_ERROR, LOG_PREFIX
+                               "%s: code 0x%02x outside permissible range (>0x%02x)\n",
+                               __FUNCTION__, v, MAX_AVR_COMMANDS - 1);
+                       return;
+               }
+               command = &commands->table[v];
+       }
+       if (!command->handler) {
+               AVR_LOG(avr, LOG_ERROR, LOG_PREFIX
+                       "%s: code 0x%02x has no handler (wrong MMCU config)\n",
+                       __FUNCTION__, v);
+               return;
+       }
+
+       if (command) {
+               if (command->handler(avr, v, command->param))
+                       commands->pending = command;
+               else
+                       commands->pending = NULL;
+       } else
+               AVR_LOG(avr, LOG_TRACE, LOG_PREFIX "%s: unknown command 0x%02x\n",
+                       __FUNCTION__, v);
+}
+
+void
+avr_cmd_set_register(
+               avr_t * avr,
+               avr_io_addr_t addr)
+{
+       if (addr)
+               avr_register_io_write(avr, addr, &_avr_cmd_io_write, NULL);
+}
+
+void
+avr_cmd_register(
+               avr_t * avr,
+               uint8_t code,
+               avr_cmd_handler_t handler,
+               void * param)
+{
+       avr_cmd_table_t * commands = &avr->commands;
+       avr_cmd_t * command;
+
+       if (!handler)
+               return;
+
+       if (code > MAX_AVR_COMMANDS) {
+               AVR_LOG(avr, LOG_ERROR, LOG_PREFIX
+                       "%s: code 0x%02x outside permissible range (>0x%02x)\n",
+                       __FUNCTION__, code, MAX_AVR_COMMANDS - 1);
+               return;
+       }
+
+       command = &commands->table[code];
+       if (command->handler) {
+               AVR_LOG(avr, LOG_ERROR, LOG_PREFIX
+                       "%s: code 0x%02x is already registered\n",
+                       __FUNCTION__, code);
+               return;
+       }
+
+       command->handler = handler;
+       command->param = param;
+}
+
+void
+avr_cmd_unregister(
+               avr_t * avr,
+               uint8_t code)
+{
+       avr_cmd_table_t * commands = &avr->commands;
+       avr_cmd_t * command;
+
+       if (code > MAX_AVR_COMMANDS) {
+               AVR_LOG(avr, LOG_ERROR, LOG_PREFIX
+                       "%s: code 0x%02x outside permissible range (>0x%02x)\n",
+                       __FUNCTION__, code, MAX_AVR_COMMANDS - 1);
+               return;
+       }
+
+       command = &commands->table[code];
+       if (command->handler) {
+               if(command->param)
+                       free(command->param);
+
+               command->handler = NULL;
+               command->param = NULL;
+       } else
+               AVR_LOG(avr, LOG_ERROR, LOG_PREFIX
+                       "%s: no command registered for code 0x%02x\n",
+                       __FUNCTION__, code);
+}
+
+static int
+_simavr_cmd_vcd_start_trace(
+               avr_t * avr,
+               uint8_t v,
+               void * param)
+{
+       if (avr->vcd)
+               avr_vcd_start(avr->vcd);
+
+       return 0;
+}
+
+static int
+_simavr_cmd_vcd_stop_trace(
+               avr_t * avr,
+               uint8_t v,
+               void * param)
+{
+       if (avr->vcd)
+               avr_vcd_stop(avr->vcd);
+
+       return 0;
+}
+
+static int
+_simavr_cmd_uart_loopback(
+               avr_t * avr,
+               uint8_t v,
+               void * param)
+{
+       avr_irq_t * src = avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUTPUT);
+       avr_irq_t * dst = avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_INPUT);
+
+       if(src && dst) {
+               AVR_LOG(avr, LOG_TRACE, LOG_PREFIX
+                       "%s: activating uart local echo; IRQ src %p dst %p\n",
+                       __FUNCTION__, src, dst);
+               avr_connect_irq(src, dst);
+       }
+
+       return 0;
+}
+
+void
+avr_cmd_init(
+               avr_t * avr)
+{
+       memset(&avr->commands, 0, sizeof(avr->commands));
+
+       // Register builtin commands
+       avr_cmd_register(avr, SIMAVR_CMD_VCD_START_TRACE, &_simavr_cmd_vcd_start_trace, NULL);
+       avr_cmd_register(avr, SIMAVR_CMD_VCD_STOP_TRACE, &_simavr_cmd_vcd_stop_trace, NULL);
+       avr_cmd_register(avr, SIMAVR_CMD_UART_LOOPBACK, &_simavr_cmd_uart_loopback, NULL);
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.h
new file mode 100644 (file)
index 0000000..aac75fc
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+       sim_cmds.h
+
+       Copyright 2014 Florian Albrechtskirchinger <falbrechtskirchinger@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "sim_avr_types.h"
+
+#define MAX_AVR_COMMANDS               32
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*avr_cmd_handler_t)(
+               struct avr_t * avr,
+               uint8_t v,
+               void * param);
+
+typedef struct avr_cmd_t {
+       avr_cmd_handler_t handler;
+       void * param;
+} avr_cmd_t;
+
+typedef struct avr_cmd_table_t {
+       avr_cmd_t table[MAX_AVR_COMMANDS];
+       avr_cmd_t * pending;    // Holds a reference to a pending multi-byte command
+} avr_cmd_table_t;
+
+// Called by avr_set_command_register()
+void
+avr_cmd_set_register(
+               struct avr_t * avr,
+               avr_io_addr_t addr);
+
+/*
+ * Register a command distinguished by 'code'.
+ *
+ * When 'code' is written to the configured IO address, 'handler' is executed
+ * with the value written, as well as 'param'.
+ * 'handler' can return non-zero, to indicate, that this is a multi-byte command.
+ * Subsequent writes are then dispatched to the same handler, until 0 is returned.
+ */
+void
+avr_cmd_register(
+               struct avr_t * avr,
+               uint8_t code,
+               avr_cmd_handler_t handler,
+               void * param);
+
+void
+avr_cmd_unregister(
+               struct avr_t * avr,
+               uint8_t code);
+
+// Private functions
+
+// Called from avr_init() to initialize the avr_cmd_table_t and register builtin commands.
+void
+avr_cmd_init(
+               struct avr_t * avr);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.c
new file mode 100644 (file)
index 0000000..ab8015c
--- /dev/null
@@ -0,0 +1,1457 @@
+/*
+       sim_core.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "sim_avr.h"
+#include "sim_core.h"
+#include "sim_gdb.h"
+#include "avr_flash.h"
+#include "avr_watchdog.h"
+
+// SREG bit names
+const char * _sreg_bit_name = "cznvshti";
+
+/*
+ * Handle "touching" registers, marking them changed.
+ * This is used only for debugging purposes to be able to
+ * print the effects of each instructions on registers
+ */
+#if CONFIG_SIMAVR_TRACE
+
+#define T(w) w
+
+#define REG_TOUCH(a, r) (a)->trace_data->touched[(r) >> 5] |= (1 << ((r) & 0x1f))
+#define REG_ISTOUCHED(a, r) ((a)->trace_data->touched[(r) >> 5] & (1 << ((r) & 0x1f)))
+
+/*
+ * This allows a "special case" to skip instruction tracing when in these
+ * symbols since printf() is useful to have, but generates a lot of cycles.
+ */
+int dont_trace(const char * name)
+{
+       return (
+               !strcmp(name, "uart_putchar") ||
+               !strcmp(name, "fputc") ||
+               !strcmp(name, "printf") ||
+               !strcmp(name, "vfprintf") ||
+               !strcmp(name, "__ultoa_invert") ||
+               !strcmp(name, "__prologue_saves__") ||
+               !strcmp(name, "__epilogue_restores__"));
+}
+
+int donttrace = 0;
+
+#define STATE(_f, args...) { \
+       if (avr->trace) {\
+               if (avr->trace_data->codeline && avr->trace_data->codeline[avr->pc>>1]) {\
+                       const char * symn = avr->trace_data->codeline[avr->pc>>1]->symbol; \
+                       int dont = 0 && dont_trace(symn);\
+                       if (dont!=donttrace) { \
+                               donttrace = dont;\
+                               DUMP_REG();\
+                       }\
+                       if (donttrace==0)\
+                               printf("%04x: %-25s " _f, avr->pc, symn, ## args);\
+               } else \
+                       printf("%s: %04x: " _f, __FUNCTION__, avr->pc, ## args);\
+               }\
+       }
+#define SREG() if (avr->trace && donttrace == 0) {\
+       printf("%04x: \t\t\t\t\t\t\t\t\tSREG = ", avr->pc); \
+       for (int _sbi = 0; _sbi < 8; _sbi++)\
+               printf("%c", avr->sreg[_sbi] ? toupper(_sreg_bit_name[_sbi]) : '.');\
+       printf("\n");\
+}
+
+void crash(avr_t* avr)
+{
+       DUMP_REG();
+       printf("*** CYCLE %" PRI_avr_cycle_count "PC %04x\n", avr->cycle, avr->pc);
+
+       for (int i = OLD_PC_SIZE-1; i > 0; i--) {
+               int pci = (avr->trace_data->old_pci + i) & 0xf;
+               printf(FONT_RED "*** %04x: %-25s RESET -%d; sp %04x\n" FONT_DEFAULT,
+                               avr->trace_data->old[pci].pc, avr->trace_data->codeline ? avr->trace_data->codeline[avr->trace_data->old[pci].pc>>1]->symbol : "unknown", OLD_PC_SIZE-i, avr->trace_data->old[pci].sp);
+       }
+
+       printf("Stack Ptr %04x/%04x = %d \n", _avr_sp_get(avr), avr->ramend, avr->ramend - _avr_sp_get(avr));
+       DUMP_STACK();
+
+       avr_sadly_crashed(avr, 0);
+}
+#else
+#define T(w)
+#define REG_TOUCH(a, r)
+#define STATE(_f, args...)
+#define SREG()
+
+void crash(avr_t* avr)
+{
+       avr_sadly_crashed(avr, 0);
+
+}
+#endif
+
+static inline uint16_t
+_avr_flash_read16le(
+       avr_t * avr,
+       avr_flashaddr_t addr)
+{
+       return(avr->flash[addr] | (avr->flash[addr + 1] << 8));
+}
+
+static inline void _call_register_irqs(avr_t * avr, uint16_t addr)
+{
+       if (addr > 31 && addr < 31 + MAX_IOs) {
+               avr_io_addr_t io = AVR_DATA_TO_IO(addr);
+
+               if (avr->io[io].irq) {
+                       uint8_t v = avr->data[addr];
+                       avr_raise_irq(avr->io[io].irq + AVR_IOMEM_IRQ_ALL, v);
+                       for (int i = 0; i < 8; i++)
+                               avr_raise_irq(avr->io[io].irq + i, (v >> i) & 1);
+               }
+       }
+}
+
+void avr_core_watch_write(avr_t *avr, uint16_t addr, uint8_t v)
+{
+       if (addr > avr->ramend) {
+               AVR_LOG(avr, LOG_WARNING,
+                               "CORE: *** Wrapping write address "
+                               "PC=%04x SP=%04x O=%04x v=%02x Address %04x %% %04x --> %04x\n",
+                               avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc), v, addr, (avr->ramend + 1), addr % (avr->ramend + 1));
+               addr = addr % (avr->ramend + 1);
+       }
+       if (addr < 32) {
+               AVR_LOG(avr, LOG_ERROR, FONT_RED
+                               "CORE: *** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x low registers\n"
+                               FONT_DEFAULT,
+                               avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc), addr, v);
+               crash(avr);
+       }
+#if AVR_STACK_WATCH
+       /*
+        * this checks that the current "function" is not doctoring the stack frame that is located
+        * higher on the stack than it should be. It's a sign of code that has overrun it's stack
+        * frame and is munching on it's own return address.
+        */
+       if (avr->trace_data->stack_frame_index > 1 && addr > avr->trace_data->stack_frame[avr->trace_data->stack_frame_index-2].sp) {
+               printf( FONT_RED "%04x : munching stack "
+                               "SP %04x, A=%04x <= %02x\n" FONT_DEFAULT,
+                               avr->pc, _avr_sp_get(avr), addr, v);
+       }
+#endif
+
+       if (avr->gdb) {
+               avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_WRITE);
+       }
+
+       avr->data[addr] = v;
+       _call_register_irqs(avr, addr);
+}
+
+uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr)
+{
+       if (addr > avr->ramend) {
+               AVR_LOG(avr, LOG_WARNING,
+                               "CORE: *** Wrapping read address "
+                               "PC=%04x SP=%04x O=%04x Address %04x %% %04x --> %04x\n"
+                               FONT_DEFAULT,
+                               avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc),
+                               addr, (avr->ramend + 1), addr % (avr->ramend + 1));
+               addr = addr % (avr->ramend + 1);
+       }
+
+       if (avr->gdb) {
+               avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_READ);
+       }
+
+//     _call_register_irqs(avr, addr);
+       return avr->data[addr];
+}
+
+/*
+ * Set a register (r < 256)
+ * if it's an IO register (> 31) also (try to) call any callback that was
+ * registered to track changes to that register.
+ */
+static inline void _avr_set_r(avr_t * avr, uint16_t r, uint8_t v)
+{
+       REG_TOUCH(avr, r);
+
+       if (r == R_SREG) {
+               avr->data[R_SREG] = v;
+               // unsplit the SREG
+               SET_SREG_FROM(avr, v);
+               SREG();
+       }
+       if (r > 31) {
+               avr_io_addr_t io = AVR_DATA_TO_IO(r);
+               if (avr->io[io].w.c) {
+                       avr->io[io].w.c(avr, r, v, avr->io[io].w.param);
+               } else {
+                       avr->data[r] = v;
+                       if (avr->io[io].irq) {
+                               avr_raise_irq(avr->io[io].irq + AVR_IOMEM_IRQ_ALL, v);
+                               for (int i = 0; i < 8; i++)
+                                       avr_raise_irq(avr->io[io].irq + i, (v >> i) & 1);
+                       }
+               }
+       } else
+               avr->data[r] = v;
+}
+
+static inline void
+_avr_set_r16le(
+       avr_t * avr,
+       uint16_t r,
+       uint16_t v)
+{
+       _avr_set_r(avr, r, v);
+       _avr_set_r(avr, r + 1, v >> 8);
+}
+
+static inline void
+_avr_set_r16le_hl(
+       avr_t * avr,
+       uint16_t r,
+       uint16_t v)
+{
+       _avr_set_r(avr, r + 1, v >> 8);
+       _avr_set_r(avr, r , v);
+}
+
+/*
+ * Stack pointer access
+ */
+inline uint16_t _avr_sp_get(avr_t * avr)
+{
+       return avr->data[R_SPL] | (avr->data[R_SPH] << 8);
+}
+
+inline void _avr_sp_set(avr_t * avr, uint16_t sp)
+{
+       _avr_set_r16le(avr, R_SPL, sp);
+}
+
+/*
+ * Set any address to a value; split between registers and SRAM
+ */
+static inline void _avr_set_ram(avr_t * avr, uint16_t addr, uint8_t v)
+{
+       if (addr <= avr->ioend)
+               _avr_set_r(avr, addr, v);
+       else
+               avr_core_watch_write(avr, addr, v);
+}
+
+/*
+ * Get a value from SRAM.
+ */
+static inline uint8_t _avr_get_ram(avr_t * avr, uint16_t addr)
+{
+       if (addr == R_SREG) {
+               /*
+                * SREG is special it's reconstructed when read
+                * while the core itself uses the "shortcut" array
+                */
+               READ_SREG_INTO(avr, avr->data[R_SREG]);
+
+       } else if (addr > 31 && addr < 31 + MAX_IOs) {
+               avr_io_addr_t io = AVR_DATA_TO_IO(addr);
+
+               if (avr->io[io].r.c)
+                       avr->data[addr] = avr->io[io].r.c(avr, addr, avr->io[io].r.param);
+#if 0
+               if (avr->io[io].irq) {
+                       uint8_t v = avr->data[addr];
+                       avr_raise_irq(avr->io[io].irq + AVR_IOMEM_IRQ_ALL, v);
+                       for (int i = 0; i < 8; i++)
+                               avr_raise_irq(avr->io[io].irq + i, (v >> i) & 1);
+               }
+#endif
+       }
+       return avr_core_watch_read(avr, addr);
+}
+
+/*
+ * Stack push accessors.
+ */
+static inline void _avr_push8(avr_t * avr, uint16_t v)
+{
+       uint16_t sp = _avr_sp_get(avr);
+       _avr_set_ram(avr, sp, v);
+       _avr_sp_set(avr, sp-1);
+}
+
+static inline uint8_t _avr_pop8(avr_t * avr)
+{
+       uint16_t sp = _avr_sp_get(avr) + 1;
+       uint8_t res = _avr_get_ram(avr, sp);
+       _avr_sp_set(avr, sp);
+       return res;
+}
+
+int _avr_push_addr(avr_t * avr, avr_flashaddr_t addr)
+{
+       uint16_t sp = _avr_sp_get(avr);
+       addr >>= 1;
+       for (int i = 0; i < avr->address_size; i++, addr >>= 8, sp--) {
+               _avr_set_ram(avr, sp, addr);
+       }
+       _avr_sp_set(avr, sp);
+       return avr->address_size;
+}
+
+avr_flashaddr_t _avr_pop_addr(avr_t * avr)
+{
+       uint16_t sp = _avr_sp_get(avr) + 1;
+       avr_flashaddr_t res = 0;
+       for (int i = 0; i < avr->address_size; i++, sp++) {
+               res = (res << 8) | _avr_get_ram(avr, sp);
+       }
+       res <<= 1;
+       _avr_sp_set(avr, sp -1);
+       return res;
+}
+
+/*
+ * "Pretty" register names
+ */
+const char * reg_names[REG_NAME_COUNT] = {
+               [R_XH] = "XH", [R_XL] = "XL",
+               [R_YH] = "YH", [R_YL] = "YL",
+               [R_ZH] = "ZH", [R_ZL] = "ZL",
+               [R_SPH] = "SPH", [R_SPL] = "SPL",
+               [R_SREG] = "SREG",
+};
+
+
+const char * avr_regname(unsigned int reg)
+{
+       if (!reg_names[reg]) {
+               char tt[16];
+               if (reg < 32)
+                       sprintf(tt, "r%d", reg);
+               else
+                       sprintf(tt, "io:%02x", reg);
+               reg_names[reg] = strdup(tt);
+       }
+       return reg_names[reg];
+}
+
+/*
+ * Called when an invalid opcode is decoded
+ */
+static void _avr_invalid_opcode(avr_t * avr)
+{
+#if CONFIG_SIMAVR_TRACE
+       printf( FONT_RED "*** %04x: %-25s Invalid Opcode SP=%04x O=%04x \n" FONT_DEFAULT,
+                       avr->pc, avr->trace_data->codeline[avr->pc>>1]->symbol, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc));
+#else
+       AVR_LOG(avr, LOG_ERROR, FONT_RED "CORE: *** %04x: Invalid Opcode SP=%04x O=%04x \n" FONT_DEFAULT,
+                       avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc));
+#endif
+}
+
+#if CONFIG_SIMAVR_TRACE
+/*
+ * Dump changed registers when tracing
+ */
+void avr_dump_state(avr_t * avr)
+{
+       if (!avr->trace || donttrace)
+               return;
+
+       int doit = 0;
+
+       for (int r = 0; r < 3 && !doit; r++)
+               if (avr->trace_data->touched[r])
+                       doit = 1;
+       if (!doit)
+               return;
+       printf("                                       ->> ");
+       const int r16[] = { R_SPL, R_XL, R_YL, R_ZL };
+       for (int i = 0; i < 4; i++)
+               if (REG_ISTOUCHED(avr, r16[i]) || REG_ISTOUCHED(avr, r16[i]+1)) {
+                       REG_TOUCH(avr, r16[i]);
+                       REG_TOUCH(avr, r16[i]+1);
+               }
+
+       for (int i = 0; i < 3*32; i++)
+               if (REG_ISTOUCHED(avr, i)) {
+                       printf("%s=%02x ", avr_regname(i), avr->data[i]);
+               }
+       printf("\n");
+}
+#endif
+
+#define get_d5(o) \
+               const uint8_t d = (o >> 4) & 0x1f;
+
+#define get_vd5(o) \
+               get_d5(o) \
+               const uint8_t vd = avr->data[d];
+
+#define get_r5(o) \
+               const uint8_t r = ((o >> 5) & 0x10) | (o & 0xf);
+
+#define get_d5_a6(o) \
+               get_d5(o); \
+               const uint8_t A = ((((o >> 9) & 3) << 4) | ((o) & 0xf)) + 32;
+
+#define get_vd5_s3(o) \
+               get_vd5(o); \
+               const uint8_t s = o & 7;
+
+#define get_vd5_s3_mask(o) \
+               get_vd5_s3(o); \
+               const uint8_t mask = 1 << s;
+
+#define get_vd5_vr5(o) \
+               get_r5(o); \
+               get_d5(o); \
+               const uint8_t vd = avr->data[d], vr = avr->data[r];
+
+#define get_d5_vr5(o) \
+               get_d5(o); \
+               get_r5(o); \
+               const uint8_t vr = avr->data[r];
+
+#define get_h4_k8(o) \
+               const uint8_t h = 16 + ((o >> 4) & 0xf); \
+               const uint8_t k = ((o & 0x0f00) >> 4) | (o & 0xf);
+
+#define get_vh4_k8(o) \
+               get_h4_k8(o) \
+               const uint8_t vh = avr->data[h];
+
+#define get_d5_q6(o) \
+               get_d5(o) \
+               const uint8_t q = ((o & 0x2000) >> 8) | ((o & 0x0c00) >> 7) | (o & 0x7);
+
+#define get_io5(o) \
+               const uint8_t io = ((o >> 3) & 0x1f) + 32;
+
+#define get_io5_b3(o) \
+               get_io5(o); \
+               const uint8_t b = o & 0x7;
+
+#define get_io5_b3mask(o) \
+               get_io5(o); \
+               const uint8_t mask = 1 << (o & 0x7);
+
+//     const int16_t o = ((int16_t)(op << 4)) >> 3; // CLANG BUG!
+#define get_o12(op) \
+               const int16_t o = ((int16_t)((op << 4) & 0xffff)) >> 3;
+
+#define get_vp2_k6(o) \
+               const uint8_t p = 24 + ((o >> 3) & 0x6); \
+               const uint8_t k = ((o & 0x00c0) >> 2) | (o & 0xf); \
+               const uint16_t vp = avr->data[p] | (avr->data[p + 1] << 8);
+
+#define get_sreg_bit(o) \
+               const uint8_t b = (o >> 4) & 7;
+
+/*
+ * Add a "jump" address to the jump trace buffer
+ */
+#if CONFIG_SIMAVR_TRACE
+#define TRACE_JUMP()\
+       avr->trace_data->old[avr->trace_data->old_pci].pc = avr->pc;\
+       avr->trace_data->old[avr->trace_data->old_pci].sp = _avr_sp_get(avr);\
+       avr->trace_data->old_pci = (avr->trace_data->old_pci + 1) & (OLD_PC_SIZE-1);\
+
+#if AVR_STACK_WATCH
+#define STACK_FRAME_PUSH()\
+       avr->trace_data->stack_frame[avr->trace_data->stack_frame_index].pc = avr->pc;\
+       avr->trace_data->stack_frame[avr->trace_data->stack_frame_index].sp = _avr_sp_get(avr);\
+       avr->trace_data->stack_frame_index++;
+#define STACK_FRAME_POP()\
+       if (avr->trace_data->stack_frame_index > 0) \
+               avr->trace_data->stack_frame_index--;
+#else
+#define STACK_FRAME_PUSH()
+#define STACK_FRAME_POP()
+#endif
+#else /* CONFIG_SIMAVR_TRACE */
+
+#define TRACE_JUMP()
+#define STACK_FRAME_PUSH()
+#define STACK_FRAME_POP()
+
+#endif
+
+/****************************************************************************\
+ *
+ * Helper functions for calculating the status register bit values.
+ * See the Atmel data sheet for the instruction set for more info.
+ *
+\****************************************************************************/
+
+static  void
+_avr_flags_zns (struct avr_t * avr, uint8_t res)
+{
+       avr->sreg[S_Z] = res == 0;
+       avr->sreg[S_N] = (res >> 7) & 1;
+       avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V];
+}
+
+static  void
+_avr_flags_zns16 (struct avr_t * avr, uint16_t res)
+{
+       avr->sreg[S_Z] = res == 0;
+       avr->sreg[S_N] = (res >> 15) & 1;
+       avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V];
+}
+
+static  void
+_avr_flags_add_zns (struct avr_t * avr, uint8_t res, uint8_t rd, uint8_t rr)
+{
+       /* carry & half carry */
+       uint8_t add_carry = (rd & rr) | (rr & ~res) | (~res & rd);
+       avr->sreg[S_H] = (add_carry >> 3) & 1;
+       avr->sreg[S_C] = (add_carry >> 7) & 1;
+
+       /* overflow */
+       avr->sreg[S_V] = (((rd & rr & ~res) | (~rd & ~rr & res)) >> 7) & 1;
+
+       /* zns */
+       _avr_flags_zns(avr, res);
+}
+
+
+static  void
+_avr_flags_sub_zns (struct avr_t * avr, uint8_t res, uint8_t rd, uint8_t rr)
+{
+       /* carry & half carry */
+       uint8_t sub_carry = (~rd & rr) | (rr & res) | (res & ~rd);
+       avr->sreg[S_H] = (sub_carry >> 3) & 1;
+       avr->sreg[S_C] = (sub_carry >> 7) & 1;
+
+       /* overflow */
+       avr->sreg[S_V] = (((rd & ~rr & ~res) | (~rd & rr & res)) >> 7) & 1;
+
+       /* zns */
+       _avr_flags_zns(avr, res);
+}
+
+static  void
+_avr_flags_Rzns (struct avr_t * avr, uint8_t res)
+{
+       if (res)
+               avr->sreg[S_Z] = 0;
+       avr->sreg[S_N] = (res >> 7) & 1;
+       avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V];
+}
+
+static  void
+_avr_flags_sub_Rzns (struct avr_t * avr, uint8_t res, uint8_t rd, uint8_t rr)
+{
+       /* carry & half carry */
+       uint8_t sub_carry = (~rd & rr) | (rr & res) | (res & ~rd);
+       avr->sreg[S_H] = (sub_carry >> 3) & 1;
+       avr->sreg[S_C] = (sub_carry >> 7) & 1;
+
+       /* overflow */
+       avr->sreg[S_V] = (((rd & ~rr & ~res) | (~rd & rr & res)) >> 7) & 1;
+
+       _avr_flags_Rzns(avr, res);
+}
+
+static  void
+_avr_flags_zcvs (struct avr_t * avr, uint8_t res, uint8_t vr)
+{
+       avr->sreg[S_Z] = res == 0;
+       avr->sreg[S_C] = vr & 1;
+       avr->sreg[S_V] = avr->sreg[S_N] ^ avr->sreg[S_C];
+       avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V];
+}
+
+static  void
+_avr_flags_zcnvs (struct avr_t * avr, uint8_t res, uint8_t vr)
+{
+       avr->sreg[S_Z] = res == 0;
+       avr->sreg[S_C] = vr & 1;
+       avr->sreg[S_N] = res >> 7;
+       avr->sreg[S_V] = avr->sreg[S_N] ^ avr->sreg[S_C];
+       avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V];
+}
+
+static  void
+_avr_flags_znv0s (struct avr_t * avr, uint8_t res)
+{
+       avr->sreg[S_V] = 0;
+       _avr_flags_zns(avr, res);
+}
+
+static inline int _avr_is_instruction_32_bits(avr_t * avr, avr_flashaddr_t pc)
+{
+       uint16_t o = _avr_flash_read16le(avr, pc) & 0xfe0f;
+       return  o == 0x9200 || // STS ! Store Direct to Data Space
+                       o == 0x9000 || // LDS Load Direct from Data Space
+                       o == 0x940c || // JMP Long Jump
+                       o == 0x940d || // JMP Long Jump
+                       o == 0x940e ||  // CALL Long Call to sub
+                       o == 0x940f; // CALL Long Call to sub
+}
+
+/*
+ * Main opcode decoder
+ *
+ * The decoder was written by following the datasheet in no particular order.
+ * As I went along, I noticed "bit patterns" that could be used to factor opcodes
+ * However, a lot of these only became apparent later on, so SOME instructions
+ * (skip of bit set etc) are compact, and some could use some refactoring (the ALU
+ * ones scream to be factored).
+ * I assume that the decoder could easily be 2/3 of it's current size.
+ *
+ * + It lacks the "extended" XMega jumps.
+ * + It also doesn't check whether the core it's
+ *   emulating is supposed to have the fancy instructions, like multiply and such.
+ *
+ * The number of cycles taken by instruction has been added, but might not be
+ * entirely accurate.
+ */
+avr_flashaddr_t avr_run_one(avr_t * avr)
+{
+run_one_again:
+#if CONFIG_SIMAVR_TRACE
+       /*
+        * this traces spurious reset or bad jumps
+        */
+       if ((avr->pc == 0 && avr->cycle > 0) || avr->pc >= avr->codeend || _avr_sp_get(avr) > avr->ramend) {
+//             avr->trace = 1;
+               STATE("RESET\n");
+               crash(avr);
+       }
+       avr->trace_data->touched[0] = avr->trace_data->touched[1] = avr->trace_data->touched[2] = 0;
+#endif
+
+       /* Ensure we don't crash simavr due to a bad instruction reading past
+        * the end of the flash.
+        */
+       if (unlikely(avr->pc >= avr->flashend)) {
+               STATE("CRASH\n");
+               crash(avr);
+               return 0;
+       }
+
+       uint32_t                opcode = _avr_flash_read16le(avr, avr->pc);
+       avr_flashaddr_t new_pc = avr->pc + 2;   // future "default" pc
+       int                     cycle = 1;
+
+       switch (opcode & 0xf000) {
+               case 0x0000: {
+                       switch (opcode) {
+                               case 0x0000: {  // NOP
+                                       STATE("nop\n");
+                               }       break;
+                               default: {
+                                       switch (opcode & 0xfc00) {
+                                               case 0x0400: {  // CPC -- Compare with carry -- 0000 01rd dddd rrrr
+                                                       get_vd5_vr5(opcode);
+                                                       uint8_t res = vd - vr - avr->sreg[S_C];
+                                                       STATE("cpc %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+                                                       _avr_flags_sub_Rzns(avr, res, vd, vr);
+                                                       SREG();
+                                               }       break;
+                                               case 0x0c00: {  // ADD -- Add without carry -- 0000 11rd dddd rrrr
+                                                       get_vd5_vr5(opcode);
+                                                       uint8_t res = vd + vr;
+                                                       if (r == d) {
+                                                               STATE("lsl %s[%02x] = %02x\n", avr_regname(d), vd, res & 0xff);
+                                                       } else {
+                                                               STATE("add %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+                                                       }
+                                                       _avr_set_r(avr, d, res);
+                                                       _avr_flags_add_zns(avr, res, vd, vr);
+                                                       SREG();
+                                               }       break;
+                                               case 0x0800: {  // SBC -- Subtract with carry -- 0000 10rd dddd rrrr
+                                                       get_vd5_vr5(opcode);
+                                                       uint8_t res = vd - vr - avr->sreg[S_C];
+                                                       STATE("sbc %s[%02x], %s[%02x] = %02x\n", avr_regname(d), avr->data[d], avr_regname(r), avr->data[r], res);
+                                                       _avr_set_r(avr, d, res);
+                                                       _avr_flags_sub_Rzns(avr, res, vd, vr);
+                                                       SREG();
+                                               }       break;
+                                               default:
+                                                       switch (opcode & 0xff00) {
+                                                               case 0x0100: {  // MOVW -- Copy Register Word -- 0000 0001 dddd rrrr
+                                                                       uint8_t d = ((opcode >> 4) & 0xf) << 1;
+                                                                       uint8_t r = ((opcode) & 0xf) << 1;
+                                                                       STATE("movw %s:%s, %s:%s[%02x%02x]\n", avr_regname(d), avr_regname(d+1), avr_regname(r), avr_regname(r+1), avr->data[r+1], avr->data[r]);
+                                                                       uint16_t vr = avr->data[r] | (avr->data[r + 1] << 8);
+                                                                       _avr_set_r16le(avr, d, vr);
+                                                               }       break;
+                                                               case 0x0200: {  // MULS -- Multiply Signed -- 0000 0010 dddd rrrr
+                                                                       int8_t r = 16 + (opcode & 0xf);
+                                                                       int8_t d = 16 + ((opcode >> 4) & 0xf);
+                                                                       int16_t res = ((int8_t)avr->data[r]) * ((int8_t)avr->data[d]);
+                                                                       STATE("muls %s[%d], %s[%02x] = %d\n", avr_regname(d), ((int8_t)avr->data[d]), avr_regname(r), ((int8_t)avr->data[r]), res);
+                                                                       _avr_set_r16le(avr, 0, res);
+                                                                       avr->sreg[S_C] = (res >> 15) & 1;
+                                                                       avr->sreg[S_Z] = res == 0;
+                                                                       cycle++;
+                                                                       SREG();
+                                                               }       break;
+                                                               case 0x0300: {  // MUL -- Multiply -- 0000 0011 fddd frrr
+                                                                       int8_t r = 16 + (opcode & 0x7);
+                                                                       int8_t d = 16 + ((opcode >> 4) & 0x7);
+                                                                       int16_t res = 0;
+                                                                       uint8_t c = 0;
+                                                                       T(const char * name = "";)
+                                                                       switch (opcode & 0x88) {
+                                                                               case 0x00:      // MULSU -- Multiply Signed Unsigned -- 0000 0011 0ddd 0rrr
+                                                                                       res = ((uint8_t)avr->data[r]) * ((int8_t)avr->data[d]);
+                                                                                       c = (res >> 15) & 1;
+                                                                                       T(name = "mulsu";)
+                                                                                       break;
+                                                                               case 0x08:      // FMUL -- Fractional Multiply Unsigned -- 0000 0011 0ddd 1rrr
+                                                                                       res = ((uint8_t)avr->data[r]) * ((uint8_t)avr->data[d]);
+                                                                                       c = (res >> 15) & 1;
+                                                                                       res <<= 1;
+                                                                                       T(name = "fmul";)
+                                                                                       break;
+                                                                               case 0x80:      // FMULS -- Multiply Signed -- 0000 0011 1ddd 0rrr
+                                                                                       res = ((int8_t)avr->data[r]) * ((int8_t)avr->data[d]);
+                                                                                       c = (res >> 15) & 1;
+                                                                                       res <<= 1;
+                                                                                       T(name = "fmuls";)
+                                                                                       break;
+                                                                               case 0x88:      // FMULSU -- Multiply Signed Unsigned -- 0000 0011 1ddd 1rrr
+                                                                                       res = ((uint8_t)avr->data[r]) * ((int8_t)avr->data[d]);
+                                                                                       c = (res >> 15) & 1;
+                                                                                       res <<= 1;
+                                                                                       T(name = "fmulsu";)
+                                                                                       break;
+                                                                       }
+                                                                       cycle++;
+                                                                       STATE("%s %s[%d], %s[%02x] = %d\n", name, avr_regname(d), ((int8_t)avr->data[d]), avr_regname(r), ((int8_t)avr->data[r]), res);
+                                                                       _avr_set_r16le(avr, 0, res);
+                                                                       avr->sreg[S_C] = c;
+                                                                       avr->sreg[S_Z] = res == 0;
+                                                                       SREG();
+                                                               }       break;
+                                                               default: _avr_invalid_opcode(avr);
+                                                       }
+                                       }
+                               }
+                       }
+               }       break;
+
+               case 0x1000: {
+                       switch (opcode & 0xfc00) {
+                               case 0x1800: {  // SUB -- Subtract without carry -- 0001 10rd dddd rrrr
+                                       get_vd5_vr5(opcode);
+                                       uint8_t res = vd - vr;
+                                       STATE("sub %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+                                       _avr_set_r(avr, d, res);
+                                       _avr_flags_sub_zns(avr, res, vd, vr);
+                                       SREG();
+                               }       break;
+                               case 0x1000: {  // CPSE -- Compare, skip if equal -- 0001 00rd dddd rrrr
+                                       get_vd5_vr5(opcode);
+                                       uint16_t res = vd == vr;
+                                       STATE("cpse %s[%02x], %s[%02x]\t; Will%s skip\n", avr_regname(d), avr->data[d], avr_regname(r), avr->data[r], res ? "":" not");
+                                       if (res) {
+                                               if (_avr_is_instruction_32_bits(avr, new_pc)) {
+                                                       new_pc += 4; cycle += 2;
+                                               } else {
+                                                       new_pc += 2; cycle++;
+                                               }
+                                       }
+                               }       break;
+                               case 0x1400: {  // CP -- Compare -- 0001 01rd dddd rrrr
+                                       get_vd5_vr5(opcode);
+                                       uint8_t res = vd - vr;
+                                       STATE("cp %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+                                       _avr_flags_sub_zns(avr, res, vd, vr);
+                                       SREG();
+                               }       break;
+                               case 0x1c00: {  // ADD -- Add with carry -- 0001 11rd dddd rrrr
+                                       get_vd5_vr5(opcode);
+                                       uint8_t res = vd + vr + avr->sreg[S_C];
+                                       if (r == d) {
+                                               STATE("rol %s[%02x] = %02x\n", avr_regname(d), avr->data[d], res);
+                                       } else {
+                                               STATE("addc %s[%02x], %s[%02x] = %02x\n", avr_regname(d), avr->data[d], avr_regname(r), avr->data[r], res);
+                                       }
+                                       _avr_set_r(avr, d, res);
+                                       _avr_flags_add_zns(avr, res, vd, vr);
+                                       SREG();
+                               }       break;
+                               default: _avr_invalid_opcode(avr);
+                       }
+               }       break;
+
+               case 0x2000: {
+                       switch (opcode & 0xfc00) {
+                               case 0x2000: {  // AND -- Logical AND -- 0010 00rd dddd rrrr
+                                       get_vd5_vr5(opcode);
+                                       uint8_t res = vd & vr;
+                                       if (r == d) {
+                                               STATE("tst %s[%02x]\n", avr_regname(d), avr->data[d]);
+                                       } else {
+                                               STATE("and %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+                                       }
+                                       _avr_set_r(avr, d, res);
+                                       _avr_flags_znv0s(avr, res);
+                                       SREG();
+                               }       break;
+                               case 0x2400: {  // EOR -- Logical Exclusive OR -- 0010 01rd dddd rrrr
+                                       get_vd5_vr5(opcode);
+                                       uint8_t res = vd ^ vr;
+                                       if (r==d) {
+                                               STATE("clr %s[%02x]\n", avr_regname(d), avr->data[d]);
+                                       } else {
+                                               STATE("eor %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+                                       }
+                                       _avr_set_r(avr, d, res);
+                                       _avr_flags_znv0s(avr, res);
+                                       SREG();
+                               }       break;
+                               case 0x2800: {  // OR -- Logical OR -- 0010 10rd dddd rrrr
+                                       get_vd5_vr5(opcode);
+                                       uint8_t res = vd | vr;
+                                       STATE("or %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+                                       _avr_set_r(avr, d, res);
+                                       _avr_flags_znv0s(avr, res);
+                                       SREG();
+                               }       break;
+                               case 0x2c00: {  // MOV -- 0010 11rd dddd rrrr
+                                       get_d5_vr5(opcode);
+                                       uint8_t res = vr;
+                                       STATE("mov %s, %s[%02x] = %02x\n", avr_regname(d), avr_regname(r), vr, res);
+                                       _avr_set_r(avr, d, res);
+                               }       break;
+                               default: _avr_invalid_opcode(avr);
+                       }
+               }       break;
+
+               case 0x3000: {  // CPI -- Compare Immediate -- 0011 kkkk hhhh kkkk
+                       get_vh4_k8(opcode);
+                       uint8_t res = vh - k;
+                       STATE("cpi %s[%02x], 0x%02x\n", avr_regname(h), vh, k);
+                       _avr_flags_sub_zns(avr, res, vh, k);
+                       SREG();
+               }       break;
+
+               case 0x4000: {  // SBCI -- Subtract Immediate With Carry -- 0100 kkkk hhhh kkkk
+                       get_vh4_k8(opcode);
+                       uint8_t res = vh - k - avr->sreg[S_C];
+                       STATE("sbci %s[%02x], 0x%02x = %02x\n", avr_regname(h), vh, k, res);
+                       _avr_set_r(avr, h, res);
+                       _avr_flags_sub_Rzns(avr, res, vh, k);
+                       SREG();
+               }       break;
+
+               case 0x5000: {  // SUBI -- Subtract Immediate -- 0101 kkkk hhhh kkkk
+                       get_vh4_k8(opcode);
+                       uint8_t res = vh - k;
+                       STATE("subi %s[%02x], 0x%02x = %02x\n", avr_regname(h), vh, k, res);
+                       _avr_set_r(avr, h, res);
+                       _avr_flags_sub_zns(avr, res, vh, k);
+                       SREG();
+               }       break;
+
+               case 0x6000: {  // ORI aka SBR -- Logical OR with Immediate -- 0110 kkkk hhhh kkkk
+                       get_vh4_k8(opcode);
+                       uint8_t res = vh | k;
+                       STATE("ori %s[%02x], 0x%02x\n", avr_regname(h), vh, k);
+                       _avr_set_r(avr, h, res);
+                       _avr_flags_znv0s(avr, res);
+                       SREG();
+               }       break;
+
+               case 0x7000: {  // ANDI -- Logical AND with Immediate -- 0111 kkkk hhhh kkkk
+                       get_vh4_k8(opcode);
+                       uint8_t res = vh & k;
+                       STATE("andi %s[%02x], 0x%02x\n", avr_regname(h), vh, k);
+                       _avr_set_r(avr, h, res);
+                       _avr_flags_znv0s(avr, res);
+                       SREG();
+               }       break;
+
+               case 0xa000:
+               case 0x8000: {
+                       /*
+                        * Load (LDD/STD) store instructions
+                        *
+                        * 10q0 qqsd dddd yqqq
+                        * s = 0 = load, 1 = store
+                        * y = 16 bits register index, 1 = Y, 0 = X
+                        * q = 6 bit displacement
+                        */
+                       switch (opcode & 0xd008) {
+                               case 0xa000:
+                               case 0x8000: {  // LD (LDD) -- Load Indirect using Z -- 10q0 qqsd dddd yqqq
+                                       uint16_t v = avr->data[R_ZL] | (avr->data[R_ZH] << 8);
+                                       get_d5_q6(opcode);
+                                       if (opcode & 0x0200) {
+                                               STATE("st (Z+%d[%04x]), %s[%02x]\n", q, v+q, avr_regname(d), avr->data[d]);
+                                               _avr_set_ram(avr, v+q, avr->data[d]);
+                                       } else {
+                                               STATE("ld %s, (Z+%d[%04x])=[%02x]\n", avr_regname(d), q, v+q, avr->data[v+q]);
+                                               _avr_set_r(avr, d, _avr_get_ram(avr, v+q));
+                                       }
+                                       cycle += 1; // 2 cycles, 3 for tinyavr
+                               }       break;
+                               case 0xa008:
+                               case 0x8008: {  // LD (LDD) -- Load Indirect using Y -- 10q0 qqsd dddd yqqq
+                                       uint16_t v = avr->data[R_YL] | (avr->data[R_YH] << 8);
+                                       get_d5_q6(opcode);
+                                       if (opcode & 0x0200) {
+                                               STATE("st (Y+%d[%04x]), %s[%02x]\n", q, v+q, avr_regname(d), avr->data[d]);
+                                               _avr_set_ram(avr, v+q, avr->data[d]);
+                                       } else {
+                                               STATE("ld %s, (Y+%d[%04x])=[%02x]\n", avr_regname(d), q, v+q, avr->data[d+q]);
+                                               _avr_set_r(avr, d, _avr_get_ram(avr, v+q));
+                                       }
+                                       cycle += 1; // 2 cycles, 3 for tinyavr
+                               }       break;
+                               default: _avr_invalid_opcode(avr);
+                       }
+               }       break;
+
+               case 0x9000: {
+                       /* this is an annoying special case, but at least these lines handle all the SREG set/clear opcodes */
+                       if ((opcode & 0xff0f) == 0x9408) {
+                               get_sreg_bit(opcode);
+                               STATE("%s%c\n", opcode & 0x0080 ? "cl" : "se", _sreg_bit_name[b]);
+                               avr_sreg_set(avr, b, (opcode & 0x0080) == 0);
+                               SREG();
+                       } else switch (opcode) {
+                               case 0x9588: { // SLEEP -- 1001 0101 1000 1000
+                                       STATE("sleep\n");
+                                       /* Don't sleep if there are interrupts about to be serviced.
+                                        * Without this check, it was possible to incorrectly enter a state
+                                        * in which the cpu was sleeping and interrupts were disabled. For more
+                                        * details, see the commit message. */
+                                       if (!avr_has_pending_interrupts(avr) || !avr->sreg[S_I])
+                                               avr->state = cpu_Sleeping;
+                               }       break;
+                               case 0x9598: { // BREAK -- 1001 0101 1001 1000
+                                       STATE("break\n");
+                                       if (avr->gdb) {
+                                               // if gdb is on, break here.
+                                               avr->state = cpu_Stopped;
+                                               avr_gdb_handle_break(avr);
+                                       }
+                               }       break;
+                               case 0x95a8: { // WDR -- Watchdog Reset -- 1001 0101 1010 1000
+                                       STATE("wdr\n");
+                                       avr_ioctl(avr, AVR_IOCTL_WATCHDOG_RESET, 0);
+                               }       break;
+                               case 0x95e8: { // SPM -- Store Program Memory -- 1001 0101 1110 1000
+                                       STATE("spm\n");
+                                       avr_ioctl(avr, AVR_IOCTL_FLASH_SPM, 0);
+                               }       break;
+                               case 0x9409:   // IJMP -- Indirect jump -- 1001 0100 0000 1001
+                               case 0x9419:   // EIJMP -- Indirect jump -- 1001 0100 0001 1001   bit 4 is "indirect"
+                               case 0x9509:   // ICALL -- Indirect Call to Subroutine -- 1001 0101 0000 1001
+                               case 0x9519: { // EICALL -- Indirect Call to Subroutine -- 1001 0101 0001 1001   bit 8 is "push pc"
+                                       int e = opcode & 0x10;
+                                       int p = opcode & 0x100;
+                                       if (e && !avr->eind)
+                                               _avr_invalid_opcode(avr);
+                                       uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8);
+                                       if (e)
+                                               z |= avr->data[avr->eind] << 16;
+                                       STATE("%si%s Z[%04x]\n", e?"e":"", p?"call":"jmp", z << 1);
+                                       if (p)
+                                               cycle += _avr_push_addr(avr, new_pc) - 1;
+                                       new_pc = z << 1;
+                                       cycle++;
+                                       TRACE_JUMP();
+                               }       break;
+                               case 0x9518:    // RETI -- Return from Interrupt -- 1001 0101 0001 1000
+                                       avr_sreg_set(avr, S_I, 1);
+                                       avr_interrupt_reti(avr);
+                                       FALLTHROUGH
+                               case 0x9508: {  // RET -- Return -- 1001 0101 0000 1000
+                                       new_pc = _avr_pop_addr(avr);
+                                       cycle += 1 + avr->address_size;
+                                       STATE("ret%s\n", opcode & 0x10 ? "i" : "");
+                                       TRACE_JUMP();
+                                       STACK_FRAME_POP();
+                               }       break;
+                               case 0x95c8: {  // LPM -- Load Program Memory R0 <- (Z) -- 1001 0101 1100 1000
+                                       uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8);
+                                       STATE("lpm %s, (Z[%04x])\n", avr_regname(0), z);
+                                       cycle += 2; // 3 cycles
+                                       _avr_set_r(avr, 0, avr->flash[z]);
+                               }       break;
+                               case 0x95d8: {  // ELPM -- Load Program Memory R0 <- (Z) -- 1001 0101 1101 1000
+                                       if (!avr->rampz)
+                                               _avr_invalid_opcode(avr);
+                                       uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) | (avr->data[avr->rampz] << 16);
+                                       STATE("elpm %s, (Z[%02x:%04x])\n", avr_regname(0), z >> 16, z & 0xffff);
+                                       _avr_set_r(avr, 0, avr->flash[z]);
+                                       cycle += 2; // 3 cycles
+                               }       break;
+                               default:  {
+                                       switch (opcode & 0xfe0f) {
+                                               case 0x9000: {  // LDS -- Load Direct from Data Space, 32 bits -- 1001 0000 0000 0000
+                                                       get_d5(opcode);
+                                                       uint16_t x = _avr_flash_read16le(avr, new_pc);
+                                                       new_pc += 2;
+                                                       STATE("lds %s[%02x], 0x%04x\n", avr_regname(d), avr->data[d], x);
+                                                       _avr_set_r(avr, d, _avr_get_ram(avr, x));
+                                                       cycle++; // 2 cycles
+                                               }       break;
+                                               case 0x9005:
+                                               case 0x9004: {  // LPM -- Load Program Memory -- 1001 000d dddd 01oo
+                                                       get_d5(opcode);
+                                                       uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8);
+                                                       int op = opcode & 1;
+                                                       STATE("lpm %s, (Z[%04x]%s)\n", avr_regname(d), z, op ? "+" : "");
+                                                       _avr_set_r(avr, d, avr->flash[z]);
+                                                       if (op) {
+                                                               z++;
+                                                               _avr_set_r16le_hl(avr, R_ZL, z);
+                                                       }
+                                                       cycle += 2; // 3 cycles
+                                               }       break;
+                                               case 0x9006:
+                                               case 0x9007: {  // ELPM -- Extended Load Program Memory -- 1001 000d dddd 01oo
+                                                       if (!avr->rampz)
+                                                               _avr_invalid_opcode(avr);
+                                                       uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) | (avr->data[avr->rampz] << 16);
+                                                       get_d5(opcode);
+                                                       int op = opcode & 1;
+                                                       STATE("elpm %s, (Z[%02x:%04x]%s)\n", avr_regname(d), z >> 16, z & 0xffff, op ? "+" : "");
+                                                       _avr_set_r(avr, d, avr->flash[z]);
+                                                       if (op) {
+                                                               z++;
+                                                               _avr_set_r(avr, avr->rampz, z >> 16);
+                                                               _avr_set_r16le_hl(avr, R_ZL, z);
+                                                       }
+                                                       cycle += 2; // 3 cycles
+                                               }       break;
+                                               /*
+                                                * Load store instructions
+                                                *
+                                                * 1001 00sr rrrr iioo
+                                                * s = 0 = load, 1 = store
+                                                * ii = 16 bits register index, 11 = X, 10 = Y, 00 = Z
+                                                * oo = 1) post increment, 2) pre-decrement
+                                                */
+                                               case 0x900c:
+                                               case 0x900d:
+                                               case 0x900e: {  // LD -- Load Indirect from Data using X -- 1001 000d dddd 11oo
+                                                       int op = opcode & 3;
+                                                       get_d5(opcode);
+                                                       uint16_t x = (avr->data[R_XH] << 8) | avr->data[R_XL];
+                                                       STATE("ld %s, %sX[%04x]%s\n", avr_regname(d), op == 2 ? "--" : "", x, op == 1 ? "++" : "");
+                                                       cycle++; // 2 cycles (1 for tinyavr, except with inc/dec 2)
+                                                       if (op == 2) x--;
+                                                       uint8_t vd = _avr_get_ram(avr, x);
+                                                       if (op == 1) x++;
+                                                       _avr_set_r16le_hl(avr, R_XL, x);
+                                                       _avr_set_r(avr, d, vd);
+                                               }       break;
+                                               case 0x920c:
+                                               case 0x920d:
+                                               case 0x920e: {  // ST -- Store Indirect Data Space X -- 1001 001d dddd 11oo
+                                                       int op = opcode & 3;
+                                                       get_vd5(opcode);
+                                                       uint16_t x = (avr->data[R_XH] << 8) | avr->data[R_XL];
+                                                       STATE("st %sX[%04x]%s, %s[%02x] \n", op == 2 ? "--" : "", x, op == 1 ? "++" : "", avr_regname(d), vd);
+                                                       cycle++; // 2 cycles, except tinyavr
+                                                       if (op == 2) x--;
+                                                       _avr_set_ram(avr, x, vd);
+                                                       if (op == 1) x++;
+                                                       _avr_set_r16le_hl(avr, R_XL, x);
+                                               }       break;
+                                               case 0x9009:
+                                               case 0x900a: {  // LD -- Load Indirect from Data using Y -- 1001 000d dddd 10oo
+                                                       int op = opcode & 3;
+                                                       get_d5(opcode);
+                                                       uint16_t y = (avr->data[R_YH] << 8) | avr->data[R_YL];
+                                                       STATE("ld %s, %sY[%04x]%s\n", avr_regname(d), op == 2 ? "--" : "", y, op == 1 ? "++" : "");
+                                                       cycle++; // 2 cycles, except tinyavr
+                                                       if (op == 2) y--;
+                                                       uint8_t vd = _avr_get_ram(avr, y);
+                                                       if (op == 1) y++;
+                                                       _avr_set_r16le_hl(avr, R_YL, y);
+                                                       _avr_set_r(avr, d, vd);
+                                               }       break;
+                                               case 0x9209:
+                                               case 0x920a: {  // ST -- Store Indirect Data Space Y -- 1001 001d dddd 10oo
+                                                       int op = opcode & 3;
+                                                       get_vd5(opcode);
+                                                       uint16_t y = (avr->data[R_YH] << 8) | avr->data[R_YL];
+                                                       STATE("st %sY[%04x]%s, %s[%02x]\n", op == 2 ? "--" : "", y, op == 1 ? "++" : "", avr_regname(d), vd);
+                                                       cycle++;
+                                                       if (op == 2) y--;
+                                                       _avr_set_ram(avr, y, vd);
+                                                       if (op == 1) y++;
+                                                       _avr_set_r16le_hl(avr, R_YL, y);
+                                               }       break;
+                                               case 0x9200: {  // STS -- Store Direct to Data Space, 32 bits -- 1001 0010 0000 0000
+                                                       get_vd5(opcode);
+                                                       uint16_t x = _avr_flash_read16le(avr, new_pc);
+                                                       new_pc += 2;
+                                                       STATE("sts 0x%04x, %s[%02x]\n", x, avr_regname(d), vd);
+                                                       cycle++;
+                                                       _avr_set_ram(avr, x, vd);
+                                               }       break;
+                                               case 0x9001:
+                                               case 0x9002: {  // LD -- Load Indirect from Data using Z -- 1001 000d dddd 00oo
+                                                       int op = opcode & 3;
+                                                       get_d5(opcode);
+                                                       uint16_t z = (avr->data[R_ZH] << 8) | avr->data[R_ZL];
+                                                       STATE("ld %s, %sZ[%04x]%s\n", avr_regname(d), op == 2 ? "--" : "", z, op == 1 ? "++" : "");
+                                                       cycle++;; // 2 cycles, except tinyavr
+                                                       if (op == 2) z--;
+                                                       uint8_t vd = _avr_get_ram(avr, z);
+                                                       if (op == 1) z++;
+                                                       _avr_set_r16le_hl(avr, R_ZL, z);
+                                                       _avr_set_r(avr, d, vd);
+                                               }       break;
+                                               case 0x9201:
+                                               case 0x9202: {  // ST -- Store Indirect Data Space Z -- 1001 001d dddd 00oo
+                                                       int op = opcode & 3;
+                                                       get_vd5(opcode);
+                                                       uint16_t z = (avr->data[R_ZH] << 8) | avr->data[R_ZL];
+                                                       STATE("st %sZ[%04x]%s, %s[%02x] \n", op == 2 ? "--" : "", z, op == 1 ? "++" : "", avr_regname(d), vd);
+                                                       cycle++; // 2 cycles, except tinyavr
+                                                       if (op == 2) z--;
+                                                       _avr_set_ram(avr, z, vd);
+                                                       if (op == 1) z++;
+                                                       _avr_set_r16le_hl(avr, R_ZL, z);
+                                               }       break;
+                                               case 0x900f: {  // POP -- 1001 000d dddd 1111
+                                                       get_d5(opcode);
+                                                       _avr_set_r(avr, d, _avr_pop8(avr));
+                                                       T(uint16_t sp = _avr_sp_get(avr);)
+                                                       STATE("pop %s (@%04x)[%02x]\n", avr_regname(d), sp, avr->data[sp]);
+                                                       cycle++;
+                                               }       break;
+                                               case 0x920f: {  // PUSH -- 1001 001d dddd 1111
+                                                       get_vd5(opcode);
+                                                       _avr_push8(avr, vd);
+                                                       T(uint16_t sp = _avr_sp_get(avr);)
+                                                       STATE("push %s[%02x] (@%04x)\n", avr_regname(d), vd, sp);
+                                                       cycle++;
+                                               }       break;
+                                               case 0x9400: {  // COM -- One's Complement -- 1001 010d dddd 0000
+                                                       get_vd5(opcode);
+                                                       uint8_t res = 0xff - vd;
+                                                       STATE("com %s[%02x] = %02x\n", avr_regname(d), vd, res);
+                                                       _avr_set_r(avr, d, res);
+                                                       _avr_flags_znv0s(avr, res);
+                                                       avr->sreg[S_C] = 1;
+                                                       SREG();
+                                               }       break;
+                                               case 0x9401: {  // NEG -- Two's Complement -- 1001 010d dddd 0001
+                                                       get_vd5(opcode);
+                                                       uint8_t res = 0x00 - vd;
+                                                       STATE("neg %s[%02x] = %02x\n", avr_regname(d), vd, res);
+                                                       _avr_set_r(avr, d, res);
+                                                       avr->sreg[S_H] = ((res >> 3) | (vd >> 3)) & 1;
+                                                       avr->sreg[S_V] = res == 0x80;
+                                                       avr->sreg[S_C] = res != 0;
+                                                       _avr_flags_zns(avr, res);
+                                                       SREG();
+                                               }       break;
+                                               case 0x9402: {  // SWAP -- Swap Nibbles -- 1001 010d dddd 0010
+                                                       get_vd5(opcode);
+                                                       uint8_t res = (vd >> 4) | (vd << 4) ;
+                                                       STATE("swap %s[%02x] = %02x\n", avr_regname(d), vd, res);
+                                                       _avr_set_r(avr, d, res);
+                                               }       break;
+                                               case 0x9403: {  // INC -- Increment -- 1001 010d dddd 0011
+                                                       get_vd5(opcode);
+                                                       uint8_t res = vd + 1;
+                                                       STATE("inc %s[%02x] = %02x\n", avr_regname(d), vd, res);
+                                                       _avr_set_r(avr, d, res);
+                                                       avr->sreg[S_V] = res == 0x80;
+                                                       _avr_flags_zns(avr, res);
+                                                       SREG();
+                                               }       break;
+                                               case 0x9405: {  // ASR -- Arithmetic Shift Right -- 1001 010d dddd 0101
+                                                       get_vd5(opcode);
+                                                       uint8_t res = (vd >> 1) | (vd & 0x80);
+                                                       STATE("asr %s[%02x]\n", avr_regname(d), vd);
+                                                       _avr_set_r(avr, d, res);
+                                                       _avr_flags_zcnvs(avr, res, vd);
+                                                       SREG();
+                                               }       break;
+                                               case 0x9406: {  // LSR -- Logical Shift Right -- 1001 010d dddd 0110
+                                                       get_vd5(opcode);
+                                                       uint8_t res = vd >> 1;
+                                                       STATE("lsr %s[%02x]\n", avr_regname(d), vd);
+                                                       _avr_set_r(avr, d, res);
+                                                       avr->sreg[S_N] = 0;
+                                                       _avr_flags_zcvs(avr, res, vd);
+                                                       SREG();
+                                               }       break;
+                                               case 0x9407: {  // ROR -- Rotate Right -- 1001 010d dddd 0111
+                                                       get_vd5(opcode);
+                                                       uint8_t res = (avr->sreg[S_C] ? 0x80 : 0) | vd >> 1;
+                                                       STATE("ror %s[%02x]\n", avr_regname(d), vd);
+                                                       _avr_set_r(avr, d, res);
+                                                       _avr_flags_zcnvs(avr, res, vd);
+                                                       SREG();
+                                               }       break;
+                                               case 0x940a: {  // DEC -- Decrement -- 1001 010d dddd 1010
+                                                       get_vd5(opcode);
+                                                       uint8_t res = vd - 1;
+                                                       STATE("dec %s[%02x] = %02x\n", avr_regname(d), vd, res);
+                                                       _avr_set_r(avr, d, res);
+                                                       avr->sreg[S_V] = res == 0x7f;
+                                                       _avr_flags_zns(avr, res);
+                                                       SREG();
+                                               }       break;
+                                               case 0x940c:
+                                               case 0x940d: {  // JMP -- Long Call to sub, 32 bits -- 1001 010a aaaa 110a
+                                                       avr_flashaddr_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1);
+                                                       uint16_t x = _avr_flash_read16le(avr, new_pc);
+                                                       a = (a << 16) | x;
+                                                       STATE("jmp 0x%06x\n", a);
+                                                       new_pc = a << 1;
+                                                       cycle += 2;
+                                                       TRACE_JUMP();
+                                               }       break;
+                                               case 0x940e:
+                                               case 0x940f: {  // CALL -- Long Call to sub, 32 bits -- 1001 010a aaaa 111a
+                                                       avr_flashaddr_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1);
+                                                       uint16_t x = _avr_flash_read16le(avr, new_pc);
+                                                       a = (a << 16) | x;
+                                                       STATE("call 0x%06x\n", a);
+                                                       new_pc += 2;
+                                                       cycle += 1 + _avr_push_addr(avr, new_pc);
+                                                       new_pc = a << 1;
+                                                       TRACE_JUMP();
+                                                       STACK_FRAME_PUSH();
+                                               }       break;
+
+                                               default: {
+                                                       switch (opcode & 0xff00) {
+                                                               case 0x9600: {  // ADIW -- Add Immediate to Word -- 1001 0110 KKpp KKKK
+                                                                       get_vp2_k6(opcode);
+                                                                       uint16_t res = vp + k;
+                                                                       STATE("adiw %s:%s[%04x], 0x%02x\n", avr_regname(p), avr_regname(p + 1), vp, k);
+                                                                       _avr_set_r16le_hl(avr, p, res);
+                                                                       avr->sreg[S_V] = ((~vp & res) >> 15) & 1;
+                                                                       avr->sreg[S_C] = ((~res & vp) >> 15) & 1;
+                                                                       _avr_flags_zns16(avr, res);
+                                                                       SREG();
+                                                                       cycle++;
+                                                               }       break;
+                                                               case 0x9700: {  // SBIW -- Subtract Immediate from Word -- 1001 0111 KKpp KKKK
+                                                                       get_vp2_k6(opcode);
+                                                                       uint16_t res = vp - k;
+                                                                       STATE("sbiw %s:%s[%04x], 0x%02x\n", avr_regname(p), avr_regname(p + 1), vp, k);
+                                                                       _avr_set_r16le_hl(avr, p, res);
+                                                                       avr->sreg[S_V] = ((vp & ~res) >> 15) & 1;
+                                                                       avr->sreg[S_C] = ((res & ~vp) >> 15) & 1;
+                                                                       _avr_flags_zns16(avr, res);
+                                                                       SREG();
+                                                                       cycle++;
+                                                               }       break;
+                                                               case 0x9800: {  // CBI -- Clear Bit in I/O Register -- 1001 1000 AAAA Abbb
+                                                                       get_io5_b3mask(opcode);
+                                                                       uint8_t res = _avr_get_ram(avr, io) & ~mask;
+                                                                       STATE("cbi %s[%04x], 0x%02x = %02x\n", avr_regname(io), avr->data[io], mask, res);
+                                                                       _avr_set_ram(avr, io, res);
+                                                                       cycle++;
+                                                               }       break;
+                                                               case 0x9900: {  // SBIC -- Skip if Bit in I/O Register is Cleared -- 1001 1001 AAAA Abbb
+                                                                       get_io5_b3mask(opcode);
+                                                                       uint8_t res = _avr_get_ram(avr, io) & mask;
+                                                                       STATE("sbic %s[%04x], 0x%02x\t; Will%s branch\n", avr_regname(io), avr->data[io], mask, !res?"":" not");
+                                                                       if (!res) {
+                                                                               if (_avr_is_instruction_32_bits(avr, new_pc)) {
+                                                                                       new_pc += 4; cycle += 2;
+                                                                               } else {
+                                                                                       new_pc += 2; cycle++;
+                                                                               }
+                                                                       }
+                                                               }       break;
+                                                               case 0x9a00: {  // SBI -- Set Bit in I/O Register -- 1001 1010 AAAA Abbb
+                                                                       get_io5_b3mask(opcode);
+                                                                       uint8_t res = _avr_get_ram(avr, io) | mask;
+                                                                       STATE("sbi %s[%04x], 0x%02x = %02x\n", avr_regname(io), avr->data[io], mask, res);
+                                                                       _avr_set_ram(avr, io, res);
+                                                                       cycle++;
+                                                               }       break;
+                                                               case 0x9b00: {  // SBIS -- Skip if Bit in I/O Register is Set -- 1001 1011 AAAA Abbb
+                                                                       get_io5_b3mask(opcode);
+                                                                       uint8_t res = _avr_get_ram(avr, io) & mask;
+                                                                       STATE("sbis %s[%04x], 0x%02x\t; Will%s branch\n", avr_regname(io), avr->data[io], mask, res?"":" not");
+                                                                       if (res) {
+                                                                               if (_avr_is_instruction_32_bits(avr, new_pc)) {
+                                                                                       new_pc += 4; cycle += 2;
+                                                                               } else {
+                                                                                       new_pc += 2; cycle++;
+                                                                               }
+                                                                       }
+                                                               }       break;
+                                                               default:
+                                                                       switch (opcode & 0xfc00) {
+                                                                               case 0x9c00: {  // MUL -- Multiply Unsigned -- 1001 11rd dddd rrrr
+                                                                                       get_vd5_vr5(opcode);
+                                                                                       uint16_t res = vd * vr;
+                                                                                       STATE("mul %s[%02x], %s[%02x] = %04x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+                                                                                       cycle++;
+                                                                                       _avr_set_r16le(avr, 0, res);
+                                                                                       avr->sreg[S_Z] = res == 0;
+                                                                                       avr->sreg[S_C] = (res >> 15) & 1;
+                                                                                       SREG();
+                                                                               }       break;
+                                                                               default: _avr_invalid_opcode(avr);
+                                                                       }
+                                                       }
+                                               }       break;
+                                       }
+                               }       break;
+                       }
+               }       break;
+
+               case 0xb000: {
+                       switch (opcode & 0xf800) {
+                               case 0xb800: {  // OUT A,Rr -- 1011 1AAd dddd AAAA
+                                       get_d5_a6(opcode);
+                                       STATE("out %s, %s[%02x]\n", avr_regname(A), avr_regname(d), avr->data[d]);
+                                       _avr_set_ram(avr, A, avr->data[d]);
+                               }       break;
+                               case 0xb000: {  // IN Rd,A -- 1011 0AAd dddd AAAA
+                                       get_d5_a6(opcode);
+                                       STATE("in %s, %s[%02x]\n", avr_regname(d), avr_regname(A), avr->data[A]);
+                                       _avr_set_r(avr, d, _avr_get_ram(avr, A));
+                               }       break;
+                               default: _avr_invalid_opcode(avr);
+                       }
+               }       break;
+
+               case 0xc000: {  // RJMP -- 1100 kkkk kkkk kkkk
+                       get_o12(opcode);
+                       STATE("rjmp .%d [%04x]\n", o >> 1, new_pc + o);
+                       new_pc = (new_pc + o) % (avr->flashend+1);
+                       cycle++;
+                       TRACE_JUMP();
+               }       break;
+
+               case 0xd000: {  // RCALL -- 1101 kkkk kkkk kkkk
+                       get_o12(opcode);
+                       STATE("rcall .%d [%04x]\n", o >> 1, new_pc + o);
+                       cycle += _avr_push_addr(avr, new_pc);
+                       new_pc = (new_pc + o) % (avr->flashend+1);
+                       // 'rcall .1' is used as a cheap "push 16 bits of room on the stack"
+                       if (o != 0) {
+                               TRACE_JUMP();
+                               STACK_FRAME_PUSH();
+                       }
+               }       break;
+
+               case 0xe000: {  // LDI Rd, K aka SER (LDI r, 0xff) -- 1110 kkkk dddd kkkk
+                       get_h4_k8(opcode);
+                       STATE("ldi %s, 0x%02x\n", avr_regname(h), k);
+                       _avr_set_r(avr, h, k);
+               }       break;
+
+               case 0xf000: {
+                       switch (opcode & 0xfe00) {
+                               case 0xf100: {  /* simavr special opcodes */
+                                       if (opcode == 0xf1f1) { // AVR_OVERFLOW_OPCODE
+                                               printf("FLASH overflow, soft reset\n");
+                                               new_pc = 0;
+                                               TRACE_JUMP();
+                                       }
+                               }       break;
+                               case 0xf000:
+                               case 0xf200:
+                               case 0xf400:
+                               case 0xf600: {  // BRXC/BRXS -- All the SREG branches -- 1111 0Boo oooo osss
+                                       int16_t o = ((int16_t)(opcode << 6)) >> 9; // offset
+                                       uint8_t s = opcode & 7;
+                                       int set = (opcode & 0x0400) == 0;               // this bit means BRXC otherwise BRXS
+                                       int branch = (avr->sreg[s] && set) || (!avr->sreg[s] && !set);
+                                       const char *names[2][8] = {
+                                                       { "brcc", "brne", "brpl", "brvc", NULL, "brhc", "brtc", "brid"},
+                                                       { "brcs", "breq", "brmi", "brvs", NULL, "brhs", "brts", "brie"},
+                                       };
+                                       if (names[set][s]) {
+                                               STATE("%s .%d [%04x]\t; Will%s branch\n", names[set][s], o, new_pc + (o << 1), branch ? "":" not");
+                                       } else {
+                                               STATE("%s%c .%d [%04x]\t; Will%s branch\n", set ? "brbs" : "brbc", _sreg_bit_name[s], o, new_pc + (o << 1), branch ? "":" not");
+                                       }
+                                       if (branch) {
+                                               cycle++; // 2 cycles if taken, 1 otherwise
+                                               new_pc = new_pc + (o << 1);
+                                       }
+                               }       break;
+                               case 0xf800:
+                               case 0xf900: {  // BLD -- Bit Store from T into a Bit in Register -- 1111 100d dddd 0bbb
+                                       get_vd5_s3_mask(opcode);
+                                       uint8_t v = (vd & ~mask) | (avr->sreg[S_T] ? mask : 0);
+                                       STATE("bld %s[%02x], 0x%02x = %02x\n", avr_regname(d), vd, mask, v);
+                                       _avr_set_r(avr, d, v);
+                               }       break;
+                               case 0xfa00:
+                               case 0xfb00:{   // BST -- Bit Store into T from bit in Register -- 1111 101d dddd 0bbb
+                                       get_vd5_s3(opcode)
+                                       STATE("bst %s[%02x], 0x%02x\n", avr_regname(d), vd, 1 << s);
+                                       avr->sreg[S_T] = (vd >> s) & 1;
+                                       SREG();
+                               }       break;
+                               case 0xfc00:
+                               case 0xfe00: {  // SBRS/SBRC -- Skip if Bit in Register is Set/Clear -- 1111 11sd dddd 0bbb
+                                       get_vd5_s3_mask(opcode)
+                                       int set = (opcode & 0x0200) != 0;
+                                       int branch = ((vd & mask) && set) || (!(vd & mask) && !set);
+                                       STATE("%s %s[%02x], 0x%02x\t; Will%s branch\n", set ? "sbrs" : "sbrc", avr_regname(d), vd, mask, branch ? "":" not");
+                                       if (branch) {
+                                               if (_avr_is_instruction_32_bits(avr, new_pc)) {
+                                                       new_pc += 4; cycle += 2;
+                                               } else {
+                                                       new_pc += 2; cycle++;
+                                               }
+                                       }
+                               }       break;
+                               default: _avr_invalid_opcode(avr);
+                       }
+               }       break;
+
+               default: _avr_invalid_opcode(avr);
+
+       }
+       avr->cycle += cycle;
+
+       if ((avr->state == cpu_Running) &&
+               (avr->run_cycle_count > cycle) &&
+               (avr->interrupt_state == 0))
+       {
+               avr->run_cycle_count -= cycle;
+               avr->pc = new_pc;
+               goto run_one_again;
+       }
+
+       return new_pc;
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.h
new file mode 100644 (file)
index 0000000..874651e
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+       sim_core.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_CORE_H__
+#define __SIM_CORE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef NO_COLOR
+       #define FONT_GREEN
+       #define FONT_RED
+       #define FONT_DEFAULT
+#else
+       #define FONT_GREEN              "\e[32m"
+       #define FONT_RED                "\e[31m"
+       #define FONT_DEFAULT    "\e[0m"
+#endif
+
+/*
+ * Instruction decoder, run ONE instruction
+ */
+avr_flashaddr_t avr_run_one(avr_t * avr);
+
+/*
+ * These are for internal access to the stack (for interrupts)
+ */
+uint16_t _avr_sp_get(avr_t * avr);
+void _avr_sp_set(avr_t * avr, uint16_t sp);
+int _avr_push_addr(avr_t * avr, avr_flashaddr_t addr);
+
+#if CONFIG_SIMAVR_TRACE
+
+/*
+ * Get a "pretty" register name
+ */
+const char * avr_regname(unsigned int reg);
+
+/*
+ * DEBUG bits follow
+ * These will disappear when gdb arrives
+ */
+void avr_dump_state(avr_t * avr);
+
+#define DUMP_REG() { \
+                               for (int i = 0; i < 32; i++) printf("%s=%02x%c", avr_regname(i), avr->data[i],i==15?'\n':' ');\
+                               printf("\n");\
+                               uint16_t y = avr->data[R_YL] | (avr->data[R_YH]<<8);\
+                               for (int i = 0; i < 20; i++) printf("Y+%02d=%02x ", i, avr->data[y+i]);\
+                               printf("\n");\
+               }
+
+
+#if AVR_STACK_WATCH
+#define DUMP_STACK() \
+               for (int i = avr->trace_data->stack_frame_index; i; i--) {\
+                       int pci = i-1;\
+                       printf(FONT_RED "*** %04x: %-25s sp %04x\n" FONT_DEFAULT,\
+                                       avr->trace_data->stack_frame[pci].pc, \
+                                       avr->trace_data->codeline ? avr->trace_data->codeline[avr->trace_data->stack_frame[pci].pc>>1]->symbol : "unknown", \
+                                                       avr->trace_data->stack_frame[pci].sp);\
+               }
+#else
+#define DUMP_STACK()
+#endif
+
+#else /* CONFIG_SIMAVR_TRACE */
+
+#define DUMP_STACK()
+#define DUMP_REG();
+
+#endif
+
+/**
+ * Reconstructs the SREG value from avr->sreg into dst.
+ */
+#define READ_SREG_INTO(avr, dst) { \
+                       dst = 0; \
+                       for (int i = 0; i < 8; i++) \
+                               if (avr->sreg[i] > 1) { \
+                                       printf("** Invalid SREG!!\n"); \
+                               } else if (avr->sreg[i]) \
+                                       dst |= (1 << i); \
+               }
+
+static inline void avr_sreg_set(avr_t * avr, uint8_t flag, uint8_t ival)
+{
+       /*
+        *      clear interrupt_state if disabling interrupts.
+        *      set wait if enabling interrupts.
+        *      no change if interrupt flag does not change.
+        */
+
+       if (flag == S_I) {
+               if (ival) {
+                       if (!avr->sreg[S_I])
+                               avr->interrupt_state = -1;
+               } else
+                       avr->interrupt_state = 0;
+       }
+
+       avr->sreg[flag] = ival;
+}
+
+/**
+ * Splits the SREG value from src into the avr->sreg array.
+ */
+#define SET_SREG_FROM(avr, src) { \
+                       for (int i = 0; i < 8; i++) \
+                               avr_sreg_set(avr, i, (src & (1 << i)) != 0); \
+               }
+
+/*
+ * Opcode is sitting at the end of the flash to catch PC overflows.
+ * Apparently it's used by some code to simulate soft reset?
+ */
+#define AVR_OVERFLOW_OPCODE 0xf1f1
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__SIM_CORE_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.c
new file mode 100644 (file)
index 0000000..93cf265
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+       sim_cycle_timers.c
+
+       Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "sim_avr.h"
+#include "sim_time.h"
+#include "sim_cycle_timers.h"
+
+#define QUEUE(__q, __e) { \
+               (__e)->next = (__q); \
+               (__q) = __e; \
+       }
+#define DETACH(__q, __l, __e) { \
+               if (__l) \
+                       (__l)->next = (__e)->next; \
+               else \
+                       (__q) = (__e)->next; \
+       }
+#define INSERT(__q, __l, __e) { \
+               if (__l) { \
+                       (__e)->next = (__l)->next; \
+                       (__l)->next = (__e); \
+               } else { \
+                       (__e)->next = (__q); \
+                       (__q) = (__e); \
+               } \
+       }
+
+#define DEFAULT_SLEEP_CYCLES 1000
+
+void
+avr_cycle_timer_reset(
+               struct avr_t * avr)
+{
+       avr_cycle_timer_pool_t * pool = &avr->cycle_timers;
+       memset(pool, 0, sizeof(*pool));
+       // queue all slots into the free queue
+       for (int i = 0; i < MAX_CYCLE_TIMERS; i++) {
+               avr_cycle_timer_slot_p t = &pool->timer_slots[i];
+               QUEUE(pool->timer_free, t);
+       }
+       avr->run_cycle_count = 1;
+       avr->run_cycle_limit = 1;
+}
+
+static avr_cycle_count_t
+avr_cycle_timer_return_sleep_run_cycles_limited(
+       avr_t *avr,
+       avr_cycle_count_t sleep_cycle_count)
+{
+       // run_cycle_count is bound to run_cycle_limit but NOT less than 1 cycle...
+       //      this is not an error!..  unless you like deadlock.
+       avr_cycle_count_t run_cycle_count = ((avr->run_cycle_limit >= sleep_cycle_count) ?
+               sleep_cycle_count : avr->run_cycle_limit);
+       avr->run_cycle_count = run_cycle_count ? run_cycle_count : 1;
+
+       // sleep cycles are returned unbounded thus preserving original behavior.
+       return(sleep_cycle_count);
+}
+
+static void
+avr_cycle_timer_reset_sleep_run_cycles_limited(
+       avr_t *avr)
+{
+       avr_cycle_timer_pool_t * pool = &avr->cycle_timers;
+       avr_cycle_count_t sleep_cycle_count = DEFAULT_SLEEP_CYCLES;
+
+       if(pool->timer) {
+               if(pool->timer->when > avr->cycle) {
+                       sleep_cycle_count = pool->timer->when - avr->cycle;
+               } else {
+                       sleep_cycle_count = 0;
+               }
+       }
+
+       avr_cycle_timer_return_sleep_run_cycles_limited(avr, sleep_cycle_count);
+}
+
+// no sanity checks checking here, on purpose
+static void
+avr_cycle_timer_insert(
+               avr_t * avr,
+               avr_cycle_count_t when,
+               avr_cycle_timer_t timer,
+               void * param)
+{
+       avr_cycle_timer_pool_t * pool = &avr->cycle_timers;
+
+       when += avr->cycle;
+
+       avr_cycle_timer_slot_p t = pool->timer_free;
+
+       if (!t) {
+               AVR_LOG(avr, LOG_ERROR, "CYCLE: %s: ran out of timers (%d)!\n", __func__, MAX_CYCLE_TIMERS);
+               return;
+       }
+       // detach head
+       pool->timer_free = t->next;
+       t->next = NULL;
+       t->timer = timer;
+       t->param = param;
+       t->when = when;
+
+       // find its place in the list
+       avr_cycle_timer_slot_p loop = pool->timer, last = NULL;
+       while (loop) {
+               if (loop->when > when)
+                       break;
+               last = loop;
+               loop = loop->next;
+       }
+       INSERT(pool->timer, last, t);
+}
+
+void
+avr_cycle_timer_register(
+               avr_t * avr,
+               avr_cycle_count_t when,
+               avr_cycle_timer_t timer,
+               void * param)
+{
+       avr_cycle_timer_pool_t * pool = &avr->cycle_timers;
+
+       // remove it if it was already scheduled
+       avr_cycle_timer_cancel(avr, timer, param);
+
+       if (!pool->timer_free) {
+               AVR_LOG(avr, LOG_ERROR, "CYCLE: %s: pool is full (%d)!\n", __func__, MAX_CYCLE_TIMERS);
+               return;
+       }
+       avr_cycle_timer_insert(avr, when, timer, param);
+       avr_cycle_timer_reset_sleep_run_cycles_limited(avr);
+}
+
+void
+avr_cycle_timer_register_usec(
+               avr_t * avr,
+               uint32_t when,
+               avr_cycle_timer_t timer,
+               void * param)
+{
+       avr_cycle_timer_register(avr, avr_usec_to_cycles(avr, when), timer, param);
+}
+
+void
+avr_cycle_timer_cancel(
+               avr_t * avr,
+               avr_cycle_timer_t timer,
+               void * param)
+{
+       avr_cycle_timer_pool_t * pool = &avr->cycle_timers;
+
+       // find its place in the list
+       avr_cycle_timer_slot_p t = pool->timer, last = NULL;
+       while (t) {
+               if (t->timer == timer && t->param == param) {
+                       DETACH(pool->timer, last, t);
+                       QUEUE(pool->timer_free, t);
+                       break;
+               }
+               last = t;
+               t = t->next;
+       }
+       avr_cycle_timer_reset_sleep_run_cycles_limited(avr);
+}
+
+/*
+ * Check to see if a timer is present, if so, return the number (+1) of
+ * cycles left for it to fire, and if not present, return zero
+ */
+avr_cycle_count_t
+avr_cycle_timer_status(
+               avr_t * avr,
+               avr_cycle_timer_t timer,
+               void * param)
+{
+       avr_cycle_timer_pool_t * pool = &avr->cycle_timers;
+
+       // find its place in the list
+       avr_cycle_timer_slot_p t = pool->timer;
+       while (t) {
+               if (t->timer == timer && t->param == param) {
+                       return 1 + (t->when - avr->cycle);
+               }
+               t = t->next;
+       }
+       return 0;
+}
+
+/*
+ * run through all the timers, call the ones that needs it,
+ * clear the ones that wants it, and calculate the next
+ * potential cycle we could sleep for...
+ */
+avr_cycle_count_t
+avr_cycle_timer_process(
+               avr_t * avr)
+{
+       avr_cycle_timer_pool_t * pool = &avr->cycle_timers;
+
+       if (pool->timer) do {
+               avr_cycle_timer_slot_p t = pool->timer;
+               avr_cycle_count_t when = t->when;
+
+               if (when > avr->cycle)
+                       return avr_cycle_timer_return_sleep_run_cycles_limited(avr, when - avr->cycle);
+
+               // detach from active timers
+               pool->timer = t->next;
+               t->next = NULL;
+               do {
+                       avr_cycle_count_t w = t->timer(avr, when, t->param);
+                       // make sure the return value is either zero, or greater
+                       // than the last one to prevent infinite loop here
+                       when = w > when ? w : 0;
+               } while (when && when <= avr->cycle);
+               
+               if (when) // reschedule then
+                       avr_cycle_timer_insert(avr, when - avr->cycle, t->timer, t->param);
+               
+               // requeue this one into the free ones
+               QUEUE(pool->timer_free, t);
+       } while (pool->timer);
+
+       // original behavior was to return 1000 cycles when no timers were present...
+       // run_cycles are bound to at least one cycle but no more than requested limit...
+       //      value passed here is returned unbounded, thus preserving original behavior.
+       return avr_cycle_timer_return_sleep_run_cycles_limited(avr, DEFAULT_SLEEP_CYCLES);
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.h
new file mode 100644 (file)
index 0000000..a4dfdf7
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+       sim_cycle_timers.h
+
+       Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * cycle timers are callbacks that will be called when "when" cycle is reached
+ * these timers are one shots, then get cleared if the timer function returns zero,
+ * they get reset if the callback function returns a new cycle number
+ *
+ * the implementation maintains a list of 'pending' timers, sorted by when they
+ * should run, it allows very quick comparison with the next timer to run, and
+ * quick removal of then from the pile once dispatched.
+ */
+#ifndef __SIM_CYCLE_TIMERS_H___
+#define __SIM_CYCLE_TIMERS_H___
+
+#include "sim_avr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_CYCLE_TIMERS       64
+
+typedef avr_cycle_count_t (*avr_cycle_timer_t)(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param);
+
+/*
+ * Each timer instance contains the absolute cycle number they
+ * are hoping to run at, a function pointer to call and a parameter
+ * 
+ * it will NEVER be the exact cycle specified, as each instruction is
+ * not divisible and might take 2 or more cycles anyway.
+ * 
+ * However if there was a LOT of cycle lag, the timer migth be called
+ * repeteadly until it 'caches up'.
+ */
+typedef struct avr_cycle_timer_slot_t {
+       struct avr_cycle_timer_slot_t *next;
+       avr_cycle_count_t       when;
+       avr_cycle_timer_t       timer;
+       void * param;
+} avr_cycle_timer_slot_t, *avr_cycle_timer_slot_p;
+
+/*
+ * Timer pool contains a pool of timer slots available, they all
+ * start queued into the 'free' qeueue, are migrated to the
+ * 'active' queue when needed and are re-queued to the free one
+ * when done
+ */
+typedef struct avr_cycle_timer_pool_t {
+       avr_cycle_timer_slot_t timer_slots[MAX_CYCLE_TIMERS];
+       avr_cycle_timer_slot_p timer_free;
+       avr_cycle_timer_slot_p timer;
+} avr_cycle_timer_pool_t, *avr_cycle_timer_pool_p;
+
+
+// register for calling 'timer' in 'when' cycles
+void
+avr_cycle_timer_register(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               avr_cycle_timer_t timer,
+               void * param);
+// register a timer to call in 'when' usec
+void
+avr_cycle_timer_register_usec(
+               struct avr_t * avr,
+               uint32_t when,
+               avr_cycle_timer_t timer,
+               void * param);
+// cancel a previously set timer
+void
+avr_cycle_timer_cancel(
+               struct avr_t * avr,
+               avr_cycle_timer_t timer,
+               void * param);
+/*
+ * Check to see if a timer is present, if so, return the number (+1) of
+ * cycles left for it to fire, and if not present, return zero
+ */
+avr_cycle_count_t
+avr_cycle_timer_status(
+               struct avr_t * avr,
+               avr_cycle_timer_t timer,
+               void * param);
+
+//
+// Private, called from the core
+//
+avr_cycle_count_t
+avr_cycle_timer_process(
+               struct avr_t * avr);
+void
+avr_cycle_timer_reset(
+               struct avr_t * avr);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_CYCLE_TIMERS_H___ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.c
new file mode 100644 (file)
index 0000000..df69b81
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+       sim_elf.c
+
+       Loads a .elf file, extract the code, the data, the eeprom and
+       the "mcu" specification section, also load usable code symbols
+       to be able to print meaningful trace information.
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libelf.h>
+#include <gelf.h>
+
+#include "sim_elf.h"
+#include "sim_vcd_file.h"
+#include "avr_eeprom.h"
+#include "avr_ioport.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void
+avr_load_firmware(
+               avr_t * avr,
+               elf_firmware_t * firmware)
+{
+       if (firmware->frequency)
+               avr->frequency = firmware->frequency;
+       if (firmware->vcc)
+               avr->vcc = firmware->vcc;
+       if (firmware->avcc)
+               avr->avcc = firmware->avcc;
+       if (firmware->aref)
+               avr->aref = firmware->aref;
+#if CONFIG_SIMAVR_TRACE && ELF_SYMBOLS
+       int scount = firmware->flashsize >> 1;
+       avr->trace_data->codeline = malloc(scount * sizeof(avr_symbol_t*));
+       memset(avr->trace_data->codeline, 0, scount * sizeof(avr_symbol_t*));
+
+       for (int i = 0; i < firmware->symbolcount; i++)
+               if (firmware->symbol[i]->addr < firmware->flashsize)    // code address
+                       avr->trace_data->codeline[firmware->symbol[i]->addr >> 1] =
+                               firmware->symbol[i];
+       // "spread" the pointers for known symbols forward
+       avr_symbol_t * last = NULL;
+       for (int i = 0; i < scount; i++) {
+               if (!avr->trace_data->codeline[i])
+                       avr->trace_data->codeline[i] = last;
+               else
+                       last = avr->trace_data->codeline[i];
+       }
+#endif
+
+       avr_loadcode(avr, firmware->flash,
+                       firmware->flashsize, firmware->flashbase);
+       avr->codeend = firmware->flashsize +
+                       firmware->flashbase - firmware->datasize;
+
+       if (firmware->eeprom && firmware->eesize) {
+               avr_eeprom_desc_t d = {
+                               .ee = firmware->eeprom,
+                               .offset = 0,
+                               .size = firmware->eesize
+               };
+               avr_ioctl(avr, AVR_IOCTL_EEPROM_SET, &d);
+       }
+       if (firmware->fuse)
+               memcpy(avr->fuse, firmware->fuse, firmware->fusesize);
+       if (firmware->lockbits)
+               avr->lockbits = firmware->lockbits[0];
+       // load the default pull up/down values for ports
+       for (int i = 0; i < 8 && firmware->external_state[i].port; i++) {
+               avr_ioport_external_t e = {
+                       .name = firmware->external_state[i].port,
+                       .mask = firmware->external_state[i].mask,
+                       .value = firmware->external_state[i].value,
+               };
+               avr_ioctl(avr, AVR_IOCTL_IOPORT_SET_EXTERNAL(e.name), &e);
+       }
+       avr_set_command_register(avr, firmware->command_register_addr);
+       avr_set_console_register(avr, firmware->console_register_addr);
+
+       // rest is initialization of the VCD file
+       if (firmware->tracecount == 0)
+               return;
+       avr->vcd = malloc(sizeof(*avr->vcd));
+       memset(avr->vcd, 0, sizeof(*avr->vcd));
+       avr_vcd_init(avr,
+               firmware->tracename[0] ? firmware->tracename: "gtkwave_trace.vcd",
+               avr->vcd,
+               firmware->traceperiod >= 1000 ? firmware->traceperiod : 1000);
+
+       AVR_LOG(avr, LOG_TRACE, "Creating VCD trace file '%s'\n",
+                       avr->vcd->filename);
+
+       for (int ti = 0; ti < firmware->tracecount; ti++) {
+               if (firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_PORTPIN) {
+                       avr_irq_t * irq = avr_io_getirq(avr,
+                                       AVR_IOCTL_IOPORT_GETIRQ(firmware->trace[ti].mask),
+                                       firmware->trace[ti].addr);
+                       if (irq) {
+                               char name[16];
+                               sprintf(name, "%c%d", firmware->trace[ti].mask,
+                                               firmware->trace[ti].addr);
+                               avr_vcd_add_signal(avr->vcd, irq, 1,
+                                       firmware->trace[ti].name[0] ?
+                                               firmware->trace[ti].name : name);
+                       }
+               } else if (firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_IRQ) {
+                       avr_irq_t * bit = avr_get_interrupt_irq(avr, firmware->trace[ti].mask);
+                       if (bit && firmware->trace[ti].addr < AVR_INT_IRQ_COUNT)
+                               avr_vcd_add_signal(avr->vcd,
+                                               &bit[firmware->trace[ti].addr],
+                                               firmware->trace[ti].mask == 0xff ? 8 : 1,
+                                               firmware->trace[ti].name);
+               } else if (firmware->trace[ti].mask == 0xff ||
+                               firmware->trace[ti].mask == 0) {
+                       // easy one
+                       avr_irq_t * all = avr_iomem_getirq(avr,
+                                       firmware->trace[ti].addr,
+                                       firmware->trace[ti].name,
+                                       AVR_IOMEM_IRQ_ALL);
+                       if (!all) {
+                               AVR_LOG(avr, LOG_ERROR,
+                                       "ELF: %s: unable to attach trace to address %04x\n",
+                                       __FUNCTION__, firmware->trace[ti].addr);
+                       } else {
+                               avr_vcd_add_signal(avr->vcd, all, 8,
+                                               firmware->trace[ti].name);
+                       }
+               } else {
+                       int count = __builtin_popcount(firmware->trace[ti].mask);
+               //      for (int bi = 0; bi < 8; bi++)
+               //              if (firmware->trace[ti].mask & (1 << bi))
+               //                      count++;
+                       for (int bi = 0; bi < 8; bi++)
+                               if (firmware->trace[ti].mask & (1 << bi)) {
+                                       avr_irq_t * bit = avr_iomem_getirq(avr,
+                                                       firmware->trace[ti].addr,
+                                                       firmware->trace[ti].name,
+                                                       bi);
+                                       if (!bit) {
+                                               AVR_LOG(avr, LOG_ERROR,
+                                                       "ELF: %s: unable to attach trace to address %04x\n",
+                                                       __FUNCTION__, firmware->trace[ti].addr);
+                                               break;
+                                       }
+
+                                       if (count == 1) {
+                                               avr_vcd_add_signal(avr->vcd,
+                                                               bit, 1, firmware->trace[ti].name);
+                                               break;
+                                       }
+                                       char comp[128];
+                                       sprintf(comp, "%s.%d", firmware->trace[ti].name, bi);
+                                       avr_vcd_add_signal(avr->vcd, bit, 1, comp);
+                               }
+               }
+       }
+       // if the firmware has specified a command register, do NOT start the trace here
+       // the firmware probably knows best when to start/stop it
+       if (!firmware->command_register_addr)
+               avr_vcd_start(avr->vcd);
+}
+
+static void
+elf_parse_mmcu_section(
+               elf_firmware_t * firmware,
+               uint8_t * src,
+               uint32_t size)
+{
+//     hdump(".mmcu", src, size);
+       while (size) {
+               uint8_t tag = *src++;
+               uint8_t ts = *src++;
+               int next = size > 2 + ts ? 2 + ts : size;
+       //      printf("elf_parse_mmcu_section %2d, size %2d / remains %3d\n",
+       //                      tag, ts, size);
+               switch (tag) {
+                       case AVR_MMCU_TAG_FREQUENCY:
+                               firmware->frequency =
+                                       src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+                               break;
+                       case AVR_MMCU_TAG_NAME:
+                               strcpy(firmware->mmcu, (char*)src);
+                               break;
+                       case AVR_MMCU_TAG_VCC:
+                               firmware->vcc =
+                                       src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+                               break;
+                       case AVR_MMCU_TAG_AVCC:
+                               firmware->avcc =
+                                       src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+                               break;
+                       case AVR_MMCU_TAG_AREF:
+                               firmware->aref =
+                                       src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+                               break;
+                       case AVR_MMCU_TAG_PORT_EXTERNAL_PULL: {
+                               for (int i = 0; i < 8; i++)
+                                       if (!firmware->external_state[i].port) {
+                                               firmware->external_state[i].port = src[2];
+                                               firmware->external_state[i].mask = src[1];
+                                               firmware->external_state[i].value = src[0];
+#if 0
+                                               AVR_LOG(NULL, LOG_DEBUG,
+                                                       "AVR_MMCU_TAG_PORT_EXTERNAL_PULL[%d] %c:%02x:%02x\n",
+                                                       i, firmware->external_state[i].port,
+                                                       firmware->external_state[i].mask,
+                                                       firmware->external_state[i].value);
+#endif
+                                               break;
+                                       }
+                       }       break;
+                       case AVR_MMCU_TAG_VCD_PORTPIN:
+                       case AVR_MMCU_TAG_VCD_IRQ:
+                       case AVR_MMCU_TAG_VCD_TRACE: {
+                               uint8_t mask = src[0];
+                               uint16_t addr = src[1] | (src[2] << 8);
+                               char * name = (char*)src + 3;
+
+#if 0
+                               AVR_LOG(NULL, LOG_DEBUG,
+                                               "VCD_TRACE %d %04x:%02x - %s\n", tag,
+                                               addr, mask, name);
+#endif
+                               firmware->trace[firmware->tracecount].kind = tag;
+                               firmware->trace[firmware->tracecount].mask = mask;
+                               firmware->trace[firmware->tracecount].addr = addr;
+                               strncpy(firmware->trace[firmware->tracecount].name, name,
+                                       sizeof(firmware->trace[firmware->tracecount].name));
+                               firmware->tracecount++;
+                       }       break;
+                       case AVR_MMCU_TAG_VCD_FILENAME: {
+                               strcpy(firmware->tracename, (char*)src);
+                       }       break;
+                       case AVR_MMCU_TAG_VCD_PERIOD: {
+                               firmware->traceperiod =
+                                       src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+                       }       break;
+                       case AVR_MMCU_TAG_SIMAVR_COMMAND: {
+                               firmware->command_register_addr = src[0] | (src[1] << 8);
+                       }       break;
+                       case AVR_MMCU_TAG_SIMAVR_CONSOLE: {
+                               firmware->console_register_addr = src[0] | (src[1] << 8);
+                       }       break;
+               }
+               size -= next;
+               src += next - 2; // already incremented
+       }
+}
+
+static int
+elf_copy_segment(int fd, Elf32_Phdr *php, uint8_t **dest)
+{
+       int rv;
+
+       if (*dest == NULL)
+               *dest = malloc(php->p_filesz);
+       if (!*dest)
+               return -1;
+
+       lseek(fd, php->p_offset, SEEK_SET);
+       rv = read(fd, *dest, php->p_filesz);
+       if (rv != php->p_filesz) {
+               AVR_LOG(NULL, LOG_ERROR,
+                               "Got %d when reading %d bytes for %x at offset %d "
+                               "from ELF file\n",
+                               rv, php->p_filesz, php->p_vaddr, php->p_offset);
+               return -1;
+       }
+       AVR_LOG(NULL, LOG_DEBUG, "Loaded %d bytes at %x\n",
+                       php->p_filesz, php->p_vaddr);
+       return 0;
+}
+
+static int
+elf_handle_segment(int fd, Elf32_Phdr *php, uint8_t **dest, const char *name)
+{
+       if (*dest) {
+               AVR_LOG(NULL, LOG_ERROR,
+                               "Unexpected extra %s data: %d bytes at %x.\n",
+                               name, php->p_filesz, php->p_vaddr);
+               return -1;
+       } else {
+               elf_copy_segment(fd, php, dest);
+               return 0;
+       }
+}
+
+/* The structure *firmware must be pre-initialised to zero, then optionally
+ * tracing and VCD information may be added.
+ */
+
+int
+elf_read_firmware(
+       const char * file,
+       elf_firmware_t * firmware)
+{
+       Elf32_Ehdr  elf_header;                 /* ELF header */
+       Elf        *elf = NULL;         /* Our Elf pointer for libelf */
+       Elf32_Phdr *php;                                /* Program header. */
+       Elf_Scn    *scn = NULL;         /* Section Descriptor */
+       size_t      ph_count;           /* Program Header entry count. */
+       int         fd, i;              /* File Descriptor */
+
+       if ((fd = open(file, O_RDONLY | O_BINARY)) == -1 ||
+                       (read(fd, &elf_header, sizeof(elf_header))) < sizeof(elf_header)) {
+               AVR_LOG(NULL, LOG_ERROR, "could not read %s\n", file);
+               perror(file);
+               close(fd);
+               return -1;
+       }
+
+#if ELF_SYMBOLS
+       firmware->symbolcount = 0;
+       firmware->symbol = NULL;
+#endif
+
+       /* this is actually mandatory !! otherwise elf_begin() fails */
+       if (elf_version(EV_CURRENT) == EV_NONE) {
+               /* library out of date - recover from error */
+               return -1;
+       }
+       // Iterate through section headers again this time well stop when we find symbols
+       elf = elf_begin(fd, ELF_C_READ, NULL);
+       //printf("Loading elf %s : %p\n", file, elf);
+
+       if (!elf)
+               return -1;
+       if (elf_kind(elf) != ELF_K_ELF) {
+               AVR_LOG(NULL, LOG_ERROR, "Unexpected ELF file type\n");
+               return -1;
+       }
+
+       /* Scan the Program Header Table. */
+
+       if (elf_getphdrnum(elf, &ph_count) != 0 || ph_count == 0 ||
+               (php = elf32_getphdr(elf)) == NULL) {
+               AVR_LOG(NULL, LOG_ERROR, "No ELF Program Headers\n");
+               return -1;
+       }
+
+       for (i = 0; i < (int)ph_count; ++i, ++php) {
+#if 0
+               printf("Header %d type %d addr %x/%x size %d/%d flags %x\n",
+                          i, php->p_type, php->p_vaddr, php->p_paddr,
+                          php->p_filesz, php->p_memsz, php->p_flags);
+#endif
+               if (php->p_type != PT_LOAD || php->p_filesz == 0)
+                       continue;
+               if (php->p_vaddr < 0x800000) {
+                       /* Explicit flash section. Load it. */
+
+                       if (elf_handle_segment(fd, php, &firmware->flash, "Flash"))
+                               continue;
+                       firmware->flashsize = php->p_filesz;
+                       firmware->flashbase = php->p_vaddr;
+               } else if (php->p_vaddr < 0x810000) {
+                       /* Data space.  If there are initialised variables, treat
+                        * them as extra initialised flash.  The C startup function
+                        * understands that and will copy them to RAM.
+                        */
+
+                       if (firmware->flash) {
+                               uint8_t *where;
+
+                               firmware->flash = realloc(firmware->flash,
+                                                                                 firmware->flashsize + php->p_filesz);
+                               if (!firmware->flash)
+                                       return -1;
+                               where = firmware->flash + firmware->flashsize;
+                               elf_copy_segment(fd, php, &where);
+                               firmware->flashsize += php->p_filesz;
+                       } else {
+                               /* If this ever happens, add a second pass. */
+
+                               AVR_LOG(NULL, LOG_ERROR,
+                                               "Initialialised data but no flash (%d bytes at %x)!\n",
+                                               php->p_filesz, php->p_vaddr);
+                               return -1;
+                       }
+               } else if (php->p_vaddr < 0x820000) {
+                       /* EEPROM. */
+
+                       if (elf_handle_segment(fd, php, &firmware->eeprom, "EEPROM"))
+                               continue;
+                       firmware->eesize = php->p_filesz;
+               } else if (php->p_vaddr < 0x830000) {
+                       /* Fuses. */
+
+                       if (elf_handle_segment(fd, php, &firmware->fuse, "Fuses"))
+                               continue;
+                       firmware->fusesize = php->p_filesz;
+               } else if (php->p_vaddr < 0x840000) {
+                       /* Lock bits. */
+
+                       elf_handle_segment(fd, php, &firmware->lockbits, "Lock bits");
+               }
+       }
+
+       /* Scan the section table for .mmcu magic and symbols. */
+
+       while ((scn = elf_nextscn(elf, scn)) != NULL) {
+               GElf_Shdr shdr;                 /* Section Header */
+               gelf_getshdr(scn, &shdr);
+               char * name = elf_strptr(elf, elf_header.e_shstrndx, shdr.sh_name);
+               //      printf("Walking elf section '%s'\n", name);
+
+               if (!strcmp(name, ".mmcu")) {
+                       Elf_Data *s = elf_getdata(scn, NULL);
+
+                       elf_parse_mmcu_section(firmware, s->d_buf, s->d_size);
+                       if (shdr.sh_addr < 0x860000)
+                               AVR_LOG(NULL, LOG_WARNING,
+                                               "Warning: ELF .mmcu section at %x may be loaded.\n",
+                                               shdr.sh_addr);
+               //      printf("%s: size %ld\n", __FUNCTION__, s->d_size);
+               //      avr->frequency = f_cpu;
+               }
+
+#if ELF_SYMBOLS
+               // When we find a section header marked SHT_SYMTAB stop and get symbols
+               if (shdr.sh_type == SHT_SYMTAB) {
+                       // edata points to our symbol table
+                       Elf_Data *edata = elf_getdata(scn, NULL);
+
+                       // how many symbols are there? this number comes from the size of
+                       // the section divided by the entry size
+                       int symbol_count = shdr.sh_size / shdr.sh_entsize;
+
+                       // loop through to grab all symbols
+                       for (int i = 0; i < symbol_count; i++) {
+                               GElf_Sym sym;                   /* Symbol */
+                               // libelf grabs the symbol data using gelf_getsym()
+                               gelf_getsym(edata, i, &sym);
+
+                               // print out the value and size
+                               if (ELF32_ST_BIND(sym.st_info) == STB_GLOBAL ||
+                                               ELF32_ST_TYPE(sym.st_info) == STT_FUNC ||
+                                               ELF32_ST_TYPE(sym.st_info) == STT_OBJECT) {
+                                       const char * name = elf_strptr(elf, shdr.sh_link, sym.st_name);
+
+                                       // if its a bootloader, this symbol will be the entry point we need
+                                       if (!strcmp(name, "__vectors"))
+                                               firmware->flashbase = sym.st_value;
+                                       avr_symbol_t * s = malloc(sizeof(avr_symbol_t) + strlen(name) + 1);
+                                       strcpy((char*)s->symbol, name);
+                                       s->addr = sym.st_value;
+                                       s->size = sym.st_size;
+                                       if (!(firmware->symbolcount % 8))
+                                               firmware->symbol = realloc(
+                                                       firmware->symbol,
+                                                       (firmware->symbolcount + 8) * sizeof(firmware->symbol[0]));
+
+                                       // insert new element, keep the array sorted
+                                       int insert = -1;
+                                       for (int si = 0; si < firmware->symbolcount && insert == -1; si++)
+                                               if (firmware->symbol[si]->addr >= s->addr)
+                                                       insert = si;
+                                       if (insert == -1)
+                                               insert = firmware->symbolcount;
+                                       else
+                                               memmove(firmware->symbol + insert + 1,
+                                                               firmware->symbol + insert,
+                                                               (firmware->symbolcount - insert) * sizeof(firmware->symbol[0]));
+                                       firmware->symbol[insert] = s;
+                                       firmware->symbolcount++;
+                               }
+                       }
+               }
+#endif // ELF_SYMBOLS
+       }
+       elf_end(elf);
+       close(fd);
+       return 0;
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.h
new file mode 100644 (file)
index 0000000..37d61d7
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+       sim_elf.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_ELF_H__
+#define __SIM_ELF_H__
+
+#include "avr/avr_mcu_section.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ELF_SYMBOLS
+#define ELF_SYMBOLS 1
+#endif
+
+/* these are the addresses the gnu linker uses to
+ * "fake" a non-Harvard addressing space for the AVR
+ */
+#define AVR_SEGMENT_OFFSET_FLASH 0
+#define AVR_SEGMENT_OFFSET_EEPROM 0x00810000
+
+#include "sim_avr.h"
+
+typedef struct elf_firmware_t {
+       char  mmcu[64];
+       uint32_t        frequency;
+       uint32_t        vcc,avcc,aref;
+
+       char            tracename[128]; // trace filename
+       uint32_t        traceperiod;
+       int                     tracecount;
+       struct {
+               uint8_t kind;
+               uint8_t mask;
+               uint16_t addr;
+               char    name[64];
+       } trace[32];
+
+       struct {
+               char port;
+               uint8_t mask, value;
+       } external_state[8];
+
+       // register to listen to for commands from the firmware
+       uint16_t        command_register_addr;
+       uint16_t        console_register_addr;
+
+       uint32_t        flashbase;      // base address
+       uint8_t *       flash;
+       uint32_t        flashsize;
+       uint32_t        datasize;
+       uint32_t        bsssize;
+       // read the .eeprom section of the elf, too
+       uint8_t *       eeprom;
+       uint32_t        eesize;
+       uint8_t *       fuse;
+       uint32_t        fusesize;
+       uint8_t *       lockbits;
+
+#if ELF_SYMBOLS
+       avr_symbol_t **  symbol;
+       uint32_t        symbolcount;
+#endif
+} elf_firmware_t ;
+
+/* The structure *firmware must be pre-initialised to zero, then optionally
+ * with tracing and VCD information.
+ */
+
+int
+elf_read_firmware(
+       const char * file,
+       elf_firmware_t * firmware);
+
+void
+avr_load_firmware(
+       avr_t * avr,
+       elf_firmware_t * firmware);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__SIM_ELF_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.c
new file mode 100644 (file)
index 0000000..cd2196e
--- /dev/null
@@ -0,0 +1,1026 @@
+/*
+       sim_gdb.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sim_network.h"
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include "sim_avr.h"
+#include "sim_core.h" // for SET_SREG_FROM, READ_SREG_INTO
+#include "sim_hex.h"
+#include "avr_eeprom.h"
+#include "sim_gdb.h"
+
+// For debug printfs: "#define DBG(w) w"
+#define DBG(w)
+
+#define WATCH_LIMIT (32)
+
+typedef struct {
+       uint32_t len; /**< How many points are taken (points[0] .. points[len - 1]). */
+       struct {
+               uint32_t addr; /**< Which address is watched. */
+               uint32_t size; /**< How large is the watched segment. */
+               uint32_t kind; /**< Bitmask of enum avr_gdb_watch_type values. */
+       } points[WATCH_LIMIT];
+} avr_gdb_watchpoints_t;
+
+/* How many AVR instructions to execute before looking for gdb input. */
+
+#define GDB_BURST 256
+
+typedef struct avr_gdb_t {
+       avr_t * avr;
+       int burst_count;        // Current instruction burst size
+       int     listen;                 // listen socket
+       int     s;                              // current gdb connection
+
+    avr_gdb_watchpoints_t breakpoints;
+       avr_gdb_watchpoints_t watchpoints;
+
+       // These are used by gdb's "info io_registers" command.
+
+       uint16_t ior_base;
+       uint8_t  ior_count, mad;
+} avr_gdb_t;
+
+
+/**
+ * Returns the index of the watchpoint if found, -1 otherwise.
+ */
+static int
+gdb_watch_find(
+               const avr_gdb_watchpoints_t * w,
+               uint32_t addr )
+{
+       for (int i = 0; i < w->len; i++) {
+               if (w->points[i].addr > addr) {
+                       return -1;
+               } else if (w->points[i].addr == addr) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+/**
+ * Contrary to gdb_watch_find, this actually checks the address against
+ * a watched memory _range_.
+ */
+static int
+gdb_watch_find_range(
+               const avr_gdb_watchpoints_t * w,
+               uint32_t addr )
+{
+       for (int i = 0; i < w->len; i++) {
+               if (w->points[i].addr > addr) {
+                       return -1;
+               } else if (w->points[i].addr <= addr &&
+                                  addr < w->points[i].addr + w->points[i].size) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+/**
+ * Returns -1 on error, 0 otherwise.
+ */
+static int
+gdb_watch_add_or_update(
+               avr_gdb_watchpoints_t * w,
+               enum avr_gdb_watch_type kind,
+               uint32_t addr,
+               uint32_t size )
+{
+       if (kind == AVR_GDB_WATCH_ACCESS)
+               kind |= AVR_GDB_WATCH_WRITE | AVR_GDB_WATCH_READ;
+
+       /* If the watchpoint exists, update it. */
+       int i = gdb_watch_find(w, addr);
+       if (i != -1) {
+               w->points[i].size = size;
+               w->points[i].kind |= kind;
+               return 0;
+       }
+
+       /* Otherwise add it. */
+       if (w->len == WATCH_LIMIT) {
+               return -1;
+       }
+
+       /* Find the insertion point. */
+       for (i = 0; i < w->len; i++) {
+               if (w->points[i].addr > addr) {
+                       break;
+               }
+       }
+
+       w->len++;
+
+       /* Make space for new element, moving old ones from the end. */
+       for (int j = w->len; j > i; j--) {
+               w->points[j] = w->points[j - 1];
+       }
+
+       /* Insert it. */
+       w->points[i].kind = kind;
+       w->points[i].addr = addr;
+       w->points[i].size = size;
+
+       return 0;
+}
+
+/**
+ * Returns -1 on error or if the specified point does not exist, 0 otherwise.
+ */
+static int
+gdb_watch_rm(
+               avr_gdb_watchpoints_t * w,
+               enum avr_gdb_watch_type kind,
+               uint32_t addr )
+{
+       int i = gdb_watch_find(w, addr);
+       if (i == -1) {
+               return -1;
+       }
+
+       w->points[i].kind &= ~kind;
+       if (w->points[i].kind) {
+               return 0;
+       }
+
+       for (i = i + 1; i < w->len; i++) {
+               w->points[i - 1] = w->points[i];
+       }
+
+       w->len--;
+
+       return 0;
+}
+
+static void
+gdb_watch_clear(
+               avr_gdb_watchpoints_t * w )
+{
+       w->len = 0;
+}
+
+static void
+gdb_send_reply(
+               avr_gdb_t * g,
+               char * cmd )
+{
+       uint8_t reply[1024];
+       uint8_t * dst = reply;
+       uint8_t check = 0;
+       *dst++ = '$';
+       while (*cmd) {
+               check += *cmd;
+               *dst++ = *cmd++;
+       }
+       sprintf((char*)dst, "#%02x", check);
+       DBG(printf("%s '%s'\n", __FUNCTION__, reply);)
+       send(g->s, reply, dst - reply + 3, 0);
+}
+
+static void
+gdb_send_stop_status(
+               avr_gdb_t  * g,
+               uint8_t     signal,
+               const char * reason,
+               uint32_t   * pp )
+{
+       avr_t   * avr;
+       uint8_t   sreg;
+       int       n;
+       char      cmd[64];
+
+       avr = g->avr;
+       READ_SREG_INTO(avr, sreg);
+
+       n = sprintf(cmd, "T%02x20:%02x;21:%02x%02x;22:%02x%02x%02x00;",
+                               signal, sreg,
+                               avr->data[R_SPL], avr->data[R_SPH],
+                               avr->pc & 0xff, (avr->pc >> 8) & 0xff,
+                               (avr->pc >> 16) & 0xff);
+       if (reason) {
+               if (pp)
+                       sprintf(cmd + n, "%s:%x;", reason, *pp);
+               else
+                       sprintf(cmd + n, "%s:;", reason);
+       }
+       gdb_send_reply(g, cmd);
+}
+
+static void
+gdb_send_quick_status(
+               avr_gdb_t * g,
+               uint8_t signal )
+{
+       gdb_send_stop_status(g, signal, NULL, NULL);
+}
+
+static int
+gdb_change_breakpoint(
+               avr_gdb_watchpoints_t * w,
+               int set,
+               enum avr_gdb_watch_type kind,
+               uint32_t addr,
+               uint32_t size )
+{
+       DBG(printf("%s kind %d addr %08x len %d\n", set ? "Set" : "Clear",
+                          kind, addr, size);)
+
+       if (set) {
+               return gdb_watch_add_or_update(w, kind, addr, size);
+       } else {
+               return gdb_watch_rm(w, kind, addr);
+       }
+       return -1;
+}
+
+static int
+gdb_write_register(
+               avr_gdb_t * g,
+               int regi,
+               uint8_t * src )
+{
+       switch (regi) {
+               case 0 ... 31:
+                       g->avr->data[regi] = *src;
+                       return 1;
+               case 32:
+                       g->avr->data[R_SREG] = *src;
+                       SET_SREG_FROM(g->avr, *src);
+                       return 1;
+               case 33:
+                       g->avr->data[R_SPL] = src[0];
+                       g->avr->data[R_SPH] = src[1];
+                       return 2;
+               case 34:
+                       g->avr->pc = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+                       return 4;
+       }
+       return 1;
+}
+
+static int
+gdb_read_register(
+               avr_gdb_t * g,
+               int regi,
+               char * rep )
+{
+       switch (regi) {
+               case 0 ... 31:
+                       sprintf(rep, "%02x", g->avr->data[regi]);
+                       break;
+               case 32: {
+                               uint8_t sreg;
+                               READ_SREG_INTO(g->avr, sreg);
+                               sprintf(rep, "%02x", sreg);
+                       }
+                       break;
+               case 33:
+                       sprintf(rep, "%02x%02x", g->avr->data[R_SPL], g->avr->data[R_SPH]);
+                       break;
+               case 34:
+                       sprintf(rep, "%02x%02x%02x00",
+                               g->avr->pc & 0xff, (g->avr->pc>>8)&0xff, (g->avr->pc>>16)&0xff);
+                       break;
+       }
+       return strlen(rep);
+}
+
+static int tohex(const char *in, char *out, unsigned int len)
+{
+       int n = 0;
+
+       while (*in && n + 2 < len)
+               n += sprintf(out + n, "%02x", (uint8_t)*in++);
+       return n;
+}
+
+/* Send a message to the user. Gdb must be expecting a reply, otherwise this
+ * is ignored.
+ */
+
+static void message(avr_gdb_t * g, const char *m)
+{
+       char buff[256];
+
+       buff[0] = 'O';
+       tohex(m, buff + 1, sizeof buff - 1);
+       gdb_send_reply(g, buff);
+}
+
+static int
+handle_monitor(avr_t * avr, avr_gdb_t * g, char * cmd)
+{
+       char         *ip, *op;
+       unsigned int  c1, c2;
+       char          dehex[128];
+
+       if (*cmd++ != ',')
+               return 1;               // Bad format
+       for (op = dehex; op < dehex + (sizeof dehex - 1); ++op) {
+               if (!*cmd)
+                       break;
+               if (sscanf(cmd, "%1x%1x", &c1, &c2) != 2)
+                       return 2;       // Bad format
+               *op = (c1 << 4) + c2;
+               cmd += 2;
+       }
+       *op = '\0';
+       if (*cmd)
+               return 3;               // Too long
+       ip = dehex;
+       while (*ip) {
+               while (*ip == ' ' || *ip == '\t')
+                       ++ip;
+
+               if (strncmp(ip, "reset", 5) == 0) {
+                       avr_reset(avr);
+                       avr->state = cpu_Stopped;
+                       ip += 5;
+               } else if (strncmp(ip, "halt", 4) == 0) {
+                       avr->state = cpu_Stopped;
+                       ip += 4;
+               } else if (strncmp(ip, "ior", 3) == 0) {
+                       unsigned int base;
+                       int          n, m, count;
+
+                       // Format is "ior <base> <count>
+                       // or just "ior" to reset.
+
+                       ip += 3;
+                       m = sscanf(ip, "%x %i%n", &base, &count, &n);
+                       if (m <= 0) {
+                               // Reset values.
+
+                               g->ior_base = g->ior_count = 0;
+                               n = 0;
+                       } else if (m != 2) {
+                               return 1;
+                       } else {
+                               if (count <= 0 || base + count + 32 > REG_NAME_COUNT ||
+                                       base + count + 32 > avr->ioend) {
+                                       return 4;       // bad value
+                               }
+                               g->ior_base = base;
+                               g->ior_count = count;
+                       }
+                       ip += n;
+       DBG(
+               } else if (strncmp(ip, "say ", 4) == 0) {
+                       // Put a message in the debug output.
+                       printf("Say: %s\n", ip + 4);
+                       ip += strlen(ip);
+               )
+               } else {
+                       tohex("Monitor subcommands are: ior halt reset" DBG(" say") "\n",
+                                 dehex, sizeof dehex);
+                       gdb_send_reply(g, dehex);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static void
+handle_io_registers(avr_t * avr, avr_gdb_t * g, char * cmd)
+{
+       extern const char *avr_regname(unsigned int); // sim_core.c
+       char *       params;
+       char *       reply;
+       unsigned int addr, count;
+       char         buff[1024];
+
+       if (g->mad) {
+               /* For this command, gdb employs a streaming protocol,
+                * with the command being repeated until the stub sends
+                * an empy packet as terminator.  That makes no sense,
+                * as the requests are sized to ensure the reply will
+                * fit in a single packet.
+                */
+
+               reply = "";
+               g->mad = 0;
+       } else {
+               params = cmd + 11;
+               if (sscanf(params, ":%x,%x", &addr, &count) == 2) {
+                       int i;
+
+                       // Send names and values.
+                       addr += 32;
+                       if (addr + count > avr->ioend)
+                               count = avr->ioend + 1 - addr;
+                       reply = buff;
+                       for (i = 0; i < count; ++i) {
+                               const char *name;
+
+                               name = avr_regname(addr + i);
+                               reply += sprintf(reply, "%s,%x;",
+                                                name, avr->data[addr + i]);
+                               if (reply > buff + sizeof buff - 20)
+                                       break;
+                       }
+               } else {
+                       // Send register count.
+
+                       count = g->ior_count ? g->ior_count :
+                                               avr->ioend > REG_NAME_COUNT ?
+                                                       REG_NAME_COUNT - 32 : avr->ioend - 32;
+                       sprintf(buff, "%x", count);
+               }
+               reply = buff;
+               g->mad = 1;
+       }
+       gdb_send_reply(g, reply);
+}
+
+static void
+handle_v(avr_t * avr, avr_gdb_t * g, char * cmd, int length)
+{
+       uint32_t  addr;
+       uint8_t  *src = NULL;
+       int       len, err = -1;
+
+       if (strncmp(cmd, "FlashErase", 10) == 0) {
+
+               sscanf(cmd, "%*[^:]:%x,%x", &addr, &len);
+               if (addr < avr->flashend) {
+                       src = avr->flash + addr;
+                       if (addr + len > avr->flashend)
+                               len = avr->flashend - addr;
+                       memset(src, 0xff, len);
+                       DBG(printf("FlashErase: %x,%x\n", addr, len);) //Remove
+               } else {
+                       err = 1;
+               }
+       } else if (strncmp(cmd, "FlashWrite", 10) == 0) {
+               if (sscanf(cmd, "%*[^:]:%x:%n", &addr, &len) != 1) {
+                       err = 2;
+               } else {
+                       if  (len >= length) {
+                               err = 99;
+                       } else if (addr < avr->flashend) {
+                               int      escaped;
+                               char    *end;
+                               uint8_t *limit;
+
+                               end = cmd + length - 1; // Ignore final '#'.
+                               cmd += len;
+                               src = avr->flash + addr;
+                               limit = avr->flash + avr->flashend;
+                               for (escaped = 0; cmd < end && src < limit; ++cmd) {
+                                       if (escaped) {
+                                               *src++ = *cmd ^ 0x20;
+                                               escaped = 0;
+                                       } else if (*cmd == '}') {
+                                               escaped = 1;
+                                       } else {
+                                               *src++ = *cmd;
+                                       }
+                               }
+                               DBG(printf("FlashWrite %x, %ld bytes\n", addr,
+                                                  (src - avr->flash) - addr);)
+                               addr = src - avr->flash; // Address of end.
+                               if (addr > avr->codeend) // Checked by sim_core.c
+                                       avr->codeend = addr;
+                               if (cmd != end) {
+                                       DBG(printf("FlashWrite %ld bytes left!\n", end - cmd));
+                               }
+                       } else {
+                               err = 1;
+                       }
+               }
+       } else if (strncmp(cmd, "FlashDone", 9) == 0) {
+               DBG(printf("FlashDone\n");) //Remove
+       } else {
+               gdb_send_reply(g, "");
+               return;
+       }
+
+       if (err < 0) {
+               gdb_send_reply(g, "OK");
+       } else {
+               char b[32];
+
+               sprintf(b, "E %.2d", err);
+               gdb_send_reply(g, b);
+       }
+}
+
+static void
+gdb_handle_command(
+               avr_gdb_t * g,
+               char      * cmd,
+               int         length)
+{
+       avr_t * avr = g->avr;
+       char rep[1024];
+       uint8_t command = *cmd++;
+       switch (command) {
+               case 'q':
+                       if (strncmp(cmd, "Supported", 9) == 0) {
+                               /* If GDB asked what features we support, report back
+                                * the features we support, which is just memory layout
+                                * information and stop reasons for now.
+                                */
+                               gdb_send_reply(g, "qXfer:memory-map:read+;swbreak+;hwbreak+");
+                               break;
+                       } else if (strncmp(cmd, "Attached", 8) == 0) {
+                               /* Respond that we are attached to an existing process..
+                                * ourselves!
+                                */
+                               gdb_send_reply(g, "1");
+                               break;
+                       // Rmoving the following 3 lines fixes #150 issue:
+                       // } else if (strncmp(cmd, "Offsets", 7) == 0) {
+                       //      gdb_send_reply(g, "Text=0;Data=800000;Bss=800000");
+                       //      break;
+                       } else if (strncmp(cmd, "Xfer:memory-map:read", 20) == 0) {
+                               snprintf(rep, sizeof(rep),
+                                               "l<memory-map>\n"
+                                               " <memory type='ram' start='0x800000' length='%#x'/>\n"
+                                               " <memory type='flash' start='0' length='%#x'>\n"
+                                               "  <property name='blocksize'>0x80</property>\n"
+                                               " </memory>\n"
+                                               "</memory-map>",
+                                               g->avr->ramend + 1, g->avr->flashend + 1);
+
+                               gdb_send_reply(g, rep);
+                               break;
+                       } else if (strncmp(cmd, "RegisterInfo", 12) == 0) {
+                               // Send back the information we have on this register (if any).
+                               long n = strtol(cmd + 12, NULL, 16);
+                               if (n < 32) {
+                                       // General purpose (8-bit) registers.
+                                       snprintf(rep, sizeof(rep), "name:r%ld;bitsize:8;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:%ld;dwarf:%ld;", n, n, n);
+                                       gdb_send_reply(g, rep);
+                                       break;
+                               } else if (n == 32) {
+                                       // SREG (flags) register.
+                                       snprintf(rep, sizeof(rep), "name:sreg;bitsize:8;offset:0;encoding:uint;format:binary;set:General Purpose Registers;gcc:32;dwarf:32;");
+                                       gdb_send_reply(g, rep);
+                                       break;
+                               } else if (n == 33) {
+                                       // SP register (SPH and SPL combined).
+                                       snprintf(rep, sizeof(rep), "name:sp;bitsize:16;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:33;dwarf:33;generic:sp;");
+                                       gdb_send_reply(g, rep);
+                                       break;
+                               } else if (n == 34) {
+                                       // PC register
+                                       snprintf(rep, sizeof(rep), "name:pc;bitsize:32;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:34;dwarf:34;generic:pc;");
+                                       gdb_send_reply(g, rep);
+                                       break;
+                               } else {
+                                       // Register not available.
+                                       // By sending back nothing, the debugger knows it has read
+                                       // all available registers.
+                               }
+                       } else if (strncmp(cmd, "Rcmd", 4) == 0) { // monitor command
+                               int err = handle_monitor(avr, g, cmd + 4);
+                               if (err > 0) {
+                                       snprintf(rep, sizeof rep,
+                                                "E%02x", err);
+                                       gdb_send_reply(g, rep);
+                               } else if (err == 0) {
+                                       gdb_send_reply(g, "OK");
+                               }
+                               break;
+                       } else if (strncmp(cmd, "Ravr.io_reg", 11) == 0) {
+                               handle_io_registers(avr, g, cmd);
+                               break;
+                       }
+                       gdb_send_reply(g, "");
+                       break;
+               case '?':
+                       gdb_send_quick_status(g, 0);
+                       break;
+               case 'G': {     // set all general purpose registers
+                       // get their binary form
+                       read_hex_string(cmd, (uint8_t*)rep, strlen(cmd));
+                       uint8_t *src = (uint8_t*)rep;
+                       for (int i = 0; i < 35; i++)
+                               src += gdb_write_register(g, i, src);
+                       gdb_send_reply(g, "OK");
+               }       break;
+               case 'g': {     // read all general purpose registers
+                       char * dst = rep;
+                       for (int i = 0; i < 35; i++)
+                               dst += gdb_read_register(g, i, dst);
+                       gdb_send_reply(g, rep);
+               }       break;
+               case 'p': {     // read register
+                       unsigned int regi = 0;
+                       sscanf(cmd, "%x", &regi);
+                       gdb_read_register(g, regi, rep);
+                       gdb_send_reply(g, rep);
+               }       break;
+               case 'P': {     // write register
+                       unsigned int regi = 0;
+                       char * val = strchr(cmd, '=');
+                       if (!val)
+                               break;
+                       *val++ = 0;
+                       sscanf(cmd, "%x", &regi);
+                       read_hex_string(val, (uint8_t*)rep, strlen(val));
+                       gdb_write_register(g, regi, (uint8_t*)rep);
+                       gdb_send_reply(g, "OK");
+               }       break;
+               case 'm': {     // read memory
+                       avr_flashaddr_t addr;
+                       uint32_t len;
+                       sscanf(cmd, "%x,%x", &addr, &len);
+                       uint8_t * src = NULL;
+                       /* GDB seems to also use 0x1800000 for sram ?!?! */
+                       addr &= 0xffffff;
+                       if (addr < avr->flashend) {
+                               src = avr->flash + addr;
+                       } else if (addr >= 0x800000 && (addr - 0x800000) <= avr->ramend) {
+                               src = avr->data + addr - 0x800000;
+                       } else if (addr == (0x800000 + avr->ramend + 1) && len == 2) {
+                               // Allow GDB to read a value just after end of stack.
+                               // This is necessary to make instruction stepping work when stack is empty
+                               AVR_LOG(avr, LOG_TRACE,
+                                               "GDB: read just past end of stack %08x, %08x; returning zero\n", addr, len);
+                               gdb_send_reply(g, "0000");
+                               break;
+                       } else if (addr >= 0x810000 && (addr - 0x810000) <= avr->e2end) {
+                               avr_eeprom_desc_t ee = {.offset = (addr - 0x810000)};
+                               avr_ioctl(avr, AVR_IOCTL_EEPROM_GET, &ee);
+                               if (ee.ee)
+                                       src = ee.ee;
+                               else {
+                                       gdb_send_reply(g, "E01");
+                                       break;
+                               }
+                       } else {
+                               AVR_LOG(avr, LOG_ERROR,
+                                               "GDB: read memory error %08x, %08x (ramend %04x)\n",
+                                               addr, len, avr->ramend+1);
+                               gdb_send_reply(g, "E01");
+                               break;
+                       }
+                       char * dst = rep;
+                       while (len--) {
+                               sprintf(dst, "%02x", *src++);
+                               dst += 2;
+                       }
+                       *dst = 0;
+                       gdb_send_reply(g, rep);
+               }       break;
+               case 'M': {     // write memory
+                       uint32_t addr, len;
+                       sscanf(cmd, "%x,%x", &addr, &len);
+                       char * start = strchr(cmd, ':');
+                       if (!start) {
+                               gdb_send_reply(g, "E01");
+                               break;
+                       }
+                       if (addr < 0xffff) {
+                               read_hex_string(start + 1, avr->flash + addr, strlen(start+1));
+                               gdb_send_reply(g, "OK");
+                       } else if (addr >= 0x800000 && (addr - 0x800000) <= avr->ramend) {
+                               read_hex_string(start + 1, avr->data + addr - 0x800000, strlen(start+1));
+                               gdb_send_reply(g, "OK");
+                       } else if (addr >= 0x810000 && (addr - 0x810000) <= avr->e2end) {
+                               read_hex_string(start + 1, (uint8_t*)rep, strlen(start+1));
+                               avr_eeprom_desc_t ee = {.offset = (addr - 0x810000), .size = len, .ee = (uint8_t*)rep };
+                               avr_ioctl(avr, AVR_IOCTL_EEPROM_SET, &ee);
+                               gdb_send_reply(g, "OK");
+                       } else {
+                               AVR_LOG(avr, LOG_ERROR, "GDB: write memory error %08x, %08x\n", addr, len);
+                               gdb_send_reply(g, "E01");
+                       }
+               }       break;
+               case 'c': {     // continue
+                       avr->state = cpu_Running;
+               }       break;
+               case 's': {     // step
+                       avr->state = cpu_Step;
+               }       break;
+               case 'r': {     // deprecated, suggested for AVRStudio compatibility
+                       avr_reset(avr);
+                       avr->state = cpu_Stopped;
+               }       break;
+               case 'Z':       // set clear break/watchpoint
+               case 'z': {
+                       uint32_t kind, addr, len;
+                       int set = (command == 'Z');
+                       sscanf(cmd, "%d,%x,%x", &kind, &addr, &len);
+//                     printf("breakpoint %d, %08x, %08x\n", kind, addr, len);
+                       switch (kind) {
+                               case 0: // software breakpoint
+                               case 1: // hardware breakpoint
+                                       if (addr > avr->flashend ||
+                                                       gdb_change_breakpoint(&g->breakpoints, set, 1 << kind, addr, len) == -1) {
+                                               gdb_send_reply(g, "E01");
+                                               break;
+                                       }
+
+                                       gdb_send_reply(g, "OK");
+                                       break;
+                               case 2: // write watchpoint
+                               case 3: // read watchpoint
+                               case 4: // access watchpoint
+                                       /* Mask out the offset applied to SRAM addresses. */
+                                       addr &= ~0x800000;
+                                       if (addr > avr->ramend ||
+                                                       gdb_change_breakpoint(&g->watchpoints, set, 1 << kind, addr, len) == -1) {
+                                               gdb_send_reply(g, "E01");
+                                               break;
+                                       }
+
+                                       gdb_send_reply(g, "OK");
+                                       break;
+                               default:
+                                       gdb_send_reply(g, "");
+                                       break;
+                       }
+               }       break;
+               case 'D':       // detach
+#ifdef DETACHABLE
+                       if (avr->state = cpu_Stopped)
+                               avr->state = cpu_Running;
+                       gdb_send_reply(g, "OK");
+                       close(g->s);
+                       g->s = -1;
+                       break;
+#endif
+               case 'k':       // kill
+                       avr->state = cpu_Done;
+                       gdb_send_reply(g, "OK");
+                       break;
+               case 'v':
+                       handle_v(avr, g, cmd, length);
+                       break;
+               default:
+                       gdb_send_reply(g, "");
+                       break;
+       }
+}
+
+static int
+gdb_network_handler(
+               avr_gdb_t * g,
+               uint32_t dosleep )
+{
+       fd_set read_set;
+       int max;
+       FD_ZERO(&read_set);
+
+       g->burst_count = 0; // Reset burst count
+       if (g->s != -1) {
+               FD_SET(g->s, &read_set);
+               max = g->s + 1;
+       } else {
+               FD_SET(g->listen, &read_set);
+               max = g->listen + 1;
+       }
+       struct timeval timo = { dosleep / 1000000, dosleep % 1000000 };
+       int ret = select(max, &read_set, NULL, NULL, &timo);
+
+       if (ret == 0)
+               return 0;
+
+       if (FD_ISSET(g->listen, &read_set)) {
+               g->s = accept(g->listen, NULL, NULL);
+
+               if (g->s == -1) {
+                       perror("gdb_network_handler accept");
+                       sleep(5);
+                       return 1;
+               }
+               int i = 1;
+               setsockopt (g->s, IPPROTO_TCP, TCP_NODELAY, &i, sizeof (i));
+               g->avr->state = cpu_Stopped;
+               DBG(printf("%s connection opened\n", __FUNCTION__);)
+       }
+
+       if (g->s != -1 && FD_ISSET(g->s, &read_set)) {
+               uint8_t buffer[1024];
+
+               ssize_t r = recv(g->s, buffer, sizeof(buffer)-1, 0);
+
+               if (r == 0) {
+                       DBG(printf("%s connection closed\n", __FUNCTION__);)
+                       close(g->s);
+                       gdb_watch_clear(&g->breakpoints);
+                       gdb_watch_clear(&g->watchpoints);
+                       g->avr->state = cpu_Running;    // resume
+                       g->s = -1;
+                       return 1;
+               }
+               if (r == -1) {
+                       perror("gdb_network_handler recv");
+                       sleep(1);
+                       return 1;
+               }
+               buffer[r] = 0;
+
+               uint8_t * src = buffer;
+               while (*src == '+' || *src == '-')
+                       src++;
+               DBG(
+                       if (!strncmp("$vFlashWrite", (char *)src, 12)) {
+                               printf("%s: received Flashwrite command %ld bytes\n",
+                                          __FUNCTION__, r);
+                       } else {
+                               printf("%s: received command %ld bytes\n'%s'\n",
+                                          __FUNCTION__, r, buffer);
+                       })
+               // hdump("gdb", buffer, r);
+               // control C -- lets send the guy a nice status packet
+               if (*src == 3) {
+                       src++;
+                       gdb_send_quick_status(g, 2); // SIGINT
+                       g->avr->state = cpu_Stopped;
+                       printf("GDB hit control-c\n");
+               } else if (*src == '$') {
+                       // strip checksum
+                       uint8_t * end = buffer + r - 1;
+                       while (end > src && *end != '#')
+                               *end-- = 0;
+                       *end = 0;
+                       src++;
+                       DBG(
+                               if (strncmp("vFlashWrite", (char *)src, 11))
+                                       printf("GDB command = '%s'\n", src);)
+                       send(g->s, "+", 1, 0);
+                       if (end > src)
+                               gdb_handle_command(g, (char*)src, end - src);
+               }
+       }
+       return 1;
+}
+
+/* Called on a hardware break instruction. */
+void avr_gdb_handle_break(avr_t *avr)
+{
+       avr_gdb_t *g = avr->gdb;
+
+       message(g, "Simavr executed 'break' instruction.\n");
+       //gdb_send_stop_status(g, 5, "swbreak", NULL);  Correct but ignored!
+       gdb_send_quick_status(g, 5);
+}
+
+/**
+ * If an applicable watchpoint exists for addr, stop the cpu and send a status report.
+ * type is one of AVR_GDB_WATCH_READ, AVR_GDB_WATCH_WRITE depending on the type of access.
+ */
+void
+avr_gdb_handle_watchpoints(
+               avr_t * avr,
+               uint16_t addr,
+               enum avr_gdb_watch_type type )
+{
+       avr_gdb_t *g = avr->gdb;
+    uint32_t   false_addr;
+
+       int i = gdb_watch_find_range(&g->watchpoints, addr);
+       if (i == -1) {
+               return;
+       }
+
+       int kind = g->watchpoints.points[i].kind;
+       DBG(printf("Addr %04x found watchpoint %d size %d type %x wanted %x\n",
+                          addr, i, g->watchpoints.points[i].size, kind, type);)
+       if (kind & type) {
+               /* Send gdb reply (see GDB user manual appendix E.3). */
+
+               const char * what;
+
+               what = (kind & AVR_GDB_WATCH_ACCESS) ? "awatch" :
+                       (kind & AVR_GDB_WATCH_WRITE) ? "watch" : "rwatch";
+               false_addr = addr + 0x800000;
+               gdb_send_stop_status(g, 5, what, &false_addr);
+               avr->state = cpu_Stopped;
+       }
+}
+
+int
+avr_gdb_processor(
+               avr_t * avr,
+               int sleep )
+{
+       if (!avr || !avr->gdb)
+               return 0;
+       avr_gdb_t * g = avr->gdb;
+
+       if (avr->state == cpu_Running &&
+                       gdb_watch_find(&g->breakpoints, avr->pc) != -1) {
+               DBG(printf("avr_gdb_processor hit breakpoint at %08x\n", avr->pc);)
+               gdb_send_stop_status(g, 5, "hwbreak", NULL);
+               avr->state = cpu_Stopped;
+       } else if (avr->state == cpu_StepDone) {
+               gdb_send_quick_status(g, 0);
+               avr->state = cpu_Stopped;
+       } else {
+               /* Look for gdb input every GDB_BURST instructions. */
+
+               if (sleep == 0 && g->burst_count++ < GDB_BURST)
+                       return 0;
+       }
+       return gdb_network_handler(g, sleep);
+}
+
+
+int
+avr_gdb_init(
+               avr_t * avr )
+{
+       if (avr->gdb)
+               return 0; // GDB server already is active
+
+       avr_gdb_t * g = malloc(sizeof(avr_gdb_t));
+       memset(g, 0, sizeof(avr_gdb_t));
+
+       avr->gdb = NULL;
+
+       if ( network_init() ) {
+               AVR_LOG(avr, LOG_ERROR, "GDB: Can't initialize network");
+               goto error;
+       }
+
+       if ((g->listen = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+               AVR_LOG(avr, LOG_ERROR, "GDB: Can't create socket: %s", strerror(errno));
+               goto error;
+       }
+
+       int optval = 1;
+       setsockopt(g->listen, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
+
+       struct sockaddr_in address = { 0 };
+       address.sin_family = AF_INET;
+       address.sin_port = htons (avr->gdb_port);
+
+       if (bind(g->listen, (struct sockaddr *) &address, sizeof(address))) {
+               AVR_LOG(avr, LOG_ERROR, "GDB: Can not bind socket: %s", strerror(errno));
+               goto error;
+       }
+       if (listen(g->listen, 1)) {
+               perror("listen");
+               goto error;
+       }
+       printf("avr_gdb_init listening on port %d\n", avr->gdb_port);
+       g->avr = avr;
+       g->s = -1;
+       avr->gdb = g;
+       // change default run behaviour to use the slightly slower versions
+       avr->run = avr_callback_run_gdb;
+       avr->sleep = avr_callback_sleep_gdb;
+
+       return 0;
+
+error:
+       if (g->listen >= 0)
+               close(g->listen);
+       free(g);
+
+       return -1;
+}
+
+void
+avr_deinit_gdb(
+               avr_t * avr )
+{
+       if (!avr->gdb)
+               return;
+       avr->run = avr_callback_run_raw; // restore normal callbacks
+       avr->sleep = avr_callback_sleep_raw;
+       if (avr->gdb->listen != -1)
+               close(avr->gdb->listen);
+       avr->gdb->listen = -1;
+       if (avr->gdb->s != -1)
+               close(avr->gdb->s);
+       avr->gdb->s = -1;
+       free(avr->gdb);
+       avr->gdb = NULL;
+
+       network_release();
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.h
new file mode 100644 (file)
index 0000000..2936df7
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+       sim_gdb.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_GDB_H__
+#define __SIM_GDB_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Watchpoint types.
+   See GDB User Manual, Appendix E.2 */
+enum avr_gdb_watch_type {
+       AVR_GDB_BREAK_SOFT   = 1 << 0,
+       AVR_GDB_BREAK_HARD   = 1 << 1,
+
+       AVR_GDB_WATCH_WRITE  = 1 << 2,
+       AVR_GDB_WATCH_READ   = 1 << 3,
+       AVR_GDB_WATCH_ACCESS = 1 << 4
+};
+
+int avr_gdb_init(avr_t * avr);
+
+void avr_deinit_gdb(avr_t * avr);
+
+// call from the main AVR decoder thread
+int avr_gdb_processor(avr_t * avr, int sleep);
+
+// Called from sim_core.c
+void avr_gdb_handle_watchpoints(avr_t * g, uint16_t addr, enum avr_gdb_watch_type type);
+void avr_gdb_handle_break(avr_t *);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.c
new file mode 100644 (file)
index 0000000..fea67c4
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+       sim_hex.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sim_hex.h"
+#include "sim_elf.h"
+
+// friendly hex dump
+void hdump(const char *w, uint8_t *b, size_t l)
+{
+       uint32_t i;
+       if (l < 16) {
+               printf("%s: ",w);
+               for (i = 0; i < l; i++) printf("%02x",b[i]);
+       } else {
+               printf("%s:\n",w);
+               for (i = 0; i < l; i++) {
+                       if (!(i & 0x1f)) printf("    ");
+                       printf("%02x",b[i]);
+                       if ((i & 0x1f) == 0x1f) {
+                               printf(" ");
+                               printf("\n");
+                       }
+               }
+       }
+       printf("\n");
+}
+
+    // decode line text hex to binary
+int read_hex_string(const char * src, uint8_t * buffer, int maxlen)
+{
+    uint8_t * dst = buffer;
+    int ls = 0;
+    uint8_t b = 0;
+    while (*src && maxlen) {
+        char c = *src++;
+        switch (c) {
+            case 'a' ... 'f':   b = (b << 4) | (c - 'a' + 0xa); break;
+            case 'A' ... 'F':   b = (b << 4) | (c - 'A' + 0xa); break;
+            case '0' ... '9':   b = (b << 4) | (c - '0'); break;
+            default:
+                if (c > ' ') {
+                    fprintf(stderr, "%s: huh '%c' (%s)\n", __FUNCTION__, c, src);
+                    return -1;
+                }
+                continue;
+        }
+        if (ls & 1) {
+            *dst++ = b; b = 0;
+            maxlen--;
+        }
+        ls++;
+    }
+
+    return dst - buffer;
+}
+
+void
+free_ihex_chunks(
+               ihex_chunk_p chunks)
+{
+       if (!chunks)
+               return;
+       for (int i = 0; chunks[i].size; i++)
+               if (chunks[i].data)
+                       free(chunks[i].data);
+}
+
+int
+read_ihex_chunks(
+               const char * fname,
+               ihex_chunk_p * chunks )
+{
+       if (!fname || !chunks)
+               return -1;
+       FILE * f = fopen(fname, "r");
+       if (!f) {
+               perror(fname);
+               return -1;
+       }
+       uint32_t segment = 0;   // segment address
+       int chunk = 0, max_chunks = 0;
+       *chunks = NULL;
+
+       while (!feof(f)) {
+               char line[128];
+               if (!fgets(line, sizeof(line)-1, f))
+                       continue;
+               if (line[0] != ':') {
+                       fprintf(stderr, "AVR: '%s' invalid ihex format (%.4s)\n", fname, line);
+                       break;
+               }
+               uint8_t bline[64];
+
+               int len = read_hex_string(line + 1, bline, sizeof(bline));
+               if (len <= 0)
+                       continue;
+
+               uint8_t chk = 0;
+               {       // calculate checksum
+                       uint8_t * src = bline;
+                       int tlen = len-1;
+                       while (tlen--)
+                               chk += *src++;
+                       chk = 0x100 - chk;
+               }
+               if (chk != bline[len-1]) {
+                       fprintf(stderr, "%s: %s, invalid checksum %02x/%02x\n", __FUNCTION__, fname, chk, bline[len-1]);
+                       break;
+               }
+               uint32_t addr = 0;
+               switch (bline[3]) {
+                       case 0: // normal data
+                               addr = segment | (bline[1] << 8) | bline[2];
+                               break;
+                       case 1: // end of file - reset segment
+                               segment = 0;
+                               continue;
+                       case 2: // extended address 2 bytes
+                               segment = ((bline[4] << 8) | bline[5]) << 4;
+                               continue;
+                       case 4:
+                               segment = ((bline[4] << 8) | bline[5]) << 16;
+                               continue;
+                       default:
+                               fprintf(stderr, "%s: %s, unsupported check type %02x\n", __FUNCTION__, fname, bline[3]);
+                               continue;
+               }
+               if (chunk < max_chunks && addr != ((*chunks)[chunk].baseaddr + (*chunks)[chunk].size)) {
+                       if ((*chunks)[chunk].size)
+                               chunk++;
+               }
+               if (chunk >= max_chunks) {
+                       max_chunks++;
+                       /* Here we allocate and zero an extra chunk, to act as terminator */
+                       *chunks = realloc(*chunks, (1 + max_chunks) * sizeof(ihex_chunk_t));
+                       memset(*chunks + chunk, 0,
+                                       (1 + (max_chunks - chunk)) * sizeof(ihex_chunk_t));
+                       (*chunks)[chunk].baseaddr = addr;
+               }
+               (*chunks)[chunk].data = realloc((*chunks)[chunk].data,
+                                                                       (*chunks)[chunk].size + bline[0]);
+               memcpy((*chunks)[chunk].data + (*chunks)[chunk].size,
+                               bline + 4, bline[0]);
+               (*chunks)[chunk].size += bline[0];
+       }
+       fclose(f);
+       return max_chunks;
+}
+
+
+uint8_t *
+read_ihex_file(
+               const char * fname, uint32_t * dsize, uint32_t * start)
+{
+       ihex_chunk_p chunks = NULL;
+       int count = read_ihex_chunks(fname, &chunks);
+       uint8_t * res = NULL;
+
+       if (count > 0) {
+               *dsize = chunks[0].size;
+               *start = chunks[0].baseaddr;
+               res = chunks[0].data;
+               chunks[0].data = NULL;
+       }
+       if (count > 1) {
+               fprintf(stderr, "AVR: '%s' ihex contains more chunks than loaded (%d)\n",
+                               fname, count);
+       }
+       free_ihex_chunks(chunks);
+       return res;
+}
+
+/* Load a firmware file, ELF or HEX format, from filename, based at
+ * loadBase, returning the data in *fp ready for loading into
+ * the simulated MCU.  Progname is the current program name for error messages.
+ *
+ * Included here as it mostly specific to HEX files.
+ */
+
+void
+sim_setup_firmware(const char * filename, uint32_t loadBase,
+                   elf_firmware_t * fp, const char * progname)
+{
+       char * suffix = strrchr(filename, '.');
+
+       if (suffix && !strcasecmp(suffix, ".hex")) {
+               if (!(fp->mmcu[0] && fp->frequency > 0)) {
+                       printf("MCU type and frequency are not set "
+                                       "when loading .hex file\n");
+               }
+               ihex_chunk_p chunk = NULL;
+               int cnt = read_ihex_chunks(filename, &chunk);
+               if (cnt <= 0) {
+                       fprintf(stderr,
+                                       "%s: Unable to load IHEX file %s\n", progname, filename);
+                       exit(1);
+               }
+               printf("Loaded %d section(s) of ihex\n", cnt);
+
+               for (int ci = 0; ci < cnt; ci++) {
+                       if (chunk[ci].baseaddr < (1*1024*1024)) {
+                               if (fp->flash) {
+                                       printf("Ignoring chunk %d, "
+                                                  "possible flash redefinition %08x, %d\n",
+                                                  ci, chunk[ci].baseaddr, chunk[ci].size);
+                                       free(chunk[ci].data);
+                                       chunk[ci].data = NULL;
+                                       continue;
+                               }
+                               fp->flash = chunk[ci].data;
+                               fp->flashsize = chunk[ci].size;
+                               fp->flashbase = chunk[ci].baseaddr;
+                               printf("Load HEX flash %08x, %d at %08x\n",
+                                          fp->flashbase, fp->flashsize, fp->flashbase);
+                       } else if (chunk[ci].baseaddr >= AVR_SEGMENT_OFFSET_EEPROM ||
+                                          (chunk[ci].baseaddr + loadBase) >=
+                                                       AVR_SEGMENT_OFFSET_EEPROM) {
+                               // eeprom!
+
+                               if (fp->eeprom) {
+
+                                       // Converting ELF with .mmcu section will do this.
+
+                                       printf("Ignoring chunk %d, "
+                                                  "possible eeprom redefinition %08x, %d\n",
+                                                  ci, chunk[ci].baseaddr, chunk[ci].size);
+                                       free(chunk[ci].data);
+                                       chunk[ci].data = NULL;
+                                       continue;
+                               }
+                               fp->eeprom = chunk[ci].data;
+                               fp->eesize = chunk[ci].size;
+                               printf("Load HEX eeprom %08x, %d\n",
+                                          chunk[ci].baseaddr, fp->eesize);
+                       }
+               }
+                free(chunk);
+       } else {
+               if (elf_read_firmware(filename, fp) == -1) {
+                       fprintf(stderr, "%s: Unable to load firmware from file %s\n",
+                                       progname, filename);
+                       exit(1);
+               }
+       }
+}
+
+#ifdef IHEX_TEST
+// gcc -std=gnu99 -Isimavr/sim simavr/sim/sim_hex.c -o sim_hex -DIHEX_TEST -Dtest_main=main
+int test_main(int argc, char * argv[])
+{
+       struct ihex_chunk_t chunk[4];
+
+       for (int fi = 1; fi < argc; fi++) {
+               int c = read_ihex_chunks(argv[fi], chunk, 4);
+               if (c == -1) {
+                       perror(argv[fi]);
+                       continue;
+               }
+               for (int ci = 0; ci < c; ci++) {
+                       char n[96];
+                       sprintf(n, "%s[%d] = %08x", argv[fi], ci, chunk[ci].baseaddr);
+                       hdump(n, chunk[ci].data, chunk[ci].size);
+               }
+       }
+}
+#endif
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.h
new file mode 100644 (file)
index 0000000..e3a1d1f
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+       sim_hex.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __SIM_HEX_H___
+#define __SIM_HEX_H___
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Load a firmware file, ELF or HEX format, ready for use.
+
+struct elf_firmware_t;                          // Predeclaration ...
+
+void
+sim_setup_firmware(
+                                  const char * filename,       // Firmware file
+                                  uint32_t loadBase,           // Base of load region
+                                  struct elf_firmware_t * fp,  // Data returned here
+                                  const char * progname);      // For error messages.
+
+// parses a hex text string 'src' of at max 'maxlen' characters, decodes it into 'buffer'
+int
+read_hex_string(
+               const char * src,
+               uint8_t * buffer,
+               int maxlen);
+
+// a .hex file chunk (base address + size)
+typedef struct ihex_chunk_t {
+       uint32_t baseaddr;      // offset it started at in the .hex file
+       uint8_t * data;         // read data
+       uint32_t size;          // read data size
+} ihex_chunk_t, *ihex_chunk_p;
+
+/*
+ * Read a .hex file, detects the various different chunks in it from their starting
+ * addresses and allocate an array of ihex_chunk_t returned in 'chunks'.
+ * Returns the number of chunks found, or -1 if an error occurs.
+ */
+int
+read_ihex_chunks(
+               const char * fname,
+               ihex_chunk_p * chunks );
+/* Frees previously allocated chunks */
+void
+free_ihex_chunks(
+               ihex_chunk_p chunks);
+
+// reads IHEX file 'fname', puts it's decoded size in *'dsize' and returns
+// a newly allocated buffer with the binary data (or NULL, if error)
+uint8_t *
+read_ihex_file(
+               const char * fname,
+               uint32_t * dsize,
+               uint32_t * start);
+
+// hex dump from pointer 'b' for 'l' bytes with string prefix 'w'
+void
+hdump(
+               const char *w,
+               uint8_t *b, size_t l);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_HEX_H___ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.c
new file mode 100644 (file)
index 0000000..9e3ed9c
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+       sim_interrupts.c
+
+       Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include "sim_interrupts.h"
+#include "sim_avr.h"
+#include "sim_core.h"
+
+DEFINE_FIFO(avr_int_vector_p, avr_int_pending);
+
+void
+avr_interrupt_init(
+               avr_t * avr )
+{
+       avr_int_table_p table = &avr->interrupts;
+       memset(table, 0, sizeof(*table));
+
+       static const char *names[] = { ">avr.int.pending", ">avr.int.running" };
+       avr_init_irq(&avr->irq_pool, table->irq,
+                       0, // base number
+                       AVR_INT_IRQ_COUNT, names);
+}
+
+void
+avr_interrupt_reset(
+               avr_t * avr )
+{
+       avr_int_table_p table = &avr->interrupts;
+
+       table->running_ptr = 0;
+       avr_int_pending_reset(&table->pending);
+       avr->interrupt_state = 0;
+       for (int i = 0; i < table->vector_count; i++)
+               table->vector[i]->pending = 0;
+}
+
+void
+avr_register_vector(
+               avr_t *avr,
+               avr_int_vector_t * vector)
+{
+       if (!vector->vector)
+               return;
+
+       avr_int_table_p table = &avr->interrupts;
+
+       char name0[48], name1[48];
+       sprintf(name0, ">avr.int.%02x.pending", vector->vector);
+       sprintf(name1, ">avr.int.%02x.running", vector->vector);
+       const char *names[2] = { name0, name1 };
+       avr_init_irq(&avr->irq_pool, vector->irq,
+                       vector->vector * 256, // base number
+                       AVR_INT_IRQ_COUNT, names);
+       table->vector[table->vector_count++] = vector;
+       if (vector->trace)
+               printf("IRQ%d registered (enabled %04x:%d)\n",
+                       vector->vector, vector->enable.reg, vector->enable.bit);
+
+       if (!vector->enable.reg)
+               AVR_LOG(avr, LOG_WARNING, "IRQ%d No 'enable' bit !\n",
+                       vector->vector);
+}
+
+int
+avr_has_pending_interrupts(
+               avr_t * avr)
+{
+       avr_int_table_p table = &avr->interrupts;
+       return !avr_int_pending_isempty(&table->pending);
+}
+
+int
+avr_is_interrupt_pending(
+               avr_t * avr,
+               avr_int_vector_t * vector)
+{
+       return vector->pending;
+}
+
+int
+avr_is_interrupt_enabled(
+               avr_t * avr,
+               avr_int_vector_t * vector)
+{
+       return avr_regbit_get(avr, vector->enable);
+}
+
+int
+avr_raise_interrupt(
+               avr_t * avr,
+               avr_int_vector_t * vector)
+{
+       if (!vector || !vector->vector)
+               return 0;
+
+       if (vector->trace)
+               printf("IRQ%d raising (enabled %d)\n",
+                       vector->vector, avr_regbit_get(avr, vector->enable));
+
+       // always mark the 'raised' flag to one, even if the interrupt is disabled
+       // this allow "polling" for the "raised" flag, like for non-interrupt
+       // driven UART and so so. These flags are often "write one to clear"
+       if (vector->raised.reg)
+               avr_regbit_set(avr, vector->raised);
+
+       if (vector->pending) {
+               if (vector->trace)
+                       printf("IRQ%d:I=%d already raised (enabled %d) (cycle %lld pc 0x%x)\n",
+                               vector->vector, !!avr->sreg[S_I], avr_regbit_get(avr, vector->enable),
+                               (long long int)avr->cycle, avr->pc);
+
+        return 0;
+       }
+
+       avr_raise_irq(vector->irq + AVR_INT_IRQ_PENDING, 1);
+       avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, vector->vector);
+
+       // If the interrupt is enabled, attempt to wake the core
+       if (avr_regbit_get(avr, vector->enable)) {
+               // Mark the interrupt as pending
+               vector->pending = 1;
+
+               avr_int_table_p table = &avr->interrupts;
+
+               avr_int_pending_write(&table->pending, vector);
+
+               if (avr->sreg[S_I] && avr->interrupt_state == 0)
+                       avr->interrupt_state = 1;
+               if (avr->state == cpu_Sleeping) {
+                       if (vector->trace)
+                               printf("IRQ%d Waking CPU due to interrupt\n",
+                                       vector->vector);
+                       avr->state = cpu_Running;       // in case we were sleeping
+               }
+       }
+       // return 'raised' even if it was already pending
+       return 1;
+}
+
+void
+avr_clear_interrupt(
+               avr_t * avr,
+               avr_int_vector_t * vector)
+{
+       if (!vector)
+               return;
+       if (vector->trace)
+               printf("IRQ%d cleared\n", vector->vector);
+       vector->pending = 0;
+
+       avr_raise_irq(vector->irq + AVR_INT_IRQ_PENDING, 0);
+       avr_raise_irq_float(avr->interrupts.irq + AVR_INT_IRQ_PENDING,
+                       avr_has_pending_interrupts(avr) ?
+                                       avr_int_pending_read_at(
+                                                       &avr->interrupts.pending, 0)->vector : 0,
+                                                       avr_has_pending_interrupts(avr));
+
+       if (vector->raised.reg && !vector->raise_sticky)
+               avr_regbit_clear(avr, vector->raised);
+}
+
+int
+avr_clear_interrupt_if(
+               avr_t * avr,
+               avr_int_vector_t * vector,
+               uint8_t old)
+{
+       avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING,
+                       avr_has_pending_interrupts(avr));
+       if (avr_regbit_get(avr, vector->raised)) {
+               avr_clear_interrupt(avr, vector);
+               return 1;
+       }
+       avr_regbit_setto(avr, vector->raised, old);
+       return 0;
+}
+
+avr_irq_t *
+avr_get_interrupt_irq(
+               avr_t * avr,
+               uint8_t v)
+{
+       avr_int_table_p table = &avr->interrupts;
+       if (v == AVR_INT_ANY)
+               return table->irq;
+       for (int i = 0; i < table->vector_count; i++)
+               if (table->vector[i]->vector == v)
+                       return table->vector[i]->irq;
+       return NULL;
+}
+
+/* this is called upon RETI. */
+void
+avr_interrupt_reti(
+               struct avr_t * avr)
+{
+       avr_int_table_p table = &avr->interrupts;
+       if (table->running_ptr) {
+               avr_int_vector_t * vector = table->running[--table->running_ptr];
+               avr_raise_irq(vector->irq + AVR_INT_IRQ_RUNNING, 0);
+       }
+       avr_raise_irq(table->irq + AVR_INT_IRQ_RUNNING,
+                       table->running_ptr > 0 ?
+                                       table->running[table->running_ptr-1]->vector : 0);
+}
+
+/*
+ * check whether interrupts are pending. If so, check if the interrupt "latency" is reached,
+ * and if so triggers the handlers and jump to the vector.
+ */
+void
+avr_service_interrupts(
+               avr_t * avr)
+{
+       if (!avr->sreg[S_I] || !avr->interrupt_state)
+               return;
+
+       if (avr->interrupt_state < 0) {
+               avr->interrupt_state++;
+               if (avr->interrupt_state == 0)
+                       avr->interrupt_state = avr_has_pending_interrupts(avr);
+               return;
+       }
+
+       avr_int_table_p table = &avr->interrupts;
+
+       // how many are pending...
+       int cnt = avr_int_pending_get_read_size(&table->pending);
+       // locate the highest priority one
+       int min = 0xff;
+       int mini = 0;
+       for (int ii = 0; ii < cnt; ii++) {
+               avr_int_vector_t * v = avr_int_pending_read_at(&table->pending, ii);
+               if (v->vector < min) {
+                       min = v->vector;
+                       mini = ii;
+               }
+       }
+       avr_int_vector_t * vector = avr_int_pending_read_at(&table->pending, mini);
+
+       // it's possible that the vector being serviced is not at the front of the fifo, because we process interrupts based
+       // on vector priority rather than position in the fifo. if this is the case, we need to manually swap the vector
+       // being serviced with the vector at the front of the fifo so that the vector at the front of the fifo can be
+       // serviced in a following iteration.
+       avr_int_vector_p fifo_front = avr_int_pending_read(&table->pending);
+       if (fifo_front->vector != vector->vector) {
+               // the read into fifo_front above has incremented pending.read, so now mini points 1 beyond the desired
+               // destination for the swap.
+               table->pending.buffer[(table->pending.read + mini - 1) % avr_int_pending_fifo_size] = fifo_front;
+       }
+
+       // if that single interrupt is masked, ignore it and continue
+       // could also have been disabled, or cleared
+       if (!avr_regbit_get(avr, vector->enable) || !vector->pending) {
+               vector->pending = 0;
+               avr->interrupt_state = avr_has_pending_interrupts(avr);
+       } else {
+               if (vector->trace)
+                       printf("IRQ%d calling\n", vector->vector);
+               _avr_push_addr(avr, avr->pc);
+               avr_sreg_set(avr, S_I, 0);
+               avr->pc = vector->vector * avr->vector_size;
+
+               avr_raise_irq(vector->irq + AVR_INT_IRQ_RUNNING, 1);
+               avr_raise_irq(table->irq + AVR_INT_IRQ_RUNNING, vector->vector);
+               if (table->running_ptr == ARRAY_SIZE(table->running)) {
+                       AVR_LOG(avr, LOG_ERROR, "%s run out of nested stack!", __func__);
+               } else {
+                       table->running[table->running_ptr++] = vector;
+               }
+               avr_clear_interrupt(avr, vector);
+       }
+}
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.h
new file mode 100644 (file)
index 0000000..bebf614
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+       sim_interrupts.h
+
+       Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_INTERRUPTS_H__
+#define __SIM_INTERRUPTS_H__
+
+#include "sim_avr_types.h"
+#include "sim_irq.h"
+#include "fifo_declare.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+       AVR_INT_IRQ_PENDING = 0,
+       AVR_INT_IRQ_RUNNING,
+       AVR_INT_IRQ_COUNT,
+       AVR_INT_ANY             = 0xff, // for avr_get_interrupt_irq()
+};
+// interrupt vector for the IO modules
+typedef struct avr_int_vector_t {
+       uint8_t                 vector;                 // vector number, zero (reset) is reserved
+       avr_regbit_t    enable;                 // IO register index for the "interrupt enable" flag for this vector
+       avr_regbit_t    raised;                 // IO register index for the register where the "raised" flag is (optional)
+
+       uint8_t                 mask; // Mask for PCINTs. this is needed for chips like the 2560 where PCINT do not align with IRQs
+       int8_t          shift;  // PCINT8 = E0, PCINT9-15 are on J0-J6. Shift shifts down (<0) or up (>0) for alignment with IRQ#.
+
+       // 'pending' IRQ, and 'running' status as signaled here
+       avr_irq_t               irq[AVR_INT_IRQ_COUNT];
+       uint8_t                 pending : 1,    // 1 while scheduled in the fifo
+                                       trace : 1,              // only for debug of a vector
+                                       raise_sticky : 1;       // 1 if the interrupt flag (= the raised regbit) is not cleared
+                                                                               // by the hardware when executing the interrupt routine (see TWINT)
+} avr_int_vector_t, *avr_int_vector_p;
+
+// Size needs to be >= max number of vectors, and a power of two
+DECLARE_FIFO(avr_int_vector_p, avr_int_pending, 64);
+
+// interrupt vectors, and their enable/clear registers
+typedef struct  avr_int_table_t {
+       avr_int_vector_t * vector[64];
+       uint8_t                 vector_count;
+       avr_int_pending_t pending;
+       uint8_t                 running_ptr;
+       avr_int_vector_t *running[64]; // stack of nested interrupts
+       // global status for pending + running in interrupt context
+       avr_irq_t               irq[AVR_INT_IRQ_COUNT];
+} avr_int_table_t, *avr_int_table_p;
+
+/*
+ * Interrupt Helper Functions
+ */
+// register an interrupt vector. It's only needed if you want to use the "r_raised" flags
+void
+avr_register_vector(
+               struct avr_t *avr,
+               avr_int_vector_t * vector);
+// raise an interrupt (if enabled). The interrupt is latched and will be called later
+// return non-zero if the interrupt was raised and is now pending
+int
+avr_raise_interrupt(
+               struct avr_t * avr,
+               avr_int_vector_t * vector);
+// return non-zero if the AVR core has any pending interrupts
+int
+avr_has_pending_interrupts(
+               struct avr_t * avr);
+// return nonzero if a specific interrupt vector is pending
+int
+avr_is_interrupt_pending(
+               struct avr_t * avr,
+               avr_int_vector_t * vector);
+// clear the "pending" status of an interrupt
+void
+avr_clear_interrupt(
+               struct avr_t * avr,
+               avr_int_vector_t * vector);
+// called by the core at each cycle to check whether an interrupt is pending
+void
+avr_service_interrupts(
+               struct avr_t * avr);
+// called by the core when RETI opcode is ran
+void
+avr_interrupt_reti(
+               struct avr_t * avr);
+// clear the interrupt (inc pending) if "raised" flag is 1
+int
+avr_clear_interrupt_if(
+               struct avr_t * avr,
+               avr_int_vector_t * vector,
+               uint8_t old);
+
+// return the IRQ that is raised when the vector is enabled and called/cleared
+// this allows tracing of pending interrupts
+avr_irq_t *
+avr_get_interrupt_irq(
+               struct avr_t * avr,
+               uint8_t v);
+
+// Initializes the interrupt table
+void
+avr_interrupt_init(
+               struct avr_t * avr );
+
+// reset the interrupt table and the fifo
+void
+avr_interrupt_reset(
+               struct avr_t * avr );
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_INTERRUPTS_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.c
new file mode 100644 (file)
index 0000000..548f030
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+       sim_io.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+#include "sim_io.h"
+
+int
+avr_ioctl(
+               avr_t *avr,
+               uint32_t ctl,
+               void * io_param)
+{
+       avr_io_t * port = avr->io_port;
+       int res = -1;
+       while (port && res == -1) {
+               if (port->ioctl)
+                       res = port->ioctl(port, ctl, io_param);
+               port = port->next;
+       }
+       return res;
+}
+
+void
+avr_register_io(
+               avr_t *avr,
+               avr_io_t * io)
+{
+       io->next = avr->io_port;
+       io->avr = avr;
+       avr->io_port = io;
+}
+
+void
+avr_register_io_read(
+               avr_t *avr,
+               avr_io_addr_t addr,
+               avr_io_read_t readp,
+               void * param)
+{
+       avr_io_addr_t a = AVR_DATA_TO_IO(addr);
+       if (avr->io[a].r.param || avr->io[a].r.c) {
+               if (avr->io[a].r.param != param || avr->io[a].r.c != readp) {
+                       AVR_LOG(avr, LOG_ERROR,
+                                       "IO: %s(): Already registered, refusing to override.\n",
+                                       __func__);
+                       AVR_LOG(avr, LOG_ERROR,
+                                       "IO: %s(%04x : %p/%p): %p/%p\n",
+                                       __func__, a,
+                                       avr->io[a].r.c, avr->io[a].r.param, readp, param);
+                       abort();
+               }
+       }
+       avr->io[a].r.param = param;
+       avr->io[a].r.c = readp;
+}
+
+static void
+_avr_io_mux_write(
+               avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
+{
+       int io = (intptr_t)param;
+       for (int i = 0; i < avr->io_shared_io[io].used; i++) {
+               avr_io_write_t c = avr->io_shared_io[io].io[i].c;
+               if (c)
+                       c(avr, addr, v, avr->io_shared_io[io].io[i].param);
+       }
+}
+
+void
+avr_register_io_write(
+               avr_t *avr,
+               avr_io_addr_t addr,
+               avr_io_write_t writep,
+               void * param)
+{
+       avr_io_addr_t a = AVR_DATA_TO_IO(addr);
+
+       if (a >= MAX_IOs) {
+               AVR_LOG(avr, LOG_ERROR,
+                               "IO: %s(): IO address 0x%04x out of range (max 0x%04x).\n",
+                               __func__, a, MAX_IOs);
+               abort();
+       }
+       /*
+        * Verifying that some other piece of code is not installed to watch write
+        * on this address. If there is, this code installs a "dispatcher" callback
+        * instead to handle multiple clients, otherwise, it continues as usual
+        */
+       if (avr->io[a].w.param || avr->io[a].w.c) {
+               if (avr->io[a].w.param != param || avr->io[a].w.c != writep) {
+                       // if the muxer not already installed, allocate a new slot
+                       if (avr->io[a].w.c != _avr_io_mux_write) {
+                               int no = avr->io_shared_io_count++;
+                               if (avr->io_shared_io_count > ARRAY_SIZE(avr->io_shared_io)) {
+                                       AVR_LOG(avr, LOG_ERROR,
+                                                       "IO: %s(): Too many shared IO registers.\n", __func__);
+                                       abort();
+                               }
+                               AVR_LOG(avr, LOG_TRACE,
+                                               "IO: %s(%04x): Installing muxer on register.\n",
+                                               __func__, addr);
+                               avr->io_shared_io[no].used = 1;
+                               avr->io_shared_io[no].io[0].param = avr->io[a].w.param;
+                               avr->io_shared_io[no].io[0].c = avr->io[a].w.c;
+                               avr->io[a].w.param = (void*)(intptr_t)no;
+                               avr->io[a].w.c = _avr_io_mux_write;
+                       }
+                       int no = (intptr_t)avr->io[a].w.param;
+                       int d = avr->io_shared_io[no].used++;
+                       if (avr->io_shared_io[no].used > ARRAY_SIZE(avr->io_shared_io[0].io)) {
+                               AVR_LOG(avr, LOG_ERROR,
+                                               "IO: %s(): Too many callbacks on %04x.\n",
+                                               __func__, addr);
+                               abort();
+                       }
+                       avr->io_shared_io[no].io[d].param = param;
+                       avr->io_shared_io[no].io[d].c = writep;
+                       return;
+               }
+       }
+
+       avr->io[a].w.param = param;
+       avr->io[a].w.c = writep;
+}
+
+avr_irq_t *
+avr_io_getirq(
+               avr_t * avr,
+               uint32_t ctl,
+               int index)
+{
+       avr_io_t * port = avr->io_port;
+       while (port) {
+               if (port->irq && port->irq_ioctl_get == ctl && port->irq_count > index)
+                       return port->irq + index;
+               port = port->next;
+       }
+       return NULL;
+}
+
+avr_irq_t *
+avr_iomem_getirq(
+               avr_t * avr,
+               avr_io_addr_t addr,
+               const char * name,
+               int index)
+{
+       if (index > 8)
+               return NULL;
+       avr_io_addr_t a = AVR_DATA_TO_IO(addr);
+       if (avr->io[a].irq == NULL) {
+               /*
+                * Prepare an array of names for the io IRQs. Ideally we'd love to have
+                * a proper name for these, but it's not possible at this time.
+                */
+               char names[9 * 20];
+               char * d = names;
+               const char * namep[9];
+               for (int ni = 0; ni < 9; ni++) {
+                       if (ni < 8)
+                               sprintf(d, "=avr.io.%04x.%d", addr, ni);
+                       else
+                               sprintf(d, "8=avr.io.%04x.all", addr);
+                       namep[ni] = d;
+                       d += strlen(d) + 1;
+               }
+               avr->io[a].irq = avr_alloc_irq(&avr->irq_pool, 0, 9, namep);
+               // mark the pin ones as filtered, so they only are raised when changing
+               for (int i = 0; i < 8; i++)
+                       avr->io[a].irq[i].flags |= IRQ_FLAG_FILTERED;
+       }
+       // if given a name, replace the default one...
+       if (name) {
+               int l = strlen(name);
+               char n[l + 10];
+               sprintf(n, "avr.io.%s", name);
+               free((void*)avr->io[a].irq[index].name);
+               avr->io[a].irq[index].name = strdup(n);
+       }
+       return avr->io[a].irq + index;
+}
+
+avr_irq_t *
+avr_io_setirqs(
+               avr_io_t * io,
+               uint32_t ctl,
+               int count,
+               avr_irq_t * irqs )
+{
+       // allocate this module's IRQ
+       io->irq_count = count;
+
+       if (!irqs) {
+               const char ** irq_names = NULL;
+
+               if (io->irq_names) {
+                       irq_names = malloc(count * sizeof(char*));
+                       memset(irq_names, 0, count * sizeof(char*));
+                       char buf[64];
+                       for (int i = 0; i < count; i++) {
+                               /*
+                                * this bit takes the io module 'kind' ("port")
+                                * the IRQ name ("=0") and the last character of the ioctl ('p','o','r','A')
+                                * to create a full name "=porta.0"
+                                */
+                               char * dst = buf;
+                               // copy the 'flags' of the name out
+                               const char * kind = io->irq_names[i];
+                               while (isdigit(*kind))
+                                       *dst++ = *kind++;
+                               while (!isalpha(*kind))
+                                       *dst++ = *kind++;
+                               // add avr name
+//                             strcpy(dst, io->avr->mmcu);
+                               strcpy(dst, "avr");
+                               dst += strlen(dst);
+                               *dst ++ = '.';
+                               // add module 'kind'
+                               strcpy(dst, io->kind);
+                               dst += strlen(dst);
+                               // add port name, if any
+                               if ((ctl & 0xff) > ' ')
+                                       *dst ++ = tolower(ctl & 0xff);
+                               *dst ++ = '.';
+                               // add the rest of the irq name
+                               strcpy(dst, kind);
+                               dst += strlen(dst);
+                               *dst = 0;
+
+//                             printf("%s\n", buf);
+                               irq_names[i] = strdup(buf);
+                       }
+               }
+               irqs = avr_alloc_irq(&io->avr->irq_pool, 0,
+                                               count, irq_names);
+               if (irq_names) {
+                       for (int i = 0; i < count; i++)
+                               free((char*)irq_names[i]);
+                       free((char*)irq_names);
+               }
+       }
+
+       io->irq = irqs;
+       io->irq_ioctl_get = ctl;
+       return io->irq;
+}
+
+static void
+avr_deallocate_io(
+               avr_io_t * io)
+{
+       if (io->dealloc)
+               io->dealloc(io);
+       avr_free_irq(io->irq, io->irq_count);
+       io->irq_count = 0;
+       io->irq_ioctl_get = 0;
+       io->avr = NULL;
+       io->next = NULL;
+}
+
+void
+avr_deallocate_ios(
+               avr_t * avr)
+{
+       avr_io_t * port = avr->io_port;
+       while (port) {
+               avr_io_t * next = port->next;
+               avr_deallocate_io(port);
+               port = next;
+       }
+       avr->io_port = NULL;
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.h
new file mode 100644 (file)
index 0000000..cfd2fdb
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+       sim_io.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_IO_H__
+#define __SIM_IO_H__
+
+#include "sim_avr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * used by the ioports to implement their own features
+ * see avr_eeprom.* for an example, and avr_ioctl().
+ */
+#define AVR_IOCTL_DEF(_a,_b,_c,_d) \
+       (((_a) << 24)|((_b) << 16)|((_c) << 8)|((_d)))
+
+/*
+ * IO module base struct
+ * Modules uses that as their first member in their own struct
+ */
+typedef struct avr_io_t {
+       struct avr_io_t *       next;
+       avr_t *                         avr;            // avr we are attached to
+       const char *            kind;           // pretty name, for debug
+
+       const char **           irq_names;      // IRQ names
+
+       uint32_t                        irq_ioctl_get;  // used to get irqs from this module
+       int                                     irq_count;      // number of (optional) irqs
+       struct avr_irq_t *      irq;            // optional external IRQs
+       // called at reset time
+       void (*reset)(struct avr_io_t *io);
+       // called externally. allow access to io modules and so on
+       int (*ioctl)(struct avr_io_t *io, uint32_t ctl, void *io_param);
+
+       // optional, a function to free up allocated system resources
+       void (*dealloc)(struct avr_io_t *io);
+} avr_io_t;
+
+/*
+ * IO modules helper functions
+ */
+
+// registers an IO module, so it's run(), reset() etc are called
+// this is called by the AVR core init functions, you /could/ register an external
+// one after instantiation, for whatever purpose...
+void
+avr_register_io(
+               avr_t *avr,
+               avr_io_t * io);
+// Sets an IO module "official" IRQs and the ioctl used to get to them. if 'irqs' is NULL,
+// 'count' will be allocated
+avr_irq_t *
+avr_io_setirqs(
+               avr_io_t * io,
+               uint32_t ctl,
+               int count,
+               avr_irq_t * irqs );
+
+// register a callback for when IO register "addr" is read
+void
+avr_register_io_read(
+               avr_t *avr,
+               avr_io_addr_t addr,
+               avr_io_read_t read,
+               void * param);
+// register a callback for when the IO register is written. callback has to set the memory itself
+void
+avr_register_io_write(
+               avr_t *avr,
+               avr_io_addr_t addr,
+               avr_io_write_t write,
+               void * param);
+// call every IO modules until one responds to this
+int
+avr_ioctl(
+               avr_t *avr,
+               uint32_t ctl,
+               void * io_param);
+// get the specific irq for a module, check AVR_IOCTL_IOPORT_GETIRQ for example
+struct avr_irq_t *
+avr_io_getirq(
+               avr_t * avr,
+               uint32_t ctl,
+               int index);
+
+// get the IRQ for an absolute IO address
+// this allows any code to hook an IRQ in any io address, for example
+// tracing changes of values into a register
+// Note that the values do not "magically" change, they change only
+// when the AVR code attempt to read and write at that address
+//
+// the "index" is a bit number, or ALL bits if index == 8
+#define AVR_IOMEM_IRQ_ALL 8
+avr_irq_t *
+avr_iomem_getirq(
+               avr_t * avr,
+               avr_io_addr_t addr,
+               const char * name /* Optional, if NULL, "ioXXXX" will be used */ ,
+               int index);
+
+// Terminates all IOs and remove from them from the io chain
+void
+avr_deallocate_ios(
+               avr_t *avr);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_IO_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.c
new file mode 100644 (file)
index 0000000..6d175cf
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+       sim_irq.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "sim_irq.h"
+
+// internal structure for a hook, never seen by the notify procs
+typedef struct avr_irq_hook_t {
+       struct avr_irq_hook_t * next;
+       int busy;       // prevent reentrance of callbacks
+
+       struct avr_irq_t * chain;       // raise the IRQ on this too - optional if "notify" is on
+       avr_irq_notify_t notify;        // called when IRQ is raised - optional if "chain" is on
+       void * param;                           // "notify" parameter
+} avr_irq_hook_t;
+
+static void
+_avr_irq_pool_add(
+               avr_irq_pool_t * pool,
+               avr_irq_t * irq)
+{
+       int insert = 0;
+       /* lookup a slot */
+       for (; insert < pool->count && pool->irq[insert]; insert++)
+               ;
+       if (insert == pool->count) {
+               if ((pool->count & 0xf) == 0) {
+                       pool->irq = (avr_irq_t**)realloc(pool->irq,
+                                       (pool->count + 16) * sizeof(avr_irq_t *));
+               }
+               pool->count++;
+       }
+       pool->irq[insert] = irq;
+       irq->pool = pool;
+}
+
+static void
+_avr_irq_pool_remove(
+               avr_irq_pool_t * pool,
+               avr_irq_t * irq)
+{
+       for (int i = 0; i < pool->count; i++)
+               if (pool->irq[i] == irq) {
+                       pool->irq[i] = 0;
+                       return;
+               }
+}
+
+void
+avr_init_irq(
+               avr_irq_pool_t * pool,
+               avr_irq_t * irq,
+               uint32_t base,
+               uint32_t count,
+               const char ** names /* optional */)
+{
+       memset(irq, 0, sizeof(avr_irq_t) * count);
+
+       for (int i = 0; i < count; i++) {
+               irq[i].irq = base + i;
+               irq[i].flags = IRQ_FLAG_INIT;
+               if (pool)
+                       _avr_irq_pool_add(pool, &irq[i]);
+               if (names && names[i])
+                       irq[i].name = strdup(names[i]);
+               else {
+                       printf("WARNING %s() with NULL name for irq %d.\n", __func__, irq[i].irq);
+               }
+       }
+}
+
+avr_irq_t *
+avr_alloc_irq(
+               avr_irq_pool_t * pool,
+               uint32_t base,
+               uint32_t count,
+               const char ** names /* optional */)
+{
+       avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count);
+       avr_init_irq(pool, irq, base, count, names);
+       for (int i = 0; i < count; i++)
+               irq[i].flags |= IRQ_FLAG_ALLOC;
+       return irq;
+}
+
+static avr_irq_hook_t *
+_avr_alloc_irq_hook(
+               avr_irq_t * irq)
+{
+       avr_irq_hook_t *hook = malloc(sizeof(avr_irq_hook_t));
+       memset(hook, 0, sizeof(avr_irq_hook_t));
+       hook->next = irq->hook;
+       irq->hook = hook;
+       return hook;
+}
+
+void
+avr_free_irq(
+               avr_irq_t * irq,
+               uint32_t count)
+{
+       if (!irq || !count)
+               return;
+       for (int i = 0; i < count; i++) {
+               avr_irq_t * iq = irq + i;
+               if (iq->pool)
+                       _avr_irq_pool_remove(iq->pool, iq);
+               if (iq->name)
+                       free((char*)iq->name);
+               iq->name = NULL;
+               // purge hooks
+               avr_irq_hook_t *hook = iq->hook;
+               while (hook) {
+                       avr_irq_hook_t * next = hook->next;
+                       free(hook);
+                       hook = next;
+               }
+               iq->hook = NULL;
+       }
+       // if that irq list was allocated by us, free it
+       if (irq->flags & IRQ_FLAG_ALLOC)
+               free(irq);
+}
+
+void
+avr_irq_register_notify(
+               avr_irq_t * irq,
+               avr_irq_notify_t notify,
+               void * param)
+{
+       if (!irq || !notify)
+               return;
+
+       avr_irq_hook_t *hook = irq->hook;
+       while (hook) {
+               if (hook->notify == notify && hook->param == param)
+                       return; // already there
+               hook = hook->next;
+       }
+       hook = _avr_alloc_irq_hook(irq);
+       hook->notify = notify;
+       hook->param = param;
+}
+
+void
+avr_irq_unregister_notify(
+               avr_irq_t * irq,
+               avr_irq_notify_t notify,
+               void * param)
+{
+       avr_irq_hook_t *hook, *prev;
+       if (!irq || !notify)
+               return;
+
+       hook = irq->hook;
+       prev = NULL;
+       while (hook) {
+               if (hook->notify == notify && hook->param == param) {
+                       if ( prev )
+                               prev->next = hook->next;
+                       else
+                               irq->hook = hook->next;
+                       free(hook);
+                       return;
+               }
+               prev = hook;
+               hook = hook->next;
+       }
+}
+
+void
+avr_raise_irq_float(
+               avr_irq_t * irq,
+               uint32_t value,
+               int floating)
+{
+       if (!irq)
+               return ;
+       uint32_t output = (irq->flags & IRQ_FLAG_NOT) ? !value : value;
+       // if value is the same but it's the first time, raise it anyway
+       if (irq->value == output &&
+                       (irq->flags & IRQ_FLAG_FILTERED) && !(irq->flags & IRQ_FLAG_INIT))
+               return;
+       irq->flags &= ~(IRQ_FLAG_INIT | IRQ_FLAG_FLOATING);
+       if (floating)
+               irq->flags |= IRQ_FLAG_FLOATING;
+       avr_irq_hook_t *hook = irq->hook;
+       while (hook) {
+               avr_irq_hook_t * next = hook->next;
+                       // prevents reentrance / endless calling loops
+               if (hook->busy == 0) {
+                       hook->busy++;
+                       if (hook->notify)
+                               hook->notify(irq, output,  hook->param);
+                       if (hook->chain)
+                               avr_raise_irq_float(hook->chain, output, floating);
+                       hook->busy--;
+               }
+               hook = next;
+       }
+       // the value is set after the callbacks are called, so the callbacks
+       // can themselves compare for old/new values between their parameter
+       // they are passed (new value) and the previous irq->value
+       irq->value = output;
+}
+
+void
+avr_raise_irq(
+               avr_irq_t * irq,
+               uint32_t value)
+{
+       avr_raise_irq_float(irq, value, !!(irq->flags & IRQ_FLAG_FLOATING));
+}
+
+void
+avr_connect_irq(
+               avr_irq_t * src,
+               avr_irq_t * dst)
+{
+       if (!src || !dst || src == dst) {
+               fprintf(stderr, "error: %s invalid irq %p/%p", __FUNCTION__, src, dst);
+               return;
+       }
+       avr_irq_hook_t *hook = src->hook;
+       while (hook) {
+               if (hook->chain == dst)
+                       return; // already there
+               hook = hook->next;
+       }
+       hook = _avr_alloc_irq_hook(src);
+       hook->chain = dst;
+}
+
+void
+avr_unconnect_irq(
+               avr_irq_t * src,
+               avr_irq_t * dst)
+{
+       avr_irq_hook_t *hook, *prev;
+
+       if (!src || !dst || src == dst) {
+               fprintf(stderr, "error: %s invalid irq %p/%p", __FUNCTION__, src, dst);
+               return;
+       }
+       hook = src->hook;
+       prev = NULL;
+       while (hook) {
+               if (hook->chain == dst) {
+                       if ( prev )
+                               prev->next = hook->next;
+                       else
+                               src->hook = hook->next;
+                       free(hook);
+                       return;
+               }
+               prev = hook;
+               hook = hook->next;
+       }
+}
+
+uint8_t
+avr_irq_get_flags(
+               avr_irq_t * irq )
+{
+       return irq->flags;
+}
+
+void
+avr_irq_set_flags(
+               avr_irq_t * irq,
+               uint8_t flags )
+{
+       irq->flags = flags;
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.h
new file mode 100644 (file)
index 0000000..b96fd47
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+       sim_irq.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_IRQ_H__
+#define __SIM_IRQ_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Internal IRQ system
+ *
+ * This subsystem allows any piece of code to "register" a hook to be called when an IRQ is
+ * raised. The IRQ definition is up to the module defining it, for example a IOPORT pin change
+ * might be an IRQ in which case any piece of code can be notified when a pin has changed state
+ *
+ * The notify hooks are chained, and duplicates are filtered out so you can't register a
+ * notify hook twice on one particular IRQ
+ *
+ * IRQ calling order is not defined, so don't rely on it.
+ *
+ * IRQ hook needs to be registered in reset() handlers, ie after all modules init() bits
+ * have been called, to prevent race condition of the initialization order.
+ */
+struct avr_irq_t;
+
+typedef void (*avr_irq_notify_t)(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param);
+
+
+enum {
+       IRQ_FLAG_NOT            = (1 << 0),     //!< change polarity of the IRQ
+       IRQ_FLAG_FILTERED       = (1 << 1),     //!< do not "notify" if "value" is the same as previous raise
+       IRQ_FLAG_ALLOC          = (1 << 2), //!< this irq structure was malloced via avr_alloc_irq
+       IRQ_FLAG_INIT           = (1 << 3), //!< this irq hasn't been used yet
+       IRQ_FLAG_FLOATING       = (1 << 4), //!< this 'pin'/signal is floating
+       IRQ_FLAG_USER           = (1 << 5), //!< Can be used by irq users
+};
+
+/*
+ * IRQ Pool structure
+ */
+typedef struct avr_irq_pool_t {
+       int count;                                              //!< number of irqs living in the pool
+       struct avr_irq_t ** irq;                //!< irqs belonging in this pool
+} avr_irq_pool_t;
+
+/*!
+ * Public IRQ structure
+ */
+typedef struct avr_irq_t {
+       struct avr_irq_pool_t * pool;
+       const char *            name;
+       uint32_t                        irq;            //!< any value the user needs
+       uint32_t                        value;          //!< current value
+       uint8_t                         flags;          //!< IRQ_* flags
+       struct avr_irq_hook_t * hook;   //!< list of hooks to be notified
+} avr_irq_t;
+
+//! allocates 'count' IRQs, initializes their "irq" starting from 'base' and increment
+avr_irq_t *
+avr_alloc_irq(
+               avr_irq_pool_t * pool,
+               uint32_t base,
+               uint32_t count,
+               const char ** names /* optional */);
+void
+avr_free_irq(
+               avr_irq_t * irq,
+               uint32_t count);
+
+//! init 'count' IRQs, initializes their "irq" starting from 'base' and increment
+void
+avr_init_irq(
+               avr_irq_pool_t * pool,
+               avr_irq_t * irq,
+               uint32_t base,
+               uint32_t count,
+               const char ** names /* optional */);
+//! Returns the current IRQ flags
+uint8_t
+avr_irq_get_flags(
+               avr_irq_t * irq );
+//! Sets this irq's flags
+void
+avr_irq_set_flags(
+               avr_irq_t * irq,
+               uint8_t flags );
+//! 'raise' an IRQ. Ie call their 'hooks', and raise any chained IRQs, and set the new 'value'
+void
+avr_raise_irq(
+               avr_irq_t * irq,
+               uint32_t value);
+//! Same as avr_raise_irq(), but also allow setting the float status
+void
+avr_raise_irq_float(
+               avr_irq_t * irq,
+               uint32_t value,
+               int floating);
+//! this connects a "source" IRQ to a "destination" IRQ
+void
+avr_connect_irq(
+               avr_irq_t * src,
+               avr_irq_t * dst);
+void
+avr_unconnect_irq(
+               avr_irq_t * src,
+               avr_irq_t * dst);
+
+//! register a notification 'hook' for 'irq' -- 'param' is anything that your want passed back as argument
+void
+avr_irq_register_notify(
+               avr_irq_t * irq,
+               avr_irq_notify_t notify,
+               void * param);
+
+void
+avr_irq_unregister_notify(
+               avr_irq_t * irq,
+               avr_irq_notify_t notify,
+               void * param);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_IRQ_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_network.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_network.h
new file mode 100644 (file)
index 0000000..da4600a
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+       sim_network.h
+
+       Copyright 2012 Stephan Veigl <veigl@gmx.net>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_NETWORK_H__
+#define __SIM_NETWORK_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __MINGW32__
+
+// Windows with MinGW
+
+#include <winsock2.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+
+#define send(sockfd, buf, len, flags) \
+       (ssize_t)send( (sockfd), (const char *)(buf), (len), (flags))
+#define setsockopt(sockfd, level, optname, optval, optlen) \
+       setsockopt( (sockfd), (level), (optname), (void *)(optval), (optlen))
+#define recv(sockfd, buf, len, flags) \
+       (ssize_t)recv( (sockfd), (char *)(buf), (len), (flags))
+#define sleep(x) Sleep((x)*1000)
+
+static inline int network_init(void)
+{
+       // Windows requires WinSock to be init before use
+       WSADATA wsaData;
+       if ( WSAStartup( MAKEWORD( 2, 2 ), &wsaData ) )
+               return -1;
+
+       return 0;
+}
+
+static inline void network_release(void)
+{
+       // close WinSock
+       WSACleanup();
+}
+
+#else
+
+// native Linux
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <poll.h>
+
+static inline int network_init(void)
+{
+       // nothing to do
+       return 0;
+}
+
+static inline void network_release(void)
+{
+       // nothing to do
+}
+
+#endif
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*__SIM_NETWORK_H__*/
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_regbit.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_regbit.h
new file mode 100644 (file)
index 0000000..862f490
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+       sim_regbit.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_REGBIT_H__
+#define __SIM_REGBIT_H__
+
+#include "sim_avr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ARRAY_SIZE(_aa) (sizeof(_aa) / sizeof((_aa)[0]))
+
+
+/*
+ * These accessors are inlined and are used to perform the operations on
+ * avr_regbit_t definitions. This is the "official" way to access bits into registers
+ * The small footprint costs brings much better versatility for functions/bits that are
+ * not always defined in the same place on real AVR cores
+ */
+/*
+ * set/get/clear io register bits in one operation
+ */
+static inline uint8_t
+avr_regbit_set(
+       avr_t * avr,
+       avr_regbit_t rb)
+{
+       uint16_t a = rb.reg;
+       uint8_t m;
+
+       if (!a)
+               return 0;
+       m = (uint8_t)(rb.mask << rb.bit);
+       avr_core_watch_write(avr, a, (uint8_t)(avr->data[a] | m));
+       return (uint8_t)((avr->data[a] >> rb.bit) & rb.mask);
+}
+
+static inline uint8_t
+avr_regbit_setto(
+       avr_t * avr,
+       avr_regbit_t rb,
+       uint8_t v)
+{
+       uint16_t a = rb.reg;
+       uint8_t m;
+
+       if (!a)
+               return 0;
+       m = (uint8_t)(rb.mask << rb.bit);
+       avr_core_watch_write(avr, a,
+                             (uint8_t)((avr->data[a] & ~(m)) |
+                                       ((v << rb.bit) & m)));
+       return (uint8_t)((avr->data[a] >> rb.bit) & rb.mask);
+}
+
+/*
+ * Set the 'raw' bits, if 'v' is the unshifted value of the bits
+ */
+static inline uint8_t
+avr_regbit_setto_raw(
+       avr_t * avr,
+       avr_regbit_t rb,
+       uint8_t v)
+{
+       uint16_t a = rb.reg;
+       uint8_t m;
+
+       if (!a)
+               return 0;
+       m = (uint8_t)(rb.mask << rb.bit);
+       avr_core_watch_write(avr, a,
+                             (uint8_t)((avr->data[a] & ~(m)) | ((v) & m)));
+       return (uint8_t)((avr->data[a]) & (rb.mask << rb.bit));
+}
+
+static inline uint8_t
+avr_regbit_get(
+       avr_t * avr,
+       avr_regbit_t rb)
+{
+       uint16_t a = rb.reg;
+       if (!a)
+               return 0;
+       //uint8_t m = rb.mask << rb.bit;
+       return (uint8_t)((avr->data[a] >> rb.bit) & rb.mask);
+}
+
+/*
+ * Using regbit from value eliminates some of the
+ * set to test then clear register operations.
+ * makes cheking register bits before setting easier.
+ */
+static inline uint8_t
+avr_regbit_from_value(
+       avr_t * avr __attribute__((unused)),
+       avr_regbit_t rb,
+       uint8_t value)
+{
+       uint16_t a = rb.reg;
+       if (!a)
+               return 0;
+       return (uint8_t)((value >> rb.bit) & rb.mask);
+}
+
+/*
+ * Return the bit(s) 'in position' instead of zero based
+ */
+static inline uint8_t
+avr_regbit_get_raw(
+       avr_t * avr,
+       avr_regbit_t rb)
+{
+       uint16_t a = rb.reg;
+       if (!a)
+               return 0;
+       //uint8_t m = rb.mask << rb.bit;
+       return (uint8_t)((avr->data[a]) & (rb.mask << rb.bit));
+}
+
+static inline uint8_t
+avr_regbit_clear(
+       avr_t * avr,
+       avr_regbit_t rb)
+{
+       uint16_t a = rb.reg;
+       if (!a)
+               return 0;
+       uint8_t m = (uint8_t)(rb.mask << rb.bit);
+       avr_core_watch_write(avr, a, (uint8_t)(avr->data[a] & ~m));
+       return avr->data[a];
+}
+
+
+/*
+ * This reads the bits for an array of avr_regbit_t, make up a "byte" with them.
+ * This allows reading bits like CS0, CS1, CS2 etc even if they are not in the same
+ * physical IO register.
+ */
+static inline uint8_t
+avr_regbit_get_array(
+       avr_t * avr,
+       avr_regbit_t *rb,
+       int count)
+{
+       uint8_t res = 0;
+       int i;
+
+       for (i = 0; i < count; i++, rb++) if (rb->reg) {
+               uint16_t a = rb->reg;
+               res |= (uint8_t)(((avr->data[a] >> rb->bit) & rb->mask) << i);
+       }
+       return res;
+}
+
+/*
+ * Does the reverse of avr_regbit_get_array
+ */
+static inline void
+avr_regbit_set_array_from_value(
+       avr_t * avr,
+       avr_regbit_t * rb,
+       uint8_t count,
+       uint8_t value)
+{
+       int i;
+       for (i = 0; i < count; i++, rb++) if (rb->reg) {
+               uint8_t rbv = (value >> (count - i)) & 1;
+               avr_regbit_setto(avr, *rb, rbv);
+       }
+}
+
+#define AVR_IO_REGBIT(_io, _bit) { . reg = (_io), .bit = (_bit), .mask = 1 }
+#define AVR_IO_REGBITS(_io, _bit, _mask) { . reg = (_io), .bit = (_bit), .mask = (_mask) }
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_REGBIT_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_time.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_time.h
new file mode 100644 (file)
index 0000000..4b81f43
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+       sim_time.h
+
+       Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __SIM_TIME_H___
+#define __SIM_TIME_H___
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sim_avr.h"
+
+// converts a number of usec to a number of machine cycles, at current speed
+static inline avr_cycle_count_t
+avr_usec_to_cycles(struct avr_t * avr, uint32_t usec)
+{
+       return avr->frequency * (avr_cycle_count_t)usec / 1000000;
+}
+
+// converts back a number of cycles to usecs (for usleep)
+static inline uint32_t
+avr_cycles_to_usec(struct avr_t * avr, avr_cycle_count_t cycles)
+{
+       return 1000000L * cycles / avr->frequency;
+}
+
+// converts back a number of cycles to nsecs
+static inline uint64_t
+avr_cycles_to_nsec(struct avr_t * avr, avr_cycle_count_t cycles)
+{
+       return (uint64_t)1E6 * (uint64_t)cycles / (avr->frequency/1000);
+}
+
+// converts a number of hz (to megahertz etc) to a number of cycle
+static inline avr_cycle_count_t
+avr_hz_to_cycles(avr_t * avr, uint32_t hz)
+{
+       return avr->frequency / hz;
+}
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_TIME_H___ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.c
new file mode 100644 (file)
index 0000000..eeb4986
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+       sim_utils.c
+
+       Implements a Value Change Dump file outout to generate
+       traces & curves and display them in gtkwave.
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "sim_utils.h"
+
+static argv_p
+argv_realloc(
+       argv_p  argv,
+       uint32_t size )
+{
+       argv = realloc(argv,
+                               sizeof(argv_t) + (size * sizeof(argv->argv[0])));
+       argv->size = size;
+       return argv;
+}
+
+argv_p
+argv_parse(
+       argv_p  argv,
+       char * line )
+{
+       if (!argv)
+               argv = argv_realloc(argv, 8);
+       argv->argc = 0;
+
+       /* strip end of lines and trailing spaces */
+       char *d = line + strlen(line);
+       while ((d - line) > 0 && *(--d) <= ' ')
+               *d = 0;
+       /* stop spaces + tabs */
+       char *s = line;
+       while (*s && *s <= ' ')
+               s++;
+       argv->line = s;
+       char * a = NULL;
+       do {
+               if (argv->argc == argv->size)
+                       argv = argv_realloc(argv, argv->size + 8);
+               if ((a = strsep(&s, " \t")) != NULL)
+                       argv->argv[argv->argc++] = a;
+       } while (a);
+       argv->argv[argv->argc] = NULL;
+       return argv;
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.h
new file mode 100644 (file)
index 0000000..44cb2c5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+       sim_utils.h
+
+       Implements a Value Change Dump file outout to generate
+       traces & curves and display them in gtkwave.
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_UTILS_H__
+#define __SIM_UTILS_H__
+
+#include <stdint.h>
+
+typedef struct argv_t {
+       uint32_t size, argc;
+       char * line;
+       char * argv[];
+} argv_t, *argv_p;
+
+/*
+ * Allocate a argv_t structure, split 'line' into words (destructively)
+ * and fills up argc, and argv fields with pointers to the individual
+ * words. The line is stripped of any \r\n as well
+ * You can pass an already allocated argv_t for it to be reused (and
+ * grown to fit).
+ *
+ * You are still responsible, as the caller, to (free) the resulting
+ * pointer, and the 'line' text, if appropriate, no duplication is made
+ */
+argv_p
+argv_parse(
+       argv_p  argv,
+       char * line );
+
+#endif /* __SIM_UTILS_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.c
new file mode 100644 (file)
index 0000000..16e4d25
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+       sim_vcd_file.c
+
+       Implements a Value Change Dump file outout to generate
+       traces & curves and display them in gtkwave.
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <time.h>
+#include "sim_vcd_file.h"
+#include "sim_avr.h"
+#include "sim_time.h"
+#include "sim_utils.h"
+#include "sim_core_config.h"
+
+DEFINE_FIFO(avr_vcd_log_t, avr_vcd_fifo);
+
+#define strdupa(__s) strcpy(alloca(strlen(__s)+1), __s)
+
+static void
+_avr_vcd_notify(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param);
+
+int
+avr_vcd_init(
+               struct avr_t * avr,
+               const char * filename,
+               avr_vcd_t * vcd,
+               uint32_t period)
+{
+       memset(vcd, 0, sizeof(avr_vcd_t));
+       vcd->avr = avr;
+       vcd->filename = strdup(filename);
+       vcd->period = avr_usec_to_cycles(vcd->avr, period);
+       return 0;
+}
+
+/*
+ * Parse a VCD 'timing' line. The lines are assumed to be:
+ * #<absolute timestamp>[\n][<value x/z/0/1><signal alias character>|
+ *             b[x/z/0/1]?<space><signal alias character|
+ *             r<real value><space><signal alias character]+
+ * For example:
+ * #1234 1' 0$
+ * Or:
+ * #1234
+ * b1101x1 '
+ * 0$
+ *
+ * This function tries to handle this transparently, and pushes the
+ * signal/values into the FIFO for processing by the timer when
+ * convenient.
+ * NOTE: Add 'floating' support here. Also, FIX THE TIMING.
+ */
+static avr_cycle_count_t
+avr_vcd_input_parse_line(
+               avr_vcd_t * vcd,
+               argv_p v )
+{
+       uint64_t res = 0;
+       int vi = 0;
+
+       if (v->argc == 0)
+               return res;
+
+       if (v->argv[0][0] == '#') {
+               res = atoll(v->argv[0] + 1) * vcd->vcd_to_ns;
+               vcd->start = vcd->period;
+               vcd->period = res;
+               vi++;
+       }
+       for (int i = vi; i < v->argc; i++) {
+               char * a = v->argv[i];
+               uint32_t val = 0;
+               int floating = 0;
+               char name = 0;
+               int sigindex;
+
+               if (*a == 'b' || *a == 'B') {   // Binary string
+                       a++;
+                       while (*a) {
+                               if (*a == 'x' || *a == 'z') {
+                                       val <<= 1;
+                                       floating |= (floating << 1) | 1;
+                               } else if (*a == '0' || *a == '1') {
+                                       val = (val << 1) | (*a - '0');
+                                       floating <<= 1;
+                               } else {
+                                       name = *a;
+                                       break;
+                               }
+                               a++;
+                       }
+               } else if (*a == '0' || *a == '1' || *a == 'x' || *a == 'z') {
+                       if (*a == 'x' || *a == 'z')
+                               floating = 1;
+                       else
+                               val = *a++ - '0';
+                       if (*a && *a > ' ')
+                               name = *a;
+               } else if (*a == 'r' || *a == 'R') {
+                       val = (uint32_t)strtod(++a, NULL);
+               }
+
+               if (!name && (i < v->argc - 1)) {
+                       const char *n = v->argv[i+1];
+                       if (strlen(n) == 1) {
+                               // we've got a name, it was not attached
+                               name = *n;
+                               i++;    // skip that one
+                       }
+               }
+               sigindex = -1;
+               if (name) {
+                       for (int si = 0;
+                                               si < vcd->signal_count &&
+                                               sigindex == -1; si++) {
+                               if (vcd->signal[si].alias == name)
+                                       sigindex = si;
+                       }
+               }
+               if (sigindex == -1) {
+                       printf("Signal name '%c' value %x not found\n",
+                                       name? name : '?', val);
+                       continue;
+               }
+               avr_vcd_log_t e = {
+                               .when = vcd->period,
+                               .sigindex = sigindex,
+                               .floating = !!floating,
+                               .value = val,
+               };
+       //      printf("%10u %d\n", e.when, e.value);
+               avr_vcd_fifo_write(&vcd->log, e);
+       }
+       return res;
+}
+
+/*
+ * Read some signals from the file and fill the FIFO with it, we read
+ * a completely arbitrary amount of stuff to fill the FIFO reasonably well
+ */
+static int
+avr_vcd_input_read(
+               avr_vcd_t * vcd )
+{
+       char line[1024];
+
+       while (fgets(line, sizeof(line), vcd->input)) {
+       //      printf("%s", line);
+               if (!line[0])   // technically can't happen, but make sure next line works
+                       continue;
+               vcd->input_line = argv_parse(vcd->input_line, line);
+               avr_vcd_input_parse_line(vcd, vcd->input_line);
+               /* stop once the fifo is full enough */
+               if (avr_vcd_fifo_get_read_size(&vcd->log) >= 128)
+                       break;
+       }
+       return avr_vcd_fifo_isempty(&vcd->log);
+}
+
+/*
+ * This is called when we need to change the state of one or more IRQ,
+ * so look in the FIFO to know 'our' stamp time, read as much as we can
+ * that is still on that same timestamp.
+ * When when the FIFO content has too far in the future, re-schedule the
+ * timer for that time and shoot off.
+ * Also try to top up the FIFO with new read stuff when it's drained
+ */
+static avr_cycle_count_t
+_avr_vcd_input_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_cycle_count_t next;
+       avr_vcd_t * vcd = param;
+
+again:
+       // get some more if needed
+       if (avr_vcd_fifo_get_read_size(&vcd->log) < (vcd->signal_count * 16))
+               avr_vcd_input_read(vcd);
+
+       if (avr_vcd_fifo_isempty(&vcd->log)) {
+               printf("%s DONE but why are we here?\n", __func__);
+               return 0;
+       }
+
+       avr_vcd_log_t log = avr_vcd_fifo_read_at(&vcd->log, 0);
+       uint64_t stamp = log.when;
+       while (!avr_vcd_fifo_isempty(&vcd->log)) {
+               log = avr_vcd_fifo_read_at(&vcd->log, 0);
+               if (log.when != stamp)  // leave those in the FIFO
+                       break;
+               // we already have it
+               avr_vcd_fifo_read_offset(&vcd->log, 1);
+               avr_vcd_signal_p signal = &vcd->signal[log.sigindex];
+               avr_raise_irq_float(&signal->irq, log.value, log.floating);
+       }
+
+       if (avr_vcd_fifo_isempty(&vcd->log)) {
+               AVR_LOG(vcd->avr, LOG_TRACE,
+                               "%s Finished reading, ending simavr\n",
+                               vcd->filename);
+               avr->state = cpu_Done;
+               return 0;
+       }
+       log = avr_vcd_fifo_read_at(&vcd->log, 0);
+
+       next = (log.when * avr->frequency) / (1000*1000*1000);
+       if (next <= when)
+               goto again;
+       return next;
+}
+
+int
+avr_vcd_init_input(
+               struct avr_t * avr,
+               const char * filename,  // filename to read
+               avr_vcd_t * vcd )       // vcd struct to initialize
+{
+       memset(vcd, 0, sizeof(avr_vcd_t));
+       vcd->avr = avr;
+       vcd->filename = strdup(filename);
+
+       vcd->input = fopen(vcd->filename, "r");
+       if (!vcd->input) {
+               perror(filename);
+               return -1;
+       }
+       char line[1024];
+       argv_p v = NULL;
+
+       while (fgets(line, sizeof(line), vcd->input)) {
+               if (!line[0])   // technically can't happen, but make sure next line works
+                       continue;
+               v = argv_parse(v, line);
+
+               // we are done reading headers, got our first timestamp
+               if (v->line[0] == '#') {
+                       uint64_t when;
+
+                       vcd->start = 0;
+                       avr_vcd_input_parse_line(vcd, v);
+                       when = (vcd->period * vcd->avr->frequency) /
+                               (1000*1000*1000);
+                       avr_cycle_timer_register(vcd->avr, when,
+                                                _avr_vcd_input_timer, vcd);
+                       break;
+               }
+               // ignore multiline stuff
+               if (v->line[0] != '$')
+                       continue;
+
+               const char * end = !strcmp(v->argv[v->argc - 1], "$end") ?
+                                                               v->argv[v->argc - 1] : NULL;
+               const char *keyword = v->argv[0];
+
+               if (keyword == end)
+                       keyword = NULL;
+               if (!keyword)
+                       continue;
+
+               if (!strcmp(keyword, "$timescale")) {
+                       // sim_vcd header allows only integer factors of ns: 1ns, 2us, 3ms, 10s, ...
+                       uint64_t cnt = 0;
+                       char *si = v->argv[1];
+
+                       vcd->vcd_to_ns = 1;
+                       while (si && *si && isdigit(*si))
+                               cnt = (cnt * 10) + (*si++ - '0');
+                       while (si && *si == ' ')
+                               si++;
+                       if (si && !*si)
+                               si = v->argv[2];
+                       if (!strcmp(si, "ns")) {
+                               // no calculation here
+                               vcd->vcd_to_ns = cnt;
+                       } else if (!strcmp(si, "us")) {
+                               cnt*=1000;
+                               vcd->vcd_to_ns = cnt;
+                       } else if (!strcmp(si, "ms")) {
+                               cnt*=1000*1000;
+                               vcd->vcd_to_ns = cnt;
+                       } else if (!strcmp(si, "s")) {
+                               cnt*=1000*1000*1000;
+                               vcd->vcd_to_ns = cnt;
+                       }
+                       // printf("cnt %dus; unit %s\n", (int)cnt, si);
+               } else if (!strcmp(keyword, "$var")) {
+                       const char *name = v->argv[4];
+
+                       vcd->signal[vcd->signal_count].alias = v->argv[3][0];
+                       vcd->signal[vcd->signal_count].size = atoi(v->argv[2]);
+                       strncpy(vcd->signal[vcd->signal_count].name, name,
+                                               sizeof(vcd->signal[0].name));
+
+                       vcd->signal_count++;
+               }
+       }
+       // reuse this one
+       vcd->input_line = v;
+
+       for (int i = 0; i < vcd->signal_count; i++) {
+               AVR_LOG(vcd->avr, LOG_TRACE, "%s %2d '%c' %s : size %d\n",
+                               __func__, i,
+                               vcd->signal[i].alias, vcd->signal[i].name,
+                               vcd->signal[i].size);
+               /* format is <four-character ioctl>[_<IRQ index>] */
+               if (strlen(vcd->signal[i].name) >= 4) {
+                       char *dup = strdupa(vcd->signal[i].name);
+                       char *ioctl = strsep(&dup, "_");
+                       int index = 0;
+                       if (dup)
+                               index = atoi(dup);
+                       if (strlen(ioctl) == 4) {
+                               uint32_t ioc = AVR_IOCTL_DEF(
+                                               ioctl[0], ioctl[1], ioctl[2], ioctl[3]);
+                               avr_irq_t * irq = avr_io_getirq(vcd->avr, ioc, index);
+                               if (irq) {
+                                       vcd->signal[i].irq.flags = IRQ_FLAG_INIT;
+                                       avr_connect_irq(&vcd->signal[i].irq, irq);
+                               } else {
+                                       AVR_LOG(vcd->avr, LOG_WARNING,
+                                                       "%s IRQ was not found\n",
+                                                       vcd->signal[i].name);
+                                }
+                               continue;
+                       }
+                       AVR_LOG(vcd->avr, LOG_WARNING,
+                                       "%s is an invalid IRQ format\n",
+                                       vcd->signal[i].name);
+               }
+       }
+       return 0;
+}
+
+void
+avr_vcd_close(
+               avr_vcd_t * vcd)
+{
+       avr_vcd_stop(vcd);
+
+       /* dispose of any link and hooks */
+       for (int i = 0; i < vcd->signal_count; i++) {
+               avr_vcd_signal_t * s = &vcd->signal[i];
+
+               avr_free_irq(&s->irq, 1);
+       }
+
+       if (vcd->filename) {
+               free(vcd->filename);
+               vcd->filename = NULL;
+       }
+}
+
+static char *
+_avr_vcd_get_float_signal_text(
+               avr_vcd_signal_t * s,
+               char * out)
+{
+       char * dst = out;
+
+       if (s->size > 1)
+               *dst++ = 'b';
+
+       for (int i = s->size; i > 0; i--)
+               *dst++ = 'x';
+       if (s->size > 1)
+               *dst++ = ' ';
+       *dst++ = s->alias;
+       *dst = 0;
+       return out;
+}
+
+static char *
+_avr_vcd_get_signal_text(
+               avr_vcd_signal_t * s,
+               char * out,
+               uint32_t value)
+{
+       char * dst = out;
+
+       if (s->size > 1)
+               *dst++ = 'b';
+
+       for (int i = s->size; i > 0; i--)
+               *dst++ = value & (1 << (i-1)) ? '1' : '0';
+       if (s->size > 1)
+               *dst++ = ' ';
+       *dst++ = s->alias;
+       *dst = 0;
+       return out;
+}
+
+/* Write queued output to the VCD file. */
+
+static void
+avr_vcd_flush_log(
+               avr_vcd_t * vcd)
+{
+#if AVR_VCD_MAX_SIGNALS > 32
+       uint64_t seen = 0;
+#else
+       uint32_t seen = 0;
+#endif
+       uint64_t oldbase = 0;   // make sure it's different
+       char out[48];
+
+       if (avr_vcd_fifo_isempty(&vcd->log) || !vcd->output)
+               return;
+
+       while (!avr_vcd_fifo_isempty(&vcd->log)) {
+               avr_vcd_log_t l = avr_vcd_fifo_read(&vcd->log);
+               // 10ns base -- 100MHz should be enough
+               uint64_t base = avr_cycles_to_nsec(vcd->avr, l.when - vcd->start) / 10;
+
+               /*
+                * if that trace was seen in this nsec already, we fudge the
+                * base time to make sure the new value is offset by one nsec,
+                * to make sure we get at least a small pulse on the waveform.
+                *
+                * This is a bit of a fudge, but it is the only way to represent
+                * very short "pulses" that are still visible on the waveform.
+                */
+               if (base == oldbase &&
+                               (seen & (1 << l.sigindex)))
+                       base++; // this forces a new timestamp
+
+               if (base > oldbase || !seen) {
+                       seen = 0;
+                       fprintf(vcd->output, "#%" PRIu64  "\n", base);
+                       oldbase = base;
+               }
+               // mark this trace as seen for this timestamp
+               seen |= (1 << l.sigindex);
+               fprintf(vcd->output, "%s\n",
+                               l.floating ?
+                                       _avr_vcd_get_float_signal_text(
+                                                       &vcd->signal[l.sigindex],
+                                                       out) :
+                                       _avr_vcd_get_signal_text(
+                                                       &vcd->signal[l.sigindex],
+                                                       out, l.value));
+       }
+}
+
+/* Cycle timer for writing queued output. */
+
+static avr_cycle_count_t
+_avr_vcd_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_vcd_t * vcd = param;
+       avr_vcd_flush_log(vcd);
+       return when + vcd->period;
+}
+
+/* Called for an IRQ that is being recorded. */
+
+static void
+_avr_vcd_notify(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       avr_vcd_t * vcd = (avr_vcd_t *)param;
+
+       if (!vcd->output) {
+               AVR_LOG(vcd->avr, LOG_WARNING,
+                               "%s: no output\n",
+                               __FUNCTION__);
+               return;
+       }
+
+       avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
+       avr_vcd_log_t l = {
+               .sigindex = s->irq.irq,
+               .when = vcd->avr->cycle,
+               .value = value,
+               .floating = !!(avr_irq_get_flags(irq) & IRQ_FLAG_FLOATING),
+       };
+       if (avr_vcd_fifo_isfull(&vcd->log)) {
+               AVR_LOG(vcd->avr, LOG_WARNING,
+                               "%s FIFO Overload, flushing!\n",
+                               __func__);
+               /* Decrease period by a quarter, for next time */
+               vcd->period -= vcd->period >> 2;
+               avr_vcd_flush_log(vcd);
+       }
+       avr_vcd_fifo_write(&vcd->log, l);
+}
+
+/* Register an IRQ whose value is to be logged. */
+
+int
+avr_vcd_add_signal(
+               avr_vcd_t * vcd,
+               avr_irq_t * signal_irq,
+               int signal_bit_size,
+               const char * name )
+{
+       if (vcd->signal_count == AVR_VCD_MAX_SIGNALS) {
+               AVR_LOG(vcd->avr, LOG_ERROR,
+                       " %s: unable add signal '%s'\n",
+                       __FUNCTION__, name);
+               return -1;
+       }
+       int index = vcd->signal_count++;
+       avr_vcd_signal_t * s = &vcd->signal[index];
+       strncpy(s->name, name, sizeof(s->name));
+       s->size = signal_bit_size;
+       s->alias = ' ' + vcd->signal_count ;
+
+       /* manufacture a nice IRQ name */
+       int l = strlen(name);
+       char iname[10 + l + 1];
+       if (signal_bit_size > 1)
+               sprintf(iname, "%d>vcd.%s", signal_bit_size, name);
+       else
+               sprintf(iname, ">vcd.%s", name);
+
+       const char * names[1] = { iname };
+       avr_init_irq(&vcd->avr->irq_pool, &s->irq, index, 1, names);
+       avr_irq_register_notify(&s->irq, _avr_vcd_notify, vcd);
+
+       avr_connect_irq(signal_irq, &s->irq);
+       return 0;
+}
+
+/* Open the VCD output file and write header.  Does nothing for input. */
+
+int
+avr_vcd_start(
+               avr_vcd_t * vcd)
+{
+       time_t now;
+
+       vcd->start = vcd->avr->cycle;
+       avr_vcd_fifo_reset(&vcd->log);
+
+       if (vcd->input) {
+               /*
+                * nothing to do here, the first cycle timer will take care
+                * if it.
+                */
+               return 0;
+       }
+       if (vcd->output)
+               avr_vcd_stop(vcd);
+       vcd->output = fopen(vcd->filename, "w");
+       if (vcd->output == NULL) {
+               perror(vcd->filename);
+               return -1;
+       }
+
+       time(&now);
+       fprintf(vcd->output, "$date %s$end\n", ctime(&now));
+       fprintf(vcd->output,
+               "$version Simavr " CONFIG_SIMAVR_VERSION " $end\n");
+       fprintf(vcd->output, "$timescale 10ns $end\n"); // 10ns base, aka 100MHz
+       fprintf(vcd->output, "$scope module logic $end\n");
+
+       for (int i = 0; i < vcd->signal_count; i++) {
+               fprintf(vcd->output, "$var wire %d %c %s $end\n",
+                       vcd->signal[i].size, vcd->signal[i].alias, vcd->signal[i].name);
+       }
+
+       fprintf(vcd->output, "$upscope $end\n");
+       fprintf(vcd->output, "$enddefinitions $end\n");
+
+       fprintf(vcd->output, "$dumpvars\n");
+       for (int i = 0; i < vcd->signal_count; i++) {
+               avr_vcd_signal_t * s = &vcd->signal[i];
+               char out[48];
+               fprintf(vcd->output, "%s\n",
+                               _avr_vcd_get_float_signal_text(s, out));
+       }
+       fprintf(vcd->output, "$end\n");
+       avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);
+       return 0;
+}
+
+int
+avr_vcd_stop(
+               avr_vcd_t * vcd)
+{
+       avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
+       avr_cycle_timer_cancel(vcd->avr, _avr_vcd_input_timer, vcd);
+
+       avr_vcd_flush_log(vcd);
+
+       if (vcd->input_line)
+               free(vcd->input_line);
+       vcd->input_line = NULL;
+       if (vcd->input)
+               fclose(vcd->input);
+       vcd->input = NULL;
+       if (vcd->output)
+               fclose(vcd->output);
+       vcd->output = NULL;
+       return 0;
+}
+
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.h
new file mode 100644 (file)
index 0000000..2dd6219
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+       sim_vcd_file.c
+
+       Implements a Value Change Dump file outout to generate
+       traces & curves and display them in gtkwave.
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_VCD_FILE_H__
+#define __SIM_VCD_FILE_H__
+
+#include <stdio.h>
+#include "sim_irq.h"
+#include "fifo_declare.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Value Change dump module for simavr.
+ *
+ * This structure registers IRQ change hooks to various "source" IRQs
+ * and dumps their values (if changed) at certain intervals into the VCD
+ * file.
+ *
+ * It can also do the reverse, load a VCD file generated by for example
+ * sigrock signal analyzer, and 'replay' digital input with the proper
+ * timing.
+ *
+ * TODO: Add support for 'looping' a VCD input.
+ */
+
+#define AVR_VCD_MAX_SIGNALS 64
+
+typedef struct avr_vcd_signal_t {
+       /*
+        * For VCD output this is the IRQ we receive new values from.
+        * For VCD input, this is the IRQ we broadcast the values to
+        */
+       avr_irq_t               irq;
+       char                    alias;                  // vcd one character alias
+       uint8_t                 size;                   // in bits
+       char                    name[32];               // full human name
+} avr_vcd_signal_t, *avr_vcd_signal_p;
+
+typedef struct avr_vcd_log_t {
+       uint64_t                when;                   // Cycles for output,
+                                                       //     nS for input.
+       uint64_t                        sigindex : 8,   // index in signal table
+                                       floating : 1,
+                                       value : 32;
+} avr_vcd_log_t, *avr_vcd_log_p;
+
+DECLARE_FIFO(avr_vcd_log_t, avr_vcd_fifo, 256);
+
+struct argv_t;
+
+typedef struct avr_vcd_t {
+       struct avr_t *  avr;    // AVR we are attaching timers to..
+
+       char *                  filename;               // .vcd filename
+       /* can be input OR output, not both */
+       FILE *                  output;
+       FILE *                  input;
+       struct argv_t   * input_line;
+
+       int                             signal_count;
+       avr_vcd_signal_t        signal[AVR_VCD_MAX_SIGNALS];
+
+       uint64_t                start;
+       uint64_t                period;         // for output cycles
+       uint64_t                vcd_to_ns;      // for input unit mapping
+
+       avr_vcd_fifo_t  log;
+} avr_vcd_t;
+
+// initializes a new VCD trace file, and returns zero if all is well
+int
+avr_vcd_init(
+               struct avr_t * avr,
+               const char * filename,  // filename to write
+               avr_vcd_t * vcd,                // vcd struct to initialize
+               uint32_t        period );       // file flushing period is in usec
+int
+avr_vcd_init_input(
+               struct avr_t * avr,
+               const char * filename,  // filename to read
+               avr_vcd_t * vcd );              // vcd struct to initialize
+void
+avr_vcd_close(
+               avr_vcd_t * vcd );
+
+// Add a trace signal to the vcd file. Must be called before avr_vcd_start()
+int
+avr_vcd_add_signal(
+               avr_vcd_t * vcd,
+               avr_irq_t * signal_irq,
+               int signal_bit_size,
+               const char * name );
+
+// Starts recording the signal value into the file
+int
+avr_vcd_start(
+               avr_vcd_t * vcd);
+// stops recording signal values into the file
+int
+avr_vcd_stop(
+               avr_vcd_t * vcd);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIM_VCD_FILE_H__ */
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/simuc b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/simuc
new file mode 100755 (executable)
index 0000000..966bd1f
Binary files /dev/null and b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/simuc differ
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/main.cpp b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/main.cpp
new file mode 100644 (file)
index 0000000..0af4178
--- /dev/null
@@ -0,0 +1,190 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "sim/sim.h"
+#include "simavr/simavr.h"
+
+void printHelp () {
+    printf("simuc V1.0.0 (%s,%s)\n", __DATE__, __TIME__);
+    printf("usage: simuc [options] elf-file\n\n");
+    printf("  available options:\n");
+    printf("    --board ...        set board (arduino, sure, evws1)\n");
+    printf("    --sync             sync elapsed ÂµC-time with real time\n\n");
+    printf("    --mmcu ...         set target device type\n");
+    printf("    --frequency ...    set target frequency in Hz\n");
+    printf("    --pc ...           set start program counter (default is 0)\n");
+    printf("    --vcc ...          set voltage VCC in Volt\n");
+    printf("    --avcc ...         set voltage AVCC in Volt\n");
+    printf("    --aref ...         set voltage AREF in Volt\n\n");
+    printf("  example:\n");
+    printf("    simuc --mmcu atmega328p --frequency 16000000 --pc 0x7000 a.out\n\n");
+    printf("    simuc --board arduino a.out\n\n");
+}
+
+int main (int argc, char **argv) {
+    struct StartParameters params = {
+        filename: NULL,
+        gdbPort: -1,
+        frequency: -1,
+        mmcu: NULL,
+        board: BoardUnknown,
+        vcc: -1,
+        avcc: -1,
+        aref: -1
+    };
+
+    if (argc <= 1) {
+        printHelp();
+        return 1;
+    }
+
+    for (int i = 1; i < argc; i++) {
+        if (argv[i][0] == '-') {
+            if (strcmp(argv[i], "--help") == 0) {
+                printHelp();
+                return 0;
+            } else if (strcmp(argv[i], "--board") == 0 && argc >= (i + 1)) {
+                i++;
+                if (strcmp("arduino", argv[i]) == 0) {
+                    params.board = BoardNano;
+                    params.mmcu = "atmega328p";
+                    params.frequency = 16000000;
+                    params.vcc = 5000;
+                    params.avcc = 5000;
+                    params.aref = 2500;
+                } else if (strcmp("sure", argv[i]) == 0) {
+                    params.board = BoardSure;
+                    params.mmcu = "atmega16";
+                    params.frequency = 12000000;
+                    params.vcc = 5000;
+                    params.avcc = 5000;
+                    params.aref = 2500;
+               } else if (strcmp("evws1", argv[i]) == 0) {
+                    params.board = BoardEWS1;
+                    params.mmcu = "atmega324p";
+                    params.frequency = 20000000;
+                    params.vcc = 5000;
+                    params.avcc = 5000;
+                    params.aref = 2500;
+               } else {
+                    fprintf(stderr, "ERROR: invalid board %s, use --help to show usage\n\n", argv[i]);
+                    return 1;
+               }
+
+            } else if (strcmp(argv[i], "--mmcu") == 0) {
+                params.mmcu = argv[++i];
+            } else if (strcmp(argv[i], "--port") == 0) {
+                sscanf(argv[++i], "%d", &params.gdbPort);
+            } else if (strcmp(argv[i], "--frequency") == 0) {
+                sscanf(argv[++i], "%" PRIu64, &params.frequency);
+            } else if (strcmp(argv[i], "--pc") == 0) {
+                i++;
+                if (argv[i][0] == '0' && argv[i][1] == 'x') {
+                    sscanf(&argv[i][2], "%x", &params.pc);
+                } else {
+                    sscanf(argv[++i], "%d", &params.pc);
+                }
+            } else if (strcmp(argv[i], "--vcc") == 0) {
+                sscanf(argv[++i], "%d", &params.vcc);
+            } else if (strcmp(argv[i], "--avcc") == 0) {
+                sscanf(argv[++i], "%d", &params.avcc);
+            } else if (strcmp(argv[i], "--aref") == 0) {
+                sscanf(argv[++i], "%d", &params.aref);
+            } else {
+                fprintf(stderr, "ERROR: invalid option %s, use --help to show usage\n\n", argv[i]);
+                return 1;
+            }
+            continue;
+        }
+        params.filename = argv[i];
+        break;
+    }
+
+    if (params.filename == NULL) {
+        printf("ERROR: missing target elf file, use --help to show usage\n\n");
+        return 1;
+    }
+
+    init(&params);
+    if (errno == 1) {
+        return 1;
+    }
+    printf("init done - press key to start\n");
+    getchar();
+    start();
+    
+    // int cnt = 0;
+    char *line = NULL;
+    size_t size = 0;
+    while (1) {
+        // struct SimAvrEvent ev = waitForEvent();
+        // printf("%10.03lf: event %s (%d) received    \r", ev.cycle / 20E6, eventText((EnumSimEvent)ev.event), ev.event);
+        // fflush(stdout);
+        // if (++cnt == 10000) {
+        //     stop();
+        //     shutdown();
+        //     break;
+        // }
+        
+        if (getline(&line, &size, stdin) > 0) {
+            const char *commands[] = { "interrupt", "continue", "stack" };
+            try {
+                int foundIndex = -1;
+                int foundCnt = 0;
+                size_t length = strlen(line) - 1;
+                if (length > 0 && size >= (length + 1)) {
+                    line[length] = 0;
+                    for (long unsigned int i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
+                        const char *cmd = commands[i];
+                        size_t max = strlen(cmd);
+                        bool ok = true;
+                        for (size_t j = 0; j < length; j++) {
+                            if (j >= max || line[j] != cmd[j]) {
+                                ok = false;
+                                break;
+                            }
+                        }
+                        if (ok) {
+                            foundCnt++;
+                            foundIndex = i;
+                        }
+                    }
+                }
+                // printf("foundCnt=%d  foundIndex=%d  command=%s\n", foundCnt, foundIndex, foundIndex >= 0 ? commands[foundIndex] : "");
+                if (foundCnt == 1 || length == 0) {
+                    setCommand((EnumSimAvrCommand)(foundCnt > 0 ? foundIndex + 2 : 1), NULL);
+                    while (1) {
+                        struct SimAvrEvent ev = waitForEvent();
+                        if (ev.event == EventCommandExecuted) {
+                            break;
+                        }
+                    }
+
+                } else {
+                    printf("invalid command, valid commands are <Enter> for status and:");
+                    for (long unsigned int i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
+                        printf(" %s", commands[i]);
+                    }
+                    printf("\n");
+                    continue;
+                }
+                
+            } catch (std::exception& e) {
+                printf("ERROR\n");
+            }
+           
+        }
+        
+    }
+    while (1) {
+        struct SimAvrEvent ev = waitForEvent();
+        printf("event %s (%d) received\n", eventText((EnumSimEvent)ev.event), ev.event);
+        if (ev.event == EventShutdown) {
+            break;
+        }
+    }
+
+    usleep(10000);
+    return 0;
+}
\ No newline at end of file
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.cpp b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.cpp
new file mode 100644 (file)
index 0000000..f59ddbd
--- /dev/null
@@ -0,0 +1,18 @@
+#include "error.h"
+#include <stdarg.h>
+
+std::string error (const char *location, const char *format, ...) {
+       va_list args;
+       va_start (args, format);
+       int length = std::vsnprintf (NULL, 0, format, args);
+    va_end (args);
+
+       va_start (args, format);
+       char* str = new char[length + 1]; // one more character for null terminator
+       std::vsnprintf (str, length + 1, format, args);
+       std::string rv = "Error at " + std::string(location) + " -> " + str;
+       delete[] str;
+    va_end (args);
+
+       return rv;
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.h
new file mode 100644 (file)
index 0000000..80d5682
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+#include <string>
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+#define AT __FILE__ ":" TOSTRING(__LINE__)
+std::string error (const char *location, const char *format, ...);
+
+#endif // ERROR_H
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.cpp b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.cpp
new file mode 100644 (file)
index 0000000..32a23b6
--- /dev/null
@@ -0,0 +1,282 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <stdexcept>
+#include <string>
+#include <iostream>
+#include <vector>
+
+#include <GL/glut.h>
+#include <pthread.h>
+#include <simavr/sim_avr.h>
+#include <simavr/sim_elf.h>
+#include <simavr/sim_gdb.h>
+
+#include "sim.h"
+#include "error.h"
+
+
+
+// https://github.com/java-native-access/jna/blob/master/www/CallbacksAndClosures.md#callbacks-function-pointers-and-closures
+
+
+int SIGUSR1 = 30;
+
+// avr_t * avr = NULL;
+SimAvr simavr;
+std::string lastErrorMessage;
+
+static int fdStdOut = -1, fdStdErr = -1;
+// static fpos_t posStdOut, posStdErr;
+
+void switchStdOut (const char * filename) {
+       fflush(stdout);
+       // fgetpos(stdout, &posStdOut);
+       if (filename == NULL) {
+               int fd = open("/dev/null", O_WRONLY);
+               fdStdOut = dup2(fd, fileno(stdout));
+       } else {
+               fdStdOut = dup(fileno(stdout));
+       }
+}
+
+void switchStdErr (const char * filename) {
+       fflush(stderr);
+       // fgetpos(stderr, &posStdErr);
+       if (filename == NULL) {
+               int fd = open("/dev/null", O_WRONLY);
+               fdStdErr = dup2(fd, fileno(stderr));
+       } else {
+               fdStdErr = dup(fileno(stderr));
+       }
+}
+
+void revertStdErrOut () {
+       if (fdStdOut >= 0) {
+               fflush(stdout);
+               dup2(fdStdOut, fileno(stdout));
+               close(fdStdOut);
+               fdStdOut = -1;
+               clearerr(stdout);
+               // fsetpos(stdout, &posStdOut);
+       }
+       if (fdStdErr >= 0) {
+               fflush(stderr);
+               dup2(fdStdErr, fileno(stderr));
+               close(fdStdErr);
+               fdStdErr = -1;
+               clearerr(stderr);
+               // fsetpos(stderr, &posStdErr);
+       }
+}
+
+const char * lastError () {
+       return lastErrorMessage.c_str();
+}
+
+
+// static void handleWritePortB (struct avr_t * avr, __attribute__((unused)) avr_io_addr_t addr, uint8_t v, SimAvr *simavr) {
+//     static int value = 0;
+//     if (value != v) {
+//             value = v;
+//             if (simavr != NULL) {
+//                     if ((value << PORTB0) != 0) {
+//                             simavr->addEvent(EventLedOn);
+//                     } else {
+//                             simavr->addEvent(EventLedOff);
+//                     }
+//             }
+//     }
+// }
+
+std::vector<uint8_t> gdbFromUartBuffer;
+std::vector<uint8_t> gdbToUartBuffer;
+
+__attribute__((unused)) static void fromGdbUart (uint8_t b) {
+       static int cnt = 0;
+       if (b == '$') {
+               cnt = 1;
+       } else if (cnt > 0 && b == '#') {
+               cnt = -1;
+       } else if (cnt < 0) {
+               cnt--;
+       }
+       gdbFromUartBuffer.push_back(b);
+
+       if (cnt <= -3 || (cnt == 0 && b == '+')) {
+               printf("\n\rgdb-uart OUT -> ");
+               for (uint8_t c : gdbFromUartBuffer) {
+                       putchar(c);
+               }
+               printf("\n\r");
+               gdbFromUartBuffer.clear();
+       }
+}
+
+__attribute__((unused)) static void toGdbUart (uint8_t b) {
+       static int cnt = 0;
+       if (b == '$') {
+               cnt = 1;
+       } else if (cnt > 0 && b == '#') {
+               cnt = -1;
+       } else if (cnt < 0) {
+               cnt--;
+       }
+       gdbToUartBuffer.push_back(b);
+
+       if (cnt <= -3 || (cnt == 0 && b == '+')) {
+               printf("\n\rgdb-uart IN <-- ");
+               for (uint8_t c : gdbToUartBuffer) {
+                       putchar(c);
+               }
+               printf("\n\r");
+               gdbToUartBuffer.clear();
+       }
+
+}
+
+void init (struct StartParameters *param) {
+       try {
+               // switchStdOut(NULL);
+               // switchStdErr(NULL);
+               // std::cout.rdbuf(0);
+               // std::cerr.rdbuf(0);
+               const char *filename = param->filename != NULL
+                       ? param->filename
+                       // : "../gdb-stub-sm_atmega324p/sim/dist/gdb-stub-sm_atmega324p.axf";
+                       : "../gdb-stub-sm_atmega324p/sim/dist/gdb-stub-sm_atmega324p.axf";
+               printf("firmware file \"%s\"\n", filename);
+               simavr.load(param);
+               if (strcmp(simavr.getTargetDeviceName(), "atmega324p") != 0) {
+                       std::logic_error(error(AT, "invalid target device %s", simavr.getTargetDeviceName()));
+               }
+               
+               simavr.setUartDumpEnabled(false);
+               // simavr.registerIoWrite(PORTB, handleWritePortB);
+               simavr.setUartPtyEnabled(0, true);
+               int uartCnt = 1;
+               if (strcmp(simavr.getTargetDeviceName(), "atmega324p") == 0) {
+                       simavr.setUartPtyEnabled(1, true);
+                       simavr.setUartPtyHook(1, fromGdbUart, toGdbUart);
+                       uartCnt = 2;
+               }
+               // printf("uart0 ->  picocom --imap lfcrlf -b 115200 %s\n", simavr.getUartPtyDeviceName(0));
+               // printf("uart1 ->  picocom --imap lfcrlf -b 115200 %s\n", simavr.getUartPtyDeviceName(1));
+               
+               printf("device %s\n", simavr.getTargetDeviceName());
+               printf("frequency %.2lfMHz\n", simavr.getTargetFrequency() / 1E6);
+
+               for (int i = 0; i < uartCnt; i++) {
+                       char s[128];
+                       snprintf(s, 128, "/tmp/sim-megaavr-%s-uart%1d", simavr.getTargetDeviceName(), i);
+                       FILE *f = fopen(s, "w");
+                       if (f == NULL) {
+                               std::logic_error(error(AT, "cannot write file %s", s));
+                       }
+                       printf("uart%1d ->  picocom %s  (see file %s)\n", i, simavr.getUartPtyDeviceName(i), s);
+                       fprintf(f, "%s\n", simavr.getUartPtyDeviceName(i));
+                       fclose(f);
+               }
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "init() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void shutdown () {
+       try {
+               simavr.shutdown();
+       } catch (std::exception& e) {
+               lastErrorMessage = "shutdown() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void start () {
+       try {
+               simavr.start();
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "start() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void stop () {
+       try {
+               simavr.stop();
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "stop() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void setCommand (EnumSimAvrCommand cmd, void *param) {
+       try {
+               simavr.setCommand(cmd, param);
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "setCommand(..) fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+               throw e;
+       }
+
+}
+
+void addEvent (EnumSimEvent event) {
+       try {
+               simavr.addEvent(event);
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "addEvent(..) fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void setTimeSync (bool sync) {
+       try {
+               simavr.setTimeSync(sync);
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "setTimeSync() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+struct SimAvrStatus getStatus () {
+       return simavr.getStatus();;
+}
+
+struct SimAvrEvent waitForEvent () {
+       return simavr.waitForEvent();
+}
+
+const char *eventText (EnumSimEvent event) {
+       const char *rv = simavr.eventText((EnumSimAvrEvent)event);
+       if (rv != NULL) {
+               return rv;
+       } else {
+               switch (event) {
+                       case EventLedOff: return "LedOff";
+                       case EventLedOn: return "LedOn";
+                       default: return "?";
+               }
+       }
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.h
new file mode 100644 (file)
index 0000000..d980f26
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef SIM_H
+#define SIM_H
+
+#include "../simavr/simavr.h"
+
+// #define _AVR_IO_H_
+// #define _SFR_IO8(reg) (0x20 + reg)
+// #include "iom324p.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+typedef enum {
+       EventInterrupt = 1,
+    EventLedOff = 10,
+    EventLedOn = 11
+} EnumSimEvent;
+
+
+
+extern void init (struct StartParameters *param);
+extern void shutdown ();
+extern const char * lastError ();
+extern void start ();
+extern void stop ();
+extern void setCommand (EnumSimAvrCommand cmd, void *param);
+extern void addEvent (EnumSimEvent event);
+extern void setTimeSync (bool sync);
+extern struct SimAvrStatus getStatus ();
+extern struct SimAvrEvent waitForEvent ();
+extern const char *eventText (EnumSimEvent event);
+
+typedef void (*NotificationListener)(char *, int);
+void callbackTrigger(const NotificationListener l);
+void getDeviceRandomStatus(char *answer, int sizeOfChars);
+int randNum( int min,  int max);
+
+#ifdef __cplusplus
+}
+#endif 
+
+#endif // SIM_H
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/fifo_declare.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/fifo_declare.h
new file mode 100644 (file)
index 0000000..8a3b2fb
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+       fido_declare.h
+       Copyright (C) 2003-2012 Michel Pollet <buserror@gmail.com>
+
+       This library is free software; you can redistribute it and/or
+       modify it under the terms of the GNU Lesser General Public
+       License as published by the Free Software Foundation; either
+       version 2.1 of the License, or (at your option) any later version.
+
+       This library is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+       Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+       License along with this library; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/*
+ * FIFO helpers, aka circular buffers
+ *
+ * these macros define accessories for FIFOs of any name, type and
+ * any (power of two) size
+ */
+
+#ifndef __FIFO_DECLARE__
+#define __FIFO_DECLARE__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+       doing a :
+       DECLARE_FIFO(uint8_t, myfifo, 128);
+
+       will declare :
+       enum : myfifo_overflow_f
+       type : myfifo_t
+       functions:
+               // write a byte into the fifo, return 1 if there was room, 0 if there wasn't
+               int myfifo_write(myfifo_t *c, uint8_t b);
+               // reads a byte from the fifo, return 0 if empty. Use myfifo_isempty() to check beforehand
+               uint8_t myfifo_read(myfifo_t *c);
+               int myfifo_isfull(myfifo_t *c);
+               int myfifo_isempty(myfifo_t *c);
+               // returns number of items to read now
+               uint16_t myfifo_get_read_size(myfifo_t *c);
+               // read item at offset o from read cursor, no cursor advance
+               uint8_t myfifo_read_at(myfifo_t *c, uint16_t o);
+               // write b at offset o compared to current write cursor, no cursor advance
+               void myfifo_write_at(myfifo_t *c, uint16_t o, uint8_t b);
+
+       In your .c you need to 'implement' the fifo:
+       DEFINE_FIFO(uint8_t, myfifo)
+
+       To use the fifo, you must declare at least one :
+       myfifo_t fifo = FIFO_NULL;
+
+       while (!myfifo_isfull(&fifo))
+               myfifo_write(&fifo, 0xaa);
+       ....
+       while (!myfifo_isempty(&fifo))
+               b = myfifo_read(&fifo);
+ */
+
+#include <stdint.h>
+
+#if __AVR__
+#define FIFO_CURSOR_TYPE       uint8_t
+#define FIFO_BOOL_TYPE char
+#define FIFO_INLINE
+#define FIFO_SYNC
+#endif
+
+#ifndef        FIFO_CURSOR_TYPE
+#define FIFO_CURSOR_TYPE       uint16_t
+#endif
+#ifndef        FIFO_BOOL_TYPE
+#define FIFO_BOOL_TYPE int
+#endif
+#ifndef        FIFO_INLINE
+#define FIFO_INLINE    inline
+#endif
+
+/* We should not need volatile */
+#ifndef FIFO_VOLATILE
+#define FIFO_VOLATILE
+#endif
+#ifndef FIFO_SYNC
+#define FIFO_SYNC __sync_synchronize()
+#endif
+
+#ifndef FIFO_ZERO_INIT
+#define FIFO_ZERO_INIT {0}
+#endif
+#define FIFO_NULL { FIFO_ZERO_INIT, 0, 0, 0 }
+
+/* New compilers don't like unused static functions. However,
+ * we do like 'static inlines' for these small accessors,
+ * so we mark them as 'unused'. It stops it complaining */
+#ifdef __GNUC__
+#define FIFO_DECL static __attribute__ ((unused))
+#else
+#define FIFO_DECL static
+#endif
+
+#define DECLARE_FIFO(__type, __name, __size) \
+enum { __name##_overflow_f = (1 << 0) }; \
+enum { __name##_fifo_size = (__size) }; \
+typedef struct __name##_t {                    \
+       __type          buffer[__name##_fifo_size];             \
+       FIFO_VOLATILE FIFO_CURSOR_TYPE  read;           \
+       FIFO_VOLATILE FIFO_CURSOR_TYPE  write;          \
+       FIFO_VOLATILE uint8_t   flags;          \
+} __name##_t
+
+#define DEFINE_FIFO(__type, __name) \
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\
+{\
+       FIFO_CURSOR_TYPE now = c->write;\
+       FIFO_CURSOR_TYPE next = (now + 1) & (__name##_fifo_size-1);\
+       if (c->read != next) {  \
+               c->buffer[now] = b;\
+               FIFO_SYNC; \
+               c->write = next;\
+               return 1;\
+       }\
+       return 0;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\
+{\
+       FIFO_CURSOR_TYPE next = (c->write + 1) & (__name##_fifo_size-1);\
+       return c->read == next;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\
+{\
+       return c->read == c->write;\
+}\
+FIFO_DECL FIFO_INLINE __type __name##_read(__name##_t * c)\
+{\
+       __type res = FIFO_ZERO_INIT; \
+       FIFO_CURSOR_TYPE read = c->read;\
+       if (read == c->write)\
+               return res;\
+       res = c->buffer[read];\
+       FIFO_SYNC; \
+       c->read = (read + 1) & (__name##_fifo_size-1);\
+       return res;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\
+{\
+       return ((c->write + __name##_fifo_size) - c->read) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_write_size(__name##_t *c)\
+{\
+       return (__name##_fifo_size-1) - __name##_get_read_size(c);\
+}\
+FIFO_DECL FIFO_INLINE void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       FIFO_SYNC; \
+       c->read = (c->read + o) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       return c->buffer[(c->read + o) & (__name##_fifo_size-1)];\
+}\
+FIFO_DECL FIFO_INLINE void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\
+{\
+       c->buffer[(c->write + o) & (__name##_fifo_size-1)] = b;\
+}\
+FIFO_DECL FIFO_INLINE void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       FIFO_SYNC; \
+       c->write = (c->write + o) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE void __name##_reset(__name##_t *c)\
+{\
+       FIFO_SYNC; \
+       c->read = c->write = c->flags = 0;\
+}\
+struct __name##_t
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.c
new file mode 100644 (file)
index 0000000..a283fef
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+       uart_pty.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <simavr/sim_network.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef __APPLE__
+#include <util.h>
+#elif defined (__FreeBSD__)
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <libutil.h>
+#else
+#include <pty.h>
+#endif
+
+#include "uart_pty.h"
+#include <simavr/avr_uart.h>
+#include <simavr/sim_time.h>
+#include <simavr/sim_hex.h>
+
+DEFINE_FIFO(uint8_t,uart_pty_fifo);
+
+//#define TRACE(_w) _w
+#ifndef TRACE
+#define TRACE(_w)
+#endif
+
+TRACE(static const char *
+uart_pty_toPrintableChar(
+               uint32_t value)
+{
+       static char rv[2] = "";
+       if (value >= ' ' && value <= 126) {
+               rv[0] = (char)value;
+               rv[1] = 0;
+       } else if (value == '\n') {
+               return "\\n";
+       } else if (value == '\r') {
+               return "\\r";
+       } else {
+               rv[0] = 0;
+       }
+       return rv;
+})
+
+
+/*
+ * called when a byte is send via the uart on the AVR
+ */
+static void
+uart_pty_in_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(printf("uart_pty_in_hook %02x  %s\n", value, uart_pty_toPrintableChar(value));)
+       if (p->port[0].crlf && value == '\n') {
+               uart_pty_fifo_write(&p->port[0].in, '\r');
+       }
+       uart_pty_fifo_write(&p->pty.in, value);
+       if (p->port[0].hook_from_uart != NULL) {
+               p->port[0].hook_from_uart(value);
+       } else if (p->hook_from_uart != NULL) {
+               p->hook_from_uart(value);
+       }
+
+       if (p->tap.s) {
+               if (p->tap.crlf && value == '\n')
+                       uart_pty_fifo_write(&p->tap.in, '\r');
+               uart_pty_fifo_write(&p->tap.in, value);
+       }
+}
+
+// try to empty our fifo, the uart_pty_xoff_hook() will be called when
+// other side is full
+static void
+uart_pty_flush_incoming(
+               uart_pty_t * p)
+{
+       while (p->xon && !uart_pty_fifo_isempty(&p->pty.out)) {
+               TRACE(int r = p->pty.out.read;)
+               uint8_t byte = uart_pty_fifo_read(&p->pty.out);
+               TRACE(printf("uart_pty_flush_incoming send r %03d:%02x\n", r, byte);)
+               avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
+
+               if (p->tap.s) {
+                       if (p->tap.crlf && byte == '\n')
+                               uart_pty_fifo_write(&p->tap.in, '\r');
+                       uart_pty_fifo_write(&p->tap.in, byte);
+               }
+       }
+       if (p->tap.s) {
+               while (p->xon && !uart_pty_fifo_isempty(&p->tap.out)) {
+                       uint8_t byte = uart_pty_fifo_read(&p->tap.out);
+                       if (p->tap.crlf && byte == '\r') {
+                               uart_pty_fifo_write(&p->tap.in, '\n');
+                       }
+                       if (byte == '\n')
+                               continue;
+                       uart_pty_fifo_write(&p->tap.in, byte);
+                       avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
+               }
+       }
+}
+
+avr_cycle_count_t
+uart_pty_flush_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+
+       uart_pty_flush_incoming(p);
+       /* always return a cycle NUMBER not a cycle count */
+       return p->xon ? when + avr_hz_to_cycles(p->avr, 1000) : 0;
+}
+
+/*
+ * Called when the uart has room in it's input buffer. This is called repeateadly
+ * if necessary, while the xoff is called only when the uart fifo is FULL
+ */
+static void
+uart_pty_xon_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(if (!p->xon) printf("uart_pty_xon_hook\n");)
+       p->xon = 1;
+
+       uart_pty_flush_incoming(p);
+
+       // if the buffer is not flushed, try to do it later
+       if (p->xon)
+                       avr_cycle_timer_register(p->avr, avr_hz_to_cycles(p->avr, 1000),
+                                               uart_pty_flush_timer, param);
+}
+
+/*
+ * Called when the uart ran out of room in it's input buffer
+ */
+static void
+uart_pty_xoff_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(if (p->xon) printf("uart_pty_xoff_hook\n");)
+       p->xon = 0;
+       avr_cycle_timer_cancel(p->avr, uart_pty_flush_timer, param);
+}
+
+static void *
+uart_pty_thread(
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+
+       while (1) {
+               fd_set read_set, write_set;
+               int max = 0;
+               FD_ZERO(&read_set);
+               FD_ZERO(&write_set);
+
+               for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) {
+                       // read more only if buffer was flushed
+                       if (p->port[ti].buffer_len == p->port[ti].buffer_done) {
+                               FD_SET(p->port[ti].s, &read_set);
+                               max = p->port[ti].s > max ? p->port[ti].s : max;
+                       }
+                       if (!uart_pty_fifo_isempty(&p->port[ti].in)) {
+                               FD_SET(p->port[ti].s, &write_set);
+                               max = p->port[ti].s > max ? p->port[ti].s : max;
+                       }
+               }
+
+               // short, but not too short interval
+               struct timeval timo = { 0, 500 };
+               int ret = select(max+1, &read_set, &write_set, NULL, &timo);
+
+               if (ret < 0)
+                       break;
+
+               for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) {
+                       if (FD_ISSET(p->port[ti].s, &read_set)) {
+                               ssize_t r = read(p->port[ti].s, p->port[ti].buffer,
+                                                                       sizeof(p->port[ti].buffer)-1);
+                               p->port[ti].buffer_len = r;
+                               p->port[ti].buffer_done = 0;
+                               TRACE(if (!p->port[ti].tap)
+                                               hdump("pty recv", p->port[ti].buffer, r);)
+                       }
+                       if (p->port[ti].buffer_done < p->port[ti].buffer_len) {
+                               // write them in fifo
+                               while (p->port[ti].buffer_done < p->port[ti].buffer_len &&
+                                               !uart_pty_fifo_isfull(&p->port[ti].out)) {
+                                       int index = p->port[ti].buffer_done++;
+                                       TRACE(int wi = p->port[ti].out.write;)
+                                       uart_pty_fifo_write(&p->port[ti].out,
+                                                       p->port[ti].buffer[index]);
+                                       TRACE(printf("w %3d:%02x (%d/%d) %s\n",
+                                                               wi, p->port[ti].buffer[index],
+                                                               p->port[ti].out.read,
+                                                               p->port[ti].out.write,
+                                                               p->xon ? "XON" : "XOFF");)
+                                       if (p->port[ti].hook_to_uart != NULL) {
+                                               p->port[ti].hook_to_uart(p->port[ti].buffer[index]);
+                                       } else if (p->hook_to_uart != NULL) {
+                                               p->hook_to_uart(p->port[ti].buffer[index]);
+                                       }
+                               }
+                       }
+                       if (FD_ISSET(p->port[ti].s, &write_set)) {
+                               uint8_t buffer[512];
+                               // write them in fifo
+                               uint8_t * dst = buffer;
+                               while (!uart_pty_fifo_isempty(&p->port[ti].in) &&
+                                               (dst - buffer) < sizeof(buffer)) {
+                                       *dst = uart_pty_fifo_read(&p->port[ti].in);
+                                       dst++;
+                               }
+                               size_t len = dst - buffer;
+                               TRACE(size_t r =) write(p->port[ti].s, buffer, len);
+                               TRACE(if (!p->port[ti].tap) hdump("pty send", buffer, r);)
+                       }
+               }
+               /* DO NOT call this, this create a concurency issue with the
+                * FIFO that can't be solved cleanly with a memory barrier
+                       uart_pty_flush_incoming(p);
+                 */
+       }
+       return NULL;
+}
+
+static const char * irq_names[IRQ_UART_PTY_COUNT] = {
+       [IRQ_UART_PTY_BYTE_IN] = "8<uart_pty.in",
+       [IRQ_UART_PTY_BYTE_OUT] = "8>uart_pty.out",
+};
+
+void
+uart_pty_init(
+               struct avr_t * avr,
+               uart_pty_t * p)
+{
+       memset(p, 0, sizeof(*p));
+
+       p->avr = avr;
+       p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names);
+       avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p);
+
+       const int hastap = (getenv("SIMAVR_UART_TAP") && atoi(getenv("SIMAVR_UART_TAP"))) ||
+                       (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM")));
+       p->hastap = hastap;
+       p->hook_from_uart = NULL;
+       p->hook_to_uart = NULL;
+
+       for (int ti = 0; ti < 1 + hastap; ti++) {
+               int m, s;
+
+               if (openpty(&m, &s, p->port[ti].slavename, NULL, NULL) < 0) {
+                       fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno));
+                       return;
+               }
+               struct termios tio;
+               tcgetattr(m, &tio);
+               cfmakeraw(&tio);
+               tio.c_cflag &= ~(ECHO | ECHONL); // no input echo
+               // tio.c_oflag |= (OPOST | ONLCR); // LF -> CRLF
+               tcsetattr(m, TCSANOW, &tio);
+               p->port[ti].s = m;
+               p->port[ti].tap = ti != 0;
+               // p->port[ti].crlf = ti != 0;
+               p->port[ti].crlf = 1;
+               p->port[ti].hook_from_uart = NULL;
+               p->port[ti].hook_to_uart = NULL;
+               if (ti > 0) {
+                       printf("uart_pty_init %s on port *** %s ***\n",
+                               ti == 0 ? "bridge" : "tap", p->port[ti].slavename);
+               }
+       }
+
+       pthread_create(&p->thread, NULL, uart_pty_thread, p);
+
+}
+
+void
+uart_pty_stop(
+               uart_pty_t * p)
+{
+       puts(__func__);
+       pthread_kill(p->thread, SIGINT);
+       for (int ti = 0; ti < 2; ti++)
+               if (p->port[ti].s)
+                       close(p->port[ti].s);
+       void * ret;
+       pthread_join(p->thread, &ret);
+}
+
+void
+uart_pty_connect(
+               uart_pty_t * p,
+               char uart)
+{
+       // disable the stdio dump, as we are sending binary there
+       uint32_t f = 0;
+       avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f);
+       f &= ~AVR_UART_FLAG_STDIO;
+       avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f);
+
+       avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT);
+       avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT);
+       avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON);
+       avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF);
+
+       if (src && dst) {
+               avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN);
+               avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst);
+       }
+       if (xon)
+               avr_irq_register_notify(xon, uart_pty_xon_hook, p);
+       if (xoff)
+               avr_irq_register_notify(xoff, uart_pty_xoff_hook, p);
+
+       for (int ti = 0; ti < 1+(p->hastap?1:0); ti++) if (p->port[ti].s) {
+               char link[128];
+               snprintf(link, sizeof(link), "/tmp/simavr-uart%c%s", uart, ti == 1 ? "-tap" : "");
+               unlink(link);
+               if (symlink(p->port[ti].slavename, link) != 0) {
+                       fprintf(stderr, "WARN %s: Can't create %s: %s", __func__, link, strerror(errno));
+               } else {
+                       // printf("%s: %s now points to %s\n", __func__, link, p->port[ti].slavename);
+               }
+       }
+       if (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) {
+               char cmd[256];
+               sprintf(cmd, "xterm -e picocom -b 115200 %s >/dev/null 2>&1 &",
+                               p->tap.slavename);
+               system(cmd);
+       } else {
+               // printf("note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal\n");
+       }
+}
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.h
new file mode 100644 (file)
index 0000000..482de7d
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+       uart_pty.h
+
+       Copyright 2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UART_PTY_H___
+#define __UART_PTY_H___
+
+#include <pthread.h>
+#include <simavr/sim_irq.h>
+#include "fifo_declare.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+enum {
+       IRQ_UART_PTY_BYTE_IN = 0,
+       IRQ_UART_PTY_BYTE_OUT,
+       IRQ_UART_PTY_COUNT
+};
+
+DECLARE_FIFO(uint8_t,uart_pty_fifo, 512);
+
+typedef struct uart_pty_port_t {
+       unsigned int    tap : 1, crlf : 1;
+       int             s;                      // socket we chat on
+       char            slavename[64];
+       uart_pty_fifo_t in;
+       uart_pty_fifo_t out;
+       uint8_t         buffer[512];
+       size_t          buffer_len, buffer_done;
+       void (*hook_from_uart)(uint8_t);
+       void (*hook_to_uart)(uint8_t);
+} uart_pty_port_t, *uart_pty_port_p;
+
+typedef struct uart_pty_t {
+       avr_irq_t *     irq;            // irq list
+       struct avr_t *avr;              // keep it around so we can pause it
+
+       pthread_t       thread;
+       int                     xon;
+       int                     hastap;
+
+       union {
+               struct {
+                       uart_pty_port_t         pty;
+                       uart_pty_port_t         tap;
+               };
+               uart_pty_port_t port[2];
+       };
+       void (*hook_from_uart)(uint8_t);
+       void (*hook_to_uart)(uint8_t);
+} uart_pty_t;
+
+void
+uart_pty_init(
+               struct avr_t * avr,
+               uart_pty_t * b);
+void
+uart_pty_stop(uart_pty_t * p);
+
+void
+uart_pty_connect(
+               uart_pty_t * p,
+               char uart);
+
+#ifdef __cplusplus
+}
+#endif 
+
+#endif /* __UART_PTY_H___ */
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/semaphore.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/semaphore.h
new file mode 100644 (file)
index 0000000..289a45d
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef SEMAPHORE_H
+#define SEMAPHORE_H
+
+#include <mutex>
+#include <condition_variable>
+
+class Semaphore {
+    public:
+        Semaphore (int count_ = 0) : count(count_) {
+        }
+        
+        inline void notify () {
+            std::unique_lock<std::mutex> lock(mtx);
+            count++;
+            //notify the waiting thread
+            cv.notify_one();
+        }
+        inline void wait () {
+            std::unique_lock<std::mutex> lock(mtx);
+            while(count == 0) {
+                //wait on the mutex until notify is called
+                cv.wait(lock);
+            }
+            count--;
+        }
+    private:
+        std::mutex mtx;
+        std::condition_variable cv;
+        int count;
+};
+
+#endif // SEMAPHORE_H
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.cpp b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.cpp
new file mode 100644 (file)
index 0000000..ba63390
--- /dev/null
@@ -0,0 +1,749 @@
+#include "simavr.h"
+#include "../sim/error.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/time.h>
+
+#include <iostream>
+#include <iomanip>
+#include <thread>
+
+#include <simavr/sim_gdb.h>
+#include <simavr/sim_irq.h>
+#include <simavr/sim_avr.h>
+#include <simavr/sim_core.h>
+#include <simavr/avr_uart.h>
+
+
+SimAvr::SimAvr () {
+       memset(&status, 0, sizeof(status));
+}
+
+SimAvr::~SimAvr () {
+       if (firmware != NULL) {
+               free(firmware);
+               firmware = NULL;
+       }
+       if (avr != NULL) {
+               free(avr);
+               avr = NULL;
+       }
+}
+
+void SimAvr::shutdown () {
+       // if (pthread_mutex_lock(&lock)) {
+       //      throw std::logic_error(error(AT, "shutdown() fails caused by mutex error"));
+       // }
+       // cancelThread = true;
+       // isShutdown = true;
+       // pthread_mutex_unlock(&lock);
+       addEvent(EventShutdown);
+}
+
+
+void SimAvr::load (struct StartParameters *params) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "load() fails caused by mutex error"));
+       }
+       try {
+               if (isShutdown) {
+                       throw std::logic_error(error(AT, "cannot load after simavr shutdown"));
+               }
+               if (firmware != NULL) {
+                       throw std::logic_error(error(AT, "firmware already loaded"));
+               }
+
+               startParameters = params;
+               firmware = (elf_firmware_t *) malloc(sizeof(elf_firmware_t));
+               std::string filename = std::string(params->filename);
+               if (firmware == NULL || elf_read_firmware(filename.c_str(), firmware) != 0) {
+                       throw std::logic_error(error(AT, "elf_read_firmware() from %s fails", filename.c_str()));
+               }
+               if (params->mmcu != NULL) {
+                       strncpy(firmware->mmcu, params->mmcu, sizeof(firmware->mmcu));
+               }
+               if (params->frequency > 0) {
+                       firmware->frequency = (uint32_t)params->frequency;
+               }
+               if (params->vcc > 0) {
+                       firmware->vcc = (uint32_t)params->vcc;
+               }
+               if (params->avcc > 0) {
+                       firmware->avcc = (uint32_t)params->avcc;
+               }
+               if (params->aref > 0) {
+                       firmware->aref = (uint32_t)params->aref;
+               }
+
+               // strncpy(firmware->mmcu, "atmega324p", sizeof(firmware->mmcu));
+               // firmware->frequency = 20000000L;
+               if (firmware->frequency == 0) {
+                       firmware->frequency = 8000000L;
+               }
+               if (firmware->vcc == 0) {
+                       firmware->vcc = 5000;
+               }
+               if (firmware->avcc == 0) {
+                       firmware->avcc = 5000;
+               }
+               if (firmware->aref == 0) {
+                       firmware->aref = 2500;
+               }
+               if (firmware->mmcu[0] == 0) {
+                       throw std::logic_error(error(AT, "missing cpu type, use --mmcu ... to set mmcu manually)", firmware->mmcu));
+               }
+
+               avr = avr_make_mcu_by_name(firmware->mmcu);
+               if (!avr) {
+                       throw std::logic_error(error(AT, "avr_make_mcu_by_name() fails (mmcu=%s)", firmware->mmcu));
+               }
+               if (params->gdbPort > 0) {
+                       avr->gdb_port = params->gdbPort;
+               } else {
+                       avr->gdb_port = 1234;
+               }
+               printf("init with port=%d, mmcu=%s, f=%u, vcc=%d, avcc=%d, aref=%d, pc=0x%04x\n",
+                       avr->gdb_port, firmware->mmcu, firmware->frequency, firmware->vcc, firmware->avcc, firmware->aref, params->pc < 0 ? 0 : params->pc);
+               status.freqHz = firmware->frequency;
+
+               if (avr_init(avr) != 0) {
+                       throw std::logic_error(error(AT, "avr_init() fails"));
+               }
+
+               
+               firmware->eeprom = (uint8_t *)malloc(1024);
+               for (int i = 0; i < 1024; i++) {
+                       firmware->eeprom[i] = 0xff;
+               }
+               firmware->eeprom[0] = 0xf0;
+               firmware->eesize = 1024;
+
+               avr_load_firmware(avr, firmware);
+               status.state = StateLoaded;
+
+               avr->fuse[AVR_FUSE_LOW] = 0xe7;
+               avr->fuse[AVR_FUSE_HIGH] = 0xd8;
+               avr->fuse[AVR_FUSE_EXT] = 0xff;
+               avr->lockbits = 0xff;
+
+               avr->gdb_port = 1234;
+               avr_gdb_init(avr);
+               if (params->pc >= 0) {
+                       avr->pc = params->pc;
+               }
+
+               pthread_mutex_unlock(&lock);
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+       addEvent(EventLoad);
+}
+
+// enabled/disable character output on stdout in case of avr uart write
+void SimAvr::setUartDumpEnabled (bool enabled) {
+       if (avr == NULL) {
+               throw std::logic_error(error(AT, "setUartDumpEnabled() fails because no avr available"));
+       }
+       uint32_t f = 0;
+       avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('0'), &f);
+       f = enabled ? f | AVR_UART_FLAG_STDIO : f & ~AVR_UART_FLAG_STDIO;
+       avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('0'), &f);
+       avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('1'), &f);
+       f = enabled ? f | AVR_UART_FLAG_STDIO : f & ~AVR_UART_FLAG_STDIO;
+       avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('1'), &f);
+}
+
+void SimAvr::registerIoWrite (avr_io_addr_t addr, avrsim_io_write_callback_t callback) {
+       avr_register_io_write(avr, addr, (avr_io_write_t)callback, this);
+} 
+
+void SimAvr::setUartPtyEnabled (int uart, bool enable) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+       uart_pty_t *p = NULL;
+    try {
+               if (uart < 0 || uart > 1) {
+                       throw std::logic_error(error(AT, "setUartPtyEnabled() fails (invalid uart %d)", uart));
+               }
+               if (enable && uartPty[uart] != NULL) {
+                       throw std::logic_error(error(AT, "enableUartPty() fails (uart %d already enabled)", uart));
+               }
+               if (!enable && uartPty[uart] == NULL) {
+                       throw std::logic_error(error(AT, "enableUartPty() fails (uart %d not enabled)", uart));
+               }
+               if (avr == NULL) {
+                       throw std::logic_error(error(AT, "enableUartPty() fails (avr not ready)"));
+               }
+               if (enable) {
+                       p = (uart_pty_t *)malloc(sizeof(uart_pty_t));
+                       if (p == NULL) {
+                               throw std::logic_error(error(AT, "enableUartPty() fails (malloc fails)"));
+                       }
+                       memset(p, 0, sizeof(uart_pty_t));
+                       // setenv("SIMAVR_UART_TAP", "1", 1);
+                       uart_pty_init(avr, p);
+                       uart_pty_connect(p, uart + '0');
+                       uartPty[uart] = p;
+               } else {
+                       uart_pty_stop(uartPty[uart]);
+                       free(uartPty[uart]);
+                       uartPty[uart] = NULL;
+               }
+               pthread_mutex_unlock(&lock);
+
+       } catch (std::exception& e) {
+               if (p != NULL) {
+                       free(p);
+               }
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+void SimAvr::setUartPtyHook (int uart, void (*fromUart)(uint8_t), void (*toUart)(uint8_t)) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "setUartPtyHook() fails caused by mutex error"));
+       }
+    try {
+               if (uart < 0 || uart > 1) {
+                       throw std::logic_error(error(AT, "setUartPtyHook() fails (invalid uart %d)", uart));
+               }
+               if (uartPty[uart] == NULL) {
+                       throw std::logic_error(error(AT, "setUartPtyHook() fails (uart %d not enabled)", uart));
+               }
+               uartPty[uart]->hook_from_uart = fromUart;
+               uartPty[uart]->hook_to_uart = toUart;
+               pthread_mutex_unlock(&lock);
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+const char *SimAvr::getUartPtyDeviceName (int uart) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+    try {
+               if (uart < 0 || uart > 1) {
+                       throw std::logic_error(error(AT, "getUartPtyDeviceName() fails (invalid uart %d)", uart));
+               }
+               uart_pty_t *p = uartPty[uart];
+               pthread_mutex_unlock(&lock);
+               return p == NULL ? NULL : p->port[0].slavename;
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+const char *SimAvr::getTargetDeviceName () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+    try {
+               if (firmware == NULL) {
+                       throw std::logic_error(error(AT, "getTargetDeviceName() fails (no firmware loaded)"));
+               }
+               pthread_mutex_unlock(&lock);
+               return firmware->mmcu;
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+uint32_t SimAvr::getTargetFrequency () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+    try {
+               if (firmware == NULL) {
+                       throw std::logic_error(error(AT, "getTargetDeviceName() fails (no firmware loaded)"));
+               }
+               pthread_mutex_unlock(&lock);
+               return firmware->frequency;
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+void SimAvr::start () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+       try {
+               if (isShutdown) {
+                       throw std::logic_error("start() not allowed after shutdown");
+               }
+               if (avrRunThread != NULL) {
+                       throw std::logic_error("simavr already started");
+               }
+               if (avr == NULL) {
+                       throw std::logic_error("avr not ready (check if load done)");
+               }
+               syncTime[0] = !syncTime[1];
+               avrRunThread = new std::thread(&SimAvr::avrRun, this);
+               pthread_mutex_unlock(&lock);
+       
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+       addEvent(EventStart);
+}
+
+
+void SimAvr::stop () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "stop() fails caused by mutex error"));
+       }
+       try {
+               if (avrRunThread == NULL) {
+                       throw std::logic_error("simavr not started");
+               }
+               cancelThread = true;
+               pthread_mutex_unlock(&lock);
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+       addEvent(EventStop);
+}
+
+void SimAvr::setCommand (EnumSimAvrCommand cmd, void *param) {
+       if (command != ReadyForNewCommand) {
+               throw std::logic_error("another command pending");
+       }
+       command = cmd;
+}
+
+void SimAvr::addEvent (int event) {
+       struct timeval tp;
+       gettimeofday(&tp, NULL);
+       SimAvrEvent *p = (SimAvrEvent *)malloc(sizeof(SimAvrEvent));
+       if (p != NULL) {
+               p->epochMillis = tp.tv_sec * 1000 + (long)tp.tv_usec / 1000;
+               p->event = event;
+               if (pthread_mutex_lock(&lock)) {
+                       throw std::logic_error(error(AT, "addEvent() fails caused by mutex error"));
+               }
+               try {
+                       p->cycle = avr->cycle;
+                       if (!isShutdown) {
+                               events.push_back(p);
+                               eventCount.notify();
+                       }
+                       pthread_mutex_unlock(&lock);
+               } catch (std::exception& e) {
+                       status.state = StateError;
+                       pthread_mutex_unlock(&lock);
+                       throw;
+               }
+       }
+}
+
+
+void SimAvr::setTimeSync (bool sync) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "setTimeSync() fails caused by mutex error"));
+       }
+       try {
+               syncTime[1] = sync;
+               pthread_mutex_unlock(&lock);
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+
+struct SimAvrEvent SimAvr::waitForEvent () {
+    eventCount.wait();
+
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "waitForEvent() fails caused by mutex error"));
+       }
+       try {
+               struct SimAvrEvent rv = { epochMillis: 0, event: EventUnknown };
+               struct SimAvrEvent *p = NULL;
+               if (events.size() > 0) {
+                       p = events.front();
+                       rv = *p;
+                       if (p->event != EventShutdown) {
+                               events.pop_front();
+                               free(p);
+                       }
+                       if (p->event == EventShutdown) {
+                               cancelThread = true;
+                               isShutdown = true;
+                       }
+               }
+               pthread_mutex_unlock(&lock);
+               return rv;
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+const char *SimAvr::eventText (EnumSimAvrEvent event) {
+       switch (event) {
+               case EventUnknown:  return "Unknown";
+               case EventShutdown: return "Shutdown";
+               case EventLoad:     return "Load";
+               case EventStart:    return "Start";
+               case EventStop:     return "Stop";
+               default: return NULL;
+       }
+}
+
+struct SimAvrStatus SimAvr::getStatus () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "getStatus() fails caused by mutex error"));
+       }
+       struct SimAvrStatus rv = status;
+       pthread_mutex_unlock(&lock);
+       return rv;
+}
+
+void SimAvr::printCyclesAndElapsedTime () {
+       uint64_t time = avr->cycle * 1000000000L / avr->frequency;
+       uint16_t nanos = time % 1000; time = time / 1000;
+       uint16_t micros = time % 1000; time = time / 1000;
+       uint16_t millis = time % 1000; time = time / 1000;
+       uint16_t seconds = time % 1000; time = time / 1000;
+       printf("cycle %" PRIu64 " = %ds %03dms %03dµs %03dns", avr->cycle, seconds, millis, micros, nanos);
+}
+
+void SimAvr::avrRun () {
+       long cnt = 0;
+       int lastAvrState = -1;
+       _avr_sp_set(avr, 0);
+       while (1) {
+               try {
+                       if (avr->state != lastAvrState) {
+                               switch (avr->state) {
+                                       case cpu_Stopped: {
+                                               printf("\ncpu stopped at 0x%04x on ", avr->pc);
+                                               printCyclesAndElapsedTime();
+                                               char sfr[9];
+                                               sfr[7] = avr->sreg[0] ? 'C' : '-';
+                                               sfr[6] = avr->sreg[1] ? 'Z' : '-';
+                                               sfr[5] = avr->sreg[2] ? 'N' : '-';
+                                               sfr[4] = avr->sreg[3] ? 'V' : '-';
+                                               sfr[3] = avr->sreg[4] ? 'S' : '-';
+                                               sfr[2] = avr->sreg[5] ? 'H' : '-';
+                                               sfr[1] = avr->sreg[6] ? 'T' : '-';
+                                               sfr[0] = avr->sreg[7] ? 'I' : '-';
+                                               sfr[8] = 0;
+                                               printf("  SFR: %s\n  ", sfr);
+                                               uint16_t x = 0, y = 0, z = 0;
+                                               for (int i = 0; i < 32; i++) {
+                                                       uint8_t b = avr->data[i];
+                                                       printf(" r%02d=%02x", i, b);
+                                                       switch (i) {
+                                                               case 0x0f: printf("\n  "); break;
+                                                               case 0x1a: x = (x & 0xff00) | b; break;
+                                                               case 0x1b: x = (x & 0x00ff) | (b << 8); break;
+                                                               case 0x1c: y = (y & 0xff00) | b; break;
+                                                               case 0x1d: y = (y & 0x00ff) | (b << 8); break;
+                                                               case 0x1e: z = (z & 0xff00) | b; break;
+                                                               case 0x1f: z = (z & 0x00ff) | (b << 8); break;
+                                                       }
+                                               }
+                                               printf("\n   X=0x%04x  Y=0x%04x  Z=0x%04x\n", x, y, z);
+
+                                               uint16_t sp = _avr_sp_get(avr);
+                                               printf("   Stack (SP=0x%04x)", sp);
+                                               int printHeader = 1;
+                                               for (int i = 0; i < 16; i++) {
+                                                       uint16_t addr = sp + 1 + i;
+                                                       if (addr <= avr->ioend) { continue; }
+                                                       if (addr > avr->ramend) { break; }
+                                                       if (printHeader) {
+                                                               printf(" -> SRAM 0x%04x:", addr);
+                                                               printHeader = 0;
+                                                       }
+                                                       uint8_t b = avr_core_watch_read(avr, addr);
+                                                       printf(" %02x", b);
+
+                                               }
+                                               printf(printHeader ? "\n" : "\n");
+                                               
+                                               printf("   Arduino LED L: ");
+                                               printf((avr->data[0x24] & 0x20) && (avr->data[0x25] & 0x20) ? "ON\n" : "OFF\n");
+
+                                               printf("\n");
+                                               break;
+                                       }
+                                       case cpu_Sleeping: printf("cpu enter sleep mode at cycle %" PRIu64 "\n", avr->cycle); break;
+                                       case cpu_Running: printf("cpu starts running at cycle %" PRIu64 "\n", avr->cycle); break;
+                                       case cpu_Step: printf("cpu step\n"); break;
+                                       case cpu_StepDone: printf("cpu step done\n"); break;
+                                       case cpu_Done: printf("cpu done\n"); break;
+                                       case cpu_Crashed: printf("cpu crashed\n"); break;
+                                       default: printf("cpu enter unknown mode at cycle %" PRIu64 "\n", avr->cycle); break;
+                               }
+                               lastAvrState = avr->state;
+                       }
+
+                       if (startParameters != NULL) {
+                               switch (startParameters->board) {
+                                       case BoardNano: {
+                                               static int8_t ledL = -1; // pin floating
+                                               uint8_t ddrb = avr->data[0x24];
+                                               uint8_t portb = avr->data[0x25];
+                                               int8_t nextLedL = -1;
+                                               if (ddrb & 0x20) {
+                                                       nextLedL = portb & 0x20 ? 1 : 0;
+                                               }
+                                               if (nextLedL != ledL) {
+                                                       ledL = nextLedL;
+                                                       printCyclesAndElapsedTime();
+                                                       printf(": LED L = %c\n", ledL ? 'X' : '.');
+                                               }
+                                               break;
+                                       }
+                                       case BoardSure: {
+                                               static int8_t led[4] = { -1, -1, -1, -1 }; // pin floating
+                                               uint8_t ddra = avr->data[0x3a];
+                                               uint8_t porta = avr->data[0x3b];
+                                               int change = 0;
+                                               for (int i = 0; i < 4; i++) {
+                                                       int8_t nextLed = -1;
+                                                       if (ddra & (1 << i)) {
+                                                               nextLed = porta & (1 << i) ? 0 : 1;
+                                                       }
+                                                       if (nextLed != led[i]) {
+                                                               change = 1;
+                                                               led[i] = nextLed;
+                                                       }
+                                               }
+                                               if (change) {
+                                                       printCyclesAndElapsedTime(); printf(": ");
+                                                       printf(" LED PA[3210] =");
+                                                       for (int i = 3; i >= 0; i--) {
+                                                                       printf(" %c", led[i] ? 'X' : '.');
+                                                       }
+                                                       printf("\n");
+                                               }
+                                               break;
+                                       }
+                                       case BoardEWS1: {
+                                               static int8_t led = -1; // pin floating
+                                               uint8_t ddrb = avr->data[0x24];
+                                               uint8_t portb = avr->data[0x25];
+                                               int8_t nextLed = -1;
+                                               if (ddrb & 0x01) {
+                                                       nextLed = portb & 0x01 ? 1 : 0;
+                                               }
+                                               if (nextLed != led) {
+                                                       led = nextLed;
+                                                       printCyclesAndElapsedTime();
+                                                       printf(": LED1 (PB0) = %c\n", led ? 'X' : '.');
+                                               }
+                                               break;
+                                       }
+                                       default: break;
+                               }
+                       }
+
+                       if (cnt <= 0) {
+                               // usleep(10000);
+                               if (pthread_mutex_lock(&lock)) {
+                                       throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (1)"));
+                               }
+                               try {
+                                       status.cycle = avr->cycle;
+                                       status.state = StateRunning;
+                                       status.running = true;
+                                       switch (avr->state) {
+                                               case cpu_Done:    status.state = StateDone; break;
+                                               case cpu_Running: status.state = StateRunning; break;
+                                               case cpu_Crashed: status.state = StateCrashed; break;
+                                               default: status.state = StateOthers; break;
+                                       }
+                                       if (cancelThread) {
+                                               cnt = -1;
+                                               cancelThread = false;
+                                       } else {
+                                               if (syncTime[0] != syncTime[1]) {
+                                                       syncTime[0] = syncTime[1];
+                                                       if (syncTime[0]) {
+                                                               cyclesOnSyncStart = avr->cycle;
+                                                               struct timeval tp;
+                                                               gettimeofday(&tp, NULL);
+                                                               timeMicrosOnSyncStart = tp.tv_usec + (uint64_t)tp.tv_sec * 1000000L;
+                                                       } else {
+                                                               cyclesOnSyncStart = -1;
+                                                               timeMicrosOnSyncStart = 0;
+                                                       }
+                                               }
+                                               if (syncTime[0]) {
+                                                       uint64_t ucMicros = (uint64_t)(status.cycle - cyclesOnSyncStart) * 1000000L / status.freqHz ;
+                                                       struct timeval tp;
+                                                       gettimeofday(&tp, NULL);
+                                                       const uint64_t timeMicros = tp.tv_usec + (uint64_t)tp.tv_sec * 1000000L - timeMicrosOnSyncStart;
+
+                                                       if (ucMicros > 3000000) {
+                                                               ucMicros++;
+                                                       }
+                                                       
+                                                       int64_t dt = ucMicros - timeMicros;
+                                                       if (dt > 0) {
+                                                               int us = dt > INT_MAX ? INT_MAX : (int)dt;
+                                                               if (us > 1000) {
+                                                                       timeval ts;
+                                                                       ts.tv_sec = 0;
+                                                                       ts.tv_usec = us;
+                                                                       select(0, 0, 0, 0, &ts);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       if (avr->state == cpu_Done) {
+                                               cnt = -1;
+                                       } else if (avr->state == cpu_Crashed) {
+                                               throw std::logic_error(error(AT, "avr state is cpu_Crashed"));
+                                       }
+                                       pthread_mutex_unlock(&lock);
+
+                               } catch (std::exception& e) {
+                                       status.state = StateError;
+                                       pthread_mutex_unlock(&lock);
+                                       throw;
+                               }
+                       }
+
+                       if (avr->state == cpu_Stopped) {
+                               timeval ts;
+                               ts.tv_sec = 0;
+                               ts.tv_usec = 1000;
+                               select(0, 0, 0, 0, &ts);
+                       }
+
+                       if (cnt < 0) {
+                               cyclesOnSyncStart = -1;
+                               timeMicrosOnSyncStart = 0;
+                               if (pthread_mutex_lock(&lock)) {
+                                       throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (2)"));
+                               }
+                               try {
+                                       if (avrRunThread != NULL) {
+                                               avrRunThread->detach();
+                                               delete avrRunThread;
+                                               avrRunThread = NULL;
+                                       }
+                                       pthread_mutex_unlock(&lock);
+                                       return;
+                               } catch (std::exception& e) {
+                                       pthread_mutex_unlock(&lock);
+                                       status.state = StateError;
+                                       throw;
+                               }
+                       
+                       } else if (++cnt >= 100000) {
+                               cnt = 0;
+                       }
+
+                       if (command != ReadyForNewCommand) {
+                               switch (command) {
+                                       case CommandStatus: {
+                                               printCyclesAndElapsedTime();
+                                               printf("\n");
+                                               break;
+                                       }
+                                       case CommandInterrupt: {
+                                               if (avr->state == cpu_Running) {
+                                                       avr->state = cpu_Stopped;
+                                               }
+                                               break;
+                                       }
+                                       case CommandContinue: {
+                                               if (avr->state == cpu_Stopped) {
+                                                       avr->state = cpu_Running;
+                                               }
+                                               break;
+                                       }
+                                       case CommandStack: {
+                                               uint16_t sp = _avr_sp_get(avr);
+                                               printf("Stack: SP=0x%04x:\n", sp);
+                                               for (uint16_t addr = ((sp + 1) / 16) * 16; addr <= avr->ramend; addr++) {
+                                                       if (addr % 16 == 0) {
+                                                               printf("  0x%04x:", addr);
+                                                       }
+                                                       if (addr % 4 == 0) {
+                                                               printf(" ");
+                                                       }
+                                                       if (addr <= sp) {
+                                                               printf("   ");
+                                                       } else {
+                                                               uint8_t b = avr_core_watch_read(avr, addr);
+                                                               printf(" %02x", b);
+                                                       }
+                                                       if (addr % 16 == 15) {
+                                                               printf("\n");
+                                                       }
+                                               }
+                                               printf("\n");
+                                               break;
+                                       }
+
+                                       default: break;
+                               }
+                               command = ReadyForNewCommand;
+                               addEvent(EventCommandExecuted);
+                       }
+
+                       avr_run(avr);
+
+               } catch (std::exception& e) {
+                       status.state = StateError;
+                       cyclesOnSyncStart = -1;
+                       timeMicrosOnSyncStart = 0;
+                       if (pthread_mutex_lock(&lock)) {
+                               throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (2)"));
+                       }
+                       try {
+                               if (avrRunThread != NULL) {
+                                       avrRunThread->detach();
+                                       delete avrRunThread;
+                                       avrRunThread = NULL;
+                               }
+                               pthread_mutex_unlock(&lock);
+                               throw;
+                       } catch (std::exception& e) {
+                               pthread_mutex_unlock(&lock);
+                               status.state = StateError;
+                               throw;
+                       }
+
+               }
+       }
+}
+
diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.h
new file mode 100644 (file)
index 0000000..e1455b7
--- /dev/null
@@ -0,0 +1,132 @@
+#ifndef SIMAVR_H
+#define SIMAVR_H
+
+#include "semaphore.h"
+#include "parts/uart_pty.h"
+
+#include <simavr/sim_avr.h>
+#include <simavr/sim_elf.h>
+#include <simavr/sim_io.h>
+
+#include <string>
+#include <thread>
+#include <list>
+
+typedef enum {
+       BoardUnknown = 0,
+       BoardNano,
+       BoardSure,
+       BoardEWS1
+} EnumStartParameterBoard;
+
+
+struct StartParameters {
+    char *filename;
+    int gdbPort;
+    int64_t frequency;
+    const char *mmcu;
+       EnumStartParameterBoard board;
+    int32_t pc;
+    int32_t vcc;
+    int32_t avcc;
+    int32_t aref;
+};
+
+enum SimAvrState {
+       StateInit = 0,
+       StateError = 1,
+       StateLoaded = 10,
+       StateRunning = 11,
+       StateDone = 12,
+       StateCrashed = 20,
+       StateOthers = 99,
+       StateUnknown = 100
+};
+
+typedef enum {
+       EventUnknown = 0,
+       EventShutdown = -1,
+       EventLoad = -2,
+       EventStart = -3,
+       EventStop = -4,
+       EventCommandExecuted = -5
+} EnumSimAvrEvent;
+
+typedef enum {
+       ReadyForNewCommand = 0,
+       CommandStatus,
+       CommandInterrupt,
+       CommandContinue,
+       CommandStack
+} EnumSimAvrCommand;
+
+struct SimAvrStatus {
+       long long freqHz;
+       long long cycle;
+       enum SimAvrState state;
+       bool running;
+};
+
+struct SimAvrEvent {
+       long long epochMillis;
+       long long cycle;
+       int event;
+};
+
+
+class SimAvr;
+typedef void (*avrsim_io_write_callback_t)(avr_t *avr, avr_io_addr_t addr, uint8_t value, SimAvr *simavr);
+
+class SimAvr {
+
+public:
+       SimAvr ();
+       ~SimAvr ();
+
+       struct SimAvrStatus getStatus ();
+       struct SimAvrEvent waitForEvent ();
+       const char *eventText (EnumSimAvrEvent event);
+
+public:    
+       void load (struct StartParameters *params);
+       void shutdown ();
+       void start ();
+       void stop ();
+       void addEvent (int event);
+       void setUartDumpEnabled (bool enabled);
+       void setUartPtyEnabled (int uart, bool enabled);
+       void setUartPtyHook (int uart, void (*fromUart)(uint8_t), void (*toUart)(uint8_t));
+    void registerIoWrite (avr_io_addr_t addr, avrsim_io_write_callback_t callback);
+
+       const char *getTargetDeviceName ();
+       uint32_t getTargetFrequency ();
+       const char *getUartPtyDeviceName (int uart);
+
+       void avrRun ();
+       void setLed (bool on);
+       void setTimeSync (bool sync);
+       void setCommand (EnumSimAvrCommand cmd, void *param);
+
+private:
+       elf_firmware_t *firmware = NULL;
+       avr_t *avr = NULL;;
+       StartParameters *startParameters = NULL;;
+       pthread_mutex_t lock;
+       std::list<struct SimAvrEvent *> events;
+       EnumSimAvrCommand command;
+       struct SimAvrStatus status;
+       bool cancelThread = false;
+       bool isShutdown = false;
+       bool syncTime[2] = { false, true };
+       std::thread *avrRunThread = NULL;
+       long cyclesOnSyncStart = -1;
+       uint64_t timeMicrosOnSyncStart = 0;
+       Semaphore eventCount;
+       uart_pty_t *uartPty[2] = { NULL, NULL };
+       void printCyclesAndElapsedTime ();
+};
+
+
+
+
+#endif // SIMAVR_H
\ No newline at end of file
diff --git a/examples/simuc/sim/vcd/.gitkeep b/examples/simuc/sim/vcd/.gitkeep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/simuc/src/main.cpp b/examples/simuc/src/main.cpp
new file mode 100644 (file)
index 0000000..0af4178
--- /dev/null
@@ -0,0 +1,190 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "sim/sim.h"
+#include "simavr/simavr.h"
+
+void printHelp () {
+    printf("simuc V1.0.0 (%s,%s)\n", __DATE__, __TIME__);
+    printf("usage: simuc [options] elf-file\n\n");
+    printf("  available options:\n");
+    printf("    --board ...        set board (arduino, sure, evws1)\n");
+    printf("    --sync             sync elapsed ÂµC-time with real time\n\n");
+    printf("    --mmcu ...         set target device type\n");
+    printf("    --frequency ...    set target frequency in Hz\n");
+    printf("    --pc ...           set start program counter (default is 0)\n");
+    printf("    --vcc ...          set voltage VCC in Volt\n");
+    printf("    --avcc ...         set voltage AVCC in Volt\n");
+    printf("    --aref ...         set voltage AREF in Volt\n\n");
+    printf("  example:\n");
+    printf("    simuc --mmcu atmega328p --frequency 16000000 --pc 0x7000 a.out\n\n");
+    printf("    simuc --board arduino a.out\n\n");
+}
+
+int main (int argc, char **argv) {
+    struct StartParameters params = {
+        filename: NULL,
+        gdbPort: -1,
+        frequency: -1,
+        mmcu: NULL,
+        board: BoardUnknown,
+        vcc: -1,
+        avcc: -1,
+        aref: -1
+    };
+
+    if (argc <= 1) {
+        printHelp();
+        return 1;
+    }
+
+    for (int i = 1; i < argc; i++) {
+        if (argv[i][0] == '-') {
+            if (strcmp(argv[i], "--help") == 0) {
+                printHelp();
+                return 0;
+            } else if (strcmp(argv[i], "--board") == 0 && argc >= (i + 1)) {
+                i++;
+                if (strcmp("arduino", argv[i]) == 0) {
+                    params.board = BoardNano;
+                    params.mmcu = "atmega328p";
+                    params.frequency = 16000000;
+                    params.vcc = 5000;
+                    params.avcc = 5000;
+                    params.aref = 2500;
+                } else if (strcmp("sure", argv[i]) == 0) {
+                    params.board = BoardSure;
+                    params.mmcu = "atmega16";
+                    params.frequency = 12000000;
+                    params.vcc = 5000;
+                    params.avcc = 5000;
+                    params.aref = 2500;
+               } else if (strcmp("evws1", argv[i]) == 0) {
+                    params.board = BoardEWS1;
+                    params.mmcu = "atmega324p";
+                    params.frequency = 20000000;
+                    params.vcc = 5000;
+                    params.avcc = 5000;
+                    params.aref = 2500;
+               } else {
+                    fprintf(stderr, "ERROR: invalid board %s, use --help to show usage\n\n", argv[i]);
+                    return 1;
+               }
+
+            } else if (strcmp(argv[i], "--mmcu") == 0) {
+                params.mmcu = argv[++i];
+            } else if (strcmp(argv[i], "--port") == 0) {
+                sscanf(argv[++i], "%d", &params.gdbPort);
+            } else if (strcmp(argv[i], "--frequency") == 0) {
+                sscanf(argv[++i], "%" PRIu64, &params.frequency);
+            } else if (strcmp(argv[i], "--pc") == 0) {
+                i++;
+                if (argv[i][0] == '0' && argv[i][1] == 'x') {
+                    sscanf(&argv[i][2], "%x", &params.pc);
+                } else {
+                    sscanf(argv[++i], "%d", &params.pc);
+                }
+            } else if (strcmp(argv[i], "--vcc") == 0) {
+                sscanf(argv[++i], "%d", &params.vcc);
+            } else if (strcmp(argv[i], "--avcc") == 0) {
+                sscanf(argv[++i], "%d", &params.avcc);
+            } else if (strcmp(argv[i], "--aref") == 0) {
+                sscanf(argv[++i], "%d", &params.aref);
+            } else {
+                fprintf(stderr, "ERROR: invalid option %s, use --help to show usage\n\n", argv[i]);
+                return 1;
+            }
+            continue;
+        }
+        params.filename = argv[i];
+        break;
+    }
+
+    if (params.filename == NULL) {
+        printf("ERROR: missing target elf file, use --help to show usage\n\n");
+        return 1;
+    }
+
+    init(&params);
+    if (errno == 1) {
+        return 1;
+    }
+    printf("init done - press key to start\n");
+    getchar();
+    start();
+    
+    // int cnt = 0;
+    char *line = NULL;
+    size_t size = 0;
+    while (1) {
+        // struct SimAvrEvent ev = waitForEvent();
+        // printf("%10.03lf: event %s (%d) received    \r", ev.cycle / 20E6, eventText((EnumSimEvent)ev.event), ev.event);
+        // fflush(stdout);
+        // if (++cnt == 10000) {
+        //     stop();
+        //     shutdown();
+        //     break;
+        // }
+        
+        if (getline(&line, &size, stdin) > 0) {
+            const char *commands[] = { "interrupt", "continue", "stack" };
+            try {
+                int foundIndex = -1;
+                int foundCnt = 0;
+                size_t length = strlen(line) - 1;
+                if (length > 0 && size >= (length + 1)) {
+                    line[length] = 0;
+                    for (long unsigned int i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
+                        const char *cmd = commands[i];
+                        size_t max = strlen(cmd);
+                        bool ok = true;
+                        for (size_t j = 0; j < length; j++) {
+                            if (j >= max || line[j] != cmd[j]) {
+                                ok = false;
+                                break;
+                            }
+                        }
+                        if (ok) {
+                            foundCnt++;
+                            foundIndex = i;
+                        }
+                    }
+                }
+                // printf("foundCnt=%d  foundIndex=%d  command=%s\n", foundCnt, foundIndex, foundIndex >= 0 ? commands[foundIndex] : "");
+                if (foundCnt == 1 || length == 0) {
+                    setCommand((EnumSimAvrCommand)(foundCnt > 0 ? foundIndex + 2 : 1), NULL);
+                    while (1) {
+                        struct SimAvrEvent ev = waitForEvent();
+                        if (ev.event == EventCommandExecuted) {
+                            break;
+                        }
+                    }
+
+                } else {
+                    printf("invalid command, valid commands are <Enter> for status and:");
+                    for (long unsigned int i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
+                        printf(" %s", commands[i]);
+                    }
+                    printf("\n");
+                    continue;
+                }
+                
+            } catch (std::exception& e) {
+                printf("ERROR\n");
+            }
+           
+        }
+        
+    }
+    while (1) {
+        struct SimAvrEvent ev = waitForEvent();
+        printf("event %s (%d) received\n", eventText((EnumSimEvent)ev.event), ev.event);
+        if (ev.event == EventShutdown) {
+            break;
+        }
+    }
+
+    usleep(10000);
+    return 0;
+}
\ No newline at end of file
diff --git a/examples/simuc/src/sim/error.cpp b/examples/simuc/src/sim/error.cpp
new file mode 100644 (file)
index 0000000..f59ddbd
--- /dev/null
@@ -0,0 +1,18 @@
+#include "error.h"
+#include <stdarg.h>
+
+std::string error (const char *location, const char *format, ...) {
+       va_list args;
+       va_start (args, format);
+       int length = std::vsnprintf (NULL, 0, format, args);
+    va_end (args);
+
+       va_start (args, format);
+       char* str = new char[length + 1]; // one more character for null terminator
+       std::vsnprintf (str, length + 1, format, args);
+       std::string rv = "Error at " + std::string(location) + " -> " + str;
+       delete[] str;
+    va_end (args);
+
+       return rv;
+}
diff --git a/examples/simuc/src/sim/error.h b/examples/simuc/src/sim/error.h
new file mode 100644 (file)
index 0000000..80d5682
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+#include <string>
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+#define AT __FILE__ ":" TOSTRING(__LINE__)
+std::string error (const char *location, const char *format, ...);
+
+#endif // ERROR_H
diff --git a/examples/simuc/src/sim/sim.cpp b/examples/simuc/src/sim/sim.cpp
new file mode 100644 (file)
index 0000000..32a23b6
--- /dev/null
@@ -0,0 +1,282 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <stdexcept>
+#include <string>
+#include <iostream>
+#include <vector>
+
+#include <GL/glut.h>
+#include <pthread.h>
+#include <simavr/sim_avr.h>
+#include <simavr/sim_elf.h>
+#include <simavr/sim_gdb.h>
+
+#include "sim.h"
+#include "error.h"
+
+
+
+// https://github.com/java-native-access/jna/blob/master/www/CallbacksAndClosures.md#callbacks-function-pointers-and-closures
+
+
+int SIGUSR1 = 30;
+
+// avr_t * avr = NULL;
+SimAvr simavr;
+std::string lastErrorMessage;
+
+static int fdStdOut = -1, fdStdErr = -1;
+// static fpos_t posStdOut, posStdErr;
+
+void switchStdOut (const char * filename) {
+       fflush(stdout);
+       // fgetpos(stdout, &posStdOut);
+       if (filename == NULL) {
+               int fd = open("/dev/null", O_WRONLY);
+               fdStdOut = dup2(fd, fileno(stdout));
+       } else {
+               fdStdOut = dup(fileno(stdout));
+       }
+}
+
+void switchStdErr (const char * filename) {
+       fflush(stderr);
+       // fgetpos(stderr, &posStdErr);
+       if (filename == NULL) {
+               int fd = open("/dev/null", O_WRONLY);
+               fdStdErr = dup2(fd, fileno(stderr));
+       } else {
+               fdStdErr = dup(fileno(stderr));
+       }
+}
+
+void revertStdErrOut () {
+       if (fdStdOut >= 0) {
+               fflush(stdout);
+               dup2(fdStdOut, fileno(stdout));
+               close(fdStdOut);
+               fdStdOut = -1;
+               clearerr(stdout);
+               // fsetpos(stdout, &posStdOut);
+       }
+       if (fdStdErr >= 0) {
+               fflush(stderr);
+               dup2(fdStdErr, fileno(stderr));
+               close(fdStdErr);
+               fdStdErr = -1;
+               clearerr(stderr);
+               // fsetpos(stderr, &posStdErr);
+       }
+}
+
+const char * lastError () {
+       return lastErrorMessage.c_str();
+}
+
+
+// static void handleWritePortB (struct avr_t * avr, __attribute__((unused)) avr_io_addr_t addr, uint8_t v, SimAvr *simavr) {
+//     static int value = 0;
+//     if (value != v) {
+//             value = v;
+//             if (simavr != NULL) {
+//                     if ((value << PORTB0) != 0) {
+//                             simavr->addEvent(EventLedOn);
+//                     } else {
+//                             simavr->addEvent(EventLedOff);
+//                     }
+//             }
+//     }
+// }
+
+std::vector<uint8_t> gdbFromUartBuffer;
+std::vector<uint8_t> gdbToUartBuffer;
+
+__attribute__((unused)) static void fromGdbUart (uint8_t b) {
+       static int cnt = 0;
+       if (b == '$') {
+               cnt = 1;
+       } else if (cnt > 0 && b == '#') {
+               cnt = -1;
+       } else if (cnt < 0) {
+               cnt--;
+       }
+       gdbFromUartBuffer.push_back(b);
+
+       if (cnt <= -3 || (cnt == 0 && b == '+')) {
+               printf("\n\rgdb-uart OUT -> ");
+               for (uint8_t c : gdbFromUartBuffer) {
+                       putchar(c);
+               }
+               printf("\n\r");
+               gdbFromUartBuffer.clear();
+       }
+}
+
+__attribute__((unused)) static void toGdbUart (uint8_t b) {
+       static int cnt = 0;
+       if (b == '$') {
+               cnt = 1;
+       } else if (cnt > 0 && b == '#') {
+               cnt = -1;
+       } else if (cnt < 0) {
+               cnt--;
+       }
+       gdbToUartBuffer.push_back(b);
+
+       if (cnt <= -3 || (cnt == 0 && b == '+')) {
+               printf("\n\rgdb-uart IN <-- ");
+               for (uint8_t c : gdbToUartBuffer) {
+                       putchar(c);
+               }
+               printf("\n\r");
+               gdbToUartBuffer.clear();
+       }
+
+}
+
+void init (struct StartParameters *param) {
+       try {
+               // switchStdOut(NULL);
+               // switchStdErr(NULL);
+               // std::cout.rdbuf(0);
+               // std::cerr.rdbuf(0);
+               const char *filename = param->filename != NULL
+                       ? param->filename
+                       // : "../gdb-stub-sm_atmega324p/sim/dist/gdb-stub-sm_atmega324p.axf";
+                       : "../gdb-stub-sm_atmega324p/sim/dist/gdb-stub-sm_atmega324p.axf";
+               printf("firmware file \"%s\"\n", filename);
+               simavr.load(param);
+               if (strcmp(simavr.getTargetDeviceName(), "atmega324p") != 0) {
+                       std::logic_error(error(AT, "invalid target device %s", simavr.getTargetDeviceName()));
+               }
+               
+               simavr.setUartDumpEnabled(false);
+               // simavr.registerIoWrite(PORTB, handleWritePortB);
+               simavr.setUartPtyEnabled(0, true);
+               int uartCnt = 1;
+               if (strcmp(simavr.getTargetDeviceName(), "atmega324p") == 0) {
+                       simavr.setUartPtyEnabled(1, true);
+                       simavr.setUartPtyHook(1, fromGdbUart, toGdbUart);
+                       uartCnt = 2;
+               }
+               // printf("uart0 ->  picocom --imap lfcrlf -b 115200 %s\n", simavr.getUartPtyDeviceName(0));
+               // printf("uart1 ->  picocom --imap lfcrlf -b 115200 %s\n", simavr.getUartPtyDeviceName(1));
+               
+               printf("device %s\n", simavr.getTargetDeviceName());
+               printf("frequency %.2lfMHz\n", simavr.getTargetFrequency() / 1E6);
+
+               for (int i = 0; i < uartCnt; i++) {
+                       char s[128];
+                       snprintf(s, 128, "/tmp/sim-megaavr-%s-uart%1d", simavr.getTargetDeviceName(), i);
+                       FILE *f = fopen(s, "w");
+                       if (f == NULL) {
+                               std::logic_error(error(AT, "cannot write file %s", s));
+                       }
+                       printf("uart%1d ->  picocom %s  (see file %s)\n", i, simavr.getUartPtyDeviceName(i), s);
+                       fprintf(f, "%s\n", simavr.getUartPtyDeviceName(i));
+                       fclose(f);
+               }
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "init() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void shutdown () {
+       try {
+               simavr.shutdown();
+       } catch (std::exception& e) {
+               lastErrorMessage = "shutdown() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void start () {
+       try {
+               simavr.start();
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "start() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void stop () {
+       try {
+               simavr.stop();
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "stop() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void setCommand (EnumSimAvrCommand cmd, void *param) {
+       try {
+               simavr.setCommand(cmd, param);
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "setCommand(..) fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+               throw e;
+       }
+
+}
+
+void addEvent (EnumSimEvent event) {
+       try {
+               simavr.addEvent(event);
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "addEvent(..) fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+void setTimeSync (bool sync) {
+       try {
+               simavr.setTimeSync(sync);
+
+       } catch (std::exception& e) {
+               lastErrorMessage = "setTimeSync() fails, caused by " + std::string(e.what());
+               std::cerr << lastErrorMessage << "\n";
+               errno = 1;
+       }
+}
+
+struct SimAvrStatus getStatus () {
+       return simavr.getStatus();;
+}
+
+struct SimAvrEvent waitForEvent () {
+       return simavr.waitForEvent();
+}
+
+const char *eventText (EnumSimEvent event) {
+       const char *rv = simavr.eventText((EnumSimAvrEvent)event);
+       if (rv != NULL) {
+               return rv;
+       } else {
+               switch (event) {
+                       case EventLedOff: return "LedOff";
+                       case EventLedOn: return "LedOn";
+                       default: return "?";
+               }
+       }
+}
diff --git a/examples/simuc/src/sim/sim.h b/examples/simuc/src/sim/sim.h
new file mode 100644 (file)
index 0000000..d980f26
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef SIM_H
+#define SIM_H
+
+#include "../simavr/simavr.h"
+
+// #define _AVR_IO_H_
+// #define _SFR_IO8(reg) (0x20 + reg)
+// #include "iom324p.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+typedef enum {
+       EventInterrupt = 1,
+    EventLedOff = 10,
+    EventLedOn = 11
+} EnumSimEvent;
+
+
+
+extern void init (struct StartParameters *param);
+extern void shutdown ();
+extern const char * lastError ();
+extern void start ();
+extern void stop ();
+extern void setCommand (EnumSimAvrCommand cmd, void *param);
+extern void addEvent (EnumSimEvent event);
+extern void setTimeSync (bool sync);
+extern struct SimAvrStatus getStatus ();
+extern struct SimAvrEvent waitForEvent ();
+extern const char *eventText (EnumSimEvent event);
+
+typedef void (*NotificationListener)(char *, int);
+void callbackTrigger(const NotificationListener l);
+void getDeviceRandomStatus(char *answer, int sizeOfChars);
+int randNum( int min,  int max);
+
+#ifdef __cplusplus
+}
+#endif 
+
+#endif // SIM_H
diff --git a/examples/simuc/src/simavr/parts/fifo_declare.h b/examples/simuc/src/simavr/parts/fifo_declare.h
new file mode 100644 (file)
index 0000000..8a3b2fb
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+       fido_declare.h
+       Copyright (C) 2003-2012 Michel Pollet <buserror@gmail.com>
+
+       This library is free software; you can redistribute it and/or
+       modify it under the terms of the GNU Lesser General Public
+       License as published by the Free Software Foundation; either
+       version 2.1 of the License, or (at your option) any later version.
+
+       This library is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+       Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+       License along with this library; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/*
+ * FIFO helpers, aka circular buffers
+ *
+ * these macros define accessories for FIFOs of any name, type and
+ * any (power of two) size
+ */
+
+#ifndef __FIFO_DECLARE__
+#define __FIFO_DECLARE__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+       doing a :
+       DECLARE_FIFO(uint8_t, myfifo, 128);
+
+       will declare :
+       enum : myfifo_overflow_f
+       type : myfifo_t
+       functions:
+               // write a byte into the fifo, return 1 if there was room, 0 if there wasn't
+               int myfifo_write(myfifo_t *c, uint8_t b);
+               // reads a byte from the fifo, return 0 if empty. Use myfifo_isempty() to check beforehand
+               uint8_t myfifo_read(myfifo_t *c);
+               int myfifo_isfull(myfifo_t *c);
+               int myfifo_isempty(myfifo_t *c);
+               // returns number of items to read now
+               uint16_t myfifo_get_read_size(myfifo_t *c);
+               // read item at offset o from read cursor, no cursor advance
+               uint8_t myfifo_read_at(myfifo_t *c, uint16_t o);
+               // write b at offset o compared to current write cursor, no cursor advance
+               void myfifo_write_at(myfifo_t *c, uint16_t o, uint8_t b);
+
+       In your .c you need to 'implement' the fifo:
+       DEFINE_FIFO(uint8_t, myfifo)
+
+       To use the fifo, you must declare at least one :
+       myfifo_t fifo = FIFO_NULL;
+
+       while (!myfifo_isfull(&fifo))
+               myfifo_write(&fifo, 0xaa);
+       ....
+       while (!myfifo_isempty(&fifo))
+               b = myfifo_read(&fifo);
+ */
+
+#include <stdint.h>
+
+#if __AVR__
+#define FIFO_CURSOR_TYPE       uint8_t
+#define FIFO_BOOL_TYPE char
+#define FIFO_INLINE
+#define FIFO_SYNC
+#endif
+
+#ifndef        FIFO_CURSOR_TYPE
+#define FIFO_CURSOR_TYPE       uint16_t
+#endif
+#ifndef        FIFO_BOOL_TYPE
+#define FIFO_BOOL_TYPE int
+#endif
+#ifndef        FIFO_INLINE
+#define FIFO_INLINE    inline
+#endif
+
+/* We should not need volatile */
+#ifndef FIFO_VOLATILE
+#define FIFO_VOLATILE
+#endif
+#ifndef FIFO_SYNC
+#define FIFO_SYNC __sync_synchronize()
+#endif
+
+#ifndef FIFO_ZERO_INIT
+#define FIFO_ZERO_INIT {0}
+#endif
+#define FIFO_NULL { FIFO_ZERO_INIT, 0, 0, 0 }
+
+/* New compilers don't like unused static functions. However,
+ * we do like 'static inlines' for these small accessors,
+ * so we mark them as 'unused'. It stops it complaining */
+#ifdef __GNUC__
+#define FIFO_DECL static __attribute__ ((unused))
+#else
+#define FIFO_DECL static
+#endif
+
+#define DECLARE_FIFO(__type, __name, __size) \
+enum { __name##_overflow_f = (1 << 0) }; \
+enum { __name##_fifo_size = (__size) }; \
+typedef struct __name##_t {                    \
+       __type          buffer[__name##_fifo_size];             \
+       FIFO_VOLATILE FIFO_CURSOR_TYPE  read;           \
+       FIFO_VOLATILE FIFO_CURSOR_TYPE  write;          \
+       FIFO_VOLATILE uint8_t   flags;          \
+} __name##_t
+
+#define DEFINE_FIFO(__type, __name) \
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\
+{\
+       FIFO_CURSOR_TYPE now = c->write;\
+       FIFO_CURSOR_TYPE next = (now + 1) & (__name##_fifo_size-1);\
+       if (c->read != next) {  \
+               c->buffer[now] = b;\
+               FIFO_SYNC; \
+               c->write = next;\
+               return 1;\
+       }\
+       return 0;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\
+{\
+       FIFO_CURSOR_TYPE next = (c->write + 1) & (__name##_fifo_size-1);\
+       return c->read == next;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\
+{\
+       return c->read == c->write;\
+}\
+FIFO_DECL FIFO_INLINE __type __name##_read(__name##_t * c)\
+{\
+       __type res = FIFO_ZERO_INIT; \
+       FIFO_CURSOR_TYPE read = c->read;\
+       if (read == c->write)\
+               return res;\
+       res = c->buffer[read];\
+       FIFO_SYNC; \
+       c->read = (read + 1) & (__name##_fifo_size-1);\
+       return res;\
+}\
+FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\
+{\
+       return ((c->write + __name##_fifo_size) - c->read) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_write_size(__name##_t *c)\
+{\
+       return (__name##_fifo_size-1) - __name##_get_read_size(c);\
+}\
+FIFO_DECL FIFO_INLINE void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       FIFO_SYNC; \
+       c->read = (c->read + o) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       return c->buffer[(c->read + o) & (__name##_fifo_size-1)];\
+}\
+FIFO_DECL FIFO_INLINE void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\
+{\
+       c->buffer[(c->write + o) & (__name##_fifo_size-1)] = b;\
+}\
+FIFO_DECL FIFO_INLINE void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       FIFO_SYNC; \
+       c->write = (c->write + o) & (__name##_fifo_size-1);\
+}\
+FIFO_DECL FIFO_INLINE void __name##_reset(__name##_t *c)\
+{\
+       FIFO_SYNC; \
+       c->read = c->write = c->flags = 0;\
+}\
+struct __name##_t
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/examples/simuc/src/simavr/parts/uart_pty.c b/examples/simuc/src/simavr/parts/uart_pty.c
new file mode 100644 (file)
index 0000000..a283fef
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+       uart_pty.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <simavr/sim_network.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef __APPLE__
+#include <util.h>
+#elif defined (__FreeBSD__)
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <libutil.h>
+#else
+#include <pty.h>
+#endif
+
+#include "uart_pty.h"
+#include <simavr/avr_uart.h>
+#include <simavr/sim_time.h>
+#include <simavr/sim_hex.h>
+
+DEFINE_FIFO(uint8_t,uart_pty_fifo);
+
+//#define TRACE(_w) _w
+#ifndef TRACE
+#define TRACE(_w)
+#endif
+
+TRACE(static const char *
+uart_pty_toPrintableChar(
+               uint32_t value)
+{
+       static char rv[2] = "";
+       if (value >= ' ' && value <= 126) {
+               rv[0] = (char)value;
+               rv[1] = 0;
+       } else if (value == '\n') {
+               return "\\n";
+       } else if (value == '\r') {
+               return "\\r";
+       } else {
+               rv[0] = 0;
+       }
+       return rv;
+})
+
+
+/*
+ * called when a byte is send via the uart on the AVR
+ */
+static void
+uart_pty_in_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(printf("uart_pty_in_hook %02x  %s\n", value, uart_pty_toPrintableChar(value));)
+       if (p->port[0].crlf && value == '\n') {
+               uart_pty_fifo_write(&p->port[0].in, '\r');
+       }
+       uart_pty_fifo_write(&p->pty.in, value);
+       if (p->port[0].hook_from_uart != NULL) {
+               p->port[0].hook_from_uart(value);
+       } else if (p->hook_from_uart != NULL) {
+               p->hook_from_uart(value);
+       }
+
+       if (p->tap.s) {
+               if (p->tap.crlf && value == '\n')
+                       uart_pty_fifo_write(&p->tap.in, '\r');
+               uart_pty_fifo_write(&p->tap.in, value);
+       }
+}
+
+// try to empty our fifo, the uart_pty_xoff_hook() will be called when
+// other side is full
+static void
+uart_pty_flush_incoming(
+               uart_pty_t * p)
+{
+       while (p->xon && !uart_pty_fifo_isempty(&p->pty.out)) {
+               TRACE(int r = p->pty.out.read;)
+               uint8_t byte = uart_pty_fifo_read(&p->pty.out);
+               TRACE(printf("uart_pty_flush_incoming send r %03d:%02x\n", r, byte);)
+               avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
+
+               if (p->tap.s) {
+                       if (p->tap.crlf && byte == '\n')
+                               uart_pty_fifo_write(&p->tap.in, '\r');
+                       uart_pty_fifo_write(&p->tap.in, byte);
+               }
+       }
+       if (p->tap.s) {
+               while (p->xon && !uart_pty_fifo_isempty(&p->tap.out)) {
+                       uint8_t byte = uart_pty_fifo_read(&p->tap.out);
+                       if (p->tap.crlf && byte == '\r') {
+                               uart_pty_fifo_write(&p->tap.in, '\n');
+                       }
+                       if (byte == '\n')
+                               continue;
+                       uart_pty_fifo_write(&p->tap.in, byte);
+                       avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
+               }
+       }
+}
+
+avr_cycle_count_t
+uart_pty_flush_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+
+       uart_pty_flush_incoming(p);
+       /* always return a cycle NUMBER not a cycle count */
+       return p->xon ? when + avr_hz_to_cycles(p->avr, 1000) : 0;
+}
+
+/*
+ * Called when the uart has room in it's input buffer. This is called repeateadly
+ * if necessary, while the xoff is called only when the uart fifo is FULL
+ */
+static void
+uart_pty_xon_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(if (!p->xon) printf("uart_pty_xon_hook\n");)
+       p->xon = 1;
+
+       uart_pty_flush_incoming(p);
+
+       // if the buffer is not flushed, try to do it later
+       if (p->xon)
+                       avr_cycle_timer_register(p->avr, avr_hz_to_cycles(p->avr, 1000),
+                                               uart_pty_flush_timer, param);
+}
+
+/*
+ * Called when the uart ran out of room in it's input buffer
+ */
+static void
+uart_pty_xoff_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(if (p->xon) printf("uart_pty_xoff_hook\n");)
+       p->xon = 0;
+       avr_cycle_timer_cancel(p->avr, uart_pty_flush_timer, param);
+}
+
+static void *
+uart_pty_thread(
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+
+       while (1) {
+               fd_set read_set, write_set;
+               int max = 0;
+               FD_ZERO(&read_set);
+               FD_ZERO(&write_set);
+
+               for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) {
+                       // read more only if buffer was flushed
+                       if (p->port[ti].buffer_len == p->port[ti].buffer_done) {
+                               FD_SET(p->port[ti].s, &read_set);
+                               max = p->port[ti].s > max ? p->port[ti].s : max;
+                       }
+                       if (!uart_pty_fifo_isempty(&p->port[ti].in)) {
+                               FD_SET(p->port[ti].s, &write_set);
+                               max = p->port[ti].s > max ? p->port[ti].s : max;
+                       }
+               }
+
+               // short, but not too short interval
+               struct timeval timo = { 0, 500 };
+               int ret = select(max+1, &read_set, &write_set, NULL, &timo);
+
+               if (ret < 0)
+                       break;
+
+               for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) {
+                       if (FD_ISSET(p->port[ti].s, &read_set)) {
+                               ssize_t r = read(p->port[ti].s, p->port[ti].buffer,
+                                                                       sizeof(p->port[ti].buffer)-1);
+                               p->port[ti].buffer_len = r;
+                               p->port[ti].buffer_done = 0;
+                               TRACE(if (!p->port[ti].tap)
+                                               hdump("pty recv", p->port[ti].buffer, r);)
+                       }
+                       if (p->port[ti].buffer_done < p->port[ti].buffer_len) {
+                               // write them in fifo
+                               while (p->port[ti].buffer_done < p->port[ti].buffer_len &&
+                                               !uart_pty_fifo_isfull(&p->port[ti].out)) {
+                                       int index = p->port[ti].buffer_done++;
+                                       TRACE(int wi = p->port[ti].out.write;)
+                                       uart_pty_fifo_write(&p->port[ti].out,
+                                                       p->port[ti].buffer[index]);
+                                       TRACE(printf("w %3d:%02x (%d/%d) %s\n",
+                                                               wi, p->port[ti].buffer[index],
+                                                               p->port[ti].out.read,
+                                                               p->port[ti].out.write,
+                                                               p->xon ? "XON" : "XOFF");)
+                                       if (p->port[ti].hook_to_uart != NULL) {
+                                               p->port[ti].hook_to_uart(p->port[ti].buffer[index]);
+                                       } else if (p->hook_to_uart != NULL) {
+                                               p->hook_to_uart(p->port[ti].buffer[index]);
+                                       }
+                               }
+                       }
+                       if (FD_ISSET(p->port[ti].s, &write_set)) {
+                               uint8_t buffer[512];
+                               // write them in fifo
+                               uint8_t * dst = buffer;
+                               while (!uart_pty_fifo_isempty(&p->port[ti].in) &&
+                                               (dst - buffer) < sizeof(buffer)) {
+                                       *dst = uart_pty_fifo_read(&p->port[ti].in);
+                                       dst++;
+                               }
+                               size_t len = dst - buffer;
+                               TRACE(size_t r =) write(p->port[ti].s, buffer, len);
+                               TRACE(if (!p->port[ti].tap) hdump("pty send", buffer, r);)
+                       }
+               }
+               /* DO NOT call this, this create a concurency issue with the
+                * FIFO that can't be solved cleanly with a memory barrier
+                       uart_pty_flush_incoming(p);
+                 */
+       }
+       return NULL;
+}
+
+static const char * irq_names[IRQ_UART_PTY_COUNT] = {
+       [IRQ_UART_PTY_BYTE_IN] = "8<uart_pty.in",
+       [IRQ_UART_PTY_BYTE_OUT] = "8>uart_pty.out",
+};
+
+void
+uart_pty_init(
+               struct avr_t * avr,
+               uart_pty_t * p)
+{
+       memset(p, 0, sizeof(*p));
+
+       p->avr = avr;
+       p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names);
+       avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p);
+
+       const int hastap = (getenv("SIMAVR_UART_TAP") && atoi(getenv("SIMAVR_UART_TAP"))) ||
+                       (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM")));
+       p->hastap = hastap;
+       p->hook_from_uart = NULL;
+       p->hook_to_uart = NULL;
+
+       for (int ti = 0; ti < 1 + hastap; ti++) {
+               int m, s;
+
+               if (openpty(&m, &s, p->port[ti].slavename, NULL, NULL) < 0) {
+                       fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno));
+                       return;
+               }
+               struct termios tio;
+               tcgetattr(m, &tio);
+               cfmakeraw(&tio);
+               tio.c_cflag &= ~(ECHO | ECHONL); // no input echo
+               // tio.c_oflag |= (OPOST | ONLCR); // LF -> CRLF
+               tcsetattr(m, TCSANOW, &tio);
+               p->port[ti].s = m;
+               p->port[ti].tap = ti != 0;
+               // p->port[ti].crlf = ti != 0;
+               p->port[ti].crlf = 1;
+               p->port[ti].hook_from_uart = NULL;
+               p->port[ti].hook_to_uart = NULL;
+               if (ti > 0) {
+                       printf("uart_pty_init %s on port *** %s ***\n",
+                               ti == 0 ? "bridge" : "tap", p->port[ti].slavename);
+               }
+       }
+
+       pthread_create(&p->thread, NULL, uart_pty_thread, p);
+
+}
+
+void
+uart_pty_stop(
+               uart_pty_t * p)
+{
+       puts(__func__);
+       pthread_kill(p->thread, SIGINT);
+       for (int ti = 0; ti < 2; ti++)
+               if (p->port[ti].s)
+                       close(p->port[ti].s);
+       void * ret;
+       pthread_join(p->thread, &ret);
+}
+
+void
+uart_pty_connect(
+               uart_pty_t * p,
+               char uart)
+{
+       // disable the stdio dump, as we are sending binary there
+       uint32_t f = 0;
+       avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f);
+       f &= ~AVR_UART_FLAG_STDIO;
+       avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f);
+
+       avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT);
+       avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT);
+       avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON);
+       avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF);
+
+       if (src && dst) {
+               avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN);
+               avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst);
+       }
+       if (xon)
+               avr_irq_register_notify(xon, uart_pty_xon_hook, p);
+       if (xoff)
+               avr_irq_register_notify(xoff, uart_pty_xoff_hook, p);
+
+       for (int ti = 0; ti < 1+(p->hastap?1:0); ti++) if (p->port[ti].s) {
+               char link[128];
+               snprintf(link, sizeof(link), "/tmp/simavr-uart%c%s", uart, ti == 1 ? "-tap" : "");
+               unlink(link);
+               if (symlink(p->port[ti].slavename, link) != 0) {
+                       fprintf(stderr, "WARN %s: Can't create %s: %s", __func__, link, strerror(errno));
+               } else {
+                       // printf("%s: %s now points to %s\n", __func__, link, p->port[ti].slavename);
+               }
+       }
+       if (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) {
+               char cmd[256];
+               sprintf(cmd, "xterm -e picocom -b 115200 %s >/dev/null 2>&1 &",
+                               p->tap.slavename);
+               system(cmd);
+       } else {
+               // printf("note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal\n");
+       }
+}
diff --git a/examples/simuc/src/simavr/parts/uart_pty.h b/examples/simuc/src/simavr/parts/uart_pty.h
new file mode 100644 (file)
index 0000000..482de7d
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+       uart_pty.h
+
+       Copyright 2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UART_PTY_H___
+#define __UART_PTY_H___
+
+#include <pthread.h>
+#include <simavr/sim_irq.h>
+#include "fifo_declare.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+enum {
+       IRQ_UART_PTY_BYTE_IN = 0,
+       IRQ_UART_PTY_BYTE_OUT,
+       IRQ_UART_PTY_COUNT
+};
+
+DECLARE_FIFO(uint8_t,uart_pty_fifo, 512);
+
+typedef struct uart_pty_port_t {
+       unsigned int    tap : 1, crlf : 1;
+       int             s;                      // socket we chat on
+       char            slavename[64];
+       uart_pty_fifo_t in;
+       uart_pty_fifo_t out;
+       uint8_t         buffer[512];
+       size_t          buffer_len, buffer_done;
+       void (*hook_from_uart)(uint8_t);
+       void (*hook_to_uart)(uint8_t);
+} uart_pty_port_t, *uart_pty_port_p;
+
+typedef struct uart_pty_t {
+       avr_irq_t *     irq;            // irq list
+       struct avr_t *avr;              // keep it around so we can pause it
+
+       pthread_t       thread;
+       int                     xon;
+       int                     hastap;
+
+       union {
+               struct {
+                       uart_pty_port_t         pty;
+                       uart_pty_port_t         tap;
+               };
+               uart_pty_port_t port[2];
+       };
+       void (*hook_from_uart)(uint8_t);
+       void (*hook_to_uart)(uint8_t);
+} uart_pty_t;
+
+void
+uart_pty_init(
+               struct avr_t * avr,
+               uart_pty_t * b);
+void
+uart_pty_stop(uart_pty_t * p);
+
+void
+uart_pty_connect(
+               uart_pty_t * p,
+               char uart);
+
+#ifdef __cplusplus
+}
+#endif 
+
+#endif /* __UART_PTY_H___ */
+
diff --git a/examples/simuc/src/simavr/semaphore.h b/examples/simuc/src/simavr/semaphore.h
new file mode 100644 (file)
index 0000000..289a45d
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef SEMAPHORE_H
+#define SEMAPHORE_H
+
+#include <mutex>
+#include <condition_variable>
+
+class Semaphore {
+    public:
+        Semaphore (int count_ = 0) : count(count_) {
+        }
+        
+        inline void notify () {
+            std::unique_lock<std::mutex> lock(mtx);
+            count++;
+            //notify the waiting thread
+            cv.notify_one();
+        }
+        inline void wait () {
+            std::unique_lock<std::mutex> lock(mtx);
+            while(count == 0) {
+                //wait on the mutex until notify is called
+                cv.wait(lock);
+            }
+            count--;
+        }
+    private:
+        std::mutex mtx;
+        std::condition_variable cv;
+        int count;
+};
+
+#endif // SEMAPHORE_H
diff --git a/examples/simuc/src/simavr/simavr.cpp b/examples/simuc/src/simavr/simavr.cpp
new file mode 100644 (file)
index 0000000..ba63390
--- /dev/null
@@ -0,0 +1,749 @@
+#include "simavr.h"
+#include "../sim/error.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/time.h>
+
+#include <iostream>
+#include <iomanip>
+#include <thread>
+
+#include <simavr/sim_gdb.h>
+#include <simavr/sim_irq.h>
+#include <simavr/sim_avr.h>
+#include <simavr/sim_core.h>
+#include <simavr/avr_uart.h>
+
+
+SimAvr::SimAvr () {
+       memset(&status, 0, sizeof(status));
+}
+
+SimAvr::~SimAvr () {
+       if (firmware != NULL) {
+               free(firmware);
+               firmware = NULL;
+       }
+       if (avr != NULL) {
+               free(avr);
+               avr = NULL;
+       }
+}
+
+void SimAvr::shutdown () {
+       // if (pthread_mutex_lock(&lock)) {
+       //      throw std::logic_error(error(AT, "shutdown() fails caused by mutex error"));
+       // }
+       // cancelThread = true;
+       // isShutdown = true;
+       // pthread_mutex_unlock(&lock);
+       addEvent(EventShutdown);
+}
+
+
+void SimAvr::load (struct StartParameters *params) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "load() fails caused by mutex error"));
+       }
+       try {
+               if (isShutdown) {
+                       throw std::logic_error(error(AT, "cannot load after simavr shutdown"));
+               }
+               if (firmware != NULL) {
+                       throw std::logic_error(error(AT, "firmware already loaded"));
+               }
+
+               startParameters = params;
+               firmware = (elf_firmware_t *) malloc(sizeof(elf_firmware_t));
+               std::string filename = std::string(params->filename);
+               if (firmware == NULL || elf_read_firmware(filename.c_str(), firmware) != 0) {
+                       throw std::logic_error(error(AT, "elf_read_firmware() from %s fails", filename.c_str()));
+               }
+               if (params->mmcu != NULL) {
+                       strncpy(firmware->mmcu, params->mmcu, sizeof(firmware->mmcu));
+               }
+               if (params->frequency > 0) {
+                       firmware->frequency = (uint32_t)params->frequency;
+               }
+               if (params->vcc > 0) {
+                       firmware->vcc = (uint32_t)params->vcc;
+               }
+               if (params->avcc > 0) {
+                       firmware->avcc = (uint32_t)params->avcc;
+               }
+               if (params->aref > 0) {
+                       firmware->aref = (uint32_t)params->aref;
+               }
+
+               // strncpy(firmware->mmcu, "atmega324p", sizeof(firmware->mmcu));
+               // firmware->frequency = 20000000L;
+               if (firmware->frequency == 0) {
+                       firmware->frequency = 8000000L;
+               }
+               if (firmware->vcc == 0) {
+                       firmware->vcc = 5000;
+               }
+               if (firmware->avcc == 0) {
+                       firmware->avcc = 5000;
+               }
+               if (firmware->aref == 0) {
+                       firmware->aref = 2500;
+               }
+               if (firmware->mmcu[0] == 0) {
+                       throw std::logic_error(error(AT, "missing cpu type, use --mmcu ... to set mmcu manually)", firmware->mmcu));
+               }
+
+               avr = avr_make_mcu_by_name(firmware->mmcu);
+               if (!avr) {
+                       throw std::logic_error(error(AT, "avr_make_mcu_by_name() fails (mmcu=%s)", firmware->mmcu));
+               }
+               if (params->gdbPort > 0) {
+                       avr->gdb_port = params->gdbPort;
+               } else {
+                       avr->gdb_port = 1234;
+               }
+               printf("init with port=%d, mmcu=%s, f=%u, vcc=%d, avcc=%d, aref=%d, pc=0x%04x\n",
+                       avr->gdb_port, firmware->mmcu, firmware->frequency, firmware->vcc, firmware->avcc, firmware->aref, params->pc < 0 ? 0 : params->pc);
+               status.freqHz = firmware->frequency;
+
+               if (avr_init(avr) != 0) {
+                       throw std::logic_error(error(AT, "avr_init() fails"));
+               }
+
+               
+               firmware->eeprom = (uint8_t *)malloc(1024);
+               for (int i = 0; i < 1024; i++) {
+                       firmware->eeprom[i] = 0xff;
+               }
+               firmware->eeprom[0] = 0xf0;
+               firmware->eesize = 1024;
+
+               avr_load_firmware(avr, firmware);
+               status.state = StateLoaded;
+
+               avr->fuse[AVR_FUSE_LOW] = 0xe7;
+               avr->fuse[AVR_FUSE_HIGH] = 0xd8;
+               avr->fuse[AVR_FUSE_EXT] = 0xff;
+               avr->lockbits = 0xff;
+
+               avr->gdb_port = 1234;
+               avr_gdb_init(avr);
+               if (params->pc >= 0) {
+                       avr->pc = params->pc;
+               }
+
+               pthread_mutex_unlock(&lock);
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+       addEvent(EventLoad);
+}
+
+// enabled/disable character output on stdout in case of avr uart write
+void SimAvr::setUartDumpEnabled (bool enabled) {
+       if (avr == NULL) {
+               throw std::logic_error(error(AT, "setUartDumpEnabled() fails because no avr available"));
+       }
+       uint32_t f = 0;
+       avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('0'), &f);
+       f = enabled ? f | AVR_UART_FLAG_STDIO : f & ~AVR_UART_FLAG_STDIO;
+       avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('0'), &f);
+       avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('1'), &f);
+       f = enabled ? f | AVR_UART_FLAG_STDIO : f & ~AVR_UART_FLAG_STDIO;
+       avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('1'), &f);
+}
+
+void SimAvr::registerIoWrite (avr_io_addr_t addr, avrsim_io_write_callback_t callback) {
+       avr_register_io_write(avr, addr, (avr_io_write_t)callback, this);
+} 
+
+void SimAvr::setUartPtyEnabled (int uart, bool enable) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+       uart_pty_t *p = NULL;
+    try {
+               if (uart < 0 || uart > 1) {
+                       throw std::logic_error(error(AT, "setUartPtyEnabled() fails (invalid uart %d)", uart));
+               }
+               if (enable && uartPty[uart] != NULL) {
+                       throw std::logic_error(error(AT, "enableUartPty() fails (uart %d already enabled)", uart));
+               }
+               if (!enable && uartPty[uart] == NULL) {
+                       throw std::logic_error(error(AT, "enableUartPty() fails (uart %d not enabled)", uart));
+               }
+               if (avr == NULL) {
+                       throw std::logic_error(error(AT, "enableUartPty() fails (avr not ready)"));
+               }
+               if (enable) {
+                       p = (uart_pty_t *)malloc(sizeof(uart_pty_t));
+                       if (p == NULL) {
+                               throw std::logic_error(error(AT, "enableUartPty() fails (malloc fails)"));
+                       }
+                       memset(p, 0, sizeof(uart_pty_t));
+                       // setenv("SIMAVR_UART_TAP", "1", 1);
+                       uart_pty_init(avr, p);
+                       uart_pty_connect(p, uart + '0');
+                       uartPty[uart] = p;
+               } else {
+                       uart_pty_stop(uartPty[uart]);
+                       free(uartPty[uart]);
+                       uartPty[uart] = NULL;
+               }
+               pthread_mutex_unlock(&lock);
+
+       } catch (std::exception& e) {
+               if (p != NULL) {
+                       free(p);
+               }
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+void SimAvr::setUartPtyHook (int uart, void (*fromUart)(uint8_t), void (*toUart)(uint8_t)) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "setUartPtyHook() fails caused by mutex error"));
+       }
+    try {
+               if (uart < 0 || uart > 1) {
+                       throw std::logic_error(error(AT, "setUartPtyHook() fails (invalid uart %d)", uart));
+               }
+               if (uartPty[uart] == NULL) {
+                       throw std::logic_error(error(AT, "setUartPtyHook() fails (uart %d not enabled)", uart));
+               }
+               uartPty[uart]->hook_from_uart = fromUart;
+               uartPty[uart]->hook_to_uart = toUart;
+               pthread_mutex_unlock(&lock);
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+const char *SimAvr::getUartPtyDeviceName (int uart) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+    try {
+               if (uart < 0 || uart > 1) {
+                       throw std::logic_error(error(AT, "getUartPtyDeviceName() fails (invalid uart %d)", uart));
+               }
+               uart_pty_t *p = uartPty[uart];
+               pthread_mutex_unlock(&lock);
+               return p == NULL ? NULL : p->port[0].slavename;
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+const char *SimAvr::getTargetDeviceName () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+    try {
+               if (firmware == NULL) {
+                       throw std::logic_error(error(AT, "getTargetDeviceName() fails (no firmware loaded)"));
+               }
+               pthread_mutex_unlock(&lock);
+               return firmware->mmcu;
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+uint32_t SimAvr::getTargetFrequency () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+    try {
+               if (firmware == NULL) {
+                       throw std::logic_error(error(AT, "getTargetDeviceName() fails (no firmware loaded)"));
+               }
+               pthread_mutex_unlock(&lock);
+               return firmware->frequency;
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+void SimAvr::start () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "start() fails caused by mutex error"));
+       }
+       try {
+               if (isShutdown) {
+                       throw std::logic_error("start() not allowed after shutdown");
+               }
+               if (avrRunThread != NULL) {
+                       throw std::logic_error("simavr already started");
+               }
+               if (avr == NULL) {
+                       throw std::logic_error("avr not ready (check if load done)");
+               }
+               syncTime[0] = !syncTime[1];
+               avrRunThread = new std::thread(&SimAvr::avrRun, this);
+               pthread_mutex_unlock(&lock);
+       
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+       addEvent(EventStart);
+}
+
+
+void SimAvr::stop () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "stop() fails caused by mutex error"));
+       }
+       try {
+               if (avrRunThread == NULL) {
+                       throw std::logic_error("simavr not started");
+               }
+               cancelThread = true;
+               pthread_mutex_unlock(&lock);
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+       addEvent(EventStop);
+}
+
+void SimAvr::setCommand (EnumSimAvrCommand cmd, void *param) {
+       if (command != ReadyForNewCommand) {
+               throw std::logic_error("another command pending");
+       }
+       command = cmd;
+}
+
+void SimAvr::addEvent (int event) {
+       struct timeval tp;
+       gettimeofday(&tp, NULL);
+       SimAvrEvent *p = (SimAvrEvent *)malloc(sizeof(SimAvrEvent));
+       if (p != NULL) {
+               p->epochMillis = tp.tv_sec * 1000 + (long)tp.tv_usec / 1000;
+               p->event = event;
+               if (pthread_mutex_lock(&lock)) {
+                       throw std::logic_error(error(AT, "addEvent() fails caused by mutex error"));
+               }
+               try {
+                       p->cycle = avr->cycle;
+                       if (!isShutdown) {
+                               events.push_back(p);
+                               eventCount.notify();
+                       }
+                       pthread_mutex_unlock(&lock);
+               } catch (std::exception& e) {
+                       status.state = StateError;
+                       pthread_mutex_unlock(&lock);
+                       throw;
+               }
+       }
+}
+
+
+void SimAvr::setTimeSync (bool sync) {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "setTimeSync() fails caused by mutex error"));
+       }
+       try {
+               syncTime[1] = sync;
+               pthread_mutex_unlock(&lock);
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+
+struct SimAvrEvent SimAvr::waitForEvent () {
+    eventCount.wait();
+
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "waitForEvent() fails caused by mutex error"));
+       }
+       try {
+               struct SimAvrEvent rv = { epochMillis: 0, event: EventUnknown };
+               struct SimAvrEvent *p = NULL;
+               if (events.size() > 0) {
+                       p = events.front();
+                       rv = *p;
+                       if (p->event != EventShutdown) {
+                               events.pop_front();
+                               free(p);
+                       }
+                       if (p->event == EventShutdown) {
+                               cancelThread = true;
+                               isShutdown = true;
+                       }
+               }
+               pthread_mutex_unlock(&lock);
+               return rv;
+
+       } catch (std::exception& e) {
+               status.state = StateError;
+               pthread_mutex_unlock(&lock);
+               throw;
+       }
+}
+
+const char *SimAvr::eventText (EnumSimAvrEvent event) {
+       switch (event) {
+               case EventUnknown:  return "Unknown";
+               case EventShutdown: return "Shutdown";
+               case EventLoad:     return "Load";
+               case EventStart:    return "Start";
+               case EventStop:     return "Stop";
+               default: return NULL;
+       }
+}
+
+struct SimAvrStatus SimAvr::getStatus () {
+       if (pthread_mutex_lock(&lock)) {
+               throw std::logic_error(error(AT, "getStatus() fails caused by mutex error"));
+       }
+       struct SimAvrStatus rv = status;
+       pthread_mutex_unlock(&lock);
+       return rv;
+}
+
+void SimAvr::printCyclesAndElapsedTime () {
+       uint64_t time = avr->cycle * 1000000000L / avr->frequency;
+       uint16_t nanos = time % 1000; time = time / 1000;
+       uint16_t micros = time % 1000; time = time / 1000;
+       uint16_t millis = time % 1000; time = time / 1000;
+       uint16_t seconds = time % 1000; time = time / 1000;
+       printf("cycle %" PRIu64 " = %ds %03dms %03dµs %03dns", avr->cycle, seconds, millis, micros, nanos);
+}
+
+void SimAvr::avrRun () {
+       long cnt = 0;
+       int lastAvrState = -1;
+       _avr_sp_set(avr, 0);
+       while (1) {
+               try {
+                       if (avr->state != lastAvrState) {
+                               switch (avr->state) {
+                                       case cpu_Stopped: {
+                                               printf("\ncpu stopped at 0x%04x on ", avr->pc);
+                                               printCyclesAndElapsedTime();
+                                               char sfr[9];
+                                               sfr[7] = avr->sreg[0] ? 'C' : '-';
+                                               sfr[6] = avr->sreg[1] ? 'Z' : '-';
+                                               sfr[5] = avr->sreg[2] ? 'N' : '-';
+                                               sfr[4] = avr->sreg[3] ? 'V' : '-';
+                                               sfr[3] = avr->sreg[4] ? 'S' : '-';
+                                               sfr[2] = avr->sreg[5] ? 'H' : '-';
+                                               sfr[1] = avr->sreg[6] ? 'T' : '-';
+                                               sfr[0] = avr->sreg[7] ? 'I' : '-';
+                                               sfr[8] = 0;
+                                               printf("  SFR: %s\n  ", sfr);
+                                               uint16_t x = 0, y = 0, z = 0;
+                                               for (int i = 0; i < 32; i++) {
+                                                       uint8_t b = avr->data[i];
+                                                       printf(" r%02d=%02x", i, b);
+                                                       switch (i) {
+                                                               case 0x0f: printf("\n  "); break;
+                                                               case 0x1a: x = (x & 0xff00) | b; break;
+                                                               case 0x1b: x = (x & 0x00ff) | (b << 8); break;
+                                                               case 0x1c: y = (y & 0xff00) | b; break;
+                                                               case 0x1d: y = (y & 0x00ff) | (b << 8); break;
+                                                               case 0x1e: z = (z & 0xff00) | b; break;
+                                                               case 0x1f: z = (z & 0x00ff) | (b << 8); break;
+                                                       }
+                                               }
+                                               printf("\n   X=0x%04x  Y=0x%04x  Z=0x%04x\n", x, y, z);
+
+                                               uint16_t sp = _avr_sp_get(avr);
+                                               printf("   Stack (SP=0x%04x)", sp);
+                                               int printHeader = 1;
+                                               for (int i = 0; i < 16; i++) {
+                                                       uint16_t addr = sp + 1 + i;
+                                                       if (addr <= avr->ioend) { continue; }
+                                                       if (addr > avr->ramend) { break; }
+                                                       if (printHeader) {
+                                                               printf(" -> SRAM 0x%04x:", addr);
+                                                               printHeader = 0;
+                                                       }
+                                                       uint8_t b = avr_core_watch_read(avr, addr);
+                                                       printf(" %02x", b);
+
+                                               }
+                                               printf(printHeader ? "\n" : "\n");
+                                               
+                                               printf("   Arduino LED L: ");
+                                               printf((avr->data[0x24] & 0x20) && (avr->data[0x25] & 0x20) ? "ON\n" : "OFF\n");
+
+                                               printf("\n");
+                                               break;
+                                       }
+                                       case cpu_Sleeping: printf("cpu enter sleep mode at cycle %" PRIu64 "\n", avr->cycle); break;
+                                       case cpu_Running: printf("cpu starts running at cycle %" PRIu64 "\n", avr->cycle); break;
+                                       case cpu_Step: printf("cpu step\n"); break;
+                                       case cpu_StepDone: printf("cpu step done\n"); break;
+                                       case cpu_Done: printf("cpu done\n"); break;
+                                       case cpu_Crashed: printf("cpu crashed\n"); break;
+                                       default: printf("cpu enter unknown mode at cycle %" PRIu64 "\n", avr->cycle); break;
+                               }
+                               lastAvrState = avr->state;
+                       }
+
+                       if (startParameters != NULL) {
+                               switch (startParameters->board) {
+                                       case BoardNano: {
+                                               static int8_t ledL = -1; // pin floating
+                                               uint8_t ddrb = avr->data[0x24];
+                                               uint8_t portb = avr->data[0x25];
+                                               int8_t nextLedL = -1;
+                                               if (ddrb & 0x20) {
+                                                       nextLedL = portb & 0x20 ? 1 : 0;
+                                               }
+                                               if (nextLedL != ledL) {
+                                                       ledL = nextLedL;
+                                                       printCyclesAndElapsedTime();
+                                                       printf(": LED L = %c\n", ledL ? 'X' : '.');
+                                               }
+                                               break;
+                                       }
+                                       case BoardSure: {
+                                               static int8_t led[4] = { -1, -1, -1, -1 }; // pin floating
+                                               uint8_t ddra = avr->data[0x3a];
+                                               uint8_t porta = avr->data[0x3b];
+                                               int change = 0;
+                                               for (int i = 0; i < 4; i++) {
+                                                       int8_t nextLed = -1;
+                                                       if (ddra & (1 << i)) {
+                                                               nextLed = porta & (1 << i) ? 0 : 1;
+                                                       }
+                                                       if (nextLed != led[i]) {
+                                                               change = 1;
+                                                               led[i] = nextLed;
+                                                       }
+                                               }
+                                               if (change) {
+                                                       printCyclesAndElapsedTime(); printf(": ");
+                                                       printf(" LED PA[3210] =");
+                                                       for (int i = 3; i >= 0; i--) {
+                                                                       printf(" %c", led[i] ? 'X' : '.');
+                                                       }
+                                                       printf("\n");
+                                               }
+                                               break;
+                                       }
+                                       case BoardEWS1: {
+                                               static int8_t led = -1; // pin floating
+                                               uint8_t ddrb = avr->data[0x24];
+                                               uint8_t portb = avr->data[0x25];
+                                               int8_t nextLed = -1;
+                                               if (ddrb & 0x01) {
+                                                       nextLed = portb & 0x01 ? 1 : 0;
+                                               }
+                                               if (nextLed != led) {
+                                                       led = nextLed;
+                                                       printCyclesAndElapsedTime();
+                                                       printf(": LED1 (PB0) = %c\n", led ? 'X' : '.');
+                                               }
+                                               break;
+                                       }
+                                       default: break;
+                               }
+                       }
+
+                       if (cnt <= 0) {
+                               // usleep(10000);
+                               if (pthread_mutex_lock(&lock)) {
+                                       throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (1)"));
+                               }
+                               try {
+                                       status.cycle = avr->cycle;
+                                       status.state = StateRunning;
+                                       status.running = true;
+                                       switch (avr->state) {
+                                               case cpu_Done:    status.state = StateDone; break;
+                                               case cpu_Running: status.state = StateRunning; break;
+                                               case cpu_Crashed: status.state = StateCrashed; break;
+                                               default: status.state = StateOthers; break;
+                                       }
+                                       if (cancelThread) {
+                                               cnt = -1;
+                                               cancelThread = false;
+                                       } else {
+                                               if (syncTime[0] != syncTime[1]) {
+                                                       syncTime[0] = syncTime[1];
+                                                       if (syncTime[0]) {
+                                                               cyclesOnSyncStart = avr->cycle;
+                                                               struct timeval tp;
+                                                               gettimeofday(&tp, NULL);
+                                                               timeMicrosOnSyncStart = tp.tv_usec + (uint64_t)tp.tv_sec * 1000000L;
+                                                       } else {
+                                                               cyclesOnSyncStart = -1;
+                                                               timeMicrosOnSyncStart = 0;
+                                                       }
+                                               }
+                                               if (syncTime[0]) {
+                                                       uint64_t ucMicros = (uint64_t)(status.cycle - cyclesOnSyncStart) * 1000000L / status.freqHz ;
+                                                       struct timeval tp;
+                                                       gettimeofday(&tp, NULL);
+                                                       const uint64_t timeMicros = tp.tv_usec + (uint64_t)tp.tv_sec * 1000000L - timeMicrosOnSyncStart;
+
+                                                       if (ucMicros > 3000000) {
+                                                               ucMicros++;
+                                                       }
+                                                       
+                                                       int64_t dt = ucMicros - timeMicros;
+                                                       if (dt > 0) {
+                                                               int us = dt > INT_MAX ? INT_MAX : (int)dt;
+                                                               if (us > 1000) {
+                                                                       timeval ts;
+                                                                       ts.tv_sec = 0;
+                                                                       ts.tv_usec = us;
+                                                                       select(0, 0, 0, 0, &ts);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       if (avr->state == cpu_Done) {
+                                               cnt = -1;
+                                       } else if (avr->state == cpu_Crashed) {
+                                               throw std::logic_error(error(AT, "avr state is cpu_Crashed"));
+                                       }
+                                       pthread_mutex_unlock(&lock);
+
+                               } catch (std::exception& e) {
+                                       status.state = StateError;
+                                       pthread_mutex_unlock(&lock);
+                                       throw;
+                               }
+                       }
+
+                       if (avr->state == cpu_Stopped) {
+                               timeval ts;
+                               ts.tv_sec = 0;
+                               ts.tv_usec = 1000;
+                               select(0, 0, 0, 0, &ts);
+                       }
+
+                       if (cnt < 0) {
+                               cyclesOnSyncStart = -1;
+                               timeMicrosOnSyncStart = 0;
+                               if (pthread_mutex_lock(&lock)) {
+                                       throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (2)"));
+                               }
+                               try {
+                                       if (avrRunThread != NULL) {
+                                               avrRunThread->detach();
+                                               delete avrRunThread;
+                                               avrRunThread = NULL;
+                                       }
+                                       pthread_mutex_unlock(&lock);
+                                       return;
+                               } catch (std::exception& e) {
+                                       pthread_mutex_unlock(&lock);
+                                       status.state = StateError;
+                                       throw;
+                               }
+                       
+                       } else if (++cnt >= 100000) {
+                               cnt = 0;
+                       }
+
+                       if (command != ReadyForNewCommand) {
+                               switch (command) {
+                                       case CommandStatus: {
+                                               printCyclesAndElapsedTime();
+                                               printf("\n");
+                                               break;
+                                       }
+                                       case CommandInterrupt: {
+                                               if (avr->state == cpu_Running) {
+                                                       avr->state = cpu_Stopped;
+                                               }
+                                               break;
+                                       }
+                                       case CommandContinue: {
+                                               if (avr->state == cpu_Stopped) {
+                                                       avr->state = cpu_Running;
+                                               }
+                                               break;
+                                       }
+                                       case CommandStack: {
+                                               uint16_t sp = _avr_sp_get(avr);
+                                               printf("Stack: SP=0x%04x:\n", sp);
+                                               for (uint16_t addr = ((sp + 1) / 16) * 16; addr <= avr->ramend; addr++) {
+                                                       if (addr % 16 == 0) {
+                                                               printf("  0x%04x:", addr);
+                                                       }
+                                                       if (addr % 4 == 0) {
+                                                               printf(" ");
+                                                       }
+                                                       if (addr <= sp) {
+                                                               printf("   ");
+                                                       } else {
+                                                               uint8_t b = avr_core_watch_read(avr, addr);
+                                                               printf(" %02x", b);
+                                                       }
+                                                       if (addr % 16 == 15) {
+                                                               printf("\n");
+                                                       }
+                                               }
+                                               printf("\n");
+                                               break;
+                                       }
+
+                                       default: break;
+                               }
+                               command = ReadyForNewCommand;
+                               addEvent(EventCommandExecuted);
+                       }
+
+                       avr_run(avr);
+
+               } catch (std::exception& e) {
+                       status.state = StateError;
+                       cyclesOnSyncStart = -1;
+                       timeMicrosOnSyncStart = 0;
+                       if (pthread_mutex_lock(&lock)) {
+                               throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (2)"));
+                       }
+                       try {
+                               if (avrRunThread != NULL) {
+                                       avrRunThread->detach();
+                                       delete avrRunThread;
+                                       avrRunThread = NULL;
+                               }
+                               pthread_mutex_unlock(&lock);
+                               throw;
+                       } catch (std::exception& e) {
+                               pthread_mutex_unlock(&lock);
+                               status.state = StateError;
+                               throw;
+                       }
+
+               }
+       }
+}
+
diff --git a/examples/simuc/src/simavr/simavr.h b/examples/simuc/src/simavr/simavr.h
new file mode 100644 (file)
index 0000000..e1455b7
--- /dev/null
@@ -0,0 +1,132 @@
+#ifndef SIMAVR_H
+#define SIMAVR_H
+
+#include "semaphore.h"
+#include "parts/uart_pty.h"
+
+#include <simavr/sim_avr.h>
+#include <simavr/sim_elf.h>
+#include <simavr/sim_io.h>
+
+#include <string>
+#include <thread>
+#include <list>
+
+typedef enum {
+       BoardUnknown = 0,
+       BoardNano,
+       BoardSure,
+       BoardEWS1
+} EnumStartParameterBoard;
+
+
+struct StartParameters {
+    char *filename;
+    int gdbPort;
+    int64_t frequency;
+    const char *mmcu;
+       EnumStartParameterBoard board;
+    int32_t pc;
+    int32_t vcc;
+    int32_t avcc;
+    int32_t aref;
+};
+
+enum SimAvrState {
+       StateInit = 0,
+       StateError = 1,
+       StateLoaded = 10,
+       StateRunning = 11,
+       StateDone = 12,
+       StateCrashed = 20,
+       StateOthers = 99,
+       StateUnknown = 100
+};
+
+typedef enum {
+       EventUnknown = 0,
+       EventShutdown = -1,
+       EventLoad = -2,
+       EventStart = -3,
+       EventStop = -4,
+       EventCommandExecuted = -5
+} EnumSimAvrEvent;
+
+typedef enum {
+       ReadyForNewCommand = 0,
+       CommandStatus,
+       CommandInterrupt,
+       CommandContinue,
+       CommandStack
+} EnumSimAvrCommand;
+
+struct SimAvrStatus {
+       long long freqHz;
+       long long cycle;
+       enum SimAvrState state;
+       bool running;
+};
+
+struct SimAvrEvent {
+       long long epochMillis;
+       long long cycle;
+       int event;
+};
+
+
+class SimAvr;
+typedef void (*avrsim_io_write_callback_t)(avr_t *avr, avr_io_addr_t addr, uint8_t value, SimAvr *simavr);
+
+class SimAvr {
+
+public:
+       SimAvr ();
+       ~SimAvr ();
+
+       struct SimAvrStatus getStatus ();
+       struct SimAvrEvent waitForEvent ();
+       const char *eventText (EnumSimAvrEvent event);
+
+public:    
+       void load (struct StartParameters *params);
+       void shutdown ();
+       void start ();
+       void stop ();
+       void addEvent (int event);
+       void setUartDumpEnabled (bool enabled);
+       void setUartPtyEnabled (int uart, bool enabled);
+       void setUartPtyHook (int uart, void (*fromUart)(uint8_t), void (*toUart)(uint8_t));
+    void registerIoWrite (avr_io_addr_t addr, avrsim_io_write_callback_t callback);
+
+       const char *getTargetDeviceName ();
+       uint32_t getTargetFrequency ();
+       const char *getUartPtyDeviceName (int uart);
+
+       void avrRun ();
+       void setLed (bool on);
+       void setTimeSync (bool sync);
+       void setCommand (EnumSimAvrCommand cmd, void *param);
+
+private:
+       elf_firmware_t *firmware = NULL;
+       avr_t *avr = NULL;;
+       StartParameters *startParameters = NULL;;
+       pthread_mutex_t lock;
+       std::list<struct SimAvrEvent *> events;
+       EnumSimAvrCommand command;
+       struct SimAvrStatus status;
+       bool cancelThread = false;
+       bool isShutdown = false;
+       bool syncTime[2] = { false, true };
+       std::thread *avrRunThread = NULL;
+       long cyclesOnSyncStart = -1;
+       uint64_t timeMicrosOnSyncStart = 0;
+       Semaphore eventCount;
+       uart_pty_t *uartPty[2] = { NULL, NULL };
+       void printCyclesAndElapsedTime ();
+};
+
+
+
+
+#endif // SIMAVR_H
\ No newline at end of file