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`
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
--- /dev/null
+dist/**
+build/**
+dpkg/**/*.deb
+**/*.vcd
--- /dev/null
+{
+ // 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"
+ }
+ ]
+}
--- /dev/null
+{
+ "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
--- /dev/null
+{
+ // 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"
+ ]
+ }
+ ]
+}
--- /dev/null
+$(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
--- /dev/null
+# 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`
--- /dev/null
+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
--- /dev/null
+../share/htl-simuc/simuc/simuc
\ No newline at end of file
--- /dev/null
+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".
+
--- /dev/null
+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
--- /dev/null
+../doc/htl-simuc/readme
\ No newline at end of file
--- /dev/null
+/*
+ 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
--- /dev/null
+/*\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
--- /dev/null
+/*\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
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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___ */
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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);
+}
+
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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);
+}
+
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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___ */
--- /dev/null
+/*
+ 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);
+ }
+}
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/*
+ 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;
+ }
+}
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/*
+ 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);
+}
+
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/* 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);
+}
+
--- /dev/null
+/* 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__*/
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+/*
+ 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___ */
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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);
+ }
+}
+
--- /dev/null
+/*
+ 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__*/
+
--- /dev/null
+/*
+ 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___ */
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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___ */
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/*
+ 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", ®i);
+ 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", ®i);
+ 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();
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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___ */
--- /dev/null
+/*
+ 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);
+ }
+}
+
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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__*/
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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___ */
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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;
+}
+
+
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+#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", ¶ms.gdbPort);
+ } else if (strcmp(argv[i], "--frequency") == 0) {
+ sscanf(argv[++i], "%" PRIu64, ¶ms.frequency);
+ } else if (strcmp(argv[i], "--pc") == 0) {
+ i++;
+ if (argv[i][0] == '0' && argv[i][1] == 'x') {
+ sscanf(&argv[i][2], "%x", ¶ms.pc);
+ } else {
+ sscanf(argv[++i], "%d", ¶ms.pc);
+ }
+ } else if (strcmp(argv[i], "--vcc") == 0) {
+ sscanf(argv[++i], "%d", ¶ms.vcc);
+ } else if (strcmp(argv[i], "--avcc") == 0) {
+ sscanf(argv[++i], "%d", ¶ms.avcc);
+ } else if (strcmp(argv[i], "--aref") == 0) {
+ sscanf(argv[++i], "%d", ¶ms.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(¶ms);
+ 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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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 "?";
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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");
+ }
+}
--- /dev/null
+/*
+ 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___ */
+
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+
+ }
+ }
+}
+
--- /dev/null
+#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
--- /dev/null
+#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", ¶ms.gdbPort);
+ } else if (strcmp(argv[i], "--frequency") == 0) {
+ sscanf(argv[++i], "%" PRIu64, ¶ms.frequency);
+ } else if (strcmp(argv[i], "--pc") == 0) {
+ i++;
+ if (argv[i][0] == '0' && argv[i][1] == 'x') {
+ sscanf(&argv[i][2], "%x", ¶ms.pc);
+ } else {
+ sscanf(argv[++i], "%d", ¶ms.pc);
+ }
+ } else if (strcmp(argv[i], "--vcc") == 0) {
+ sscanf(argv[++i], "%d", ¶ms.vcc);
+ } else if (strcmp(argv[i], "--avcc") == 0) {
+ sscanf(argv[++i], "%d", ¶ms.avcc);
+ } else if (strcmp(argv[i], "--aref") == 0) {
+ sscanf(argv[++i], "%d", ¶ms.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(¶ms);
+ 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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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 "?";
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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");
+ }
+}
--- /dev/null
+/*
+ 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___ */
+
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+
+ }
+ }
+}
+
--- /dev/null
+#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