From: Manfred Steiner Date: Sun, 27 Nov 2022 15:24:15 +0000 (+0100) Subject: simuc option board, debian packet creation X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=46b57676df609623515f691b0af46192ecc74615;p=sx%2Fsimavr.git simuc option board, debian packet creation --- diff --git a/README.md b/README.md index 606e9f9..450d4db 100644 --- a/README.md +++ b/README.md @@ -2,29 +2,46 @@ This repository is mirrored from [https://github.com/buserror/simavr.git](https://github.com/buserror/simavr.git), commit 7003af0 (Mon Jul 18 10:29:17 2022) -Original `README.md`: see [README-simavr.md](README-simavr.md) +* **simuc**: see [examples/](examples/simuc/README.md) +* Original `README.md`: see [README-simavr.md](README-simavr.md) -## build +------------------------------------------ + +## Build simavr library on Debian- or Ubuntu-systems ### Prepare system ```sh $ sudo apt install gcc make gcc-avr avr-libc $ sudo apt install libelf-dev $ sudo apt install freeglut3 freeglut3-dev +$ sudo apt install libncurses3 ``` -### make simavr libs +### Clone repository + +```sh +user@host:~ $ git clone https://git.htl-mechatronik.at/public/sx/simavr.git +user@host:~ $ cd simavr +user@host:~/simavr $ +``` -Make sure your shell working directory is simavr +### Make libaries and example executables ```sh -$ cd simavr -$ make +user@host:~/simavr $ make ``` Result is available in: -* static lib: `obj-x86_64-linux-gnu/libsimavr.a` -* shared lib: `obj-x86_64-linux-gnu/libsimavr.so` + +* static lib: `obj-.../libsimavr.a` +* shared lib: `obj-.../libsimavr.so` + +The ... is a placeholder for the *architecture*, which is the result of `gcc -dumpmachine` and depends on system architecture: +* i386: `obj-i686-linux-gnu` +* amd64: `obj-x86_64-linux-gnu` +* arm64: `obj-aarch64-linux-gnu` +* ... + Project examples available in folder `examples` diff --git a/examples/Makefile b/examples/Makefile index 65cc893..7fe92ee 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -9,9 +9,11 @@ endif all: for bi in ${boards}; do $(MAKE) -C $$bi; done + $(MAKE) -C simuc clean: for bi in ${boards}; do $(MAKE) -C $$bi clean; done + $(MAKE) -C simuc clean # # The USB example is not made by default, as it downloads stuff diff --git a/examples/simuc/.gitignore b/examples/simuc/.gitignore new file mode 100644 index 0000000..d1ad022 --- /dev/null +++ b/examples/simuc/.gitignore @@ -0,0 +1,4 @@ +dist/** +build/** +dpkg/**/*.deb +**/*.vcd diff --git a/examples/simuc/.vscode/launch.json b/examples/simuc/.vscode/launch.json new file mode 100644 index 0000000..64e527c --- /dev/null +++ b/examples/simuc/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/dist/simuc", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "preLaunchTask": "build" + } + ] +} diff --git a/examples/simuc/.vscode/settings.json b/examples/simuc/.vscode/settings.json new file mode 100644 index 0000000..189a435 --- /dev/null +++ b/examples/simuc/.vscode/settings.json @@ -0,0 +1,87 @@ +{ + "files.associations": { + "cstdio": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdlib": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "semaphore": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "variant": "cpp" + }, + "cSpell.words": [ + "aref", + "atmega", + "avcc", + "avrsim", + "ddrb", + "eeprom", + "eesize", + "evws", + "ioend", + "lfcrlf", + "lockbits", + "megaavr", + "millis", + "mmcu", + "picocom", + "PORTB", + "ramend", + "simavr", + "simuc", + "slavename", + "SRAM", + "sreg" + ] +} \ No newline at end of file diff --git a/examples/simuc/.vscode/tasks.json b/examples/simuc/.vscode/tasks.json new file mode 100644 index 0000000..4e8bd30 --- /dev/null +++ b/examples/simuc/.vscode/tasks.json @@ -0,0 +1,26 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "make", + "args": [], + "problemMatcher": [ "$gcc" ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "clean", + "type": "shell", + "command": "make", + "args": [ + "clean" + ] + } + ] +} diff --git a/examples/simuc/Makefile b/examples/simuc/Makefile new file mode 100644 index 0000000..456d1d5 --- /dev/null +++ b/examples/simuc/Makefile @@ -0,0 +1,111 @@ +$(shell mkdir -p dist >/dev/null) +$(shell mkdir -p build >/dev/null) + +PRJ="simuc" +$(PRJ): dist/simuc + +start2: $(PRJ) + LD_LIBRARY_PATH=$(pwd);dist/simuc + +start: $(PRJ) + dist/simuc + +CFLAGS = -Os -Wall +#X_CFLAGS = -O0 -g -Wall +X_CFLAGS = -Os -g -Wall + + +dist/simuc: build/main.o build/sim.o build/simavr.o build/error.o build/uart_pty.o + #g++ -o $@ -g -Wall -gdwarf-2 $^ -Wl,-rpath,$(CURDIR) -L$(CURDIR) -lsimavr -lpthread -lutil + g++ -o $@ -g -Wall -gdwarf-2 $^ ../../simavr/obj-$(shell gcc -dumpmachine)/libsimavr.a -lelf -lpthread -lutil + + +#dist/libsim.so: build/sim.o build/simavr.o build/error.o build/uart_pty.o +# gcc -o $@ $(CFLAGS) -shared $^ + +build/sim.o: src/sim/sim.cpp src/sim/sim.h src/sim/error.h src/simavr/simavr.h + gcc -o $@ $(CFLAGS) -fPIC -c $< + +build/simavr.o: src/simavr/simavr.cpp src/simavr/simavr.h src/sim/error.h + gcc -o $@ $(CFLAGS) -fPIC -c $< + +build/error.o: src/sim/error.cpp src/sim/error.h + gcc -o $@ $(CFLAGS) -fPIC -c $< + +build/uart_pty.o: src/simavr/parts/uart_pty.c src/simavr/parts/uart_pty.h src/simavr/parts/fifo_declare.h + gcc -o $@ $(CFLAGS) -Wno-unused-result -fPIC -c $< + +build/main.o: src/main.cpp src/sim/sim.h src/simavr/simavr.h + g++ -o $@ $(X_CFLAGS) -c $< + + +# ############################################ +# Debian Packet for htl-simuc_version_arch.deb +# ############################################ + +ARCH := $(shell arch) +ifeq ($(ARCH), x86_64) + DEBARCH="amd64" + OBJARCH="" +endif +ifeq ($(ARCH), x86_32) + DEBARCH="i386" +endif +ifeq ($(ARCH), aarch64) + DEBARCH="arm64" +endif + +DEBVERSION := 0.0.1~1 +DEBNAME := dpkg/htl-simuc_version_arch +DEBSRC := dpkg/htl-simuc_version_arch + +DEB := $(DEBNAME:%version=%$(DEBVERSION),%arch=%$(DEBARCH).deb) + +deb: dpkg $(DEBSRC)/DEBIAN/control $(DEBSRC)/usr/share/doc/htl-simuc/readme + @dpkg-deb --root-owner-group --build $(DEBSRC) ${DEB} + +.PHONY: $(DEBSRC)/DEBIAN/control $(DEBSRC)/usr/share/doc/htl-simuc/readme + +$(DEBSRC)/DEBIAN/control: + echo "Package: htl-simuc" > $@ + echo "Version:" ${DEBVERSION} >> $@ + @echo "Section: devel" >> $@ + echo "Architecture:" $(DEBARCH) >> $@ + @echo "Depends: libelf1" >> $@ + @echo "Recommends: gcc-avr, avr-libc, gdb-avr, binutils-avr, avrdude" >> $@ + echo "Installed-Size:" $(shell du -s $(DEBSRC) | cut -f1) >> $@ + @echo "Priority: optional" >> $@ + @echo "Maintainer: Manfred Steiner " >> $@ + @echo "Description: megaavr microcontroller simulation" >> $@ + @chmod 644 $@ + +$(DEBSRC)/usr/share/doc/htl-simuc/readme: + @echo "created: $(shell date)" > $@ + @echo "https://git.htl-mechatronik.at/public/?p=sx/simavr.git;a=home" >> $@ + @echo >> $@ + @echo "$ git clone https://git.htl-mechatronik.at/public/sx/simavr.git" >> $@ + @echo "$ git checkout $(shell git rev-parse HEAD)" >> $@ + @chmod 644 $@ + +dpkg: $(PRJ) + @test -d $(DEBSRC)/usr/share/htl-simuc || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc + @test -d $(DEBSRC)/usr/share/htl-simuc/simuc || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc/simuc + rsync -a --delete dist/simuc $(DEBSRC)/usr/share/htl-simuc/simuc + @test -d $(DEBSRC)/usr/share/htl-simuc/simuc/src || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc/simuc/src + rsync -a --delete src/ $(DEBSRC)/usr/share/htl-simuc/simuc/src/ + @test -d $(DEBSRC)/usr/share/htl-simuc/simavr || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc/simavr + @test -d $(DEBSRC)/usr/share/htl-simuc/simavr/sim || mkdir -m 755 $(DEBSRC)/usr/share/htl-simuc/simavr/sim + rsync -a --delete ../../simavr/sim/ $(DEBSRC)/usr/share/htl-simuc/simavr/sim/ + @find $(DEBSRC) -type d -exec chmod 755 {} \; + @find $(DEBSRC)/usr/share/htl-simuc/ -type f -exec chmod 644 {} \; + @chmod 755 $(DEBSRC)/usr/share/htl-simuc/simuc/simuc + @touch $(DEBSRC) + +# ##################################################################### + +clean: + -@rm -r dist + -@rm -r build + -@find dpkg -name "*.deb" -delete + -@rm -rf $(DEBSRC)/usr/share/htl-simuc + -@rm -f $(DEBSRC)/DEBIAN/control diff --git a/examples/simuc/README.md b/examples/simuc/README.md new file mode 100644 index 0000000..5dee7f8 --- /dev/null +++ b/examples/simuc/README.md @@ -0,0 +1,64 @@ +# simuc + +**simuc** is used to simulate a megaAvr microcontroller. + +If the cpu is stopped by command or `avr-gdb`, some information is printed on standard output: +* Number of cycles and elapsed time +* Status Flags +* General Purpose registers r0..r31 +* address registers X, Y and Z +* Stack pointer SP and stack content + +Commands available via standard input (keyboard): +* *Enter* print current cycles and elapsed time +* `interrupt`: stop cpu +* `continue`: continue cpu run +* `stack`: print complete stack content on standard output + +Microcontrollers UART interface(s) is/are connected to local devices and can be used with terminal programs (like `picocom`). + +After start of `simuc` the program waits for an keyboard action to start simulation. So you are able to connect with avr-gdb before the simulation is executing some machine code. + +1) start `simuc` with proper options and arguments +2) start new shell and execute `avr-gdb` +3) inside (gdb) connect to simuc with the gdb shell command `target remote :1234` +4) load symbols with gdb command `file ...` +5) optionally set source directory with `directory ...` +6) press enter in the shell of simuc +7) now debug avr program step by step with gdb commands `stepi`, `step`, `nexti`, `next` or continue execution with `continue` + +------------------------------------------------------------- + +## Build simavr + +### Prepare system +```sh +$ sudo apt install gcc make gcc-avr avr-libc +$ sudo apt install libelf-dev +$ sudo apt install freeglut3 freeglut3-dev +$ sudo apt install libncurses3 +``` + +### Clone repository + +```sh +user@host:~ $ git clone https://git.htl-mechatronik.at/public/sx/simavr.git +user@host:~ $ cd simavr/examples/simuc +user@host:~/simavr/examples/simuc $ +``` + +### Make simuc executable + +```sh +user@host:~/simavr/examples/simuc $ make +``` + +Executable `simuc` available in folder `dist` + +### Make debian packet `htl-simuc` + +```sh +user@host:~/simavr/examples/simuc $ make deb +``` + +Debian packet available in folder `dpkg` diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/DEBIAN/control b/examples/simuc/dpkg/htl-simuc_version_arch/DEBIAN/control new file mode 100644 index 0000000..6b155e4 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/DEBIAN/control @@ -0,0 +1,10 @@ +Package: htl-simuc +Version: 0.0.1~1 +Section: devel +Architecture: amd64 +Depends: libelf1 +Recommends: gcc-avr, avr-libc, gdb-avr, binutils-avr, avrdude +Installed-Size: 4328 +Priority: optional +Maintainer: Manfred Steiner +Description: megaavr microcontroller simulation diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/bin/simuc b/examples/simuc/dpkg/htl-simuc_version_arch/usr/bin/simuc new file mode 120000 index 0000000..c184c52 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/bin/simuc @@ -0,0 +1 @@ +../share/htl-simuc/simuc/simuc \ No newline at end of file diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/copyright b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/copyright new file mode 100644 index 0000000..5cf1991 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/copyright @@ -0,0 +1,43 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: htl-simuc + +Files: * +Copyright: 2022 Manfred Steiner +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 . + . + 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 + . + . + On Debian systems, the complete text of the GNU Lesser General + Public License version 3 can be found in + "/usr/share/common-licenses/LGPL-3". + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/readme b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/readme new file mode 100644 index 0000000..06453d5 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/doc/htl-simuc/readme @@ -0,0 +1,5 @@ +created: So 27 Nov 2022 16:21:49 CET +https://git.htl-mechatronik.at/public/?p=sx/simavr.git;a=home + +git clone https://git.htl-mechatronik.at/public/sx/simavr.git +git checkout 0a12726a5335284495e557078a73381dc5fc8599 diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/readme b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/readme new file mode 120000 index 0000000..bf9863d --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/readme @@ -0,0 +1 @@ +../doc/htl-simuc/readme \ No newline at end of file diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr/avr_mcu_section.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr/avr_mcu_section.h new file mode 100644 index 0000000..3deae7b --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr/avr_mcu_section.h @@ -0,0 +1,332 @@ +/* + avr_mcu_section.h + + Copyright 2008-2013 Michel Pollet + + 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 . + */ + +#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 + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AVR_MMCU_TAG = 0, + AVR_MMCU_TAG_NAME, + AVR_MMCU_TAG_FREQUENCY, + AVR_MMCU_TAG_VCC, + AVR_MMCU_TAG_AVCC, + AVR_MMCU_TAG_AREF, + AVR_MMCU_TAG_LFUSE, + AVR_MMCU_TAG_HFUSE, + AVR_MMCU_TAG_EFUSE, + AVR_MMCU_TAG_SIGNATURE, + AVR_MMCU_TAG_SIMAVR_COMMAND, + AVR_MMCU_TAG_SIMAVR_CONSOLE, + AVR_MMCU_TAG_VCD_FILENAME, + AVR_MMCU_TAG_VCD_PERIOD, + AVR_MMCU_TAG_VCD_TRACE, + AVR_MMCU_TAG_VCD_PORTPIN, + AVR_MMCU_TAG_VCD_IRQ, + AVR_MMCU_TAG_PORT_EXTERNAL_PULL, +}; + +enum { + SIMAVR_CMD_NONE = 0, + SIMAVR_CMD_VCD_START_TRACE, + SIMAVR_CMD_VCD_STOP_TRACE, + SIMAVR_CMD_UART_LOOPBACK, +}; + +#if __AVR__ +/* + * WARNING. Due to newer GCC being stupid, they introduced a bug that + * prevents us introducing variable length strings in the declaration + * of structs. Worked for a million years, and no longer. + * So the new method declares the string as fixed size, and the parser + * is forced to skip the zeroes in padding. Dumbo. + */ +#define _MMCU_ __attribute__((section(".mmcu"))) __attribute__((used)) +struct avr_mmcu_long_t { + uint8_t tag; + uint8_t len; + uint32_t val; +} __attribute__((__packed__)); + +struct avr_mmcu_string_t { + uint8_t tag; + uint8_t len; + char string[64]; +} __attribute__((__packed__)); + +struct avr_mmcu_addr_t { + uint8_t tag; + uint8_t len; + void * what; +} __attribute__((__packed__)); + +struct avr_mmcu_vcd_trace_t { + uint8_t tag; + uint8_t len; + uint8_t mask; + void * what; + char name[32]; +} __attribute__((__packed__)); + +#define AVR_MCU_STRING(_tag, _str) \ + const struct avr_mmcu_string_t _##_tag _MMCU_ = {\ + .tag = _tag,\ + .len = sizeof(struct avr_mmcu_string_t) - 2,\ + .string = _str,\ + } +/* + * This trick allows concatenation of tokens. We need a macro redirection + * for it to work. + * The goal is to make unique variable names (they don't matter anyway) + */ +#define DO_CONCAT2(_a, _b) _a##_b +#define DO_CONCAT(_a, _b) DO_CONCAT2(_a,_b) + +#define AVR_MCU_LONG(_tag, _val) \ + const struct avr_mmcu_long_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\ + .tag = _tag,\ + .len = sizeof(struct avr_mmcu_long_t) - 2,\ + .val = _val,\ + } + +#define AVR_MCU_BYTE(_tag, _val) \ + const uint8_t _##_tag _MMCU_ = { _tag, 1, _val } + +/*! + * This Macro allows you to specify traces for the VCD file output + * engine. This specifies a default header, and let you fill in the + * relevant bits. + * Example: + * const struct avr_mmcu_vcd_trace_t _mytrace[] _MMCU_ = { + * { AVR_MCU_VCD_SYMBOL("UDR0"), .what = (void*)&UDR0, }, + * { AVR_MCU_VCD_SYMBOL("UDRE0"), .mask = (1 << UDRE0), .what = (void*)&UCSR0A, }, + * }; + * This structure will automatically tell simavr to add a VCD trace + * for the UART register, and the UDRE0 bit, so you can trace exactly + * the timing of the changed using gtkwave. + */ +#define AVR_MCU_VCD_SYMBOL(_name) \ + .tag = AVR_MMCU_TAG_VCD_TRACE, \ + .len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\ + .name = _name + +/*! + * Specifies the name and wanted period (in usec) for a VCD file + * this is not mandatory for the VCD output to work, if this tag + * is not used, a VCD file will still be created with default values + */ +#define AVR_MCU_VCD_FILE(_name, _period) \ + AVR_MCU_STRING(AVR_MMCU_TAG_VCD_FILENAME, _name);\ + AVR_MCU_LONG(AVR_MMCU_TAG_VCD_PERIOD, _period) + +/*! + * It is possible to send "commands" to simavr from the + * firmware itself. For this to work you need to specify + * an IO register that is to be used for a write-only + * bridge. A favourite is one of the usual "GPIO register" + * that most (all ?) AVR have. + * See definition of SIMAVR_CMD_* to see what commands can + * be used from your firmware. + */ +#define AVR_MCU_SIMAVR_COMMAND(_register) \ + const struct avr_mmcu_addr_t _simavr_command_register _MMCU_ = {\ + .tag = AVR_MMCU_TAG_SIMAVR_COMMAND,\ + .len = sizeof(void *),\ + .what = (void*)_register, \ + } +/*! + * Similar to AVR_MCU_SIMAVR_COMMAND, The CONSOLE allows the AVR code + * to declare a register (typically a GPIO register, but any unused + * register can work...) that will allow printing on the host's console + * without using a UART to do debug. + */ +#define AVR_MCU_SIMAVR_CONSOLE(_register) \ + const struct avr_mmcu_addr_t _simavr_console_register _MMCU_ = {\ + .tag = AVR_MMCU_TAG_SIMAVR_CONSOLE,\ + .len = sizeof(void *),\ + .what = (void*)_register, \ + } +/*! + * Allows the firmware to hint simavr as to wether there are external + * pullups/down on PORT pins. It helps if the firmware uses "open drain" + * pins by toggling the DDR pins to switch between an output state and + * a "default" state. + * The value passed here will be output on the PORT IRQ when the DDR + * pin is set to input again + */ +#define AVR_MCU_EXTERNAL_PORT_PULL(_port, _mask, _val) \ + AVR_MCU_LONG(AVR_MMCU_TAG_PORT_EXTERNAL_PULL, \ + (((unsigned long)((_port)&0xff) << 16) | \ + ((unsigned long)((_mask)&0xff) << 8) | \ + ((_val)&0xff))); +/*! + * Add this port/pin to the VCD file. The syntax uses the name of the + * port as a character, and not a pointer to a register. + * AVR_MCU_VCD_PORT_PIN('B', 5); + */ +#define AVR_MCU_VCD_PORT_PIN(_port, _pin, _name) \ + const struct avr_mmcu_vcd_trace_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\ + .tag = AVR_MMCU_TAG_VCD_PORTPIN, \ + .len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\ + .mask = _port, \ + .what = (void*)_pin, \ + .name = _name, \ + } + +/*! + * These allows you to add a trace showing how long an IRQ vector is pending, + * and also how long it is running. You can specify the IRQ as a vector name + * straight from the firmware file, and it will be named properly in the trace + */ + +#define AVR_MCU_VCD_IRQ_TRACE(_vect_number, __what, _trace_name) \ + const struct avr_mmcu_vcd_trace_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\ + .tag = AVR_MMCU_TAG_VCD_IRQ, \ + .len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\ + .mask = _vect_number, \ + .what = (void*)__what, \ + .name = _trace_name, \ + }; +#define AVR_MCU_VCD_IRQ(_irq_name) \ + AVR_MCU_VCD_IRQ_TRACE(_irq_name##_vect_num, 1, #_irq_name) +#define AVR_MCU_VCD_IRQ_PENDING(_irq_name) \ + AVR_MCU_VCD_IRQ_TRACE(_irq_name##_vect_num, 0, #_irq_name "_pend") +#define AVR_MCU_VCD_ALL_IRQ() \ + AVR_MCU_VCD_IRQ_TRACE(0xff, 1, "IRQ") +#define AVR_MCU_VCD_ALL_IRQ_PENDING() \ + AVR_MCU_VCD_IRQ_TRACE(0xff, 0, "IRQ_PENDING") + +/*! + * This tag allows you to specify the voltages used by your board + * It is optional in most cases, but you will need it if you use + * ADC module's IRQs. Not specifying it in this case might lead + * to a divide-by-zero crash. + * The units are Volts*1000 (millivolts) + */ +#define AVR_MCU_VOLTAGES(_vcc, _avcc, _aref) \ + AVR_MCU_LONG(AVR_MMCU_TAG_VCC, (_vcc));\ + AVR_MCU_LONG(AVR_MMCU_TAG_AVCC, (_avcc));\ + AVR_MCU_LONG(AVR_MMCU_TAG_AREF, (_aref)); + +/*! + * This the has to be used if you want to add other tags to the .mmcu section + * the _mmcu symbol is used as an anchor to make sure it stays linked in. + */ +#define AVR_MCU(_speed, _name) \ + AVR_MCU_STRING(AVR_MMCU_TAG_NAME, _name);\ + AVR_MCU_LONG(AVR_MMCU_TAG_FREQUENCY, _speed);\ + const uint8_t _mmcu[2] _MMCU_ = { AVR_MMCU_TAG, 0 } + +/* + * The following MAP macros where copied from + * https://github.com/swansontec/map-macro/blob/master/map.h + * + * The license header for that file is reproduced below: + * + * Copyright (C) 2012 William Swanson + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors or + * their institutions shall not be used in advertising or otherwise to + * promote the sale, use or other dealings in this Software without + * prior written authorization from the authors. + */ + +#define _EVAL0(...) __VA_ARGS__ +#define _EVAL1(...) _EVAL0 (_EVAL0 (_EVAL0 (__VA_ARGS__))) +#define _EVAL2(...) _EVAL1 (_EVAL1 (_EVAL1 (__VA_ARGS__))) +#define _EVAL3(...) _EVAL2 (_EVAL2 (_EVAL2 (__VA_ARGS__))) +#define _EVAL4(...) _EVAL3 (_EVAL3 (_EVAL3 (__VA_ARGS__))) +#define _EVAL(...) _EVAL4 (_EVAL4 (_EVAL4 (__VA_ARGS__))) + +#define _MAP_END(...) +#define _MAP_OUT + +#define _MAP_GET_END() 0, _MAP_END +#define _MAP_NEXT0(test, next, ...) next _MAP_OUT +#define _MAP_NEXT1(test, next) _MAP_NEXT0 (test, next, 0) +#define _MAP_NEXT(test, next) _MAP_NEXT1 (_MAP_GET_END test, next) + +#define _MAP0(f, x, peek, ...) f(x) _MAP_NEXT (peek, _MAP1) (f, peek, __VA_ARGS__) +#define _MAP1(f, x, peek, ...) f(x) _MAP_NEXT (peek, _MAP0) (f, peek, __VA_ARGS__) +#define _MAP(f, ...) _EVAL (-MAP1 (f, __VA_ARGS__, (), 0)) + +/* End of original MAP macros. */ + +// Define MAP macros with one additional argument +#define _MAP0_1(f, a, x, peek, ...) f(a, x) _MAP_NEXT (peek, _MAP1_1) (f, a, peek, __VA_ARGS__) +#define _MAP1_1(f, a, x, peek, ...) f(a, x) _MAP_NEXT (peek, _MAP0_1) (f, a, peek, __VA_ARGS__) +#define _MAP_1(f, a, ...) _EVAL (_MAP1_1 (f, a, __VA_ARGS__, (), 0)) + +#define _SEND_SIMAVR_CMD_BYTE(reg, b) reg = b; + +// A helper macro for sending multi-byte commands +#define SEND_SIMAVR_CMD(reg, ...) \ + do { \ + _MAP_1(_SEND_SIMAVR_CMD_BYTE, reg, __VA_ARGS__) \ + } while(0) + +#endif /* __AVR__ */ + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.c new file mode 100644 index 0000000..f3e14e1 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.c @@ -0,0 +1,241 @@ +/* + avr_acomp.c + + Copyright 2017 Konstantin Begun + + 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 . + */ + +#include +#include "avr_acomp.h" +#include "avr_timer.h" + +static uint8_t +avr_acomp_get_state( + struct avr_t * avr, + avr_acomp_t *ac) +{ + if (avr_regbit_get(avr, ac->disabled)) + return 0; + + // get positive voltage + uint16_t positive_v; + + if (avr_regbit_get(avr, ac->acbg)) { // if bandgap + positive_v = ACOMP_BANDGAP; + } else { + positive_v = ac->ain_values[0]; // AIN0 + } + + // get negative voltage + uint16_t negative_v = 0; + + // multiplexer is enabled if acme is set and adc is off + if (avr_regbit_get(avr, ac->acme) && !avr_regbit_get(avr, ac->aden)) { + if (!avr_regbit_get(avr, ac->pradc)) { + uint8_t adc_i = avr_regbit_get_array(avr, ac->mux, ARRAY_SIZE(ac->mux)); + if (adc_i < ac->mux_inputs && adc_i < ARRAY_SIZE(ac->adc_values)) { + negative_v = ac->adc_values[adc_i]; + } + } + + } else { + negative_v = ac->ain_values[1]; // AIN1 + } + + return positive_v > negative_v; +} + +static avr_cycle_count_t +avr_acomp_sync_state( + struct avr_t * avr, + avr_cycle_count_t when, + void * param) +{ + avr_acomp_t * p = (avr_acomp_t *)param; + if (!avr_regbit_get(avr, p->disabled)) { + + uint8_t cur_state = avr_regbit_get(avr, p->aco); + uint8_t new_state = avr_acomp_get_state(avr, p); + + if (new_state != cur_state) { + avr_regbit_setto(avr, p->aco, new_state); // set ACO + + uint8_t acis0 = avr_regbit_get(avr, p->acis[0]); + uint8_t acis1 = avr_regbit_get(avr, p->acis[1]); + + if ((acis0 == 0 && acis1 == 0) || (acis1 == 1 && acis0 == new_state)) { + avr_raise_interrupt(avr, &p->ac); + } + + avr_raise_irq(p->io.irq + ACOMP_IRQ_OUT, new_state); + } + + } + + return 0; +} + +static inline void +avr_schedule_sync_state( + struct avr_t * avr, + void *param) +{ + avr_cycle_timer_register(avr, 1, avr_acomp_sync_state, param); +} + +static void +avr_acomp_write_acsr( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_acomp_t * p = (avr_acomp_t *)param; + + avr_core_watch_write(avr, addr, v); + + if (avr_regbit_get(avr, p->acic) != (p->timer_irq ? 1:0)) { + if (p->timer_irq) { + avr_unconnect_irq(p->io.irq + ACOMP_IRQ_OUT, p->timer_irq); + p->timer_irq = NULL; + } + else { + avr_irq_t *irq = avr_io_getirq(avr, AVR_IOCTL_TIMER_GETIRQ(p->timer_name), TIMER_IRQ_IN_ICP); + if (irq) { + avr_connect_irq(p->io.irq + ACOMP_IRQ_OUT, irq); + p->timer_irq = irq; + } + } + } + + avr_schedule_sync_state(avr, param); +} + +static void +avr_acomp_dependencies_changed( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + avr_acomp_t * p = (avr_acomp_t *)param; + avr_schedule_sync_state(p->io.avr, param); +} + +static void +avr_acomp_irq_notify( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + avr_acomp_t * p = (avr_acomp_t *)param; + + switch (irq->irq) { + case ACOMP_IRQ_AIN0 ... ACOMP_IRQ_AIN1: { + p->ain_values[irq->irq - ACOMP_IRQ_AIN0] = value; + avr_schedule_sync_state(p->io.avr, param); + } break; + case ACOMP_IRQ_ADC0 ... ACOMP_IRQ_ADC15: { + p->adc_values[irq->irq - ACOMP_IRQ_ADC0] = value; + avr_schedule_sync_state(p->io.avr, param); + } break; + } +} + +static void +avr_acomp_register_dependencies( + avr_acomp_t *p, + avr_regbit_t rb) +{ + if (rb.reg) { + avr_irq_register_notify( + avr_iomem_getirq(p->io.avr, rb.reg, NULL, rb.bit), + avr_acomp_dependencies_changed, + p); + } +} + +static void +avr_acomp_reset(avr_io_t * port) +{ + avr_acomp_t * p = (avr_acomp_t *)port; + + for (int i = 0; i < ACOMP_IRQ_COUNT; i++) + avr_irq_register_notify(p->io.irq + i, avr_acomp_irq_notify, p); + + // register notification for changes of registers comparator does not own + // avr_register_io_write is tempting instead, but it requires that the handler + // updates the actual memory too. Given this is for the registers this module + // does not own, it is tricky to know whether it should write to the actual memory. + // E.g., if there is already a native handler for it then it will do the writing + // (possibly even omitting some bits etc). IInterefering would probably be wrong. + // On the other hand if there isn't a handler already, then this hadnler would have to, + // as otherwise nobody will. + // This write notification mechanism should probably need reviewing and fixing + // For now using IRQ mechanism, as it is not intrusive + + avr_acomp_register_dependencies(p, p->pradc); + avr_acomp_register_dependencies(p, p->aden); + avr_acomp_register_dependencies(p, p->acme); + + // mux + for (int i = 0; i < ARRAY_SIZE(p->mux); ++i) { + avr_acomp_register_dependencies(p, p->mux[i]); + } +} + +static const char * irq_names[ACOMP_IRQ_COUNT] = { + [ACOMP_IRQ_AIN0] = "16io = _io; + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->ac); + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_ACOMP_GETIRQ, ACOMP_IRQ_COUNT, NULL); + + avr_register_io_write(avr, p->r_acsr, avr_acomp_write_acsr, p); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.h new file mode 100644 index 0000000..caa3c4e --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_acomp.h @@ -0,0 +1,89 @@ +/* + avr_acomp.h + + Copyright 2017 Konstantin Begun + + 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 . + */ + +#ifndef __AVR_COMP_H___ +#define __AVR_COMP_H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +/* + * simavr Analog Comparator allows external code to feed real voltages to the + * simulator, and the simulator uses it's 'real' reference voltage + * to set comparator output accordingly and trigger in interrupt, if set up this way + * + */ + +enum { + // input IRQ values. Values are /always/ volts * 1000 (millivolts) + ACOMP_IRQ_AIN0 = 0, ACOMP_IRQ_AIN1, + ACOMP_IRQ_ADC0, ACOMP_IRQ_ADC1, ACOMP_IRQ_ADC2, ACOMP_IRQ_ADC3, + ACOMP_IRQ_ADC4, ACOMP_IRQ_ADC5, ACOMP_IRQ_ADC6, ACOMP_IRQ_ADC7, + ACOMP_IRQ_ADC8, ACOMP_IRQ_ADC9, ACOMP_IRQ_ADC10, ACOMP_IRQ_ADC11, + ACOMP_IRQ_ADC12, ACOMP_IRQ_ADC13, ACOMP_IRQ_ADC14, ACOMP_IRQ_ADC15, + ACOMP_IRQ_OUT, // output has changed + ACOMP_IRQ_COUNT +}; + +// Get the internal IRQ corresponding to the INT +#define AVR_IOCTL_ACOMP_GETIRQ AVR_IOCTL_DEF('a','c','m','p') + +enum { + ACOMP_BANDGAP = 1100 +}; + +typedef struct avr_acomp_t { + avr_io_t io; + + uint8_t mux_inputs; // number of inputs (not mux bits!) in multiplexer. Other bits in mux below would be expected to be zero + avr_regbit_t mux[4]; + avr_regbit_t pradc; // ADC power reduction, this impacts on ability to use adc multiplexer + avr_regbit_t aden; // ADC Enabled, this impacts on ability to use adc multiplexer + avr_regbit_t acme; // AC multiplexed input enabled + + avr_io_addr_t r_acsr; // control & status register + avr_regbit_t acis[2]; // + avr_regbit_t acic; // input capture enable + avr_regbit_t aco; // output + avr_regbit_t acbg; // bandgap select + avr_regbit_t disabled; + + char timer_name; // connected timer for incput capture triggering + + // use ACI and ACIE bits + avr_int_vector_t ac; + + // runtime data + uint16_t adc_values[16]; // current values on the ADCs inputs + uint16_t ain_values[2]; // current values on AIN inputs + avr_irq_t* timer_irq; +} avr_acomp_t; + +void avr_acomp_init(avr_t * avr, avr_acomp_t * port); + +#ifdef __cplusplus +}; +#endif + +#endif // __AVR_COMP_H___ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.c new file mode 100644 index 0000000..6adf1f2 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.c @@ -0,0 +1,427 @@ +/* + avr_adc.c + + Copyright 2008, 2010 Michel Pollet + + 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 . + */ + +#include +#include +#include +#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] = "16io = _io; + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->adc); + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_ADC_GETIRQ, ADC_IRQ_COUNT, NULL); + + avr_register_io_write(avr, p->r_adcsra, avr_adc_write_adcsra, p); + // some ADCs don't have ADCSRB (atmega8/16/32) + if (p->r_adcsrb) + avr_register_io_write(avr, p->r_adcsrb, avr_adc_write_adcsrb, p); + avr_register_io_read(avr, p->r_adcl, avr_adc_read_l, p); + avr_register_io_read(avr, p->r_adch, avr_adc_read_h, p); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.h new file mode 100644 index 0000000..c9dbfa0 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_adc.h @@ -0,0 +1,193 @@ +/* + avr_adc.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __AVR_ADC_H___ +#define __AVR_ADC_H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +/* + * simavr ADC allows external code to feed real voltages to the + * simulator, and the simulator uses it's 'real' reference voltage + * to do the right thing and return the 'proper' 10 bits ADC value + * to the AVR firmware. + * + * To send values to the ADC, register your code to wait for the + * ADC_IRQ_OUT_TRIGGER irq, and at that point send any of the + * ADC_IRQ_ADC* with Millivolts as value. + * + * External trigger is not done yet. + */ + +enum { + // input IRQ values. Values are /always/ volts * 1000 (millivolts) + ADC_IRQ_ADC0 = 0, ADC_IRQ_ADC1, ADC_IRQ_ADC2, ADC_IRQ_ADC3, + ADC_IRQ_ADC4, ADC_IRQ_ADC5, ADC_IRQ_ADC6, ADC_IRQ_ADC7, + ADC_IRQ_ADC8, ADC_IRQ_ADC9, ADC_IRQ_ADC10, ADC_IRQ_ADC11, + ADC_IRQ_ADC12, ADC_IRQ_ADC13, ADC_IRQ_ADC14, ADC_IRQ_ADC15, + ADC_IRQ_TEMP, // see the datasheet + ADC_IRQ_IN_TRIGGER, + ADC_IRQ_OUT_TRIGGER, // sends a avr_adc_mux_t + ADC_IRQ_COUNT +}; + +// Get the internal IRQ corresponding to the INT +#define AVR_IOCTL_ADC_GETIRQ AVR_IOCTL_DEF('a','d','c','0') + +/* + * Definition of a ADC mux mode. + */ +enum { + ADC_MUX_NONE = 0, // Nothing. return 0 + ADC_MUX_NOISE, // Nothing. return something random + ADC_MUX_SINGLE, // Normal ADC pin reading + ADC_MUX_DIFF, // differential channels (src-diff) + ADC_MUX_TEMP, // internal temp sensor + ADC_MUX_REF, // reference voltage (in src * 100) + ADC_MUX_VCC4, // VCC/4 +}; +typedef struct avr_adc_mux_t { + unsigned long kind : 3, gain : 8, diff : 8, src : 13; +} avr_adc_mux_t; + +enum { + ADC_VREF_AREF = 0, // default mode + ADC_VREF_VCC, + ADC_VREF_AVCC, + ADC_VREF_V110 = 1100, + ADC_VREF_V256 = 2560, +}; + +// ADC trigger sources +typedef enum { + avr_adts_none = 0, + avr_adts_free_running, + avr_adts_analog_comparator_0, + avr_adts_analog_comparator_1, + avr_adts_analog_comparator_2, + avr_adts_analog_comparator_3, + avr_adts_external_interrupt_0, + avr_adts_timer_0_compare_match_a, + avr_adts_timer_0_compare_match_b, + avr_adts_timer_0_overflow, + avr_adts_timer_1_compare_match_b, + avr_adts_timer_1_overflow, + avr_adts_timer_1_capture_event, + avr_adts_pin_change_interrupt, + avr_adts_psc_module_0_sync_signal, + avr_adts_psc_module_1_sync_signal, + avr_adts_psc_module_2_sync_signal, +} avr_adts_type; + +typedef struct avr_adc_t { + avr_io_t io; + + uint8_t r_admux; + // if the last bit exists in the mux, we are an extended ADC + avr_regbit_t mux[6]; + avr_regbit_t ref[3]; // reference voltages bits + uint16_t ref_values[8]; // ADC_VREF_* + + avr_regbit_t adlar; // left/right adjustment bit + + uint8_t r_adcsra; // ADC Control and Status Register A + avr_regbit_t aden; // ADC Enabled + avr_regbit_t adsc; // ADC Start Conversion + avr_regbit_t adate; // ADC Auto Trigger Enable + + avr_regbit_t adps[3]; // Prescaler bits. Note that it's a frequency bit shift + + uint8_t r_adcl, r_adch; // Data Registers + + uint8_t r_adcsrb; // ADC Control and Status Register B + avr_regbit_t adts[4]; // Timing Source + avr_adts_type adts_op[16]; // ADTS type + uint8_t adts_mode; // the extracted ADTS mode + avr_regbit_t bin; // Bipolar Input Mode (tinyx5 have it) + avr_regbit_t ipr; // Input Polarity Reversal (tinyx5 have it) + + // use ADIF and ADIE bits + avr_int_vector_t adc; + + avr_adc_mux_t muxmode[64]; // maximum 6 bits of mux modes + + /* + * runtime bits + */ + + uint16_t adc_values[16]; // current values on the ADCs + uint16_t temp; // temp sensor reading + uint8_t first; + uint8_t read_status; // marked one when adcl is read + + /* Conversion parameters saved at start (ADSC is set). */ + + uint8_t current_muxi; + uint8_t current_refi; + uint8_t current_prescale; + struct { + unsigned int bipolar : 1; // BIN bit. + unsigned int negate : 1; // IPR bit. + unsigned int adjust : 1; // ADLAR bit. + } current_extras; + + /* Buffered conversion result. */ + + uint16_t result; +} avr_adc_t; + +void avr_adc_init(avr_t * avr, avr_adc_t * port); + + +/* + * Helper macros for the Cores definition of muxes + */ +#define AVR_ADC_SINGLE(_chan) { \ + .kind = ADC_MUX_SINGLE, \ + .src = (_chan), \ + } +#define AVR_ADC_DIFF(_a,_b,_g) { \ + .kind = ADC_MUX_DIFF, \ + .src = (_a), \ + .diff = (_b), \ + .gain = (_g), \ + } +#define AVR_ADC_REF(_t) { \ + .kind = ADC_MUX_REF, \ + .src = (_t), \ + } +#define AVR_ADC_TEMP() { \ + .kind = ADC_MUX_TEMP, \ + } + +#define AVR_ADC_VCC4() { \ + .kind = ADC_MUX_VCC4, \ + } + +#ifdef __cplusplus +}; +#endif + +#endif /* __AVR_ADC_H___ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.c new file mode 100644 index 0000000..e34aab2 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.c @@ -0,0 +1,248 @@ +/* + avr_bitbang.c + + Copyright 2008, 2009 Michel Pollet + 2011 Stephan Veigl + + 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 . + */ + +#ifndef __AVR_BITBANG_H__ +#define __AVR_BITBANG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "avr_bitbang.h" + +#include "sim_regbit.h" +#include "sim_core.h" +#include "avr_ioport.h" + +///@todo refactor SPI to bitbang + +#define BITBANG_MASK 0xFFFFFFFFUL + +/** + * read (sample) data from input pin + * + * @param p internal bitbang structure + */ +static void avr_bitbang_read_bit(avr_bitbang_t *p) +{ + avr_ioport_state_t iostate; + uint8_t bit = 0; + + if ( !p->enabled ) + return; + + // read from HW pin + if ( p->p_in.port ) { + avr_ioctl(p->avr, AVR_IOCTL_IOPORT_GETSTATE( p->p_in.port ), &iostate); + bit = ( iostate.pin >> p->p_in.pin ) & 1; + + if ( p->data_order ) { + // data order: shift right + p->data = (p->data >> 1) | ( bit << (p->buffer_size-1)); + } else { + // data order: shift left + p->data = (p->data << 1) | bit; + } + + } + + // module callback + if ( p->callback_bit_read ) { + p->callback_bit_read(bit, p->callback_param); + } + + // data sanitary + p->data = p->data & ~(BITBANG_MASK << p->buffer_size); +} + +/** + * write data to output pin + * + * @param p bitbang structure + */ +static void avr_bitbang_write_bit(avr_bitbang_t *p) +{ + uint8_t bit = 0; + + if ( !p->enabled ) + return; + + if ( p->data_order ) { + // data order: shift right + bit = p->data & 1; + } else { + // data order: shift left + bit = (p->data >> (p->buffer_size-1)) & 1; + } + + // output to HW pin + if ( p->p_out.port ) { + avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_out.port ), p->p_out.pin), bit); + } + + // module callback + if ( p->callback_bit_write ) { + p->callback_bit_write(bit, p->callback_param); + } +} + + +/** + * process clock edges (both: positive and negative edges) + * + * @param p bitbang structure + * + */ +static void avr_bitbang_clk_edge(avr_bitbang_t *p) +{ + uint8_t phase = (p->clk_count & 1) ^ p->clk_phase; + uint8_t clk = (p->clk_count & 1) ^ p->clk_pol; + + if ( !p->enabled ) + return; + + // increase clock + p->clk_count++; + clk ^= 1; + phase ^= 1; + + // generate clock output on HW pin + if ( p->clk_generate && p->p_clk.port ) { + avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), clk); + } + + if ( phase ) { + // read data in + avr_bitbang_read_bit(p); + + } else { + // write data out + avr_bitbang_write_bit(p); + } + + if ( p->clk_count >= (p->buffer_size*2) ) { + // transfer finished + if ( p->callback_transfer_finished ) { + p->data = p->callback_transfer_finished(p->data, p->callback_param); + } + p->clk_count = 0; + } +} + +static avr_cycle_count_t avr_bitbang_clk_timer(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_bitbang_t * p = (avr_bitbang_t *)param; + + avr_bitbang_clk_edge(p); + + if ( p->enabled ) + return when + p->clk_cycles/2; + else + return 0; +} + +static void avr_bitbang_clk_hook(struct avr_irq_t * irq, uint32_t value, void * param) +{ + avr_bitbang_t * p = (avr_bitbang_t *)param; + uint8_t clk = (p->clk_count & 1) ^ p->clk_pol; + + // no clock change + if ( clk == value ) + return; + + avr_bitbang_clk_edge(p); +} + +/** + * reset bitbang sub-module + * + * @param avr avr attached to + * @param p bitbang structure + */ +void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p) +{ + p->avr = avr; + p->enabled = 0; + p->clk_count = 0; + p->data = 0; + + if ( p->buffer_size < 1 || p->buffer_size > 32 ) { + AVR_LOG(avr, LOG_ERROR, + "Error: bitbang buffer size should be between 1 and 32. set value: %d\n", p->buffer_size); + abort(); + } + +} + +/** + * start bitbang transfer + * + * buffers should be written / cleared in advanced + * timers and interrupts are connected + * + * @param p bitbang structure + */ +void avr_bitbang_start(avr_bitbang_t * p) +{ + p->enabled = 1; + p->clk_count = 0; + + if ( p->clk_phase == 0 ) { + // write first bit + avr_bitbang_write_bit(p); + } + + if ( p->clk_generate ) { + // master mode, generate clock -> set timer + avr_cycle_timer_register(p->avr, (p->clk_cycles/2), avr_bitbang_clk_timer, p); + } else { + // slave mode -> attach clock function to clock pin + ///@todo test + avr_irq_register_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p); + } + +} + + +/** + * stop bitbang transfer + * + * timers and interrupts are disabled + * + * @param p bitbang structure + */ +void avr_bitbang_stop(avr_bitbang_t * p) +{ + + p->enabled = 0; + avr_cycle_timer_cancel(p->avr, avr_bitbang_clk_timer, p); + avr_irq_unregister_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p); +} + +#ifdef __cplusplus +}; +#endif + +#endif /*__AVR_BITBANG_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.h new file mode 100644 index 0000000..3d8832f --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_bitbang.h @@ -0,0 +1,117 @@ +/* + avr_bitbang.h + + Copyright 2008, 2009 Michel Pollet + 2011 Stephan Veigl + + 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 . + */ + +/** + @defgroup avr_bitbang Generic BitBang Module + @{ + + Generic BitBang Module of simavr AVR simulator. + + @par Features / Implementation Status + - easy buffer access with push() / pop() functions + - one input and one output pin (can be the same HW pin for I2C) + + @todo + - one input and one output pin (can be the same HW pin for I2C) + - one clock pin which can be configured as input or output + when the clock is output, the clock signal is generated with a + configured frequency (master / slave mode) + - 2x 32-bit buffers (input / output) (allows start, stop bits for UART, etc.) + - on each read / write a callback is executed to notify the master module + +*/ + + +#ifndef AVR_BITBANG_H_ +#define AVR_BITBANG_H_ + +#include "sim_avr.h" +#include "avr_ioport.h" + + + + +/// SPI Module initialization and state structure +typedef struct avr_bitbang_t { + avr_t * avr; ///< avr we are attached to + + uint8_t enabled; ///< bit-bang enabled flag + uint8_t clk_generate; ///< generate clock and write to clock pin (if available) -> master / slave mode + uint8_t clk_pol; ///< clock polarity, base (inactive) value of clock + uint8_t clk_phase; ///< clock phase / data sampling edge + /// - 0: data are sampled at first clock edge + /// - 1: data are sampled at second clock edge + uint32_t clk_cycles; ///< cycles per clock period - must be multiple of 2! (used if clk_generate is enabled) + uint8_t data_order; ///< data order / shift + /// - 0: shift left + /// - 1: shift right + + uint8_t buffer_size; ///< size of buffer in bits (1...32) + + void *callback_param; /// anonymous parameter for callback functions + void (*callback_bit_read)(uint8_t bit, void *param); ///< callback function to notify about bit read + void (*callback_bit_write)(uint8_t bit, void *param); ///< callback function to notify about bit write + uint32_t (*callback_transfer_finished)(uint32_t data, void *param); ///< callback function to notify about a complete transfer + /// (read received data and write new output data) + + avr_iopin_t p_clk; ///< clock pin (optional) + avr_iopin_t p_in; ///< data in pin + avr_iopin_t p_out; ///< data out pin + +// private data + uint32_t data; ///< data buffer + /// - latest received bit the is lowest / most right one, bit number: 0 + /// - next bit to be written is the highest one, bit number: (buffer_size-1) + int8_t clk_count; ///< internal clock edge count +} avr_bitbang_t; + +/** + * reset bitbang sub-module + * + * @param avr avr attached to + * @param p bitbang structure + */ +void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p); + +/** + * start bitbang transfer + * + * buffers should be written / cleared in advanced + * timers and interrupts are connected + * + * @param p bitbang structure + */ +void avr_bitbang_start(avr_bitbang_t * p); + + +/** + * stop bitbang transfer + * + * timers and interrupts are disabled + * + * @param p bitbang structure + */ +void avr_bitbang_stop(avr_bitbang_t * p); + + +#endif /* AVR_BITBANG_H_ */ +/// @} end of avr_bitbang group diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.c new file mode 100644 index 0000000..c348f3f --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.c @@ -0,0 +1,148 @@ +/* + avr_eeprom.c + + IO module that simulates the AVR EEProm + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include "avr_eeprom.h" + +static avr_cycle_count_t avr_eempe_clear(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_eeprom_t * p = (avr_eeprom_t *)param; + avr_regbit_clear(p->io.avr, p->eempe); + return 0; +} + +static avr_cycle_count_t avr_eei_raise(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_eeprom_t * p = (avr_eeprom_t *)param; + avr_raise_interrupt(p->io.avr, &p->ready); + return 0; +} + +static void avr_eeprom_write(avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_eeprom_t * p = (avr_eeprom_t *)param; + uint8_t eempe = avr_regbit_get(avr, p->eempe); + + avr_core_watch_write(avr, addr, v); + + if (!eempe && avr_regbit_get(avr, p->eempe)) { + avr_cycle_timer_register(avr, 4, avr_eempe_clear, p); + } + + uint16_t ee_addr; + if (p->r_eearh) + ee_addr = avr->data[p->r_eearl] | (avr->data[p->r_eearh] << 8); + else + ee_addr = avr->data[p->r_eearl]; + if (((eempe && avr_regbit_get(avr, p->eepe)) || avr_regbit_get(avr, p->eere)) && + ee_addr >= p->size) { + AVR_LOG(avr, LOG_ERROR, "EEPROM: *** %s address out of bounds: %04x > %04x," + " wrapping to %04x (PC=%04x)\n", + eempe ? "Write" : "Read", + ee_addr, p->size-1, ee_addr & (p->size-1), + avr->pc); + ee_addr = ee_addr & (p->size-1); + } + if (eempe && avr_regbit_get(avr, p->eepe)) { // write operation + // printf("eeprom write %04x <- %02x\n", addr, avr->data[p->r_eedr]); + p->eeprom[ee_addr] = avr->data[p->r_eedr]; + // Automatically clears that bit (?) + avr_regbit_clear(avr, p->eempe); + + avr_cycle_timer_register_usec(avr, 3400, avr_eei_raise, p); // 3.4ms here + } + if (avr_regbit_get(avr, p->eere)) { // read operation + avr->data[p->r_eedr] = p->eeprom[ee_addr]; + // printf("eeprom read %04x : %02x\n", addr, p->eeprom[addr]); + } + + // autocleared + avr_regbit_clear(avr, p->eepe); + avr_regbit_clear(avr, p->eere); +} + +static int avr_eeprom_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param) +{ + avr_eeprom_t * p = (avr_eeprom_t *)port; + int res = -1; + + switch(ctl) { + case AVR_IOCTL_EEPROM_SET: { + avr_eeprom_desc_t * desc = (avr_eeprom_desc_t*)io_param; + if (!desc || !desc->size || !desc->ee || (desc->offset + desc->size) > p->size) { + AVR_LOG(port->avr, LOG_WARNING, "EEPROM: %s: AVR_IOCTL_EEPROM_SET Invalid argument\n", + __FUNCTION__); + return -2; + } + memcpy(p->eeprom + desc->offset, desc->ee, desc->size); + AVR_LOG(port->avr, LOG_TRACE, "EEPROM: %s: AVR_IOCTL_EEPROM_SET Loaded %d at offset %d\n", + __FUNCTION__, desc->size, desc->offset); + } break; + case AVR_IOCTL_EEPROM_GET: { + avr_eeprom_desc_t * desc = (avr_eeprom_desc_t*)io_param; + if (!desc || (desc->offset + desc->size) > p->size) { + AVR_LOG(port->avr, LOG_WARNING, "EEPROM: %s: AVR_IOCTL_EEPROM_GET Invalid argument\n", + __FUNCTION__); + return -2; + } + if (desc->ee) + memcpy(desc->ee, p->eeprom + desc->offset, desc->size); + else // allow to get access to the read data, for gdb support + desc->ee = p->eeprom + desc->offset; + } break; + } + + return res; +} + +static void avr_eeprom_dealloc(struct avr_io_t * port) +{ + avr_eeprom_t * p = (avr_eeprom_t *)port; + if (p->eeprom) + free(p->eeprom); + p->eeprom = NULL; +} + +static avr_io_t _io = { + .kind = "eeprom", + .ioctl = avr_eeprom_ioctl, + .dealloc = avr_eeprom_dealloc, +}; + +void avr_eeprom_init(avr_t * avr, avr_eeprom_t * p) +{ + p->io = _io; +// printf("%s init (%d bytes) EEL/H:%02x/%02x EED=%02x EEC=%02x\n", +// __FUNCTION__, p->size, p->r_eearl, p->r_eearh, p->r_eedr, p->r_eecr); + + p->eeprom = malloc(p->size); + memset(p->eeprom, 0xff, p->size); + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->ready); + + avr_register_io_write(avr, p->r_eecr, avr_eeprom_write, p); +} + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.h new file mode 100644 index 0000000..9ad1c5a --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_eeprom.h @@ -0,0 +1,131 @@ +/* + avr_eeprom.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __AVR_EEPROM_H__ +#define __AVR_EEPROM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +typedef struct avr_eeprom_t { + avr_io_t io; + + uint8_t * eeprom; // actual bytes + uint16_t size; // size for this MCU + + uint8_t r_eearh; + uint8_t r_eearl; + uint8_t r_eedr; + + // eepm -- eeprom write mode + uint8_t r_eecr; // shortcut, assumes these bits fit in that register + avr_regbit_t eepm[4]; + avr_regbit_t eempe; // eeprom master program enable + avr_regbit_t eepe; // eeprom program enable + avr_regbit_t eere; // eeprom read enable + + avr_int_vector_t ready; // EERIE vector +} avr_eeprom_t; + +void avr_eeprom_init(avr_t * avr, avr_eeprom_t * port); + +typedef struct avr_eeprom_desc_t { + uint8_t * ee; + uint16_t offset; + uint32_t size; +} avr_eeprom_desc_t; + +#define AVR_IOCTL_EEPROM_GET AVR_IOCTL_DEF('e','e','g','p') +#define AVR_IOCTL_EEPROM_SET AVR_IOCTL_DEF('e','e','s','p') + + +/* + * the eeprom block seems to be very similar across AVRs, + * so here is a macro to declare a "typical" one in a core. + */ + +#define AVR_EEPROM_DECLARE(_vector) \ + .eeprom = {\ + .size = E2END+1,\ + .r_eearh = EEARH,\ + .r_eearl = EEARL,\ + .r_eedr = EEDR,\ + .r_eecr = EECR,\ + .eepm = { AVR_IO_REGBIT(EECR, EEPM0), AVR_IO_REGBIT(EECR, EEPM1) },\ + .eempe = AVR_IO_REGBIT(EECR, EEMPE),\ + .eepe = AVR_IO_REGBIT(EECR, EEPE),\ + .eere = AVR_IO_REGBIT(EECR, EERE),\ + .ready = {\ + .enable = AVR_IO_REGBIT(EECR, EERIE),\ + .vector = _vector,\ + },\ + } + +/* + * no EEPM registers in atmega128 + */ +#define AVR_EEPROM_DECLARE_NOEEPM(_vector) \ + .eeprom = {\ + .size = E2END+1,\ + .r_eearh = EEARH,\ + .r_eearl = EEARL,\ + .r_eedr = EEDR,\ + .r_eecr = EECR,\ + .eepm = { }, \ + .eempe = AVR_IO_REGBIT(EECR, EEMWE),\ + .eepe = AVR_IO_REGBIT(EECR, EEWE),\ + .eere = AVR_IO_REGBIT(EECR, EERE),\ + .ready = {\ + .enable = AVR_IO_REGBIT(EECR, EERIE),\ + .vector = _vector,\ + },\ + } + + +/* + * macro definition without a high address bit register, + * which is not implemented in some tiny AVRs. + */ + +#define AVR_EEPROM_DECLARE_8BIT(_vector) \ + .eeprom = {\ + .size = E2END+1,\ + .r_eearl = EEAR,\ + .r_eedr = EEDR,\ + .r_eecr = EECR,\ + .eepm = { AVR_IO_REGBIT(EECR, EEPM0), AVR_IO_REGBIT(EECR, EEPM1) },\ + .eempe = AVR_IO_REGBIT(EECR, EEMPE),\ + .eepe = AVR_IO_REGBIT(EECR, EEPE),\ + .eere = AVR_IO_REGBIT(EECR, EERE),\ + .ready = {\ + .enable = AVR_IO_REGBIT(EECR, EERIE),\ + .vector = _vector,\ + },\ + } + +#ifdef __cplusplus +}; +#endif + +#endif /* __AVR_EEPROM_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.c new file mode 100644 index 0000000..6f85660 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.c @@ -0,0 +1,228 @@ +/* + avr_extint.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + + +#include +#include +#include +#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] = "io = _io; + + avr_register_io(avr, &p->io); + for (int i = 0; i < EXTINT_COUNT; i++) { + if (!p->eint[i].port_ioctl) + break; + avr_register_vector(avr, &p->eint[i].vector); + } + // allocate this module's IRQ + + avr_io_setirqs(&p->io, AVR_IOCTL_EXTINT_GETIRQ(), EXTINT_COUNT, NULL); +} + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.h new file mode 100644 index 0000000..62aa154 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_extint.h @@ -0,0 +1,129 @@ +/* + avr_extint.h + + External Interrupt Handling (for INT0-3) + + Copyright 2008, 2009 Michel Pollet + Copyright 2014 Doug Szumski + + 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 . + */ + +#ifndef __AVR_EXTINT_H__ +#define __AVR_EXTINT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + + +enum { + EXTINT_IRQ_OUT_INT0 = 0, + EXTINT_IRQ_OUT_INT1, EXTINT_IRQ_OUT_INT2, EXTINT_IRQ_OUT_INT3, + EXTINT_IRQ_OUT_INT4, EXTINT_IRQ_OUT_INT5, EXTINT_IRQ_OUT_INT6, + EXTINT_IRQ_OUT_INT7, + EXTINT_COUNT +}; + +// Get the internal IRQ corresponding to the INT +#define AVR_IOCTL_EXTINT_GETIRQ() AVR_IOCTL_DEF('i','n','t',' ') + +/* + * This module is just a "relay" for the pin change IRQ in the IO port + * module. We hook up to their IRQ and raise out interrupt vectors as needed + * + * "isc" is handled, apart from the "level" mode that doesn't make sense here (?) + */ +typedef struct avr_extint_t { + avr_io_t io; + + struct { + avr_regbit_t isc[2]; // interrupt sense control bits + avr_int_vector_t vector; // interrupt vector + + uint32_t port_ioctl; // ioctl to use to get port + uint8_t port_pin; // pin number in said port + uint8_t strict_lvl_trig;// enforces a repetitive interrupt triggering while the pin is held low + } eint[EXTINT_COUNT]; + +} avr_extint_t; + +void avr_extint_init(avr_t * avr, avr_extint_t * p); +int avr_extint_is_strict_lvl_trig(avr_t * avr, uint8_t extint_no); +void avr_extint_set_strict_lvl_trig(avr_t * avr, uint8_t extint_no, uint8_t strict); + + +// Declares a typical INT into a avr_extint_t in a core. +// this is a shortcut since INT declarations are pretty standard. +// The Tinies as well as the atmega1280 are slightly different. +// See sim_tinyx5.h and sim_mega1280.h +#define AVR_EXTINT_DECLARE(_index, _portname, _portpin) \ + .eint[_index] = { \ + .port_ioctl = AVR_IOCTL_IOPORT_GETIRQ(_portname), \ + .port_pin = _portpin, \ + .isc = { AVR_IO_REGBIT(EICRA, ISC##_index##0), AVR_IO_REGBIT(EICRA, ISC##_index##1) },\ + .vector = { \ + .enable = AVR_IO_REGBIT(EIMSK, INT##_index), \ + .raised = AVR_IO_REGBIT(EIFR, INTF##_index), \ + .vector = INT##_index##_vect, \ + },\ + } + +// Asynchronous External Interrupt, for example INT2 on the m16 and m32 +// Uses only 1 interrupt sense control bit +#define AVR_ASYNC_EXTINT_DECLARE(_index, _portname, _portpin) \ + .eint[_index] = { \ + .port_ioctl = AVR_IOCTL_IOPORT_GETIRQ(_portname), \ + .port_pin = _portpin, \ + .isc = { AVR_IO_REGBIT(MCUCSR, ISC##_index) },\ + .vector = { \ + .enable = AVR_IO_REGBIT(GICR, INT##_index), \ + .raised = AVR_IO_REGBIT(GIFR, INTF##_index), \ + .vector = INT##_index##_vect, \ + },\ + } + +#define AVR_EXTINT_MEGA_DECLARE(_index, _portname, _portpin, _EICR) \ + .eint[_index] = { \ + .port_ioctl = AVR_IOCTL_IOPORT_GETIRQ(_portname), \ + .port_pin = _portpin, \ + .isc = { AVR_IO_REGBIT(EICR##_EICR, ISC##_index##0), AVR_IO_REGBIT(EICR##_EICR, ISC##_index##1) },\ + .vector = { \ + .enable = AVR_IO_REGBIT(EIMSK, INT##_index), \ + .raised = AVR_IO_REGBIT(EIFR, INTF##_index), \ + .vector = INT##_index##_vect, \ + },\ + } + +#define AVR_EXTINT_TINY_DECLARE(_index, _portname, _portpin, _IFR) \ + .eint[_index] = { \ + .port_ioctl = AVR_IOCTL_IOPORT_GETIRQ(_portname), \ + .port_pin = _portpin, \ + .isc = { AVR_IO_REGBIT(MCUCR, ISC##_index##0), AVR_IO_REGBIT(MCUCR, ISC##_index##1) }, \ + .vector = { \ + .enable = AVR_IO_REGBIT(GIMSK, INT##_index), \ + .raised = AVR_IO_REGBIT(_IFR, INTF##_index), \ + .vector = INT##_index##_vect, \ + }, \ + } + +#ifdef __cplusplus +}; +#endif + +#endif /*__AVR_EXTINT_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.c new file mode 100644 index 0000000..cc1fd96 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.c @@ -0,0 +1,145 @@ +/* + avr_flash.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include "avr_flash.h" + +static avr_cycle_count_t avr_progen_clear(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_flash_t * p = (avr_flash_t *)param; + avr_regbit_clear(p->io.avr, p->selfprgen); + AVR_LOG(avr, LOG_WARNING, "FLASH: avr_progen_clear - SPM not received, clearing PRGEN bit\n"); + return 0; +} + + +static void avr_flash_write(avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_flash_t * p = (avr_flash_t *)param; + + avr_core_watch_write(avr, addr, v); + +// printf("** avr_flash_write %02x\n", v); + + if (avr_regbit_get(avr, p->selfprgen)) + avr_cycle_timer_register(avr, 4, avr_progen_clear, p); // 4 cycles is very little! +} + +static void avr_flash_clear_temppage(avr_flash_t *p) +{ + for (int i = 0; i < p->spm_pagesize / 2; i++) { + p->tmppage[i] = 0xff; + p->tmppage_used[i] = 0; + } +} + +static int avr_flash_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param) +{ + if (ctl != AVR_IOCTL_FLASH_SPM) + return -1; + + avr_flash_t * p = (avr_flash_t *)port; + avr_t * avr = p->io.avr; + + avr_flashaddr_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); + if (avr->rampz) + z |= avr->data[avr->rampz] << 16; + uint16_t r01 = avr->data[0] | (avr->data[1] << 8); + +// printf("AVR_IOCTL_FLASH_SPM %02x Z:%04x R01:%04x\n", avr->data[p->r_spm], z,r01); + if (avr_regbit_get(avr, p->selfprgen)) { + avr_cycle_timer_cancel(avr, avr_progen_clear, p); + + if (avr_regbit_get(avr, p->pgers)) { + z &= ~1; + AVR_LOG(avr, LOG_TRACE, "FLASH: Erasing page %04x (%d)\n", (z / p->spm_pagesize), p->spm_pagesize); + for (int i = 0; i < p->spm_pagesize; i++) + avr->flash[z++] = 0xff; + } else if (avr_regbit_get(avr, p->pgwrt)) { + z &= ~(p->spm_pagesize - 1); + AVR_LOG(avr, LOG_TRACE, "FLASH: Writing page %04x (%d)\n", (z / p->spm_pagesize), p->spm_pagesize); + for (int i = 0; i < p->spm_pagesize / 2; i++) { + avr->flash[z++] = p->tmppage[i]; + avr->flash[z++] = p->tmppage[i] >> 8; + } + avr_flash_clear_temppage(p); + } else if (avr_regbit_get(avr, p->blbset)) { + AVR_LOG(avr, LOG_TRACE, "FLASH: Setting lock bits (ignored)\n"); + } else if (p->flags & AVR_SELFPROG_HAVE_RWW && avr_regbit_get(avr, p->rwwsre)) { + avr_flash_clear_temppage(p); + } else { + AVR_LOG(avr, LOG_TRACE, "FLASH: Writing temppage %08x (%04x)\n", z, r01); + z >>= 1; + if (!p->tmppage_used[z % (p->spm_pagesize / 2)]) { + p->tmppage[z % (p->spm_pagesize / 2)] = r01; + p->tmppage_used[z % (p->spm_pagesize / 2)] = 1; + } + } + } + avr_regbit_clear(avr, p->selfprgen); + return 0; +} + +static void +avr_flash_reset(avr_io_t * port) +{ + avr_flash_t * p = (avr_flash_t *) port; + + avr_flash_clear_temppage(p); +} + +static void +avr_flash_dealloc(struct avr_io_t * port) +{ + avr_flash_t * p = (avr_flash_t *) port; + + if (p->tmppage) + free(p->tmppage); + + if (p->tmppage_used) + free(p->tmppage_used); +} + +static avr_io_t _io = { + .kind = "flash", + .ioctl = avr_flash_ioctl, + .reset = avr_flash_reset, + .dealloc = avr_flash_dealloc, +}; + +void avr_flash_init(avr_t * avr, avr_flash_t * p) +{ + p->io = _io; +// printf("%s init SPM %04x\n", __FUNCTION__, p->r_spm); + + if (!p->tmppage) + p->tmppage = malloc(p->spm_pagesize); + + if (!p->tmppage_used) + p->tmppage_used = malloc(p->spm_pagesize / 2); + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->flash); + + avr_register_io_write(avr, p->r_spm, avr_flash_write, p); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.h new file mode 100644 index 0000000..07747fc --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_flash.h @@ -0,0 +1,92 @@ +/* + avr_flash.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + + +#ifndef __AVR_FLASH_H___ +#define __AVR_FLASH_H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +/* + * Handles self-programming subsystem if the core + * supports it. + */ +typedef struct avr_flash_t { + avr_io_t io; + + uint16_t flags; + uint16_t *tmppage; + uint8_t *tmppage_used; + uint16_t spm_pagesize; + uint8_t r_spm; + avr_regbit_t selfprgen; + avr_regbit_t pgers; // page erase + avr_regbit_t pgwrt; // page write + avr_regbit_t blbset; // lock bit set + avr_regbit_t rwwsre; // read while write section read enable + avr_regbit_t rwwsb; // read while write section busy + + avr_int_vector_t flash; // Interrupt vector +} avr_flash_t; + +/* Set if the flash supports a Read While Write section */ +#define AVR_SELFPROG_HAVE_RWW (1 << 0) + +void avr_flash_init(avr_t * avr, avr_flash_t * p); + + +#define AVR_IOCTL_FLASH_SPM AVR_IOCTL_DEF('f','s','p','m') + +#define AVR_SELFPROG_DECLARE_INTERNAL(_spmr, _spen, _vector) \ + .r_spm = _spmr,\ + .spm_pagesize = SPM_PAGESIZE,\ + .selfprgen = AVR_IO_REGBIT(_spmr, _spen),\ + .pgers = AVR_IO_REGBIT(_spmr, PGERS),\ + .pgwrt = AVR_IO_REGBIT(_spmr, PGWRT),\ + .blbset = AVR_IO_REGBIT(_spmr, BLBSET),\ + .flash = {\ + .enable = AVR_IO_REGBIT(_spmr, SPMIE),\ + .vector = _vector,\ + }\ + +#define AVR_SELFPROG_DECLARE_NORWW(_spmr, _spen, _vector) \ + .selfprog = {\ + .flags = 0,\ + AVR_SELFPROG_DECLARE_INTERNAL(_spmr, _spen, _vector),\ + } + +#define AVR_SELFPROG_DECLARE(_spmr, _spen, _vector) \ + .selfprog = {\ + .flags = AVR_SELFPROG_HAVE_RWW,\ + AVR_SELFPROG_DECLARE_INTERNAL(_spmr, _spen, _vector),\ + .rwwsre = AVR_IO_REGBIT(_spmr, RWWSRE),\ + .rwwsb = AVR_IO_REGBIT(_spmr, RWWSB),\ + } + +#ifdef __cplusplus +}; +#endif + +#endif /* __AVR_FLASH_H___ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.c new file mode 100644 index 0000000..91cf106 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.c @@ -0,0 +1,340 @@ +/* + avr_ioport.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include "avr_ioport.h" + +#define D(_w) + +static void +avr_ioport_flag_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + + // Clear interrupt if 1 is written to flag. + + if (avr_regbit_from_value(avr, p->pcint.raised, v)) + avr_clear_interrupt(avr, &p->pcint); +} + +static uint8_t +avr_ioport_read( + struct avr_t * avr, + avr_io_addr_t addr, + void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + uint8_t ddr = avr->data[p->r_ddr]; + uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr); + avr->data[addr] = v; + avr_raise_irq(p->io.irq + IOPORT_IRQ_REG_PIN, v); + D(if (avr->data[addr] != v) printf("** PIN%c(%02x) = %02x\r\n", p->name, addr, v);) + + // made to trigger potential watchpoints + v = avr_core_watch_read(avr, addr); + return v; +} + +static void +avr_ioport_update_irqs( + avr_ioport_t * p) +{ + avr_t * avr = p->io.avr; + uint8_t ddr = avr->data[p->r_ddr]; + // Set the PORT value if the pin is marked as output + // otherwise, if there is an 'external' pullup, set it + // otherwise, if the PORT pin was 1 to indicate an + // internal pullup, set that. + for (int i = 0; i < 8; i++) { + if (ddr & (1 << i)) + avr_raise_irq(p->io.irq + i, (avr->data[p->r_port] >> i) & 1); + else if (p->external.pull_mask & (1 << i)) + avr_raise_irq(p->io.irq + i, (p->external.pull_value >> i) & 1); + else if ((avr->data[p->r_port] >> i) & 1) + avr_raise_irq(p->io.irq + i, 1); + } + uint8_t pin = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr); + pin = (pin & ~p->external.pull_mask) | p->external.pull_value; + avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, pin); + + // if IRQs are registered on the PORT register (for example, VCD dumps) send + // those as well + avr_io_addr_t port_io = AVR_DATA_TO_IO(p->r_port); + if (avr->io[port_io].irq) { + avr_raise_irq(avr->io[port_io].irq + AVR_IOMEM_IRQ_ALL, avr->data[p->r_port]); + for (int i = 0; i < 8; i++) + avr_raise_irq(avr->io[port_io].irq + i, (avr->data[p->r_port] >> i) & 1); + } +} + +static void +avr_ioport_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + + D(if (avr->data[addr] != v) printf("** PORT%c(%02x) = %02x\r\n", p->name, addr, v);) + avr_core_watch_write(avr, addr, v); + avr_raise_irq(p->io.irq + IOPORT_IRQ_REG_PORT, v); + avr_ioport_update_irqs(p); +} + +/* + * This is a reasonably new behaviour for the io-ports. Writing 1's to the PIN register + * toggles the PORT equivalent bit (regardless of direction + */ +static void +avr_ioport_pin_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + + avr_ioport_write(avr, p->r_port, avr->data[p->r_port] ^ v, param); +} + +/* + * This is a the callback for the DDR register. There is nothing much to do here, apart + * from triggering an IRQ in case any 'client' code is interested in the information, + * and restoring all PIN bits marked as output to PORT values. + */ +static void +avr_ioport_ddr_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + + D(if (avr->data[addr] != v) printf("** DDR%c(%02x) = %02x\r\n", p->name, addr, v);) + avr_raise_irq(p->io.irq + IOPORT_IRQ_DIRECTION_ALL, v); + avr_core_watch_write(avr, addr, v); + avr_ioport_update_irqs(p); +} + +/* + * this is our "main" pin change callback, it can be triggered by either the + * AVR code, or any external piece of code that see fit to do it. + * Either way, this will raise pin change interrupts, if needed + */ +void +avr_ioport_irq_notify( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + avr_t * avr = p->io.avr; + + int output = value & AVR_IOPORT_OUTPUT; + value &= 0xff; + uint8_t mask = 1 << irq->irq; + uint8_t ddr = avr->data[p->r_ddr]; + + if (output) { + if ((mask & ddr) == 0) + return; // TODO: stop further processing of IRQ. + + // If the IRQ was marked as Output, also do the IO write. + + avr_ioport_write(avr, + p->r_port, + (avr->data[p->r_port] & ~mask) | + (value ? mask : 0), + p); + } else { + // Set the real PIN bit. Ignore DDR as it's masked when read. + + avr_core_watch_write(avr, p->r_pin, + (avr->data[p->r_pin] & ~mask) | + (value ? mask : 0)); + + /* BUG: If DDR bit is set here, there should be no + * interrupt. But a spurious IRQ call by the user + * is indistinguishable from an internal one + * caused by writing the output port register and + * that should cause an interrupt. Doh! + */ + } + + if (p->r_pcint) { + // Ignore lingering copy of AVR_IOPORT_OUTPUT, or + // differing non-zero values. + + if (!value == !(irq->value & 0xff)) + return; + + // if the pcint bit is on, try to raise it + + int raisedata = avr->data[p->r_pcint]; + uint8_t uiRegMask = p->mask; + int8_t iShift = p->shift; + + if (uiRegMask) // If mask is 0, do nothing (backwards compat) + raisedata &= uiRegMask; // Mask off + + if (iShift>0) // Shift data if necessary for alignment. + raisedata <<= iShift; + else if (iShift<0) + raisedata >>= -iShift; + + int raise = raisedata & mask; + if (raise) + avr_raise_interrupt(avr, &p->pcint); + } +} + +static void +avr_ioport_reset( + avr_io_t * port) +{ + avr_ioport_t * p = (avr_ioport_t *)port; + for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) + avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p); +} + +static int +avr_ioport_ioctl( + struct avr_io_t * port, + uint32_t ctl, + void * io_param) +{ + avr_ioport_t * p = (avr_ioport_t *)port; + avr_t * avr = p->io.avr; + int res = -1; + + // all IOCTls require some sort of valid parameter, bail if not + if (!io_param) + return -1; + + switch(ctl) { + case AVR_IOCTL_IOPORT_GETIRQ_REGBIT: { + avr_ioport_getirq_t * r = (avr_ioport_getirq_t*)io_param; + + if (r->bit.reg == p->r_port || r->bit.reg == p->r_pin || r->bit.reg == p->r_ddr) { + // it's us ! check the special case when the "all pins" irq is requested + int o = 0; + if (r->bit.mask == 0xff) + r->irq[o++] = &p->io.irq[IOPORT_IRQ_PIN_ALL]; + else { + // otherwise fill up the ones needed + for (int bi = 0; bi < 8; bi++) + if (r->bit.mask & (1 << bi)) + r->irq[o++] = &p->io.irq[r->bit.bit + bi]; + } + if (o < 8) + r->irq[o] = NULL; + return o; + } + } break; + default: { + /* + * Return the port state if the IOCTL matches us. + */ + if (ctl == AVR_IOCTL_IOPORT_GETSTATE(p->name)) { + avr_ioport_state_t state = { + .name = p->name, + .port = avr->data[p->r_port], + .ddr = avr->data[p->r_ddr], + .pin = avr->data[p->r_pin], + }; + if (io_param) + *((avr_ioport_state_t*)io_param) = state; + res = 0; + } + /* + * Set the default IRQ values when pin is set as input + */ + if (ctl == AVR_IOCTL_IOPORT_SET_EXTERNAL(p->name)) { + avr_ioport_external_t * m = (avr_ioport_external_t*)io_param; + p->external.pull_mask = m->mask; + p->external.pull_value = m->value; + res = 0; + } + } + } + + return res; +} + +static const char * irq_names[IOPORT_IRQ_COUNT] = { + [IOPORT_IRQ_PIN0] = "=pin0", + [IOPORT_IRQ_PIN1] = "=pin1", + [IOPORT_IRQ_PIN2] = "=pin2", + [IOPORT_IRQ_PIN3] = "=pin3", + [IOPORT_IRQ_PIN4] = "=pin4", + [IOPORT_IRQ_PIN5] = "=pin5", + [IOPORT_IRQ_PIN6] = "=pin6", + [IOPORT_IRQ_PIN7] = "=pin7", + [IOPORT_IRQ_PIN_ALL] = "8>all", + [IOPORT_IRQ_DIRECTION_ALL] = "8>ddr", + [IOPORT_IRQ_REG_PORT] = "8>port", + [IOPORT_IRQ_REG_PIN] = "8>pin", +}; + +static avr_io_t _io = { + .kind = "port", + .reset = avr_ioport_reset, + .ioctl = avr_ioport_ioctl, + .irq_names = irq_names, +}; + +void avr_ioport_init(avr_t * avr, avr_ioport_t * p) +{ + if (!p->r_port) { + printf("skipping PORT%c for core %s\n", p->name, avr->mmcu); + return; + } + p->io = _io; +// printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__, +// p->name, p->r_pin, +// p->name, p->r_ddr, +// p->name, p->r_port); + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->pcint); + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_IOPORT_GETIRQ(p->name), IOPORT_IRQ_COUNT, NULL); + + for (int i = 0; i < IOPORT_IRQ_REG_PIN; i++) { + p->io.irq[i].flags |= IRQ_FLAG_FILTERED; + if (i < IOPORT_IRQ_PIN_ALL) + p->io.irq[i].flags &= ~IRQ_FLAG_INIT; + } + avr_register_io_write(avr, p->r_port, avr_ioport_write, p); + avr_register_io_read(avr, p->r_pin, avr_ioport_read, p); + avr_register_io_write(avr, p->r_pin, avr_ioport_pin_write, p); + avr_register_io_write(avr, p->r_ddr, avr_ioport_ddr_write, p); + if (p->pcint.raised.reg) { + avr_register_io_write(avr, p->pcint.raised.reg, + avr_ioport_flag_write, p); + } +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.h new file mode 100644 index 0000000..024b695 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_ioport.h @@ -0,0 +1,148 @@ +/* + avr_ioport.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __AVR_IOPORT_H__ +#define __AVR_IOPORT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +enum { + IOPORT_IRQ_PIN0 = 0, + IOPORT_IRQ_PIN1,IOPORT_IRQ_PIN2,IOPORT_IRQ_PIN3,IOPORT_IRQ_PIN4, + IOPORT_IRQ_PIN5,IOPORT_IRQ_PIN6,IOPORT_IRQ_PIN7, + IOPORT_IRQ_PIN_ALL, + IOPORT_IRQ_DIRECTION_ALL, + IOPORT_IRQ_REG_PORT, + IOPORT_IRQ_REG_PIN, + IOPORT_IRQ_COUNT +}; + +#define AVR_IOPORT_OUTPUT 0x100 + +// add port name (uppercase) to get the real IRQ +#define AVR_IOCTL_IOPORT_GETIRQ(_name) AVR_IOCTL_DEF('i','o','g',(_name)) + + +// this ioctl takes a avr_regbit_t, compares the register address +// to PORT/PIN/DDR and return the corresponding IRQ(s) if it matches +typedef struct avr_ioport_getirq_t { + avr_regbit_t bit; // bit wanted + avr_irq_t * irq[8]; // result, terminated by NULL if < 8 +} avr_ioport_getirq_t; + +#define AVR_IOCTL_IOPORT_GETIRQ_REGBIT AVR_IOCTL_DEF('i','o','g','r') + +/* + * ioctl used to get a port state. + * + * for (int i = 'A'; i <= 'F'; i++) { + * avr_ioport_state_t state; + * if (avr_ioctl(AVR_IOCTL_IOPORT_GETSTATE(i), &state) == 0) + * printf("PORT%c %02x DDR %02x PIN %02x\n", + * state.name, state.port, state.ddr, state.pin); + * } + */ +typedef struct avr_ioport_state_t { + unsigned long name : 7, + port : 8, ddr : 8, pin : 8; +} avr_ioport_state_t; + +// add port name (uppercase) to get the port state +#define AVR_IOCTL_IOPORT_GETSTATE(_name) AVR_IOCTL_DEF('i','o','s',(_name)) + +/* + * ioctl used to set default port state when set as input. + * + */ +typedef struct avr_ioport_external_t { + unsigned long name : 7, + mask : 8, value : 8; +} avr_ioport_external_t; + +// add port name (uppercase) to set default input pin IRQ values +#define AVR_IOCTL_IOPORT_SET_EXTERNAL(_name) AVR_IOCTL_DEF('i','o','p',(_name)) + +/** + * pin structure + */ +typedef struct avr_iopin_t { + uint16_t port : 8; ///< port e.g. 'B' + uint16_t pin : 8; ///< pin number +} avr_iopin_t; +#define AVR_IOPIN(_port, _pin) { .port = _port, .pin = _pin } + +/* + * Definition for an IO port + */ +typedef struct avr_ioport_t { + avr_io_t io; + char name; + avr_io_addr_t r_port; + avr_io_addr_t r_ddr; + avr_io_addr_t r_pin; + + avr_int_vector_t pcint; // PCINT vector + avr_io_addr_t r_pcint; // pcint 8 pins mask + + // Mask and shift for PCINTs. This is needed for chips like the 2560 + // where PCINT do not align with IRQs. + + uint8_t mask; + int8_t shift; + + // This represent the default IRQ value when + // the port is set as input. + // If the mask is not set, no output value is sent + // on the output IRQ. If the mask is set, the specified + // value is sent. + struct { + uint8_t pull_mask, pull_value; + } external; +} avr_ioport_t; + +void avr_ioport_init(avr_t * avr, avr_ioport_t * port); + +#define AVR_IOPORT_DECLARE(_lname, _cname, _uname) \ + .port ## _lname = { \ + .name = _cname, .r_port = PORT ## _uname, .r_ddr = DDR ## _uname, .r_pin = PIN ## _uname, \ + } + +#define AVR_IOPORT_DECLARE_PC(_lname, _cname, _uname, _pcnum) \ + .port ## _lname = { \ + .name = _cname, .r_port = PORT ## _uname, \ + .r_ddr = DDR ## _uname, .r_pin = PIN ## _uname, \ + .pcint = { \ + .enable = AVR_IO_REGBIT(PCICR, PCIE ## _pcnum), \ + .raised = AVR_IO_REGBIT(PCIFR, PCIF ## _pcnum), \ + .vector = PCINT ## _pcnum ## _vect, \ + }, \ + .r_pcint = PCMSK ## _pcnum, \ + } + +#ifdef __cplusplus +}; +#endif + +#endif /* __AVR_IOPORT_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.c new file mode 100644 index 0000000..001b34c --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.c @@ -0,0 +1,106 @@ +/* + avr_lin.h + + Copyright 2008, 2011 Michel Pollet + Copyright 2011 Markus Lampert + + 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 . + */ + +#include +#include "avr_lin.h" +#include "sim_time.h" + + +static void +avr_lin_baud_write( + struct avr_t *avr, + avr_io_addr_t addr, + uint8_t v, + void *param) +{ + avr_lin_t *p = (avr_lin_t*) param; + + if (p->r_linbtr != p->ldisr.reg || p->r_linbtr != p->lbt.reg) { // sanity check + AVR_LOG(avr, LOG_ERROR, "LIN: LDISR and LBT[x] register different!\n"); + return; + } + + AVR_LOG(avr, LOG_TRACE, "LIN: addr[%04x] = %02x\n", addr, v); + if (addr == p->ldisr.reg) { + if (avr_regbit_get(avr, p->lena)) { + AVR_LOG(avr, LOG_WARNING, "LIN: LENA bit set on changing LBTR\n"); + return; + } + if ((v >> p->ldisr.bit) & p->ldisr.mask) { + uint8_t lbt = (v >> p->lbt.bit) & p->lbt.mask; + uint8_t ov = v; + v = (1 << p->ldisr.bit) | (lbt << p->lbt.bit); + AVR_LOG(avr, LOG_TRACE, "LIN: v=%02x -> LBT = %02x -> LINBT = %02x\n", ov, lbt, v); + } else { + v = 0x20; + } + } + avr_core_watch_write(avr, addr, v); // actually set the value + + uint32_t lbt = avr_regbit_get(avr, p->lbt); // Min value CANNOT be zero + uint32_t lbrr = (avr->data[p->r_linbrrh] << 8) | avr->data[p->r_linbrrl]; + AVR_LOG(avr, LOG_TRACE, "LIN: UART LBT/LBRR to %04x/%04x\n", lbt, lbrr); + // there is no division by zero case here, lbt is >= 8 + //uint32_t baud = avr->frequency / (lbt * (lbrr + 1)); + uint32_t word_size = 1 /*start*/+ 8 /*data bits*/+ 1 /*parity*/+ 1 /*stop*/; + int cycles_per_bit = lbt * (lbrr + 1); + double baud = ((double)avr->frequency) / cycles_per_bit; // can be less than 1 + p->uart.cycles_per_byte = cycles_per_bit * word_size; + + AVR_LOG(avr, LOG_TRACE, "LIN: UART configured to %04x/%04x = %.4f bps, 8 data 1 stop\n", lbt, + lbrr, baud); + + //p->uart.cycles_per_byte = 1000000 / (baud / word_size); + AVR_LOG(avr, LOG_TRACE, "LIN: Roughly %d usec per byte\n", + avr_cycles_to_usec(avr, p->uart.cycles_per_byte)); +} + +static void +avr_lin_reset( + avr_io_t *port) +{ + avr_lin_t *p = (avr_lin_t*) port; + avr_t * avr = p->io.avr; + + AVR_LOG(avr, LOG_TRACE, "LIN: UART: reset\n"); + + p->uart.io.reset(&p->uart.io); + avr->data[p->r_linbtr] = 0x20; +} + +static avr_io_t _io = { + .kind = "lin", + .reset = avr_lin_reset, +}; + +void +avr_lin_init( + avr_t *avr, + avr_lin_t *p) +{ + // init uart part + avr_uart_init(avr, &p->uart); + + p->io = _io; + avr_register_io_write(avr, p->r_linbtr, avr_lin_baud_write, p); + avr_register_io_write(avr, p->r_linbrrl, avr_lin_baud_write, p); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.h new file mode 100644 index 0000000..b1ecadf --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_lin.h @@ -0,0 +1,55 @@ +/* + avr_lin.h + + Copyright 2008, 2011 Michel Pollet + Copyright 2011 Markus Lampert + + 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 . + */ + +#ifndef __AVR_LIN_H__ +#define __AVR_LIN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" +#include "avr_uart.h" + +typedef struct avr_lin_t { + avr_io_t io; + + avr_io_addr_t r_linbtr; + avr_io_addr_t r_linbrrh, r_linbrrl; + + avr_regbit_t lena; + avr_regbit_t ldisr; + avr_regbit_t lbt; + + avr_uart_t uart; // used when LIN controller is setup as a UART +} avr_lin_t; + +void +avr_lin_init( + avr_t *avr, + avr_lin_t *port); + +#ifdef __cplusplus +}; +#endif + +#endif /* __AVR_LIN_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.c new file mode 100644 index 0000000..bb602ac --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.c @@ -0,0 +1,119 @@ +/* + avr_spi.c + + Copyright 2008, 2009 Michel Pollet + Modified 2020 by 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 . + */ + +#include +#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] = "8io = _io; + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->spi); + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_SPI_GETIRQ(p->name), SPI_IRQ_COUNT, NULL); + + avr_register_io_write(avr, p->r_spdr, avr_spi_write, p); + avr_register_io_read(avr, p->r_spdr, avr_spi_read, p); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.h new file mode 100644 index 0000000..c676adb --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_spi.h @@ -0,0 +1,107 @@ +/* + avr_spi.h + + Copyright 2008, 2009 Michel Pollet + Modified 2020 by 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 . + */ + +#ifndef __AVR_SPI_H__ +#define __AVR_SPI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +enum { + SPI_IRQ_INPUT = 0, + SPI_IRQ_OUTPUT, + SPI_IRQ_COUNT +}; + +// add port number to get the real IRQ +#define AVR_IOCTL_SPI_GETIRQ(_name) AVR_IOCTL_DEF('s','p','i',(_name)) + +typedef struct avr_spi_t { + avr_io_t io; + char name; + avr_regbit_t disabled; // bit in the PRR + + avr_io_addr_t r_spdr; // data register + avr_io_addr_t r_spcr; // control register + avr_io_addr_t r_spsr; // status register + + avr_regbit_t spe; // spi enable + avr_regbit_t mstr; // master/slave + avr_regbit_t spr[4]; // clock divider + + avr_int_vector_t spi; // spi interrupt + + uint8_t input_data_register; +} avr_spi_t; + +void avr_spi_init(avr_t * avr, avr_spi_t * port); + +#define AVR_SPIX_DECLARE(_name, _prr, _prspi) \ + .spi = { \ + .name = '0' + _name,\ + .disabled = AVR_IO_REGBIT(_prr, _prspi), \ + \ + .r_spdr = SPDR ## _name, \ + .r_spcr = SPCR ## _name, \ + .r_spsr = SPSR ## _name, \ + \ + .spe = AVR_IO_REGBIT(SPCR ## _name, SPE ## _name), \ + .mstr = AVR_IO_REGBIT(SPCR ## _name, MSTR ## _name), \ + \ + .spr = { AVR_IO_REGBIT(SPCR ## _name, SPR0 ## _name), \ + AVR_IO_REGBIT(SPCR ## _name, SPR1 ## _name), \ + AVR_IO_REGBIT(SPSR ## _name, SPI2X ## _name) }, \ + .spi = { \ + .enable = AVR_IO_REGBIT(SPCR ## _name, SPIE ## _name), \ + .raised = AVR_IO_REGBIT(SPSR ## _name, SPIF ## _name), \ + .vector = SPI_STC_vect, \ + }, \ + } + + +#define AVR_SPI_DECLARE(_prr, _prspi) \ + .spi = { \ + .disabled = AVR_IO_REGBIT(_prr, _prspi), \ + \ + .r_spdr = SPDR, \ + .r_spcr = SPCR, \ + .r_spsr = SPSR, \ + \ + .spe = AVR_IO_REGBIT(SPCR, SPE), \ + .mstr = AVR_IO_REGBIT(SPCR, MSTR), \ + \ + .spr = { AVR_IO_REGBIT(SPCR, SPR0), AVR_IO_REGBIT(SPCR, SPR1), AVR_IO_REGBIT(SPSR, SPI2X) }, \ + .spi = { \ + .enable = AVR_IO_REGBIT(SPCR, SPIE), \ + .raised = AVR_IO_REGBIT(SPSR, SPIF), \ + .vector = SPI_STC_vect, \ + }, \ + } + +#ifdef __cplusplus +}; +#endif + +#endif /*__AVR_SPI_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.c new file mode 100644 index 0000000..39a46bd --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.c @@ -0,0 +1,985 @@ +/* + avr_timer.c + + Handles the 8 bits and 16 bits AVR timer. + Handles + + CDC + + Fast PWM + + Copyright 2008-2012 Michel Pollet + + 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 . + */ + +#include +#include + +#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] = "io = _io; + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->overflow); + avr_register_vector(avr, &p->icr); + + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_TIMER_GETIRQ(p->name), TIMER_IRQ_COUNT, NULL); + + // marking IRQs as "filtered" means they don't propagate if the + // new value raised is the same as the last one.. in the case of the + // pwm value it makes sense not to bother. + p->io.irq[TIMER_IRQ_OUT_PWM0].flags |= IRQ_FLAG_FILTERED; + p->io.irq[TIMER_IRQ_OUT_PWM1].flags |= IRQ_FLAG_FILTERED; + p->io.irq[TIMER_IRQ_OUT_PWM2].flags |= IRQ_FLAG_FILTERED; + + if (p->wgm[0].reg) // these are not present on older AVRs + avr_register_io_write(avr, p->wgm[0].reg, avr_timer_write, p); + if (p->wgm[1].reg && + (p->wgm[1].reg != p->wgm[0].reg)) + avr_register_io_write(avr, p->wgm[1].reg, avr_timer_write, p); + if (p->wgm[2].reg && + (p->wgm[2].reg != p->wgm[0].reg) && + (p->wgm[2].reg != p->wgm[1].reg)) + avr_register_io_write(avr, p->wgm[2].reg, avr_timer_write, p); + if (p->wgm[3].reg && + (p->wgm[3].reg != p->wgm[0].reg) && + (p->wgm[3].reg != p->wgm[1].reg) && + (p->wgm[3].reg != p->wgm[2].reg)) + avr_register_io_write(avr, p->wgm[3].reg, avr_timer_write, p); + + avr_register_io_write(avr, p->cs[0].reg, avr_timer_write, p); + if (p->cs[1].reg && + (p->cs[1].reg != p->cs[0].reg)) + avr_register_io_write(avr, p->cs[1].reg, avr_timer_write, p); + if (p->cs[2].reg && + (p->cs[2].reg != p->cs[0].reg) && (p->cs[2].reg != p->cs[1].reg)) + avr_register_io_write(avr, p->cs[2].reg, avr_timer_write, p); + if (p->cs[3].reg && + (p->cs[3].reg != p->cs[0].reg) && + (p->cs[3].reg != p->cs[1].reg) && + (p->cs[3].reg != p->cs[2].reg)) + avr_register_io_write(avr, p->cs[3].reg, avr_timer_write, p); + + if (p->as2.reg) // as2 signifies timer/counter 2... therefore must check for register. + avr_register_io_write(avr, p->as2.reg, avr_timer_write, p); + + // this assumes all the "pending" interrupt bits are in the same + // register. Might not be true on all devices ? + avr_register_io_write(avr, p->overflow.raised.reg, avr_timer_write_pending, p); + + /* + * Even if the timer is 16 bits, we don't care to have watches on the + * high bytes because the datasheet says that the low address is always + * the trigger. + */ + for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) { + p->comp[compi].timer = p; + + avr_register_vector(avr, &p->comp[compi].interrupt); + + if (p->comp[compi].r_ocr) // not all timers have all comparators + avr_register_io_write(avr, p->comp[compi].r_ocr, avr_timer_write_ocr, &p->comp[compi]); + if (p->comp[compi].foc.reg) + avr_register_io_write(avr, p->comp[compi].foc.reg, avr_timer_write_foc, p); + } + avr_register_io_write(avr, p->r_tcnt, avr_timer_tcnt_write, p); + avr_register_io_read(avr, p->r_tcnt, avr_timer_tcnt_read, p); + + if (p->as2.reg) { + p->ext_clock_flags = AVR_TIMER_EXTCLK_FLAG_VIRT; + p->ext_clock = 32768.0f; + } else { + p->ext_clock_flags = 0; + p->ext_clock = 0.0f; + } +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.h new file mode 100644 index 0000000..837936c --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_timer.h @@ -0,0 +1,176 @@ +/* + avr_timer.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __AVR_TIMER_H__ +#define __AVR_TIMER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +enum { + AVR_TIMER_COMPA = 0, + AVR_TIMER_COMPB, + AVR_TIMER_COMPC, + + AVR_TIMER_COMP_COUNT +}; + +enum { + TIMER_IRQ_OUT_PWM0 = 0, + TIMER_IRQ_OUT_PWM1, + TIMER_IRQ_OUT_PWM2, + TIMER_IRQ_IN_ICP, // input capture + TIMER_IRQ_OUT_COMP, // comparator pins output IRQ + + TIMER_IRQ_COUNT = TIMER_IRQ_OUT_COMP + AVR_TIMER_COMP_COUNT +}; + +// Get the internal IRQ corresponding to the INT +#define AVR_IOCTL_TIMER_GETIRQ(_name) AVR_IOCTL_DEF('t','m','r',(_name)) + +// add timer number/name (character) to set tracing flags +#define AVR_IOCTL_TIMER_SET_TRACE(_number) AVR_IOCTL_DEF('t','m','t',(_number)) +// enforce using virtual clock generator when external clock is chosen by firmware +#define AVR_IOCTL_TIMER_SET_VIRTCLK(_number) AVR_IOCTL_DEF('t','m','v',(_number)) +// set frequency of the virtual clock generator +#define AVR_IOCTL_TIMER_SET_FREQCLK(_number) AVR_IOCTL_DEF('t','m','f',(_number)) + +// Waveform generation modes +enum { + avr_timer_wgm_none = 0, // invalid mode + avr_timer_wgm_normal, + avr_timer_wgm_ctc, + avr_timer_wgm_pwm, + avr_timer_wgm_fast_pwm, + avr_timer_wgm_fc_pwm, +}; + +// Compare output modes +enum { + avr_timer_com_normal = 0,// Normal mode, OCnx disconnected + avr_timer_com_toggle, // Toggle OCnx on compare match + avr_timer_com_clear, // clear OCnx on compare match + avr_timer_com_set, // set OCnx on compare match + +}; + +enum { + avr_timer_wgm_reg_constant = 0, + avr_timer_wgm_reg_ocra, + avr_timer_wgm_reg_icr, +}; + +typedef struct avr_timer_wgm_t { + uint32_t top: 8, bottom: 8, size : 8, kind : 8; +} avr_timer_wgm_t; + +#define AVR_TIMER_EXTCLK_CHOOSE 0x80 // marker value for cs_div specifying ext clock selection +#define AVR_TIMER_EXTCLK_FLAG_TN 0x80 // Tn external clock chosen +#define AVR_TIMER_EXTCLK_FLAG_STARTED 0x40 // peripheral started +#define AVR_TIMER_EXTCLK_FLAG_REVDIR 0x20 // reverse counting (decrement) +#define AVR_TIMER_EXTCLK_FLAG_AS2 0x10 // asynchronous external clock chosen +#define AVR_TIMER_EXTCLK_FLAG_VIRT 0x08 // don't use the input pin, generate clock internally +#define AVR_TIMER_EXTCLK_FLAG_EDGE 0x01 // use the rising edge + +#define AVR_TIMER_WGM_NORMAL8() { .kind = avr_timer_wgm_normal, .size=8 } +#define AVR_TIMER_WGM_NORMAL16() { .kind = avr_timer_wgm_normal, .size=16 } +#define AVR_TIMER_WGM_CTC() { .kind = avr_timer_wgm_ctc, .top = avr_timer_wgm_reg_ocra } +#define AVR_TIMER_WGM_ICCTC() { .kind = avr_timer_wgm_ctc, .top = avr_timer_wgm_reg_icr } +#define AVR_TIMER_WGM_FASTPWM8() { .kind = avr_timer_wgm_fast_pwm, .size=8 } +#define AVR_TIMER_WGM_FASTPWM9() { .kind = avr_timer_wgm_fast_pwm, .size=9 } +#define AVR_TIMER_WGM_FASTPWM10() { .kind = avr_timer_wgm_fast_pwm, .size=10 } +#define AVR_TIMER_WGM_FCPWM8() { .kind = avr_timer_wgm_fc_pwm, .size=8 } +#define AVR_TIMER_WGM_FCPWM9() { .kind = avr_timer_wgm_fc_pwm, .size=9 } +#define AVR_TIMER_WGM_FCPWM10() { .kind = avr_timer_wgm_fc_pwm, .size=10 } +#define AVR_TIMER_WGM_OCPWM() { .kind = avr_timer_wgm_pwm, .top = avr_timer_wgm_reg_ocra } +#define AVR_TIMER_WGM_ICPWM() { .kind = avr_timer_wgm_pwm, .top = avr_timer_wgm_reg_icr } +#define AVR_TIMER_WGM_ICFASTPWM() { .kind = avr_timer_wgm_fast_pwm, .top = avr_timer_wgm_reg_icr } + +typedef struct avr_timer_comp_t { + avr_int_vector_t interrupt; // interrupt vector + struct avr_timer_t *timer; // parent timer + avr_io_addr_t r_ocr; // comparator register low byte + avr_io_addr_t r_ocrh; // comparator register hi byte + avr_regbit_t com; // comparator output mode registers + avr_regbit_t com_pin; // where comparator output is connected + uint64_t comp_cycles; + avr_regbit_t foc; // "force compare match" strobe +} avr_timer_comp_t, *avr_timer_comp_p; + +enum { + avr_timer_trace_ocr = (1 << 0), + avr_timer_trace_tcnt = (1 << 1), + + avr_timer_trace_compa = (1 << 8), + avr_timer_trace_compb = (1 << 9), + avr_timer_trace_compc = (1 << 10), +}; + +typedef struct avr_timer_t { + avr_io_t io; + char name; + uint32_t trace; // debug trace + + avr_regbit_t disabled; // bit in the PRR + + avr_io_addr_t r_tcnt, r_icr; + avr_io_addr_t r_tcnth, r_icrh; + + avr_regbit_t wgm[4]; + avr_timer_wgm_t wgm_op[16]; + avr_timer_wgm_t mode; + int wgm_op_mode_kind; + uint32_t wgm_op_mode_size; + + avr_regbit_t as2; // asynchronous clock 32khz + avr_regbit_t cs[4]; // specify control register bits choosing clock sourcre + uint8_t cs_div[16]; // translate control register value to clock prescaler (orders of 2 exponent) + uint32_t cs_div_value; + + avr_regbit_t ext_clock_pin; // external clock input pin, to link IRQs + uint8_t ext_clock_flags; // holds AVR_TIMER_EXTCLK_FLAG_ON, AVR_TIMER_EXTCLK_FLAG_EDGE and other ext. clock mode flags + float ext_clock; // external clock frequency, e.g. 32768Hz + + avr_regbit_t icp; // input capture pin, to link IRQs + avr_regbit_t ices; // input capture edge select + + avr_timer_comp_t comp[AVR_TIMER_COMP_COUNT]; + + avr_int_vector_t overflow; // overflow + avr_int_vector_t icr; // input capture + + uint64_t tov_cycles; // number of cycles from zero to overflow + float tov_cycles_fract; // fractional part for external clock with non int ratio to F_CPU + float phase_accumulator; + uint64_t tov_base; // MCU cycle when the last overflow occured; when clocked externally holds external clock count + uint16_t tov_top; // current top value to calculate tnct +} avr_timer_t; + +void avr_timer_init(avr_t * avr, avr_timer_t * port); + +#ifdef __cplusplus +}; +#endif + +#endif /*__AVR_TIMER_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.c new file mode 100644 index 0000000..521c03c --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.c @@ -0,0 +1,521 @@ +/* + avr_twi.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#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] = "8io = _io; + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->twi); + + //printf("%s TWI%c init\n", __FUNCTION__, p->name); + + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_TWI_GETIRQ(p->name), TWI_IRQ_COUNT, NULL); + + avr_register_io_write(avr, p->twen.reg, avr_twi_write, p); + avr_register_io_write(avr, p->r_twdr, avr_twi_write_data, p); + avr_register_io_read(avr, p->r_twdr, avr_twi_read_data, p); + avr_register_io_write(avr, p->twsr.reg, avr_twi_write_status, p); +} + +uint32_t +avr_twi_irq_msg( + uint8_t msg, + uint8_t addr, + uint8_t data) +{ + avr_twi_msg_irq_t _msg = { + .u.twi.msg = msg, + .u.twi.addr = addr, + .u.twi.data = data, + }; + return _msg.u.v; +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.h new file mode 100644 index 0000000..8b37ba3 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_twi.h @@ -0,0 +1,116 @@ +/* + avr_twi.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __AVR_TWI_H__ +#define __AVR_TWI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +//#include "sim_twi.h" + +enum { + TWI_IRQ_INPUT = 0, + TWI_IRQ_OUTPUT, + TWI_IRQ_STATUS, + TWI_IRQ_COUNT +}; + +enum { + TWI_COND_START = (1 << 0), + TWI_COND_STOP = (1 << 1), + TWI_COND_ADDR = (1 << 2), + TWI_COND_ACK = (1 << 3), + TWI_COND_WRITE = (1 << 4), + TWI_COND_READ = (1 << 5), + // internal state, do not use in irq messages + TWI_COND_SLAVE = (1 << 6), +}; + +typedef struct avr_twi_msg_t { + uint32_t unused : 8, + msg : 8, + addr : 8, + data : 8; +} avr_twi_msg_t; + +typedef struct avr_twi_msg_irq_t { + union { + uint32_t v; + avr_twi_msg_t twi; + } u; +} avr_twi_msg_irq_t; + +// add port number to get the real IRQ +#define AVR_IOCTL_TWI_GETIRQ(_name) AVR_IOCTL_DEF('t','w','i',(_name)) + +typedef struct avr_twi_t { + avr_io_t io; + char name; + + avr_regbit_t disabled; // bit in the PRR + + avr_io_addr_t r_twbr; // bit rate register + avr_io_addr_t r_twcr; // control register + avr_io_addr_t r_twsr; // status register + avr_io_addr_t r_twar; // address register (slave) + avr_io_addr_t r_twamr; // address mask register + avr_io_addr_t r_twdr; // data register + + avr_regbit_t twen; // twi enable bit + avr_regbit_t twea; // enable acknowledge bit + avr_regbit_t twsta; // start condition + avr_regbit_t twsto; // stop condition + avr_regbit_t twwc; // write collision + + avr_regbit_t twsr; // status registers, (5 bits) + avr_regbit_t twps; // prescaler bits (2 bits) + + avr_int_vector_t twi; // twi interrupt + + uint8_t state; + uint8_t peer_addr; + uint8_t next_twstate; +} avr_twi_t; + +void +avr_twi_init( + avr_t * avr, + avr_twi_t * port); + +/* + * Create a message value for twi including the 'msg' bitfield, + * 'addr' and data. This value is what is sent as the IRQ value + */ +uint32_t +avr_twi_irq_msg( + uint8_t msg, + uint8_t addr, + uint8_t data); + +#ifdef __cplusplus +}; +#endif + +#endif /*__AVR_TWI_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.c new file mode 100644 index 0000000..25b6fe6 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.c @@ -0,0 +1,546 @@ +/* + avr_uart.c + + Handles UART access + Right now just handle "write" to the serial port at any speed + and printf to the console when '\n' is written. + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifdef NO_COLOR + #define FONT_GREEN + #define FONT_DEFAULT +#else + #define FONT_GREEN "\e[32m" + #define FONT_DEFAULT "\e[0m" +#endif + +#include +#include +#include +#include +#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] = "8io = _io; + +// printf("%s UART%c UDR=%02x\n", __FUNCTION__, p->name, p->r_udr); + + p->flags = AVR_UART_FLAG_POLL_SLEEP|AVR_UART_FLAG_STDIO; + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->rxc); + avr_register_vector(avr, &p->txc); + avr_register_vector(avr, &p->udrc); + + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_UART_GETIRQ(p->name), UART_IRQ_COUNT, NULL); + // Only call callbacks when the value change... + p->io.irq[UART_IRQ_OUT_XOFF].flags |= IRQ_FLAG_FILTERED; + + avr_register_io_write(avr, p->r_udr, avr_uart_udr_write, p); + avr_register_io_read(avr, p->r_udr, avr_uart_read, p); + + // status bits + // monitor code that reads the rxc flag, and delay it a bit + avr_register_io_read(avr, p->rxc.raised.reg, avr_uart_status_read, p); + if (p->fe.reg != p->rxc.raised.reg) + avr_register_io_read(avr, p->fe.reg, avr_uart_status_read, p); + + if (p->udrc.vector) + avr_register_io_write(avr, p->udrc.enable.reg, avr_uart_write, p); + if (p->r_ucsra) + avr_register_io_write(avr, p->r_ucsra, avr_uart_write, p); + if (p->ubrrl.reg) + avr_register_io_write(avr, p->ubrrl.reg, avr_uart_baud_write, p); + avr_register_io_write(avr, p->rxen.reg, avr_uart_write, p); +} + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.h new file mode 100644 index 0000000..107359e --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_uart.h @@ -0,0 +1,229 @@ +/* + avr_uart.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __AVR_UART_H__ +#define __AVR_UART_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +#include "fifo_declare.h" + +DECLARE_FIFO(uint16_t, uart_fifo, 64); + +/* + * The method of "connecting" the the UART from external code is to use 4 IRQS. + * The easy one is UART->YOU, where you will be called with the byte every time + * the AVR firmware sends one. Do whatever you like with it. + * + * The slightly more tricky one is the INPUT part. Since the AVR is quite a bit + * slower than your code most likely, there is a way for the AVR UART to tell + * you to "pause" sending it bytes when its own input buffer is full. + * So, the UART will send XON to you when its fifo is empty, XON means you can + * send as many bytes as you have until XOFF is sent. Note that these are two + * IRQs because you /will/ be called with XOFF when sending a byte in INPUT... + * So it's a reentrant process. + * + * When XOFF has been called, do not send any new bytes, they would be dropped. + * Instead wait for XON again and continue. + * See examples/parts/uart_udp.c for a full implementation + * + * Pseudo code: + * + * volatile int off = 0; + * void irq_xon() + * { + * off = 0; + * while (!off && bytes_left) + * avr_raise_irq(UART_IRQ_INPUT, a_byte); + * } + * void irq_xoff() + * { + * off = 1; + * } + * + */ +enum { + UART_IRQ_INPUT = 0, + UART_IRQ_OUTPUT, + UART_IRQ_OUT_XON, // signaled (continuously) when input fifo is not full + UART_IRQ_OUT_XOFF, // signaled when input fifo IS full + UART_IRQ_COUNT +}; + +enum { + UART_INPUT_FE = 0x8000 // framing error +}; + +// add port number to get the real IRQ +#define AVR_IOCTL_UART_GETIRQ(_name) AVR_IOCTL_DEF('u','a','r',(_name)) + +enum { + // the uart code monitors for firmware that poll on + // reception registers, and can do an atomic usleep() + // if it's detected, this helps regulating CPU + AVR_UART_FLAG_POOL_SLEEP = (1 << 0), + AVR_UART_FLAG_POLL_SLEEP = (1 << 0), // to replace pool_sleep + AVR_UART_FLAG_STDIO = (1 << 1), // print lines on the console +}; + +typedef struct avr_uart_t { + avr_io_t io; + char name; + avr_regbit_t disabled; // bit in the PRR + + avr_io_addr_t r_udr; + avr_io_addr_t r_ucsra; + avr_io_addr_t r_ucsrb; + avr_io_addr_t r_ucsrc; + + avr_regbit_t rxen; // receive enabled + avr_regbit_t txen; // transmit enable + avr_regbit_t u2x; // double UART speed + avr_regbit_t usbs; // stop bits + avr_regbit_t ucsz; // data bits + avr_regbit_t ucsz2; // data bits, continued + + // read-only bits (just to mask it out) + avr_regbit_t fe; // frame error bit + avr_regbit_t dor; // data overrun bit + avr_regbit_t upe; // parity error bit + avr_regbit_t rxb8; // receive data bit 8 + + avr_regbit_t ubrrl; + avr_regbit_t ubrrh; + + avr_int_vector_t rxc; + avr_int_vector_t txc; + avr_int_vector_t udrc; + + uart_fifo_t input; + uint8_t tx_cnt; // number of unsent characters in the output buffer + uint32_t rx_cnt; // number of characters read by app since rxc_raise_time + + uint32_t flags; + avr_cycle_count_t cycles_per_byte; + avr_cycle_count_t rxc_raise_time; // the cpu cycle when rxc flag was raised last time + + uint8_t * stdio_out; + int stdio_len; // current size in the stdio output +} avr_uart_t; + +/* takes a uint32_t* as parameter */ +#define AVR_IOCTL_UART_SET_FLAGS(_name) AVR_IOCTL_DEF('u','a','s',(_name)) +#define AVR_IOCTL_UART_GET_FLAGS(_name) AVR_IOCTL_DEF('u','a','g',(_name)) + +void avr_uart_init(avr_t * avr, avr_uart_t * port); + +#define AVR_UARTX_DECLARE(_name, _prr, _prusart) \ + .uart ## _name = { \ + .name = '0' + _name, \ + .disabled = AVR_IO_REGBIT(_prr, _prusart), \ + \ + .r_udr = UDR ## _name, \ + \ + .fe = AVR_IO_REGBIT(UCSR ## _name ## A, FE ## _name), \ + .dor = AVR_IO_REGBIT(UCSR ## _name ## A, DOR ## _name), \ + .upe = AVR_IO_REGBIT(UCSR ## _name ## A, UPE ## _name), \ + .u2x = AVR_IO_REGBIT(UCSR ## _name ## A, U2X ## _name), \ + .txen = AVR_IO_REGBIT(UCSR ## _name ## B, TXEN ## _name), \ + .rxen = AVR_IO_REGBIT(UCSR ## _name ## B, RXEN ## _name), \ + .rxb8 = AVR_IO_REGBIT(UCSR ## _name ## B, RXB8 ## _name), \ + .usbs = AVR_IO_REGBIT(UCSR ## _name ## C, USBS ## _name), \ + .ucsz = AVR_IO_REGBITS(UCSR ## _name ## C, UCSZ ## _name ## 0, 0x3), \ + .ucsz2 = AVR_IO_REGBIT(UCSR ## _name ## B, UCSZ ## _name ## 2), \ + .ubrrl = AVR_IO_REGBITS(UBRR ## _name ## L, 0,0xFF), \ + .ubrrh = AVR_IO_REGBITS(UBRR ## _name ## H, 0,0xF), \ + \ + .r_ucsra = UCSR ## _name ## A, \ + .r_ucsrb = UCSR ## _name ## B, \ + .r_ucsrc = UCSR ## _name ## C, \ + \ + .rxc = { \ + .enable = AVR_IO_REGBIT(UCSR ## _name ## B, RXCIE ## _name), \ + .raised = AVR_IO_REGBIT(UCSR ## _name ## A, RXC ## _name), \ + .vector = USART ## _name ## _RX_vect, \ + .raise_sticky = 1, \ + }, \ + .txc = { \ + .enable = AVR_IO_REGBIT(UCSR ## _name ## B, TXCIE ## _name), \ + .raised = AVR_IO_REGBIT(UCSR ## _name ## A, TXC ## _name), \ + .vector = USART ## _name ## _TX_vect, \ + }, \ + .udrc = { \ + .enable = AVR_IO_REGBIT(UCSR ## _name ## B, UDRIE ## _name), \ + .raised = AVR_IO_REGBIT(UCSR ## _name ## A, UDRE ## _name), \ + .vector = USART ## _name ## _UDRE_vect, \ + .raise_sticky = 1, \ + }, \ + } + +// This macro is for older single-interface devices where variable names are bit divergent +#define AVR_UART_DECLARE(_prr, _prusart, _upe_name, _rname_ix, _intr_c) \ + .uart = { \ + .name = '0', \ + .disabled = AVR_IO_REGBIT(_prr, _prusart), \ + .r_udr = UDR ## _rname_ix, \ + \ + .fe = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, FE ## _rname_ix), \ + .dor = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, DOR ## _rname_ix), \ + .upe = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, _upe_name ## _rname_ix), \ + .u2x = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, U2X ## _rname_ix), \ + .txen = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, TXEN ## _rname_ix), \ + .rxen = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, RXEN ## _rname_ix), \ + .rxb8 = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, RXB8 ## _rname_ix), \ + .usbs = AVR_IO_REGBIT(UCSR ## _rname_ix ## C, USBS ## _rname_ix), \ + .ucsz = AVR_IO_REGBITS(UCSR ## _rname_ix ## C, UCSZ ## _rname_ix ## 0, 0x3), \ + .ucsz2 = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, UCSZ ## _rname_ix ## 2), \ + .ubrrl = AVR_IO_REGBITS(UBRR ## _rname_ix ## L, 0,0xFF), \ + .ubrrh = AVR_IO_REGBITS(UBRR ## _rname_ix ## H, 0,0xF), \ + \ + .r_ucsra = UCSR ## _rname_ix ## A, \ + .r_ucsrb = UCSR ## _rname_ix ## B, \ + .r_ucsrc = UCSR ## _rname_ix ## C, \ + \ + .rxc = { \ + .enable = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, RXCIE ## _rname_ix), \ + .raised = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, RXC ## _rname_ix), \ + .vector = USART_RX ## _intr_c ## _vect, \ + .raise_sticky = 1, \ + }, \ + .txc = { \ + .enable = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, TXCIE ## _rname_ix), \ + .raised = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, TXC ## _rname_ix), \ + .vector = USART_TX ## _intr_c ## _vect, \ + }, \ + .udrc = { \ + .enable = AVR_IO_REGBIT(UCSR ## _rname_ix ## B, UDRIE ## _rname_ix), \ + .raised = AVR_IO_REGBIT(UCSR ## _rname_ix ## A, UDRE ## _rname_ix), \ + .vector = USART_UDRE_vect, \ + .raise_sticky = 1, \ + }, \ + } + +#ifdef __cplusplus +}; +#endif + +#endif /*__AVR_UART_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.c new file mode 100644 index 0000000..4acbcfd --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.c @@ -0,0 +1,801 @@ +/* vim: set sts=4:sw=4:ts=4:noexpandtab + avr_usb.c + + Copyright 2012 Torbjorn Tyridal + + 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 . + */ + +/* 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 +#include +#include +#include +#include "avr_usb.h" + +enum usb_regs +{ + usbcon = 0, + udcon = 8, + udint = 9, + udien = 10, + udaddr = 11, + udfnuml = 12, + udfnumh = 13, + udmfn = 14, +// _res=15, + ueintx = 16, + uenum = 17, + uerst = 18, + ueconx = 19, + uecfg0x = 20, + uecfg1x = 21, + uesta0x = 22, + uesta1x = 23, + ueienx = 24, + uedatx = 25, + uebclx = 26, +// _res2=27, + ueint = 28, + otgtcon = 29, +}; + +union _ueintx { + struct { + uint8_t txini :1; + uint8_t stalledi :1; + uint8_t rxouti :1; + uint8_t rxstpi :1; + uint8_t nakouti :1; + uint8_t rwal :1; + uint8_t nakini :1; + uint8_t fifocon :1; + }; + uint8_t v; +}; + +struct _epstate { + union _ueintx ueintx; + uint8_t dummy1; + uint8_t dummy2; + union { + struct { + uint8_t epen :1; + uint8_t res :2; + uint8_t rstdt :1; + uint8_t stallrqc :1; + uint8_t stallrq :1; + }; + uint8_t v; + } ueconx; + union { + struct { + uint8_t epdir :1; + uint8_t res :5; + uint8_t eptype :2; + }; + uint8_t v; + } uecfg0x; + union { + struct { + uint8_t res :1; + uint8_t alloc :1; + uint8_t epbk1 :2; + uint8_t epsize :3; + uint8_t res2 :1; + }; + uint8_t v; + } uecfg1x; + union { + struct { + uint8_t nbusybk :2; + uint8_t dtseq :2; + uint8_t res :1; + uint8_t underfi :1; + uint8_t overfi :1; + uint8_t cfgok :1; + }; + uint8_t v; + } uesta0x; + union { + struct { + uint8_t curbk :2; + uint8_t ctrldir :1; + uint8_t res :5; + }; + uint8_t v; + } uesta1x; + union { + struct { + uint8_t txine :1; + uint8_t stallede :1; + uint8_t rxoute :1; + uint8_t rxstpe :1; + uint8_t nakoute :1; + uint8_t res :1; + uint8_t nakine :1; + uint8_t flerre :1; + }; + uint8_t v; + } ueienx; + + struct { + uint8_t bytes[64]; + uint8_t tail; + } bank[2]; + uint8_t current_bank; + int setup_is_read; +}; + +struct usb_internal_state { + struct _epstate ep_state[5]; + avr_int_vector_t com_vect; + avr_int_vector_t gen_vect; +}; + +const uint8_t num_endpoints = 5;//sizeof (struct usb_internal_state.ep_state) / sizeof (struct usb_internal_state.ep_state[0]); + +static uint8_t +current_ep_to_cpu( + avr_usb_t * p) +{ + return p->io.avr->data[p->r_usbcon + uenum]; +} + +static struct _epstate * +get_epstate( + avr_usb_t * p, + uint8_t ep) +{ + assert(ep < num_endpoints); + return &p->state->ep_state[ep]; +} + + +enum epints { + txini = 0, + stalledi = 1, + rxouti = 2, + rxstpi = 3, + nakouti = 4, + nakini = 6, + overfi = 10, + underfi = 11, +}; + +static void +raise_ep_interrupt( + struct avr_t * avr, + avr_usb_t * p, + uint8_t ep, + enum epints irq) +{ + struct _epstate * epstate = get_epstate(p, ep); + assert(ep < num_endpoints); + avr->data[p->r_usbcon + ueint] |= 1 << ep; + switch (irq) { + case txini: + case stalledi: + case rxouti: + case nakouti: + case nakini: + epstate->ueintx.v |= 1 << irq; + if (epstate->ueienx.v & (1 << irq)) + avr_raise_interrupt(avr, &p->state->com_vect); + break; + case rxstpi: + epstate->ueintx.v |= 1 << irq; + if (epstate->ueienx.v & (1 << irq)) + avr_raise_interrupt(avr, &p->state->com_vect); + break; + case overfi: + epstate->uesta0x.overfi = 1; + if (epstate->ueienx.flerre) + avr_raise_interrupt(avr, &p->state->com_vect); + break; + case underfi: + epstate->uesta0x.underfi = 1; + if (epstate->ueienx.flerre) + avr_raise_interrupt(avr, &p->state->com_vect); + break; + default: + assert(0); + } +} + +enum usbints { + suspi = 0, sofi = 2, eorsti = 3, wakeupi = 4, eorsmi = 5, uprsmi = 6 +}; +static void +raise_usb_interrupt( + avr_usb_t * p, + enum usbints irq) +{ + uint8_t * Rudien = &p->io.avr->data[p->r_usbcon + udien]; + uint8_t * Rudint = &p->io.avr->data[p->r_usbcon + udint]; + + switch (irq) { + case uprsmi: + case eorsmi: + case wakeupi: + case eorsti: + case sofi: + case suspi: + *Rudint |= 1 << irq; + if (*Rudien & (1 << irq)) + avr_raise_interrupt(p->io.avr, &p->state->gen_vect); + break; + default: + assert(0); + } + +} + +static void +reset_endpoints( + struct avr_t * avr, + avr_usb_t * p) +{ + memset(&p->state->ep_state[1], 0, + sizeof p->state->ep_state - sizeof p->state->ep_state[0]); +} + +static int +ep_fifo_empty( + struct _epstate * epstate) +{ + return epstate->bank[epstate->current_bank].tail == 0; +} + +static int +ep_fifo_full( + struct _epstate * epstate) +{ + return epstate->bank[epstate->current_bank].tail >= + (8 << epstate->uecfg1x.epsize); +} + +static uint8_t +ep_fifo_size( + struct _epstate * epstate) +{ + assert(epstate->ueconx.epen); + return (8 << epstate->uecfg1x.epsize); +} + +static uint8_t +ep_fifo_count( + struct _epstate * epstate) +{ + return epstate->bank[epstate->current_bank].tail; +} + +static int +ep_fifo_cpu_readbyte( + struct _epstate * epstate) +{ + uint8_t i, j; + uint8_t v = epstate->bank[epstate->current_bank].bytes[0]; + + if (!epstate->ueconx.epen) { + printf("WARNING! Adding bytes to non configured endpoint\n"); + return -1; + } + + if (ep_fifo_empty(epstate)) + return -2; + + for (i = 0, j = ep_fifo_count(epstate) - 1; i < j; i++) + epstate->bank[epstate->current_bank].bytes[i] = + epstate->bank[epstate->current_bank].bytes[i + 1]; + epstate->bank[epstate->current_bank].tail--; + return v; +} + +static int +ep_fifo_cpu_writebyte( + struct _epstate * epstate, + uint8_t v) +{ + if (!epstate->ueconx.epen) { + printf("WARNING! Adding bytes to non configured endpoint\n"); + return -1; + } + if (ep_fifo_full(epstate)) + return -2; + + epstate->bank[epstate->current_bank].bytes[epstate->bank[epstate->current_bank].tail++] = v; + return 0; +} + +static int +ep_fifo_usb_read( + struct _epstate * epstate, + uint8_t * buf) +{ + if (!epstate->ueconx.epen) { + printf("WARNING! Reading from non configured endpoint\n"); + return -1; + } + if (epstate->ueintx.txini) { + return AVR_IOCTL_USB_NAK; + } + if (epstate->ueintx.fifocon && epstate->uecfg0x.eptype != 0) { + return AVR_IOCTL_USB_NAK; + } + + int ret = epstate->bank[epstate->current_bank].tail; + memcpy(buf, epstate->bank[epstate->current_bank].bytes, + epstate->bank[epstate->current_bank].tail); + epstate->bank[epstate->current_bank].tail = 0; + return ret; +} + +static int +ep_fifo_usb_write( + struct _epstate * epstate, + uint8_t * buf, + uint8_t len) +{ + if (!epstate->ueconx.epen) { + printf("WARNING! Adding bytes to non configured endpoint\n"); + return -1; + } + + if (epstate->ueintx.rxouti) { + return AVR_IOCTL_USB_NAK; + } + if (epstate->ueintx.fifocon && epstate->uecfg0x.eptype != 0) { + return AVR_IOCTL_USB_NAK; + } + + if (len > ep_fifo_size(epstate)) { + printf("EP OVERFI\n"); + len = sizeof epstate->bank[epstate->current_bank].bytes; + } + memcpy(epstate->bank[epstate->current_bank].bytes, buf, len); + epstate->bank[epstate->current_bank].tail = len; + + return 0; +} + +static uint8_t +avr_usb_ep_read_bytecount( + struct avr_t * avr, + avr_io_addr_t addr, + void * param) +{ + avr_usb_t * p = (avr_usb_t *) param; + return ep_fifo_count(get_epstate(p, current_ep_to_cpu(p))); +} + +static void +avr_usb_udaddr_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + if (v & 0x80) + AVR_LOG(avr, LOG_TRACE, "USB: Activate address %d\n", v & 0x7f); + avr_core_watch_write(avr, addr, v); +} + +static void +avr_usb_udcon_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_usb_t * p = (avr_usb_t *)param; + + if(avr->data[addr]&1 && !(v&1)) + avr_raise_irq(p->io.irq + USB_IRQ_ATTACH, !(v&1)); + avr_core_watch_write(avr, addr, v); +} + +static void +avr_usb_uenum_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + assert(v < num_endpoints); + avr_core_watch_write(avr, addr, v); +} + +static uint8_t +avr_usb_ep_read_ueintx( + struct avr_t * avr, + avr_io_addr_t addr, + void * param) +{ + avr_usb_t * p = (avr_usb_t *) param; + uint8_t ep = current_ep_to_cpu(p); + + if (p->state->ep_state[ep].uecfg0x.epdir) + p->state->ep_state[ep].ueintx.rwal = !ep_fifo_full(get_epstate(p, ep)); + else + p->state->ep_state[ep].ueintx.rwal = !ep_fifo_empty(get_epstate(p, ep)); + + return p->state->ep_state[ep].ueintx.v; +} + +static void +avr_usb_ep_write_ueintx( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_usb_t * p = (avr_usb_t *) param; + uint8_t ep = current_ep_to_cpu(p); + + union _ueintx * newstate = (union _ueintx*) &v; + union _ueintx * curstate = &p->state->ep_state[ep].ueintx; + + if (curstate->rxouti & !newstate->rxouti) + curstate->rxouti = 0; + if (curstate->txini & !newstate->txini) + curstate->txini = 0; + if (curstate->rxstpi & !newstate->rxstpi) { + curstate->txini = 1; + curstate->rxouti = 0; + curstate->rxstpi = 0; + } + if (curstate->fifocon & !newstate->fifocon) + curstate->fifocon = 0; + if (curstate->nakini & !newstate->nakini) + curstate->nakini = 0; + if (curstate->nakouti & !newstate->nakouti) + curstate->nakouti = 0; + if (curstate->stalledi & !newstate->stalledi) + curstate->stalledi = 0; + if (curstate->rwal & !newstate->rwal) + AVR_LOG(avr, LOG_WARNING, "USB: Pointless change of ueintx.rwal\n"); + + if ((curstate->v & 0xdf) == 0) + avr->data[p->r_usbcon + ueint] &= 0xff ^ (1 << ep); // mark ep0 interrupt +} + +static uint8_t +avr_usb_ep_read( + struct avr_t * avr, + avr_io_addr_t addr, + void * param) +{ + avr_usb_t * p = (avr_usb_t *) param; + uint8_t laddr = addr - p->r_usbcon; + uint8_t v; + struct _epstate * epstate = get_epstate(p, current_ep_to_cpu(p)); + + switch(laddr) { + case ueconx: v = epstate->ueconx.v; break; + case uecfg0x: v = epstate->uecfg0x.v; break; + case uecfg1x: v = epstate->uecfg1x.v; break; + case uesta0x: v = epstate->uesta0x.v; break; + case uesta1x: v = epstate->uesta1x.v; break; + case ueienx: v = epstate->ueienx.v; break; + default:assert(0); + } + return v; +} + +static void +avr_usb_ep_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_usb_t * p = (avr_usb_t *) param; + struct _epstate * epstate = get_epstate(p, current_ep_to_cpu(p)); + uint8_t laddr = addr - p->r_usbcon; + + switch (laddr) { + case ueconx: + if (v & 1 << 4) + epstate->ueconx.stallrq = 0; + if (v & 1 << 5) + epstate->ueconx.stallrq = 1; + epstate->ueconx.epen = (v & 1) != 0; + break; + case uecfg0x: + epstate->uecfg0x.v = v; + epstate->uesta0x.cfgok = 0; + break; + case uecfg1x: + epstate->uecfg1x.v = v; + epstate->uesta0x.cfgok = epstate->uecfg1x.alloc; + if (epstate->uecfg0x.eptype == 0) + epstate->ueintx.txini = 1; + else if (epstate->uecfg0x.epdir) { + epstate->ueintx.txini = 1; + epstate->ueintx.rwal = 1; + epstate->ueintx.fifocon = 1; + } else + epstate->ueintx.rxouti = 0; + avr_core_watch_write(avr, p->r_usbcon + uesta0x, + epstate->uesta0x.v); + break; + case uesta0x: + v = (epstate->uesta0x.v & 0x9f) + (v & (0x60 & epstate->uesta0x.v)); + epstate->uesta0x.v = v; + break; + case ueienx: + epstate->ueienx.v = v; + break; + default: + assert(0); + } +} + +static uint8_t +avr_usb_ep_read_data( + struct avr_t * avr, + avr_io_addr_t addr, + void * param) +{ + avr_usb_t * p = (avr_usb_t *) param; + int ret = ep_fifo_cpu_readbyte(get_epstate(p, current_ep_to_cpu(p))); + + if (ret < 0) { + if (ret == -2) + raise_ep_interrupt(avr, p, current_ep_to_cpu(p), underfi); + return 0; + } else + return (uint8_t) ret; +} + +static void +avr_usb_ep_write_data( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_usb_t * p = (avr_usb_t *) param; + int ret = ep_fifo_cpu_writebyte(get_epstate(p, current_ep_to_cpu(p)), v); + if (ret == 0) + return; + + if (ret == -2) + raise_ep_interrupt(avr, p, current_ep_to_cpu(p), overfi); +} + +static void +avr_usb_pll_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + v |= (v >> 1) & 1; + avr_core_watch_write(avr, addr, v); +} + + +avr_cycle_count_t +sof_generator( + struct avr_t * avr, + avr_cycle_count_t when, + void * param) +{ + avr_usb_t * p = (avr_usb_t *) param; + //stop sof generation if detached + if (avr->data[p->r_usbcon + udcon] & 1) + return 0; + else { + raise_usb_interrupt(p, sofi); + return when; + } +} + +static int +avr_usb_ioctl( + struct avr_io_t * io, + uint32_t ctl, + void * io_param) +{ + avr_usb_t * p = (avr_usb_t *) io; + struct avr_io_usb * d = (struct avr_io_usb*) io_param; + struct _epstate * epstate = 0; + int ret; + uint8_t ep; + + switch (ctl) { + case AVR_IOCTL_USB_READ: + ep = d->pipe & 0x7f; + epstate = get_epstate(p, ep); + + if (epstate->ueconx.stallrq) { + raise_ep_interrupt(io->avr, p, 0, stalledi); + return AVR_IOCTL_USB_STALL; + } + if (ep && !epstate->uecfg0x.epdir) + AVR_LOG(io->avr, LOG_WARNING, "USB: Reading from IN endpoint from host??\n"); + + ret = ep_fifo_usb_read(epstate, d->buf); + if (ret < 0) { + // is this correct? It makes the cdc example work. + // Linux stops polling the data ep if we send naks,but + // according to usb spec nak'ing should be ok. + if (epstate->uecfg0x.eptype == 2) { + d->sz = 0; + return 0; + } else + return ret; + } + d->sz = ret; + ret = 0; + epstate->ueintx.fifocon = 1; + raise_ep_interrupt(io->avr, p, ep, txini); + return ret; + case AVR_IOCTL_USB_WRITE: + ep = d->pipe & 0x7f; + epstate = get_epstate(p, ep); + + if (ep && epstate->uecfg0x.epdir) + AVR_LOG(io->avr, LOG_WARNING, "USB: Writing to IN endpoint from host??\n"); + + if (epstate->ueconx.stallrq) { + raise_ep_interrupt(io->avr, p, 0, stalledi); + return AVR_IOCTL_USB_STALL; + } + + ret = ep_fifo_usb_write(epstate, d->buf, d->sz); + if (ret < 0) + return ret; + + epstate->ueintx.fifocon = 1; + raise_ep_interrupt(io->avr, p, ep, rxouti); + return 0; + case AVR_IOCTL_USB_SETUP: + ep = d->pipe & 0x7f; + epstate = get_epstate(p, ep); + + epstate->ueconx.stallrq = 0; + // teensy actually depends on this (fails to ack rxouti on usb + // control read status stage) even if the datasheet clearly states + // that one should do so. + epstate->ueintx.rxouti = 0; + + ret = ep_fifo_usb_write(epstate, d->buf, d->sz); + if (ret < 0) + return ret; + raise_ep_interrupt(io->avr, p, ep, rxstpi); + + return 0; + case AVR_IOCTL_USB_RESET: + AVR_LOG(io->avr, LOG_TRACE, "USB: __USB_RESET__\n"); + reset_endpoints(io->avr, p); + raise_usb_interrupt(p, eorsti); + if (0) + avr_cycle_timer_register_usec(io->avr, 1000, sof_generator, p); + return 0; + default: + return -1; + } +} + +void +avr_usb_reset( + struct avr_io_t *io) +{ + avr_usb_t * p = (avr_usb_t *) io; + uint8_t i; + + memset(p->state->ep_state, 0, sizeof p->state->ep_state); + + for (i = 0; i < otgtcon; i++) + p->io.avr->data[p->r_usbcon + i] = 0; + + p->io.avr->data[p->r_usbcon] = 0x20; + p->io.avr->data[p->r_usbcon + udcon] = 1; + + AVR_LOG(io->avr, LOG_TRACE, "USB: %s\n", __FUNCTION__); +} + +static const char * irq_names[USB_IRQ_COUNT] = { + [USB_IRQ_ATTACH] = ">attach", +}; + +static void +avr_usb_dealloc( + struct avr_io_t * port) +{ + avr_usb_t * p = (avr_usb_t *) port; + free(p->state); +} + +static avr_io_t _io = { + .kind = "usb", + .reset = avr_usb_reset, + .irq_names = irq_names, + .ioctl = avr_usb_ioctl, + .dealloc = avr_usb_dealloc, +}; + +static void +register_io_ep_readwrite( + avr_t * avr, + avr_usb_t * p, + uint8_t laddr) +{ + avr_register_io_write(avr, p->r_usbcon + laddr, avr_usb_ep_write, p); + avr_register_io_read(avr, p->r_usbcon + laddr, avr_usb_ep_read, p); +} + +static void +register_vectors( + avr_t * avr, + avr_usb_t * p) +{ + // usb interrupts are multiplexed into just two vectors. + // we therefore need fake bits for enable & raise + + // use usbe as fake enable bit + p->state->com_vect.enable = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon, 7); + p->state->gen_vect.enable = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon, 7); + +// // use reserved/unused bits in usbsta as fake raised bits +// p->state->com_vect.raised = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon+1,7); +// p->state->gen_vect.raised = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon+1,6); + + p->state->com_vect.vector = p->usb_com_vect; + p->state->gen_vect.vector = p->usb_gen_vect; + + avr_register_vector(avr, &p->state->com_vect); + avr_register_vector(avr, &p->state->gen_vect); +} + +void avr_usb_init(avr_t * avr, avr_usb_t * p) +{ + p->io = _io; + + p->state = calloc(1, sizeof *p->state); + + avr_register_io(avr, &p->io); + register_vectors(avr, p); + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_USB_GETIRQ(), USB_IRQ_COUNT, NULL); + + avr_register_io_write(avr, p->r_usbcon + udaddr, avr_usb_udaddr_write, p); + avr_register_io_write(avr, p->r_usbcon + udcon, avr_usb_udcon_write, p); + avr_register_io_write(avr, p->r_usbcon + uenum, avr_usb_uenum_write, p); + + avr_register_io_read(avr, p->r_usbcon + uedatx, avr_usb_ep_read_data, p); + avr_register_io_write(avr, p->r_usbcon + uedatx, avr_usb_ep_write_data, p); + avr_register_io_read(avr, p->r_usbcon + uebclx, avr_usb_ep_read_bytecount, p); //ro + + avr_register_io_read(avr, p->r_usbcon + ueintx, avr_usb_ep_read_ueintx, p); + avr_register_io_write(avr, p->r_usbcon + ueintx, avr_usb_ep_write_ueintx, p); + + register_io_ep_readwrite(avr, p, ueconx); + register_io_ep_readwrite(avr, p, uecfg0x); + register_io_ep_readwrite(avr, p, uecfg1x); + register_io_ep_readwrite(avr, p, uesta0x); + register_io_ep_readwrite(avr, p, uesta1x); + register_io_ep_readwrite(avr, p, ueienx); + + avr_register_io_write(avr, p->r_pllcsr, avr_usb_pll_write, p); +} + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.h new file mode 100644 index 0000000..17e5c0d --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_usb.h @@ -0,0 +1,74 @@ +/* vim: set sts=4:sw=4:ts=4:noexpandtab + avr_usb.h + + Copyright 2012 Torbjorn Tyridal + + 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 . + */ + +#ifndef __AVR_USB_H__ +#define __AVR_USB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +enum { + USB_IRQ_ATTACH = 0, + USB_IRQ_COUNT +}; + +// add port number to get the real IRQ +#define AVR_IOCTL_USB_WRITE AVR_IOCTL_DEF('u','s','b','w') +#define AVR_IOCTL_USB_READ AVR_IOCTL_DEF('u','s','b','r') +#define AVR_IOCTL_USB_SETUP AVR_IOCTL_DEF('u','s','b','s') +#define AVR_IOCTL_USB_RESET AVR_IOCTL_DEF('u','s','b','R') +#define AVR_IOCTL_USB_VBUS AVR_IOCTL_DEF('u','s','b','V') +#define AVR_IOCTL_USB_GETIRQ() AVR_IOCTL_DEF('u','s','b',' ') + +struct avr_io_usb { + uint8_t pipe; //[in] + uint32_t sz; //[in/out] + uint8_t * buf; //[in/out] +}; +#define AVR_IOCTL_USB_NAK -2 +#define AVR_IOCTL_USB_STALL -3 +#define AVR_IOCTL_USB_OK 0 + +typedef struct avr_usb_t { + avr_io_t io; + char name; + avr_regbit_t disabled; // bit in the PRR + avr_regbit_t usbrf; // bit in the MCUSR + avr_io_addr_t r_usbcon; // every usb reg is an offset of this. + avr_io_addr_t r_pllcsr; + + + uint8_t usb_com_vect; + uint8_t usb_gen_vect; + + struct usb_internal_state * state; +} avr_usb_t; + +void avr_usb_init(avr_t * avr, avr_usb_t * port); + +#ifdef __cplusplus +}; +#endif + +#endif /*__AVR_USB_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.c new file mode 100644 index 0000000..b7a5d01 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.c @@ -0,0 +1,231 @@ +/* + avr_watchdog.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + + +#include +#include +#include "avr_watchdog.h" + +static void avr_watchdog_run_callback_software_reset(avr_t * avr) +{ + avr_reset(avr); +} + +static avr_cycle_count_t avr_watchdog_timer( + struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)param; + + if (avr_regbit_get(avr, p->watchdog.enable)) { + AVR_LOG(avr, LOG_TRACE, "WATCHDOG: timer fired.\n"); + avr_raise_interrupt(avr, &p->watchdog); + return when + p->cycle_count; + } else if (avr_regbit_get(avr, p->wde)) { + AVR_LOG(avr, LOG_TRACE, + "WATCHDOG: timer fired without interrupt. Resetting\n"); + + p->reset_context.avr_run = avr->run; + p->reset_context.wdrf = 1; + + /* Ideally we would perform a reset here via 'avr_reset' + * However, returning after reset would result in an unconsistent state. + * It seems our best (and cleanest) solution is to set a temporary call + * back which can safely perform the reset for us... During reset, + * the previous callback can be restored and safely resume. + */ + avr->run = avr_watchdog_run_callback_software_reset; + } + + return 0; +} + +static avr_cycle_count_t avr_wdce_clear( + struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)param; + avr_regbit_clear(p->io.avr, p->wdce); + return 0; +} + +static void avr_watchdog_set_cycle_count_and_timer( + avr_t * avr, + avr_watchdog_t * p, + uint8_t was_enabled, + int8_t old_wdp) +{ + // If nothing else, always ensure we have a valid cycle count... + uint8_t wdp = avr_regbit_get_array(avr, p->wdp, 4); + + p->cycle_count = 2048 << wdp; + p->cycle_count = (p->cycle_count * avr->frequency) / 128000; + + uint8_t wde = avr_regbit_get(avr, p->wde); + uint8_t wdie = avr_regbit_get(avr, p->watchdog.enable); + + uint8_t enable_changed = (was_enabled != (wde || wdie)); + + uint8_t wdp_changed = ((old_wdp >= 0) ? (wdp != old_wdp) : 0); + + if (!enable_changed && !wdp_changed) + return; + + static char *message[2][2] = { + { 0, "reset" }, { "enabled", "enabled and set" } }; + + if (wde || wdie) { + AVR_LOG(avr, LOG_TRACE, + "WATCHDOG: %s to %d cycles @ 128kz (* %d) = %d CPU cycles.\n", + message[enable_changed][wdp_changed], 2048 << wdp, + 1 << wdp, (int)p->cycle_count); + + avr_cycle_timer_register(avr, p->cycle_count, avr_watchdog_timer, p); + } else if (enable_changed) { + AVR_LOG(avr, LOG_TRACE, "WATCHDOG: disabled\n"); + avr_cycle_timer_cancel(avr, avr_watchdog_timer, p); + } +} + +static void avr_watchdog_write( + avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)param; + + uint8_t old_wde = avr_regbit_get(avr, p->wde); + uint8_t old_wdie = avr_regbit_get(avr, p->watchdog.enable); + uint8_t old_wdce = avr_regbit_get(avr, p->wdce); + + uint8_t was_enabled = (old_wde || old_wdie); + + uint8_t old_v = avr->data[addr]; // allow gdb to see write... + avr_core_watch_write(avr, addr, v); + + if (old_wdce) { + uint8_t old_wdp = avr_regbit_get_array(avr, p->wdp, 4); + + // wdrf (watchdog reset flag) must be cleared before wde can be cleared. + if (avr_regbit_get(avr, p->wdrf)) + avr_regbit_set(avr, p->wde); + + avr_watchdog_set_cycle_count_and_timer(avr, p, was_enabled, old_wdp); + } else { + /* easier to change only what we need rather than check and reset + * locked/read-only bits. + */ + avr->data[addr] = old_v; + + uint8_t wdce_v = avr_regbit_from_value(avr, p->wdce, v); + uint8_t wde_v = avr_regbit_from_value(avr, p->wde, v); + + if (wdce_v && wde_v) { + avr_regbit_set(avr, p->wdce); + + avr_cycle_timer_register(avr, 4, avr_wdce_clear, p); + } else { + if (wde_v) // wde can be set but not cleared + avr_regbit_set(avr, p->wde); + + avr_regbit_setto_raw(avr, p->watchdog.enable, v); + + avr_watchdog_set_cycle_count_and_timer(avr, p, was_enabled, -1); + } + } +} + +/* + * called by the core when a WTD instruction is found + */ +static int avr_watchdog_ioctl( + struct avr_io_t * port, uint32_t ctl, void * io_param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)port; + int res = -1; + + if (ctl == AVR_IOCTL_WATCHDOG_RESET) { + if (avr_regbit_get(p->io.avr, p->wde) || + avr_regbit_get(p->io.avr, p->watchdog.enable)) + avr_cycle_timer_register(p->io.avr, p->cycle_count, + avr_watchdog_timer, p); + res = 0; + } + + return res; +} + +static void avr_watchdog_irq_notify( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)param; + avr_t * avr = p->io.avr; + + /* interrupt handling calls this twice... + * first when raised (during queuing), value = 1 + * again when cleared (after servicing), value = 0 + */ + + if (!value && avr_regbit_get(avr, p->watchdog.raised) && avr_regbit_get(avr, p->wde)) { + avr_regbit_clear(avr, p->watchdog.enable); + } +} + +static void avr_watchdog_reset(avr_io_t * port) +{ + avr_watchdog_t * p = (avr_watchdog_t *)port; + avr_t * avr = p->io.avr; + + if (p->reset_context.wdrf) { + p->reset_context.wdrf = 0; + /* + * if watchdog reset kicked, then watchdog gets restarted at + * fastest interval + */ + avr->run = p->reset_context.avr_run; + + avr_regbit_set(avr, p->wde); + avr_regbit_set(avr, p->wdrf); + avr_regbit_set_array_from_value(avr, p->wdp, 4, 0); + + avr_watchdog_set_cycle_count_and_timer(avr, p, 0, 0); + } + /* TODO could now use the two pending/running IRQs to do the same + * as before */ + avr_irq_register_notify(p->watchdog.irq, avr_watchdog_irq_notify, p); +} + +static avr_io_t _io = { + .kind = "watchdog", + .reset = avr_watchdog_reset, + .ioctl = avr_watchdog_ioctl, +}; + +void avr_watchdog_init(avr_t * avr, avr_watchdog_t * p) +{ + p->io = _io; + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->watchdog); + + avr_register_io_write(avr, p->wdce.reg, avr_watchdog_write, p); + + p->reset_context.wdrf = 0; +} + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.h new file mode 100644 index 0000000..da86371 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/avr_watchdog.h @@ -0,0 +1,96 @@ +/* + avr_watchdog.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + + +#ifndef __AVR_WATCHDOG_H___ +#define __AVR_WATCHDOG_H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +typedef struct avr_watchdog_t { + avr_io_t io; + + avr_regbit_t wdrf; // watchdog reset flag (in MCU Status Register) + + avr_regbit_t wdce; // watchdog change enable + avr_regbit_t wde; // watchdog enabled + avr_regbit_t wdp[4]; // watchdog Timer Prescaler + + avr_int_vector_t watchdog; // watchdog interrupt + + avr_cycle_count_t cycle_count; + + struct { + uint8_t wdrf; // saved watchdog reset flag + avr_run_t avr_run; // restored during reset + } reset_context; +} avr_watchdog_t; + +/* takes no parameter */ +#define AVR_IOCTL_WATCHDOG_RESET AVR_IOCTL_DEF('w','d','t','r') + +void avr_watchdog_init(avr_t * avr, avr_watchdog_t * p); + + +/* + * This helps declare a watchdog block into a core. + * No guarantee it will work with all, but it works + * with the one we have right now + */ +#define AVR_WATCHDOG_DECLARE(_WDSR, _vec) \ + .watchdog = {\ + .wdrf = AVR_IO_REGBIT(MCUSR, WDRF),\ + .wdce = AVR_IO_REGBIT(_WDSR, WDCE),\ + .wde = AVR_IO_REGBIT(_WDSR, WDE),\ + .wdp = { AVR_IO_REGBIT(_WDSR, WDP0),AVR_IO_REGBIT(_WDSR, WDP1),\ + AVR_IO_REGBIT(_WDSR, WDP2),AVR_IO_REGBIT(_WDSR, WDP3) },\ + .watchdog = {\ + .enable = AVR_IO_REGBIT(_WDSR, WDIE),\ + .raised = AVR_IO_REGBIT(_WDSR, WDIF),\ + .vector = _vec,\ + },\ + } + +/* no WDP3, WDIE, WDIF in atmega128 */ +/* MCUSR is called MCUCSR in atmega128 */ +#define AVR_WATCHDOG_DECLARE_128(_WDSR, _vec) \ + .watchdog = {\ + .wdrf = AVR_IO_REGBIT(MCUCSR, WDRF),\ + .wdce = AVR_IO_REGBIT(_WDSR, WDCE),\ + .wde = AVR_IO_REGBIT(_WDSR, WDE),\ + .wdp = { AVR_IO_REGBIT(_WDSR, WDP0),AVR_IO_REGBIT(_WDSR, WDP1),\ + AVR_IO_REGBIT(_WDSR, WDP2) },\ + .watchdog = {\ + .enable = AVR_IO_REGBIT(_WDSR, 6),\ + .raised = AVR_IO_REGBIT(_WDSR, 7),\ + .vector = _vec,\ + },\ + } + +#ifdef __cplusplus +}; +#endif + +#endif /* __AVR_WATCHDOG_H___ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/fifo_declare.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/fifo_declare.h new file mode 100644 index 0000000..8a3b2fb --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/fifo_declare.h @@ -0,0 +1,189 @@ +/* + fido_declare.h + Copyright (C) 2003-2012 Michel Pollet + + 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 + +#if __AVR__ +#define FIFO_CURSOR_TYPE uint8_t +#define FIFO_BOOL_TYPE char +#define FIFO_INLINE +#define FIFO_SYNC +#endif + +#ifndef FIFO_CURSOR_TYPE +#define FIFO_CURSOR_TYPE uint16_t +#endif +#ifndef FIFO_BOOL_TYPE +#define FIFO_BOOL_TYPE int +#endif +#ifndef FIFO_INLINE +#define FIFO_INLINE inline +#endif + +/* We should not need volatile */ +#ifndef FIFO_VOLATILE +#define FIFO_VOLATILE +#endif +#ifndef FIFO_SYNC +#define FIFO_SYNC __sync_synchronize() +#endif + +#ifndef FIFO_ZERO_INIT +#define FIFO_ZERO_INIT {0} +#endif +#define FIFO_NULL { FIFO_ZERO_INIT, 0, 0, 0 } + +/* New compilers don't like unused static functions. However, + * we do like 'static inlines' for these small accessors, + * so we mark them as 'unused'. It stops it complaining */ +#ifdef __GNUC__ +#define FIFO_DECL static __attribute__ ((unused)) +#else +#define FIFO_DECL static +#endif + +#define DECLARE_FIFO(__type, __name, __size) \ +enum { __name##_overflow_f = (1 << 0) }; \ +enum { __name##_fifo_size = (__size) }; \ +typedef struct __name##_t { \ + __type buffer[__name##_fifo_size]; \ + FIFO_VOLATILE FIFO_CURSOR_TYPE read; \ + FIFO_VOLATILE FIFO_CURSOR_TYPE write; \ + FIFO_VOLATILE uint8_t flags; \ +} __name##_t + +#define DEFINE_FIFO(__type, __name) \ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\ +{\ + FIFO_CURSOR_TYPE now = c->write;\ + FIFO_CURSOR_TYPE next = (now + 1) & (__name##_fifo_size-1);\ + if (c->read != next) { \ + c->buffer[now] = b;\ + FIFO_SYNC; \ + c->write = next;\ + return 1;\ + }\ + return 0;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\ +{\ + FIFO_CURSOR_TYPE next = (c->write + 1) & (__name##_fifo_size-1);\ + return c->read == next;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\ +{\ + return c->read == c->write;\ +}\ +FIFO_DECL FIFO_INLINE __type __name##_read(__name##_t * c)\ +{\ + __type res = FIFO_ZERO_INIT; \ + FIFO_CURSOR_TYPE read = c->read;\ + if (read == c->write)\ + return res;\ + res = c->buffer[read];\ + FIFO_SYNC; \ + c->read = (read + 1) & (__name##_fifo_size-1);\ + return res;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\ +{\ + return ((c->write + __name##_fifo_size) - c->read) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_write_size(__name##_t *c)\ +{\ + return (__name##_fifo_size-1) - __name##_get_read_size(c);\ +}\ +FIFO_DECL FIFO_INLINE void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + FIFO_SYNC; \ + c->read = (c->read + o) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + return c->buffer[(c->read + o) & (__name##_fifo_size-1)];\ +}\ +FIFO_DECL FIFO_INLINE void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\ +{\ + c->buffer[(c->write + o) & (__name##_fifo_size-1)] = b;\ +}\ +FIFO_DECL FIFO_INLINE void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + FIFO_SYNC; \ + c->write = (c->write + o) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE void __name##_reset(__name##_t *c)\ +{\ + FIFO_SYNC; \ + c->read = c->write = c->flags = 0;\ +}\ +struct __name##_t + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/run_avr.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/run_avr.c new file mode 100644 index 0000000..dfa6fce --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/run_avr.c @@ -0,0 +1,287 @@ +/* + run_avr.c + + Copyright 2008, 2010 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include +#include +#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 [...] \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 ] Sets the frequency for an .hex firmware\n" + " [--mcu|-m ] Sets the MCU type for an .hex firmware\n" + " [--gdb|-g []] Listen for gdb connection on " + "(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 ] Add traces for IRQ vector \n" + " [--input|-i ] A VCD file to use as input signals\n" + " [--output|-o ] A VCD file to save the traced signals\n" + " [--add-trace|-at ]\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" + " A .hex or an ELF file. ELF files are\n" + " preferred, and can include " + "debugging syms\n"); + exit(1); +} + +static void +list_cores() +{ + printf( "Supported AVR cores:\n"); + for (int i = 0; avr_kind[i]; i++) { + printf(" "); + for (int ti = 0; ti < 4 && avr_kind[i]->names[ti]; ti++) + printf("%s ", avr_kind[i]->names[ti]); + printf("\n"); + } + exit(1); +} + +static avr_t * avr = NULL; + +static void +sig_int( + int sign) +{ + printf("signal caught, simavr terminating\n"); + if (avr) + avr_terminate(avr); + exit(0); +} + +int +main( + int argc, + char *argv[]) +{ +#ifdef CONFIG_SIMAVR_TRACE + int trace = 0; +#endif //CONFIG_SIMAVR_TRACE + elf_firmware_t f = {{0}}; + uint32_t f_cpu = 0; + int gdb = 0; + int log = 1; + int port = 1234; + char name[24] = ""; + uint32_t loadBase = AVR_SEGMENT_OFFSET_FLASH; + int trace_vectors[8] = {0}; + int trace_vectors_count = 0; + const char *vcd_input = NULL; + + if (argc == 1) + display_usage(basename(argv[0])); + + for (int pi = 1; pi < argc; pi++) { + if (!strcmp(argv[pi], "--list-cores")) { + list_cores(); + } else if (!strcmp(argv[pi], "-h") || !strcmp(argv[pi], "--help")) { + display_usage(basename(argv[0])); + } else if (!strcmp(argv[pi], "-m") || !strcmp(argv[pi], "--mcu")) { + if (pi < argc-1) { + snprintf(name, sizeof(name), "%s", argv[++pi]); + strcpy(f.mmcu, name); + } else { + display_usage(basename(argv[0])); + } + } else if (!strcmp(argv[pi], "-f") || !strcmp(argv[pi], "--freq")) { + if (pi < argc-1) { + f_cpu = atoi(argv[++pi]); + f.frequency = f_cpu; + } else { + display_usage(basename(argv[0])); + } + } else if (!strcmp(argv[pi], "-i") || !strcmp(argv[pi], "--input")) { + if (pi < argc-1) + vcd_input = argv[++pi]; + else + display_usage(basename(argv[0])); + } else if (!strcmp(argv[pi], "-o") || + !strcmp(argv[pi], "--output")) { + if (pi + 1 >= argc) { + fprintf(stderr, "%s: missing mandatory argument for %s.\n", argv[0], argv[pi]); + exit(1); + } + snprintf(f.tracename, sizeof(f.tracename), "%s", argv[++pi]); + } else if (!strcmp(argv[pi], "-t") || + !strcmp(argv[pi], "--trace")) { +#ifdef CONFIG_SIMAVR_TRACE + trace++; +#else + fprintf(stderr, + "%s: tracing option '%s' requires " + "compilation option CONFIG_SIMAVR_TRACE.\n", + argv[0], argv[pi]); +#endif //CONFIG_SIMAVR_TRACE + } else if (!strcmp(argv[pi], "-at") || + !strcmp(argv[pi], "--add-trace")) { + if (pi + 1 >= argc) { + fprintf(stderr, "%s: missing mandatory argument for %s.\n", argv[0], argv[pi]); + exit(1); + } + ++pi; + struct { + char kind[64]; + uint8_t mask; + uint16_t addr; + char name[64]; + } trace; + const int n_args = sscanf( + argv[pi], + "%63[^=]=%63[^@]@0x%hx/0x%hhx", + &trace.name[0], + &trace.kind[0], + &trace.addr, + &trace.mask + ); + if (n_args != 4) { + --pi; + fprintf(stderr, "%s: format for %s is name=kind@addr/mask.\n", argv[0], argv[pi]); + exit(1); + } + + /****/ if (!strcmp(trace.kind, "portpin")) { + f.trace[f.tracecount].kind = AVR_MMCU_TAG_VCD_PORTPIN; + } else if (!strcmp(trace.kind, "irq")) { + f.trace[f.tracecount].kind = AVR_MMCU_TAG_VCD_IRQ; + } else if (!strcmp(trace.kind, "trace")) { + f.trace[f.tracecount].kind = AVR_MMCU_TAG_VCD_TRACE; + } else { + fprintf( + stderr, + "%s: unknown trace kind '%s', not one of 'portpin', 'irq', or 'trace'.\n", + argv[0], + trace.kind + ); + exit(1); + } + f.trace[f.tracecount].mask = trace.mask; + f.trace[f.tracecount].addr = trace.addr; + strncpy(f.trace[f.tracecount].name, trace.name, sizeof(f.trace[f.tracecount].name)); + + printf( + "Adding %s trace on address 0x%04x, mask 0x%02x ('%s')\n", + f.trace[f.tracecount].kind == AVR_MMCU_TAG_VCD_PORTPIN ? "portpin" + : f.trace[f.tracecount].kind == AVR_MMCU_TAG_VCD_IRQ ? "irq" + : f.trace[f.tracecount].kind == AVR_MMCU_TAG_VCD_TRACE ? "trace" + : "unknown", + f.trace[f.tracecount].addr, + f.trace[f.tracecount].mask, + f.trace[f.tracecount].name + ); + + ++f.tracecount; + } else if (!strcmp(argv[pi], "-ti")) { + if (pi < argc-1) + trace_vectors[trace_vectors_count++] = atoi(argv[++pi]); + } else if (!strcmp(argv[pi], "-g") || + !strcmp(argv[pi], "--gdb")) { + gdb++; + if (pi < (argc-2) && argv[pi+1][0] != '-' ) + port = atoi(argv[++pi]); + } else if (!strcmp(argv[pi], "-v")) { + log++; + } else if (!strcmp(argv[pi], "-ee")) { + loadBase = AVR_SEGMENT_OFFSET_EEPROM; + } else if (!strcmp(argv[pi], "-ff")) { + loadBase = AVR_SEGMENT_OFFSET_FLASH; + } else if (argv[pi][0] != '-') { + sim_setup_firmware(argv[pi], loadBase, &f, argv[0]); + } + } + + // Frequency and MCU type were set early so they can be checked when + // loading a hex file. Set them again because they can also be set + // in an ELF firmware file. + + if (strlen(name)) + strcpy(f.mmcu, name); + if (f_cpu) + f.frequency = f_cpu; + + avr = avr_make_mcu_by_name(f.mmcu); + if (!avr) { + fprintf(stderr, "%s: AVR '%s' not known\n", argv[0], f.mmcu); + exit(1); + } + avr_init(avr); + avr->log = (log > LOG_TRACE ? LOG_TRACE : log); +#ifdef CONFIG_SIMAVR_TRACE + avr->trace = trace; +#endif //CONFIG_SIMAVR_TRACE + + avr_load_firmware(avr, &f); + if (f.flashbase) { + printf("Attempted to load a bootloader at %04x\n", f.flashbase); + avr->pc = f.flashbase; + } + for (int ti = 0; ti < trace_vectors_count; ti++) { + for (int vi = 0; vi < avr->interrupts.vector_count; vi++) + if (avr->interrupts.vector[vi]->vector == trace_vectors[ti]) + avr->interrupts.vector[vi]->trace = 1; + } + if (vcd_input) { + static avr_vcd_t input; + if (avr_vcd_init_input(avr, vcd_input, &input)) { + fprintf(stderr, "%s: Warning: VCD input file %s failed\n", argv[0], vcd_input); + } + } + + // even if not setup at startup, activate gdb if crashing + avr->gdb_port = port; + if (gdb) { + avr->state = cpu_Stopped; + avr_gdb_init(avr); + } + + signal(SIGINT, sig_int); + signal(SIGTERM, sig_int); + + for (;;) { + int state = avr_run(avr); + if (state == cpu_Done || state == cpu_Crashed) + break; + } + + avr_terminate(avr); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.c new file mode 100644 index 0000000..24cb244 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.c @@ -0,0 +1,453 @@ +/* + sim_avr.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "sim_avr.h" +#include "sim_core.h" +#include "sim_time.h" +#include "sim_gdb.h" +#include "avr_uart.h" +#include "sim_vcd_file.h" +#include "avr/avr_mcu_section.h" + +#define AVR_KIND_DECL +#include "sim_core_decl.h" + +static void +std_logger( + avr_t * avr, + const int level, + const char * format, + va_list ap); +static avr_logger_p _avr_global_logger = std_logger; + +void +avr_global_logger( + struct avr_t* avr, + const int level, + const char * format, + ... ) +{ + va_list args; + va_start(args, format); + if (_avr_global_logger) + _avr_global_logger(avr, level, format, args); + va_end(args); +} + +void +avr_global_logger_set( + avr_logger_p logger) +{ + _avr_global_logger = logger ? logger : std_logger; +} + +avr_logger_p +avr_global_logger_get(void) +{ + return _avr_global_logger; +} + +uint64_t +avr_get_time_stamp( + avr_t * avr ) +{ + uint64_t stamp; +#ifndef CLOCK_MONOTONIC_RAW + /* CLOCK_MONOTONIC_RAW isn't portable, here is the POSIX alternative. + * Only downside is that it will drift if the system clock changes */ + struct timeval tv; + gettimeofday(&tv, NULL); + stamp = (((uint64_t)tv.tv_sec) * 1E9) + (tv.tv_usec * 1000); +#else + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC_RAW, &tp); + stamp = (tp.tv_sec * 1E9) + tp.tv_nsec; +#endif + if (!avr->time_base) + avr->time_base = stamp; + return stamp - avr->time_base; +} + +int +avr_init( + avr_t * avr) +{ + avr->flash = malloc(avr->flashend + 4); + memset(avr->flash, 0xff, avr->flashend + 1); + *((uint16_t*)&avr->flash[avr->flashend + 1]) = AVR_OVERFLOW_OPCODE; + avr->codeend = avr->flashend; + avr->data = malloc(avr->ramend + 1); + memset(avr->data, 0, avr->ramend + 1); +#ifdef CONFIG_SIMAVR_TRACE + avr->trace_data = calloc(1, sizeof(struct avr_trace_data_t)); +#endif + + AVR_LOG(avr, LOG_TRACE, "%s init\n", avr->mmcu); + + // cpu is in limbo before init is finished. + avr->state = cpu_Limbo; + avr->frequency = 1000000; // can be overridden via avr_mcu_section + avr_cmd_init(avr); + avr_interrupt_init(avr); + if (avr->custom.init) + avr->custom.init(avr, avr->custom.data); + if (avr->init) + avr->init(avr); + // set default (non gdb) fast callbacks + avr->run = avr_callback_run_raw; + avr->sleep = avr_callback_sleep_raw; + // number of address bytes to push/pull on/off the stack + avr->address_size = avr->eind ? 3 : 2; + avr->log = 1; + avr_reset(avr); + avr_regbit_set(avr, avr->reset_flags.porf); // by default set to power-on reset + return 0; +} + +void +avr_terminate( + avr_t * avr) +{ + if (avr->custom.deinit) + avr->custom.deinit(avr, avr->custom.data); + if (avr->gdb) { + avr_deinit_gdb(avr); + avr->gdb = NULL; + } + if (avr->vcd) { + avr_vcd_close(avr->vcd); + avr->vcd = NULL; + } + avr_deallocate_ios(avr); + + if (avr->flash) free(avr->flash); + if (avr->data) free(avr->data); + if (avr->io_console_buffer.buf) { + avr->io_console_buffer.len = 0; + avr->io_console_buffer.size = 0; + free(avr->io_console_buffer.buf); + avr->io_console_buffer.buf = NULL; + } + avr->flash = avr->data = NULL; +} + +void +avr_reset( + avr_t * avr) +{ + AVR_LOG(avr, LOG_TRACE, "%s reset\n", avr->mmcu); + + avr->state = cpu_Running; + for(int i = 0x20; i <= avr->ioend; i++) + avr->data[i] = 0; + _avr_sp_set(avr, avr->ramend); + avr->pc = avr->reset_pc; // Likely to be zero + for (int i = 0; i < 8; i++) + avr->sreg[i] = 0; + avr_interrupt_reset(avr); + avr_cycle_timer_reset(avr); + if (avr->reset) + avr->reset(avr); + avr_io_t * port = avr->io_port; + while (port) { + if (port->reset) + port->reset(port); + port = port->next; + } + avr->cycle = 0; // Prevent crash +} + +void +avr_sadly_crashed( + avr_t *avr, + uint8_t signal) +{ + AVR_LOG(avr, LOG_ERROR, "%s\n", __FUNCTION__); + avr->state = cpu_Stopped; + if (avr->gdb_port) { + // enable gdb server, and wait + if (!avr->gdb) + avr_gdb_init(avr); + } + if (!avr->gdb) + avr->state = cpu_Crashed; +} + +void +avr_set_command_register( + avr_t * avr, + avr_io_addr_t addr) +{ + avr_cmd_set_register(avr, addr); +} + +static void +_avr_io_console_write( + struct avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + if (v == '\r' && avr->io_console_buffer.buf) { + avr->io_console_buffer.buf[avr->io_console_buffer.len] = 0; + AVR_LOG(avr, LOG_OUTPUT, "O:" "%s" "" "\n", + avr->io_console_buffer.buf); + avr->io_console_buffer.len = 0; + return; + } + if (avr->io_console_buffer.len + 1 >= avr->io_console_buffer.size) { + avr->io_console_buffer.size += 128; + avr->io_console_buffer.buf = (char*)realloc( + avr->io_console_buffer.buf, + avr->io_console_buffer.size); + } + if (v >= ' ') + avr->io_console_buffer.buf[avr->io_console_buffer.len++] = v; +} + +void +avr_set_console_register( + avr_t * avr, + avr_io_addr_t addr) +{ + if (addr) + avr_register_io_write(avr, addr, _avr_io_console_write, NULL); +} + +void +avr_loadcode( + avr_t * avr, + uint8_t * code, + uint32_t size, + avr_flashaddr_t address) +{ + if ((address + size) > avr->flashend+1) { + AVR_LOG(avr, LOG_ERROR, "avr_loadcode(): Attempted to load code of size %d but flash size is only %d.\n", + size, avr->flashend + 1); + abort(); + } + memcpy(avr->flash + address, code, size); +} + +/** + * Accumulates sleep requests (and returns a sleep time of 0) until + * a minimum count of requested sleep microseconds are reached + * (low amounts cannot be handled accurately). + */ +uint32_t +avr_pending_sleep_usec( + avr_t * avr, + avr_cycle_count_t howLong) +{ + avr->sleep_usec += avr_cycles_to_usec(avr, howLong); + uint32_t usec = avr->sleep_usec; + if (usec > 200) { + avr->sleep_usec = 0; + return usec; + } + return 0; +} + +void +avr_callback_sleep_gdb( + avr_t * avr, + avr_cycle_count_t howLong) +{ + uint32_t usec = avr_pending_sleep_usec(avr, howLong); + while (avr_gdb_processor(avr, usec)) + ; +} + +void +avr_callback_run_gdb( + avr_t * avr) +{ + avr_gdb_processor(avr, avr->state == cpu_Stopped ? 50000 : 0); + + if (avr->state == cpu_Stopped) + return ; + + // if we are stepping one instruction, we "run" for one.. + int step = avr->state == cpu_Step; + if (step) + avr->state = cpu_Running; + + avr_flashaddr_t new_pc = avr->pc; + + if (avr->state == cpu_Running) { + new_pc = avr_run_one(avr); +#if CONFIG_SIMAVR_TRACE + avr_dump_state(avr); +#endif + } + + // run the cycle timers, get the suggested sleep time + // until the next timer is due + avr_cycle_count_t sleep = avr_cycle_timer_process(avr); + + avr->pc = new_pc; + + if (avr->state == cpu_Sleeping) { + if (!avr->sreg[S_I]) { + if (avr->log) + AVR_LOG(avr, LOG_TRACE, "simavr: sleeping with interrupts off, quitting gracefully\n"); + avr->state = cpu_Done; + return; + } + /* + * try to sleep for as long as we can (?) + */ + avr->sleep(avr, sleep); + avr->cycle += 1 + sleep; + } + // Interrupt servicing might change the PC too, during 'sleep' + if (avr->state == cpu_Running || avr->state == cpu_Sleeping) + avr_service_interrupts(avr); + + // if we were stepping, use this state to inform remote gdb + if (step) + avr->state = cpu_StepDone; +} + +/* +To avoid simulated time and wall clock time to diverge over time +this function tries to keep them in sync (roughly) by sleeping +for the time required to match the expected sleep deadline +in wall clock time. +*/ +void +avr_callback_sleep_raw( + avr_t *avr, + avr_cycle_count_t how_long) +{ + /* figure out how long we should wait to match the sleep deadline */ + uint64_t deadline_ns = avr_cycles_to_nsec(avr, avr->cycle + how_long); + uint64_t runtime_ns = avr_get_time_stamp(avr); + if (runtime_ns >= deadline_ns) + return; + uint64_t sleep_us = (deadline_ns - runtime_ns) / 1000; + usleep(sleep_us); + return; +} + +void +avr_callback_run_raw( + avr_t * avr) +{ + avr_flashaddr_t new_pc = avr->pc; + + if (avr->state == cpu_Running) { + new_pc = avr_run_one(avr); +#if CONFIG_SIMAVR_TRACE + avr_dump_state(avr); +#endif + } + + // run the cycle timers, get the suggested sleep time + // until the next timer is due + avr_cycle_count_t sleep = avr_cycle_timer_process(avr); + + avr->pc = new_pc; + + if (avr->state == cpu_Sleeping) { + if (!avr->sreg[S_I]) { + if (avr->log) + AVR_LOG(avr, LOG_TRACE, "simavr: sleeping with interrupts off, quitting gracefully\n"); + avr->state = cpu_Done; + return; + } + /* + * try to sleep for as long as we can (?) + */ + avr->sleep(avr, sleep); + avr->cycle += 1 + sleep; + } + // Interrupt servicing might change the PC too, during 'sleep' + if (avr->state == cpu_Running || avr->state == cpu_Sleeping) { + /* Note: checking interrupt_state here is completely superfluous, however + as interrupt_state tells us all we really need to know, here + a simple check here may be cheaper than a call not needed. */ + if (avr->interrupt_state) + avr_service_interrupts(avr); + } +} + + +int +avr_run( + avr_t * avr) +{ + avr->run(avr); + return avr->state; +} + +avr_t * +avr_core_allocate( + const avr_t * core, + uint32_t coreLen) +{ + uint8_t * b = malloc(coreLen); + memcpy(b, core, coreLen); + return (avr_t *)b; +} + +avr_t * +avr_make_mcu_by_name( + const char *name) +{ + avr_kind_t * maker = NULL; + for (int i = 0; avr_kind[i] && !maker; i++) { + for (int j = 0; avr_kind[i]->names[j]; j++) + if (!strcmp(avr_kind[i]->names[j], name)) { + maker = avr_kind[i]; + break; + } + } + if (!maker) { + AVR_LOG(((avr_t*)0), LOG_ERROR, "%s: AVR '%s' not known\n", __FUNCTION__, name); + return NULL; + } + + avr_t * avr = maker->make(); + AVR_LOG(avr, LOG_TRACE, "Starting %s - flashend %04x ramend %04x e2end %04x\n", + avr->mmcu, avr->flashend, avr->ramend, avr->e2end); + return avr; +} + +static void +std_logger( + avr_t * avr, + const int level, + const char * format, + va_list ap) +{ + if (!avr || avr->log >= level) { + vfprintf((level < LOG_ERROR) ? stdout : stderr, format, ap); + } +} + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.h new file mode 100644 index 0000000..e9a97cd --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr.h @@ -0,0 +1,517 @@ +/* + sim_avr.h + + Copyright 2008-2012 Michel Pollet + + 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 . + */ + +#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 +/* + * Type for custom logging functions + */ +typedef void (*avr_logger_p)(struct avr_t* avr, const int level, const char * format, va_list ap); + +/* Sets a global logging function in place of the default */ +void +avr_global_logger_set( + avr_logger_p logger); +/* Gets the current global logger function */ +avr_logger_p +avr_global_logger_get(void); +#endif + +/* + * These are callbacks for the two 'main' behaviour in simavr + */ +void avr_callback_sleep_gdb(avr_t * avr, avr_cycle_count_t howLong); +void avr_callback_run_gdb(avr_t * avr); +void avr_callback_sleep_raw(avr_t * avr, avr_cycle_count_t howLong); +void avr_callback_run_raw(avr_t * avr); + +/** + * Accumulates sleep requests (and returns a sleep time of 0) until + * a minimum count of requested sleep microseconds are reached + * (low amounts cannot be handled accurately). + * This function is an utility function for the sleep callbacks + */ +uint32_t +avr_pending_sleep_usec( + avr_t * avr, + avr_cycle_count_t howLong); +/* Return the number of 'real time' spent since sim started, in uS */ +uint64_t +avr_get_time_stamp( + avr_t * avr ); + +#ifdef __cplusplus +}; +#endif + +#include "sim_io.h" +#include "sim_regbit.h" + +#ifdef __GNUC__ + +# ifndef likely +# define likely(x) __builtin_expect(!!(x), 1) +# endif + +# ifndef unlikely +# define unlikely(x) __builtin_expect(!!(x), 0) +# endif + +#else /* ! __GNUC__ */ + +# ifndef likely +# define likely(x) x +# endif + +# ifndef unlikely +# define unlikely(x) x +# endif + +#endif /* __GNUC__ */ + +#endif /*__SIM_AVR_H__*/ + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr_types.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr_types.h new file mode 100644 index 0000000..b15bafc --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_avr_types.h @@ -0,0 +1,57 @@ +/* + sim_avr_types.h + + Copyright 2008-2012 Michel Pollet + + 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 . + */ + + +#ifndef __SIM_AVR_TYPES_H___ +#define __SIM_AVR_TYPES_H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef uint64_t avr_cycle_count_t; +typedef uint16_t avr_io_addr_t; + +/* + * this 'structure' is a packed representation of an IO register 'bit' + * (or consecutive bits). This allows a way to set/get/clear them. + * gcc is happy passing these as register value, so you don't need to + * use a pointer when passing them along to functions. + * + * 9 bits ought to be enough, as it's the maximum I've seen (atmega2560) + */ +typedef struct avr_regbit_t { + uint32_t reg : 9, bit : 3, mask : 8; +} avr_regbit_t; + +// printf() conversion specifier for avr_cycle_count_t +#define PRI_avr_cycle_count PRIu64 + +struct avr_t; + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_AVR_TYPES_H___ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.c new file mode 100644 index 0000000..587f259 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.c @@ -0,0 +1,193 @@ +/* + sim_cmds.c + + Copyright 2014 Florian Albrechtskirchinger + + 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 . + */ + + +#include +#include +#include "sim_avr.h" +#include "sim_cmds.h" +#include "sim_vcd_file.h" +#include "avr_uart.h" +#include "avr/avr_mcu_section.h" + +#define LOG_PREFIX "CMDS: " + +static void +_avr_cmd_io_write( + avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + avr_cmd_table_t * commands = &avr->commands; + avr_cmd_t * command = commands->pending; + + AVR_LOG(avr, LOG_TRACE, LOG_PREFIX "%s: 0x%02x\n", __FUNCTION__, v); + + if (!command) { + if (v > MAX_AVR_COMMANDS) { + AVR_LOG(avr, LOG_ERROR, LOG_PREFIX + "%s: code 0x%02x outside permissible range (>0x%02x)\n", + __FUNCTION__, v, MAX_AVR_COMMANDS - 1); + return; + } + command = &commands->table[v]; + } + if (!command->handler) { + AVR_LOG(avr, LOG_ERROR, LOG_PREFIX + "%s: code 0x%02x has no handler (wrong MMCU config)\n", + __FUNCTION__, v); + return; + } + + if (command) { + if (command->handler(avr, v, command->param)) + commands->pending = command; + else + commands->pending = NULL; + } else + AVR_LOG(avr, LOG_TRACE, LOG_PREFIX "%s: unknown command 0x%02x\n", + __FUNCTION__, v); +} + +void +avr_cmd_set_register( + avr_t * avr, + avr_io_addr_t addr) +{ + if (addr) + avr_register_io_write(avr, addr, &_avr_cmd_io_write, NULL); +} + +void +avr_cmd_register( + avr_t * avr, + uint8_t code, + avr_cmd_handler_t handler, + void * param) +{ + avr_cmd_table_t * commands = &avr->commands; + avr_cmd_t * command; + + if (!handler) + return; + + if (code > MAX_AVR_COMMANDS) { + AVR_LOG(avr, LOG_ERROR, LOG_PREFIX + "%s: code 0x%02x outside permissible range (>0x%02x)\n", + __FUNCTION__, code, MAX_AVR_COMMANDS - 1); + return; + } + + command = &commands->table[code]; + if (command->handler) { + AVR_LOG(avr, LOG_ERROR, LOG_PREFIX + "%s: code 0x%02x is already registered\n", + __FUNCTION__, code); + return; + } + + command->handler = handler; + command->param = param; +} + +void +avr_cmd_unregister( + avr_t * avr, + uint8_t code) +{ + avr_cmd_table_t * commands = &avr->commands; + avr_cmd_t * command; + + if (code > MAX_AVR_COMMANDS) { + AVR_LOG(avr, LOG_ERROR, LOG_PREFIX + "%s: code 0x%02x outside permissible range (>0x%02x)\n", + __FUNCTION__, code, MAX_AVR_COMMANDS - 1); + return; + } + + command = &commands->table[code]; + if (command->handler) { + if(command->param) + free(command->param); + + command->handler = NULL; + command->param = NULL; + } else + AVR_LOG(avr, LOG_ERROR, LOG_PREFIX + "%s: no command registered for code 0x%02x\n", + __FUNCTION__, code); +} + +static int +_simavr_cmd_vcd_start_trace( + avr_t * avr, + uint8_t v, + void * param) +{ + if (avr->vcd) + avr_vcd_start(avr->vcd); + + return 0; +} + +static int +_simavr_cmd_vcd_stop_trace( + avr_t * avr, + uint8_t v, + void * param) +{ + if (avr->vcd) + avr_vcd_stop(avr->vcd); + + return 0; +} + +static int +_simavr_cmd_uart_loopback( + avr_t * avr, + uint8_t v, + void * param) +{ + avr_irq_t * src = avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUTPUT); + avr_irq_t * dst = avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_INPUT); + + if(src && dst) { + AVR_LOG(avr, LOG_TRACE, LOG_PREFIX + "%s: activating uart local echo; IRQ src %p dst %p\n", + __FUNCTION__, src, dst); + avr_connect_irq(src, dst); + } + + return 0; +} + +void +avr_cmd_init( + avr_t * avr) +{ + memset(&avr->commands, 0, sizeof(avr->commands)); + + // Register builtin commands + avr_cmd_register(avr, SIMAVR_CMD_VCD_START_TRACE, &_simavr_cmd_vcd_start_trace, NULL); + avr_cmd_register(avr, SIMAVR_CMD_VCD_STOP_TRACE, &_simavr_cmd_vcd_stop_trace, NULL); + avr_cmd_register(avr, SIMAVR_CMD_UART_LOOPBACK, &_simavr_cmd_uart_loopback, NULL); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.h new file mode 100644 index 0000000..aac75fc --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cmds.h @@ -0,0 +1,82 @@ +/* + sim_cmds.h + + Copyright 2014 Florian Albrechtskirchinger + + 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 . + */ + +#pragma once + +#include "sim_avr_types.h" + +#define MAX_AVR_COMMANDS 32 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*avr_cmd_handler_t)( + struct avr_t * avr, + uint8_t v, + void * param); + +typedef struct avr_cmd_t { + avr_cmd_handler_t handler; + void * param; +} avr_cmd_t; + +typedef struct avr_cmd_table_t { + avr_cmd_t table[MAX_AVR_COMMANDS]; + avr_cmd_t * pending; // Holds a reference to a pending multi-byte command +} avr_cmd_table_t; + +// Called by avr_set_command_register() +void +avr_cmd_set_register( + struct avr_t * avr, + avr_io_addr_t addr); + +/* + * Register a command distinguished by 'code'. + * + * When 'code' is written to the configured IO address, 'handler' is executed + * with the value written, as well as 'param'. + * 'handler' can return non-zero, to indicate, that this is a multi-byte command. + * Subsequent writes are then dispatched to the same handler, until 0 is returned. + */ +void +avr_cmd_register( + struct avr_t * avr, + uint8_t code, + avr_cmd_handler_t handler, + void * param); + +void +avr_cmd_unregister( + struct avr_t * avr, + uint8_t code); + +// Private functions + +// Called from avr_init() to initialize the avr_cmd_table_t and register builtin commands. +void +avr_cmd_init( + struct avr_t * avr); + +#ifdef __cplusplus +} +#endif diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.c new file mode 100644 index 0000000..ab8015c --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.c @@ -0,0 +1,1457 @@ +/* + sim_core.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include +#include "sim_avr.h" +#include "sim_core.h" +#include "sim_gdb.h" +#include "avr_flash.h" +#include "avr_watchdog.h" + +// SREG bit names +const char * _sreg_bit_name = "cznvshti"; + +/* + * Handle "touching" registers, marking them changed. + * This is used only for debugging purposes to be able to + * print the effects of each instructions on registers + */ +#if CONFIG_SIMAVR_TRACE + +#define T(w) w + +#define REG_TOUCH(a, r) (a)->trace_data->touched[(r) >> 5] |= (1 << ((r) & 0x1f)) +#define REG_ISTOUCHED(a, r) ((a)->trace_data->touched[(r) >> 5] & (1 << ((r) & 0x1f))) + +/* + * This allows a "special case" to skip instruction tracing when in these + * symbols since printf() is useful to have, but generates a lot of cycles. + */ +int dont_trace(const char * name) +{ + return ( + !strcmp(name, "uart_putchar") || + !strcmp(name, "fputc") || + !strcmp(name, "printf") || + !strcmp(name, "vfprintf") || + !strcmp(name, "__ultoa_invert") || + !strcmp(name, "__prologue_saves__") || + !strcmp(name, "__epilogue_restores__")); +} + +int donttrace = 0; + +#define STATE(_f, args...) { \ + if (avr->trace) {\ + if (avr->trace_data->codeline && avr->trace_data->codeline[avr->pc>>1]) {\ + const char * symn = avr->trace_data->codeline[avr->pc>>1]->symbol; \ + int dont = 0 && dont_trace(symn);\ + if (dont!=donttrace) { \ + donttrace = dont;\ + DUMP_REG();\ + }\ + if (donttrace==0)\ + printf("%04x: %-25s " _f, avr->pc, symn, ## args);\ + } else \ + printf("%s: %04x: " _f, __FUNCTION__, avr->pc, ## args);\ + }\ + } +#define SREG() if (avr->trace && donttrace == 0) {\ + printf("%04x: \t\t\t\t\t\t\t\t\tSREG = ", avr->pc); \ + for (int _sbi = 0; _sbi < 8; _sbi++)\ + printf("%c", avr->sreg[_sbi] ? toupper(_sreg_bit_name[_sbi]) : '.');\ + printf("\n");\ +} + +void crash(avr_t* avr) +{ + DUMP_REG(); + printf("*** CYCLE %" PRI_avr_cycle_count "PC %04x\n", avr->cycle, avr->pc); + + for (int i = OLD_PC_SIZE-1; i > 0; i--) { + int pci = (avr->trace_data->old_pci + i) & 0xf; + printf(FONT_RED "*** %04x: %-25s RESET -%d; sp %04x\n" FONT_DEFAULT, + avr->trace_data->old[pci].pc, avr->trace_data->codeline ? avr->trace_data->codeline[avr->trace_data->old[pci].pc>>1]->symbol : "unknown", OLD_PC_SIZE-i, avr->trace_data->old[pci].sp); + } + + printf("Stack Ptr %04x/%04x = %d \n", _avr_sp_get(avr), avr->ramend, avr->ramend - _avr_sp_get(avr)); + DUMP_STACK(); + + avr_sadly_crashed(avr, 0); +} +#else +#define T(w) +#define REG_TOUCH(a, r) +#define STATE(_f, args...) +#define SREG() + +void crash(avr_t* avr) +{ + avr_sadly_crashed(avr, 0); + +} +#endif + +static inline uint16_t +_avr_flash_read16le( + avr_t * avr, + avr_flashaddr_t addr) +{ + return(avr->flash[addr] | (avr->flash[addr + 1] << 8)); +} + +static inline void _call_register_irqs(avr_t * avr, uint16_t addr) +{ + if (addr > 31 && addr < 31 + MAX_IOs) { + avr_io_addr_t io = AVR_DATA_TO_IO(addr); + + if (avr->io[io].irq) { + uint8_t v = avr->data[addr]; + avr_raise_irq(avr->io[io].irq + AVR_IOMEM_IRQ_ALL, v); + for (int i = 0; i < 8; i++) + avr_raise_irq(avr->io[io].irq + i, (v >> i) & 1); + } + } +} + +void avr_core_watch_write(avr_t *avr, uint16_t addr, uint8_t v) +{ + if (addr > avr->ramend) { + AVR_LOG(avr, LOG_WARNING, + "CORE: *** Wrapping write address " + "PC=%04x SP=%04x O=%04x v=%02x Address %04x %% %04x --> %04x\n", + avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc), v, addr, (avr->ramend + 1), addr % (avr->ramend + 1)); + addr = addr % (avr->ramend + 1); + } + if (addr < 32) { + AVR_LOG(avr, LOG_ERROR, FONT_RED + "CORE: *** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x low registers\n" + FONT_DEFAULT, + avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc), addr, v); + crash(avr); + } +#if AVR_STACK_WATCH + /* + * this checks that the current "function" is not doctoring the stack frame that is located + * higher on the stack than it should be. It's a sign of code that has overrun it's stack + * frame and is munching on it's own return address. + */ + if (avr->trace_data->stack_frame_index > 1 && addr > avr->trace_data->stack_frame[avr->trace_data->stack_frame_index-2].sp) { + printf( FONT_RED "%04x : munching stack " + "SP %04x, A=%04x <= %02x\n" FONT_DEFAULT, + avr->pc, _avr_sp_get(avr), addr, v); + } +#endif + + if (avr->gdb) { + avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_WRITE); + } + + avr->data[addr] = v; + _call_register_irqs(avr, addr); +} + +uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr) +{ + if (addr > avr->ramend) { + AVR_LOG(avr, LOG_WARNING, + "CORE: *** Wrapping read address " + "PC=%04x SP=%04x O=%04x Address %04x %% %04x --> %04x\n" + FONT_DEFAULT, + avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc), + addr, (avr->ramend + 1), addr % (avr->ramend + 1)); + addr = addr % (avr->ramend + 1); + } + + if (avr->gdb) { + avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_READ); + } + +// _call_register_irqs(avr, addr); + return avr->data[addr]; +} + +/* + * Set a register (r < 256) + * if it's an IO register (> 31) also (try to) call any callback that was + * registered to track changes to that register. + */ +static inline void _avr_set_r(avr_t * avr, uint16_t r, uint8_t v) +{ + REG_TOUCH(avr, r); + + if (r == R_SREG) { + avr->data[R_SREG] = v; + // unsplit the SREG + SET_SREG_FROM(avr, v); + SREG(); + } + if (r > 31) { + avr_io_addr_t io = AVR_DATA_TO_IO(r); + if (avr->io[io].w.c) { + avr->io[io].w.c(avr, r, v, avr->io[io].w.param); + } else { + avr->data[r] = v; + if (avr->io[io].irq) { + avr_raise_irq(avr->io[io].irq + AVR_IOMEM_IRQ_ALL, v); + for (int i = 0; i < 8; i++) + avr_raise_irq(avr->io[io].irq + i, (v >> i) & 1); + } + } + } else + avr->data[r] = v; +} + +static inline void +_avr_set_r16le( + avr_t * avr, + uint16_t r, + uint16_t v) +{ + _avr_set_r(avr, r, v); + _avr_set_r(avr, r + 1, v >> 8); +} + +static inline void +_avr_set_r16le_hl( + avr_t * avr, + uint16_t r, + uint16_t v) +{ + _avr_set_r(avr, r + 1, v >> 8); + _avr_set_r(avr, r , v); +} + +/* + * Stack pointer access + */ +inline uint16_t _avr_sp_get(avr_t * avr) +{ + return avr->data[R_SPL] | (avr->data[R_SPH] << 8); +} + +inline void _avr_sp_set(avr_t * avr, uint16_t sp) +{ + _avr_set_r16le(avr, R_SPL, sp); +} + +/* + * Set any address to a value; split between registers and SRAM + */ +static inline void _avr_set_ram(avr_t * avr, uint16_t addr, uint8_t v) +{ + if (addr <= avr->ioend) + _avr_set_r(avr, addr, v); + else + avr_core_watch_write(avr, addr, v); +} + +/* + * Get a value from SRAM. + */ +static inline uint8_t _avr_get_ram(avr_t * avr, uint16_t addr) +{ + if (addr == R_SREG) { + /* + * SREG is special it's reconstructed when read + * while the core itself uses the "shortcut" array + */ + READ_SREG_INTO(avr, avr->data[R_SREG]); + + } else if (addr > 31 && addr < 31 + MAX_IOs) { + avr_io_addr_t io = AVR_DATA_TO_IO(addr); + + if (avr->io[io].r.c) + avr->data[addr] = avr->io[io].r.c(avr, addr, avr->io[io].r.param); +#if 0 + if (avr->io[io].irq) { + uint8_t v = avr->data[addr]; + avr_raise_irq(avr->io[io].irq + AVR_IOMEM_IRQ_ALL, v); + for (int i = 0; i < 8; i++) + avr_raise_irq(avr->io[io].irq + i, (v >> i) & 1); + } +#endif + } + return avr_core_watch_read(avr, addr); +} + +/* + * Stack push accessors. + */ +static inline void _avr_push8(avr_t * avr, uint16_t v) +{ + uint16_t sp = _avr_sp_get(avr); + _avr_set_ram(avr, sp, v); + _avr_sp_set(avr, sp-1); +} + +static inline uint8_t _avr_pop8(avr_t * avr) +{ + uint16_t sp = _avr_sp_get(avr) + 1; + uint8_t res = _avr_get_ram(avr, sp); + _avr_sp_set(avr, sp); + return res; +} + +int _avr_push_addr(avr_t * avr, avr_flashaddr_t addr) +{ + uint16_t sp = _avr_sp_get(avr); + addr >>= 1; + for (int i = 0; i < avr->address_size; i++, addr >>= 8, sp--) { + _avr_set_ram(avr, sp, addr); + } + _avr_sp_set(avr, sp); + return avr->address_size; +} + +avr_flashaddr_t _avr_pop_addr(avr_t * avr) +{ + uint16_t sp = _avr_sp_get(avr) + 1; + avr_flashaddr_t res = 0; + for (int i = 0; i < avr->address_size; i++, sp++) { + res = (res << 8) | _avr_get_ram(avr, sp); + } + res <<= 1; + _avr_sp_set(avr, sp -1); + return res; +} + +/* + * "Pretty" register names + */ +const char * reg_names[REG_NAME_COUNT] = { + [R_XH] = "XH", [R_XL] = "XL", + [R_YH] = "YH", [R_YL] = "YL", + [R_ZH] = "ZH", [R_ZL] = "ZL", + [R_SPH] = "SPH", [R_SPL] = "SPL", + [R_SREG] = "SREG", +}; + + +const char * avr_regname(unsigned int reg) +{ + if (!reg_names[reg]) { + char tt[16]; + if (reg < 32) + sprintf(tt, "r%d", reg); + else + sprintf(tt, "io:%02x", reg); + reg_names[reg] = strdup(tt); + } + return reg_names[reg]; +} + +/* + * Called when an invalid opcode is decoded + */ +static void _avr_invalid_opcode(avr_t * avr) +{ +#if CONFIG_SIMAVR_TRACE + printf( FONT_RED "*** %04x: %-25s Invalid Opcode SP=%04x O=%04x \n" FONT_DEFAULT, + avr->pc, avr->trace_data->codeline[avr->pc>>1]->symbol, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc)); +#else + AVR_LOG(avr, LOG_ERROR, FONT_RED "CORE: *** %04x: Invalid Opcode SP=%04x O=%04x \n" FONT_DEFAULT, + avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc)); +#endif +} + +#if CONFIG_SIMAVR_TRACE +/* + * Dump changed registers when tracing + */ +void avr_dump_state(avr_t * avr) +{ + if (!avr->trace || donttrace) + return; + + int doit = 0; + + for (int r = 0; r < 3 && !doit; r++) + if (avr->trace_data->touched[r]) + doit = 1; + if (!doit) + return; + printf(" ->> "); + const int r16[] = { R_SPL, R_XL, R_YL, R_ZL }; + for (int i = 0; i < 4; i++) + if (REG_ISTOUCHED(avr, r16[i]) || REG_ISTOUCHED(avr, r16[i]+1)) { + REG_TOUCH(avr, r16[i]); + REG_TOUCH(avr, r16[i]+1); + } + + for (int i = 0; i < 3*32; i++) + if (REG_ISTOUCHED(avr, i)) { + printf("%s=%02x ", avr_regname(i), avr->data[i]); + } + printf("\n"); +} +#endif + +#define get_d5(o) \ + const uint8_t d = (o >> 4) & 0x1f; + +#define get_vd5(o) \ + get_d5(o) \ + const uint8_t vd = avr->data[d]; + +#define get_r5(o) \ + const uint8_t r = ((o >> 5) & 0x10) | (o & 0xf); + +#define get_d5_a6(o) \ + get_d5(o); \ + const uint8_t A = ((((o >> 9) & 3) << 4) | ((o) & 0xf)) + 32; + +#define get_vd5_s3(o) \ + get_vd5(o); \ + const uint8_t s = o & 7; + +#define get_vd5_s3_mask(o) \ + get_vd5_s3(o); \ + const uint8_t mask = 1 << s; + +#define get_vd5_vr5(o) \ + get_r5(o); \ + get_d5(o); \ + const uint8_t vd = avr->data[d], vr = avr->data[r]; + +#define get_d5_vr5(o) \ + get_d5(o); \ + get_r5(o); \ + const uint8_t vr = avr->data[r]; + +#define get_h4_k8(o) \ + const uint8_t h = 16 + ((o >> 4) & 0xf); \ + const uint8_t k = ((o & 0x0f00) >> 4) | (o & 0xf); + +#define get_vh4_k8(o) \ + get_h4_k8(o) \ + const uint8_t vh = avr->data[h]; + +#define get_d5_q6(o) \ + get_d5(o) \ + const uint8_t q = ((o & 0x2000) >> 8) | ((o & 0x0c00) >> 7) | (o & 0x7); + +#define get_io5(o) \ + const uint8_t io = ((o >> 3) & 0x1f) + 32; + +#define get_io5_b3(o) \ + get_io5(o); \ + const uint8_t b = o & 0x7; + +#define get_io5_b3mask(o) \ + get_io5(o); \ + const uint8_t mask = 1 << (o & 0x7); + +// const int16_t o = ((int16_t)(op << 4)) >> 3; // CLANG BUG! +#define get_o12(op) \ + const int16_t o = ((int16_t)((op << 4) & 0xffff)) >> 3; + +#define get_vp2_k6(o) \ + const uint8_t p = 24 + ((o >> 3) & 0x6); \ + const uint8_t k = ((o & 0x00c0) >> 2) | (o & 0xf); \ + const uint16_t vp = avr->data[p] | (avr->data[p + 1] << 8); + +#define get_sreg_bit(o) \ + const uint8_t b = (o >> 4) & 7; + +/* + * Add a "jump" address to the jump trace buffer + */ +#if CONFIG_SIMAVR_TRACE +#define TRACE_JUMP()\ + avr->trace_data->old[avr->trace_data->old_pci].pc = avr->pc;\ + avr->trace_data->old[avr->trace_data->old_pci].sp = _avr_sp_get(avr);\ + avr->trace_data->old_pci = (avr->trace_data->old_pci + 1) & (OLD_PC_SIZE-1);\ + +#if AVR_STACK_WATCH +#define STACK_FRAME_PUSH()\ + avr->trace_data->stack_frame[avr->trace_data->stack_frame_index].pc = avr->pc;\ + avr->trace_data->stack_frame[avr->trace_data->stack_frame_index].sp = _avr_sp_get(avr);\ + avr->trace_data->stack_frame_index++; +#define STACK_FRAME_POP()\ + if (avr->trace_data->stack_frame_index > 0) \ + avr->trace_data->stack_frame_index--; +#else +#define STACK_FRAME_PUSH() +#define STACK_FRAME_POP() +#endif +#else /* CONFIG_SIMAVR_TRACE */ + +#define TRACE_JUMP() +#define STACK_FRAME_PUSH() +#define STACK_FRAME_POP() + +#endif + +/****************************************************************************\ + * + * Helper functions for calculating the status register bit values. + * See the Atmel data sheet for the instruction set for more info. + * +\****************************************************************************/ + +static void +_avr_flags_zns (struct avr_t * avr, uint8_t res) +{ + avr->sreg[S_Z] = res == 0; + avr->sreg[S_N] = (res >> 7) & 1; + avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V]; +} + +static void +_avr_flags_zns16 (struct avr_t * avr, uint16_t res) +{ + avr->sreg[S_Z] = res == 0; + avr->sreg[S_N] = (res >> 15) & 1; + avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V]; +} + +static void +_avr_flags_add_zns (struct avr_t * avr, uint8_t res, uint8_t rd, uint8_t rr) +{ + /* carry & half carry */ + uint8_t add_carry = (rd & rr) | (rr & ~res) | (~res & rd); + avr->sreg[S_H] = (add_carry >> 3) & 1; + avr->sreg[S_C] = (add_carry >> 7) & 1; + + /* overflow */ + avr->sreg[S_V] = (((rd & rr & ~res) | (~rd & ~rr & res)) >> 7) & 1; + + /* zns */ + _avr_flags_zns(avr, res); +} + + +static void +_avr_flags_sub_zns (struct avr_t * avr, uint8_t res, uint8_t rd, uint8_t rr) +{ + /* carry & half carry */ + uint8_t sub_carry = (~rd & rr) | (rr & res) | (res & ~rd); + avr->sreg[S_H] = (sub_carry >> 3) & 1; + avr->sreg[S_C] = (sub_carry >> 7) & 1; + + /* overflow */ + avr->sreg[S_V] = (((rd & ~rr & ~res) | (~rd & rr & res)) >> 7) & 1; + + /* zns */ + _avr_flags_zns(avr, res); +} + +static void +_avr_flags_Rzns (struct avr_t * avr, uint8_t res) +{ + if (res) + avr->sreg[S_Z] = 0; + avr->sreg[S_N] = (res >> 7) & 1; + avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V]; +} + +static void +_avr_flags_sub_Rzns (struct avr_t * avr, uint8_t res, uint8_t rd, uint8_t rr) +{ + /* carry & half carry */ + uint8_t sub_carry = (~rd & rr) | (rr & res) | (res & ~rd); + avr->sreg[S_H] = (sub_carry >> 3) & 1; + avr->sreg[S_C] = (sub_carry >> 7) & 1; + + /* overflow */ + avr->sreg[S_V] = (((rd & ~rr & ~res) | (~rd & rr & res)) >> 7) & 1; + + _avr_flags_Rzns(avr, res); +} + +static void +_avr_flags_zcvs (struct avr_t * avr, uint8_t res, uint8_t vr) +{ + avr->sreg[S_Z] = res == 0; + avr->sreg[S_C] = vr & 1; + avr->sreg[S_V] = avr->sreg[S_N] ^ avr->sreg[S_C]; + avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V]; +} + +static void +_avr_flags_zcnvs (struct avr_t * avr, uint8_t res, uint8_t vr) +{ + avr->sreg[S_Z] = res == 0; + avr->sreg[S_C] = vr & 1; + avr->sreg[S_N] = res >> 7; + avr->sreg[S_V] = avr->sreg[S_N] ^ avr->sreg[S_C]; + avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V]; +} + +static void +_avr_flags_znv0s (struct avr_t * avr, uint8_t res) +{ + avr->sreg[S_V] = 0; + _avr_flags_zns(avr, res); +} + +static inline int _avr_is_instruction_32_bits(avr_t * avr, avr_flashaddr_t pc) +{ + uint16_t o = _avr_flash_read16le(avr, pc) & 0xfe0f; + return o == 0x9200 || // STS ! Store Direct to Data Space + o == 0x9000 || // LDS Load Direct from Data Space + o == 0x940c || // JMP Long Jump + o == 0x940d || // JMP Long Jump + o == 0x940e || // CALL Long Call to sub + o == 0x940f; // CALL Long Call to sub +} + +/* + * Main opcode decoder + * + * The decoder was written by following the datasheet in no particular order. + * As I went along, I noticed "bit patterns" that could be used to factor opcodes + * However, a lot of these only became apparent later on, so SOME instructions + * (skip of bit set etc) are compact, and some could use some refactoring (the ALU + * ones scream to be factored). + * I assume that the decoder could easily be 2/3 of it's current size. + * + * + It lacks the "extended" XMega jumps. + * + It also doesn't check whether the core it's + * emulating is supposed to have the fancy instructions, like multiply and such. + * + * The number of cycles taken by instruction has been added, but might not be + * entirely accurate. + */ +avr_flashaddr_t avr_run_one(avr_t * avr) +{ +run_one_again: +#if CONFIG_SIMAVR_TRACE + /* + * this traces spurious reset or bad jumps + */ + if ((avr->pc == 0 && avr->cycle > 0) || avr->pc >= avr->codeend || _avr_sp_get(avr) > avr->ramend) { +// avr->trace = 1; + STATE("RESET\n"); + crash(avr); + } + avr->trace_data->touched[0] = avr->trace_data->touched[1] = avr->trace_data->touched[2] = 0; +#endif + + /* Ensure we don't crash simavr due to a bad instruction reading past + * the end of the flash. + */ + if (unlikely(avr->pc >= avr->flashend)) { + STATE("CRASH\n"); + crash(avr); + return 0; + } + + uint32_t opcode = _avr_flash_read16le(avr, avr->pc); + avr_flashaddr_t new_pc = avr->pc + 2; // future "default" pc + int cycle = 1; + + switch (opcode & 0xf000) { + case 0x0000: { + switch (opcode) { + case 0x0000: { // NOP + STATE("nop\n"); + } break; + default: { + switch (opcode & 0xfc00) { + case 0x0400: { // CPC -- Compare with carry -- 0000 01rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd - vr - avr->sreg[S_C]; + STATE("cpc %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res); + _avr_flags_sub_Rzns(avr, res, vd, vr); + SREG(); + } break; + case 0x0c00: { // ADD -- Add without carry -- 0000 11rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd + vr; + if (r == d) { + STATE("lsl %s[%02x] = %02x\n", avr_regname(d), vd, res & 0xff); + } else { + STATE("add %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res); + } + _avr_set_r(avr, d, res); + _avr_flags_add_zns(avr, res, vd, vr); + SREG(); + } break; + case 0x0800: { // SBC -- Subtract with carry -- 0000 10rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd - vr - avr->sreg[S_C]; + STATE("sbc %s[%02x], %s[%02x] = %02x\n", avr_regname(d), avr->data[d], avr_regname(r), avr->data[r], res); + _avr_set_r(avr, d, res); + _avr_flags_sub_Rzns(avr, res, vd, vr); + SREG(); + } break; + default: + switch (opcode & 0xff00) { + case 0x0100: { // MOVW -- Copy Register Word -- 0000 0001 dddd rrrr + uint8_t d = ((opcode >> 4) & 0xf) << 1; + uint8_t r = ((opcode) & 0xf) << 1; + STATE("movw %s:%s, %s:%s[%02x%02x]\n", avr_regname(d), avr_regname(d+1), avr_regname(r), avr_regname(r+1), avr->data[r+1], avr->data[r]); + uint16_t vr = avr->data[r] | (avr->data[r + 1] << 8); + _avr_set_r16le(avr, d, vr); + } break; + case 0x0200: { // MULS -- Multiply Signed -- 0000 0010 dddd rrrr + int8_t r = 16 + (opcode & 0xf); + int8_t d = 16 + ((opcode >> 4) & 0xf); + int16_t res = ((int8_t)avr->data[r]) * ((int8_t)avr->data[d]); + STATE("muls %s[%d], %s[%02x] = %d\n", avr_regname(d), ((int8_t)avr->data[d]), avr_regname(r), ((int8_t)avr->data[r]), res); + _avr_set_r16le(avr, 0, res); + avr->sreg[S_C] = (res >> 15) & 1; + avr->sreg[S_Z] = res == 0; + cycle++; + SREG(); + } break; + case 0x0300: { // MUL -- Multiply -- 0000 0011 fddd frrr + int8_t r = 16 + (opcode & 0x7); + int8_t d = 16 + ((opcode >> 4) & 0x7); + int16_t res = 0; + uint8_t c = 0; + T(const char * name = "";) + switch (opcode & 0x88) { + case 0x00: // MULSU -- Multiply Signed Unsigned -- 0000 0011 0ddd 0rrr + res = ((uint8_t)avr->data[r]) * ((int8_t)avr->data[d]); + c = (res >> 15) & 1; + T(name = "mulsu";) + break; + case 0x08: // FMUL -- Fractional Multiply Unsigned -- 0000 0011 0ddd 1rrr + res = ((uint8_t)avr->data[r]) * ((uint8_t)avr->data[d]); + c = (res >> 15) & 1; + res <<= 1; + T(name = "fmul";) + break; + case 0x80: // FMULS -- Multiply Signed -- 0000 0011 1ddd 0rrr + res = ((int8_t)avr->data[r]) * ((int8_t)avr->data[d]); + c = (res >> 15) & 1; + res <<= 1; + T(name = "fmuls";) + break; + case 0x88: // FMULSU -- Multiply Signed Unsigned -- 0000 0011 1ddd 1rrr + res = ((uint8_t)avr->data[r]) * ((int8_t)avr->data[d]); + c = (res >> 15) & 1; + res <<= 1; + T(name = "fmulsu";) + break; + } + cycle++; + STATE("%s %s[%d], %s[%02x] = %d\n", name, avr_regname(d), ((int8_t)avr->data[d]), avr_regname(r), ((int8_t)avr->data[r]), res); + _avr_set_r16le(avr, 0, res); + avr->sreg[S_C] = c; + avr->sreg[S_Z] = res == 0; + SREG(); + } break; + default: _avr_invalid_opcode(avr); + } + } + } + } + } break; + + case 0x1000: { + switch (opcode & 0xfc00) { + case 0x1800: { // SUB -- Subtract without carry -- 0001 10rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd - vr; + STATE("sub %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res); + _avr_set_r(avr, d, res); + _avr_flags_sub_zns(avr, res, vd, vr); + SREG(); + } break; + case 0x1000: { // CPSE -- Compare, skip if equal -- 0001 00rd dddd rrrr + get_vd5_vr5(opcode); + uint16_t res = vd == vr; + STATE("cpse %s[%02x], %s[%02x]\t; Will%s skip\n", avr_regname(d), avr->data[d], avr_regname(r), avr->data[r], res ? "":" not"); + if (res) { + if (_avr_is_instruction_32_bits(avr, new_pc)) { + new_pc += 4; cycle += 2; + } else { + new_pc += 2; cycle++; + } + } + } break; + case 0x1400: { // CP -- Compare -- 0001 01rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd - vr; + STATE("cp %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res); + _avr_flags_sub_zns(avr, res, vd, vr); + SREG(); + } break; + case 0x1c00: { // ADD -- Add with carry -- 0001 11rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd + vr + avr->sreg[S_C]; + if (r == d) { + STATE("rol %s[%02x] = %02x\n", avr_regname(d), avr->data[d], res); + } else { + STATE("addc %s[%02x], %s[%02x] = %02x\n", avr_regname(d), avr->data[d], avr_regname(r), avr->data[r], res); + } + _avr_set_r(avr, d, res); + _avr_flags_add_zns(avr, res, vd, vr); + SREG(); + } break; + default: _avr_invalid_opcode(avr); + } + } break; + + case 0x2000: { + switch (opcode & 0xfc00) { + case 0x2000: { // AND -- Logical AND -- 0010 00rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd & vr; + if (r == d) { + STATE("tst %s[%02x]\n", avr_regname(d), avr->data[d]); + } else { + STATE("and %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res); + } + _avr_set_r(avr, d, res); + _avr_flags_znv0s(avr, res); + SREG(); + } break; + case 0x2400: { // EOR -- Logical Exclusive OR -- 0010 01rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd ^ vr; + if (r==d) { + STATE("clr %s[%02x]\n", avr_regname(d), avr->data[d]); + } else { + STATE("eor %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res); + } + _avr_set_r(avr, d, res); + _avr_flags_znv0s(avr, res); + SREG(); + } break; + case 0x2800: { // OR -- Logical OR -- 0010 10rd dddd rrrr + get_vd5_vr5(opcode); + uint8_t res = vd | vr; + STATE("or %s[%02x], %s[%02x] = %02x\n", avr_regname(d), vd, avr_regname(r), vr, res); + _avr_set_r(avr, d, res); + _avr_flags_znv0s(avr, res); + SREG(); + } break; + case 0x2c00: { // MOV -- 0010 11rd dddd rrrr + get_d5_vr5(opcode); + uint8_t res = vr; + STATE("mov %s, %s[%02x] = %02x\n", avr_regname(d), avr_regname(r), vr, res); + _avr_set_r(avr, d, res); + } break; + default: _avr_invalid_opcode(avr); + } + } break; + + case 0x3000: { // CPI -- Compare Immediate -- 0011 kkkk hhhh kkkk + get_vh4_k8(opcode); + uint8_t res = vh - k; + STATE("cpi %s[%02x], 0x%02x\n", avr_regname(h), vh, k); + _avr_flags_sub_zns(avr, res, vh, k); + SREG(); + } break; + + case 0x4000: { // SBCI -- Subtract Immediate With Carry -- 0100 kkkk hhhh kkkk + get_vh4_k8(opcode); + uint8_t res = vh - k - avr->sreg[S_C]; + STATE("sbci %s[%02x], 0x%02x = %02x\n", avr_regname(h), vh, k, res); + _avr_set_r(avr, h, res); + _avr_flags_sub_Rzns(avr, res, vh, k); + SREG(); + } break; + + case 0x5000: { // SUBI -- Subtract Immediate -- 0101 kkkk hhhh kkkk + get_vh4_k8(opcode); + uint8_t res = vh - k; + STATE("subi %s[%02x], 0x%02x = %02x\n", avr_regname(h), vh, k, res); + _avr_set_r(avr, h, res); + _avr_flags_sub_zns(avr, res, vh, k); + SREG(); + } break; + + case 0x6000: { // ORI aka SBR -- Logical OR with Immediate -- 0110 kkkk hhhh kkkk + get_vh4_k8(opcode); + uint8_t res = vh | k; + STATE("ori %s[%02x], 0x%02x\n", avr_regname(h), vh, k); + _avr_set_r(avr, h, res); + _avr_flags_znv0s(avr, res); + SREG(); + } break; + + case 0x7000: { // ANDI -- Logical AND with Immediate -- 0111 kkkk hhhh kkkk + get_vh4_k8(opcode); + uint8_t res = vh & k; + STATE("andi %s[%02x], 0x%02x\n", avr_regname(h), vh, k); + _avr_set_r(avr, h, res); + _avr_flags_znv0s(avr, res); + SREG(); + } break; + + case 0xa000: + case 0x8000: { + /* + * Load (LDD/STD) store instructions + * + * 10q0 qqsd dddd yqqq + * s = 0 = load, 1 = store + * y = 16 bits register index, 1 = Y, 0 = X + * q = 6 bit displacement + */ + switch (opcode & 0xd008) { + case 0xa000: + case 0x8000: { // LD (LDD) -- Load Indirect using Z -- 10q0 qqsd dddd yqqq + uint16_t v = avr->data[R_ZL] | (avr->data[R_ZH] << 8); + get_d5_q6(opcode); + if (opcode & 0x0200) { + STATE("st (Z+%d[%04x]), %s[%02x]\n", q, v+q, avr_regname(d), avr->data[d]); + _avr_set_ram(avr, v+q, avr->data[d]); + } else { + STATE("ld %s, (Z+%d[%04x])=[%02x]\n", avr_regname(d), q, v+q, avr->data[v+q]); + _avr_set_r(avr, d, _avr_get_ram(avr, v+q)); + } + cycle += 1; // 2 cycles, 3 for tinyavr + } break; + case 0xa008: + case 0x8008: { // LD (LDD) -- Load Indirect using Y -- 10q0 qqsd dddd yqqq + uint16_t v = avr->data[R_YL] | (avr->data[R_YH] << 8); + get_d5_q6(opcode); + if (opcode & 0x0200) { + STATE("st (Y+%d[%04x]), %s[%02x]\n", q, v+q, avr_regname(d), avr->data[d]); + _avr_set_ram(avr, v+q, avr->data[d]); + } else { + STATE("ld %s, (Y+%d[%04x])=[%02x]\n", avr_regname(d), q, v+q, avr->data[d+q]); + _avr_set_r(avr, d, _avr_get_ram(avr, v+q)); + } + cycle += 1; // 2 cycles, 3 for tinyavr + } break; + default: _avr_invalid_opcode(avr); + } + } break; + + case 0x9000: { + /* this is an annoying special case, but at least these lines handle all the SREG set/clear opcodes */ + if ((opcode & 0xff0f) == 0x9408) { + get_sreg_bit(opcode); + STATE("%s%c\n", opcode & 0x0080 ? "cl" : "se", _sreg_bit_name[b]); + avr_sreg_set(avr, b, (opcode & 0x0080) == 0); + SREG(); + } else switch (opcode) { + case 0x9588: { // SLEEP -- 1001 0101 1000 1000 + STATE("sleep\n"); + /* Don't sleep if there are interrupts about to be serviced. + * Without this check, it was possible to incorrectly enter a state + * in which the cpu was sleeping and interrupts were disabled. For more + * details, see the commit message. */ + if (!avr_has_pending_interrupts(avr) || !avr->sreg[S_I]) + avr->state = cpu_Sleeping; + } break; + case 0x9598: { // BREAK -- 1001 0101 1001 1000 + STATE("break\n"); + if (avr->gdb) { + // if gdb is on, break here. + avr->state = cpu_Stopped; + avr_gdb_handle_break(avr); + } + } break; + case 0x95a8: { // WDR -- Watchdog Reset -- 1001 0101 1010 1000 + STATE("wdr\n"); + avr_ioctl(avr, AVR_IOCTL_WATCHDOG_RESET, 0); + } break; + case 0x95e8: { // SPM -- Store Program Memory -- 1001 0101 1110 1000 + STATE("spm\n"); + avr_ioctl(avr, AVR_IOCTL_FLASH_SPM, 0); + } break; + case 0x9409: // IJMP -- Indirect jump -- 1001 0100 0000 1001 + case 0x9419: // EIJMP -- Indirect jump -- 1001 0100 0001 1001 bit 4 is "indirect" + case 0x9509: // ICALL -- Indirect Call to Subroutine -- 1001 0101 0000 1001 + case 0x9519: { // EICALL -- Indirect Call to Subroutine -- 1001 0101 0001 1001 bit 8 is "push pc" + int e = opcode & 0x10; + int p = opcode & 0x100; + if (e && !avr->eind) + _avr_invalid_opcode(avr); + uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); + if (e) + z |= avr->data[avr->eind] << 16; + STATE("%si%s Z[%04x]\n", e?"e":"", p?"call":"jmp", z << 1); + if (p) + cycle += _avr_push_addr(avr, new_pc) - 1; + new_pc = z << 1; + cycle++; + TRACE_JUMP(); + } break; + case 0x9518: // RETI -- Return from Interrupt -- 1001 0101 0001 1000 + avr_sreg_set(avr, S_I, 1); + avr_interrupt_reti(avr); + FALLTHROUGH + case 0x9508: { // RET -- Return -- 1001 0101 0000 1000 + new_pc = _avr_pop_addr(avr); + cycle += 1 + avr->address_size; + STATE("ret%s\n", opcode & 0x10 ? "i" : ""); + TRACE_JUMP(); + STACK_FRAME_POP(); + } break; + case 0x95c8: { // LPM -- Load Program Memory R0 <- (Z) -- 1001 0101 1100 1000 + uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); + STATE("lpm %s, (Z[%04x])\n", avr_regname(0), z); + cycle += 2; // 3 cycles + _avr_set_r(avr, 0, avr->flash[z]); + } break; + case 0x95d8: { // ELPM -- Load Program Memory R0 <- (Z) -- 1001 0101 1101 1000 + if (!avr->rampz) + _avr_invalid_opcode(avr); + uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) | (avr->data[avr->rampz] << 16); + STATE("elpm %s, (Z[%02x:%04x])\n", avr_regname(0), z >> 16, z & 0xffff); + _avr_set_r(avr, 0, avr->flash[z]); + cycle += 2; // 3 cycles + } break; + default: { + switch (opcode & 0xfe0f) { + case 0x9000: { // LDS -- Load Direct from Data Space, 32 bits -- 1001 0000 0000 0000 + get_d5(opcode); + uint16_t x = _avr_flash_read16le(avr, new_pc); + new_pc += 2; + STATE("lds %s[%02x], 0x%04x\n", avr_regname(d), avr->data[d], x); + _avr_set_r(avr, d, _avr_get_ram(avr, x)); + cycle++; // 2 cycles + } break; + case 0x9005: + case 0x9004: { // LPM -- Load Program Memory -- 1001 000d dddd 01oo + get_d5(opcode); + uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); + int op = opcode & 1; + STATE("lpm %s, (Z[%04x]%s)\n", avr_regname(d), z, op ? "+" : ""); + _avr_set_r(avr, d, avr->flash[z]); + if (op) { + z++; + _avr_set_r16le_hl(avr, R_ZL, z); + } + cycle += 2; // 3 cycles + } break; + case 0x9006: + case 0x9007: { // ELPM -- Extended Load Program Memory -- 1001 000d dddd 01oo + if (!avr->rampz) + _avr_invalid_opcode(avr); + uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) | (avr->data[avr->rampz] << 16); + get_d5(opcode); + int op = opcode & 1; + STATE("elpm %s, (Z[%02x:%04x]%s)\n", avr_regname(d), z >> 16, z & 0xffff, op ? "+" : ""); + _avr_set_r(avr, d, avr->flash[z]); + if (op) { + z++; + _avr_set_r(avr, avr->rampz, z >> 16); + _avr_set_r16le_hl(avr, R_ZL, z); + } + cycle += 2; // 3 cycles + } break; + /* + * Load store instructions + * + * 1001 00sr rrrr iioo + * s = 0 = load, 1 = store + * ii = 16 bits register index, 11 = X, 10 = Y, 00 = Z + * oo = 1) post increment, 2) pre-decrement + */ + case 0x900c: + case 0x900d: + case 0x900e: { // LD -- Load Indirect from Data using X -- 1001 000d dddd 11oo + int op = opcode & 3; + get_d5(opcode); + uint16_t x = (avr->data[R_XH] << 8) | avr->data[R_XL]; + STATE("ld %s, %sX[%04x]%s\n", avr_regname(d), op == 2 ? "--" : "", x, op == 1 ? "++" : ""); + cycle++; // 2 cycles (1 for tinyavr, except with inc/dec 2) + if (op == 2) x--; + uint8_t vd = _avr_get_ram(avr, x); + if (op == 1) x++; + _avr_set_r16le_hl(avr, R_XL, x); + _avr_set_r(avr, d, vd); + } break; + case 0x920c: + case 0x920d: + case 0x920e: { // ST -- Store Indirect Data Space X -- 1001 001d dddd 11oo + int op = opcode & 3; + get_vd5(opcode); + uint16_t x = (avr->data[R_XH] << 8) | avr->data[R_XL]; + STATE("st %sX[%04x]%s, %s[%02x] \n", op == 2 ? "--" : "", x, op == 1 ? "++" : "", avr_regname(d), vd); + cycle++; // 2 cycles, except tinyavr + if (op == 2) x--; + _avr_set_ram(avr, x, vd); + if (op == 1) x++; + _avr_set_r16le_hl(avr, R_XL, x); + } break; + case 0x9009: + case 0x900a: { // LD -- Load Indirect from Data using Y -- 1001 000d dddd 10oo + int op = opcode & 3; + get_d5(opcode); + uint16_t y = (avr->data[R_YH] << 8) | avr->data[R_YL]; + STATE("ld %s, %sY[%04x]%s\n", avr_regname(d), op == 2 ? "--" : "", y, op == 1 ? "++" : ""); + cycle++; // 2 cycles, except tinyavr + if (op == 2) y--; + uint8_t vd = _avr_get_ram(avr, y); + if (op == 1) y++; + _avr_set_r16le_hl(avr, R_YL, y); + _avr_set_r(avr, d, vd); + } break; + case 0x9209: + case 0x920a: { // ST -- Store Indirect Data Space Y -- 1001 001d dddd 10oo + int op = opcode & 3; + get_vd5(opcode); + uint16_t y = (avr->data[R_YH] << 8) | avr->data[R_YL]; + STATE("st %sY[%04x]%s, %s[%02x]\n", op == 2 ? "--" : "", y, op == 1 ? "++" : "", avr_regname(d), vd); + cycle++; + if (op == 2) y--; + _avr_set_ram(avr, y, vd); + if (op == 1) y++; + _avr_set_r16le_hl(avr, R_YL, y); + } break; + case 0x9200: { // STS -- Store Direct to Data Space, 32 bits -- 1001 0010 0000 0000 + get_vd5(opcode); + uint16_t x = _avr_flash_read16le(avr, new_pc); + new_pc += 2; + STATE("sts 0x%04x, %s[%02x]\n", x, avr_regname(d), vd); + cycle++; + _avr_set_ram(avr, x, vd); + } break; + case 0x9001: + case 0x9002: { // LD -- Load Indirect from Data using Z -- 1001 000d dddd 00oo + int op = opcode & 3; + get_d5(opcode); + uint16_t z = (avr->data[R_ZH] << 8) | avr->data[R_ZL]; + STATE("ld %s, %sZ[%04x]%s\n", avr_regname(d), op == 2 ? "--" : "", z, op == 1 ? "++" : ""); + cycle++;; // 2 cycles, except tinyavr + if (op == 2) z--; + uint8_t vd = _avr_get_ram(avr, z); + if (op == 1) z++; + _avr_set_r16le_hl(avr, R_ZL, z); + _avr_set_r(avr, d, vd); + } break; + case 0x9201: + case 0x9202: { // ST -- Store Indirect Data Space Z -- 1001 001d dddd 00oo + int op = opcode & 3; + get_vd5(opcode); + uint16_t z = (avr->data[R_ZH] << 8) | avr->data[R_ZL]; + STATE("st %sZ[%04x]%s, %s[%02x] \n", op == 2 ? "--" : "", z, op == 1 ? "++" : "", avr_regname(d), vd); + cycle++; // 2 cycles, except tinyavr + if (op == 2) z--; + _avr_set_ram(avr, z, vd); + if (op == 1) z++; + _avr_set_r16le_hl(avr, R_ZL, z); + } break; + case 0x900f: { // POP -- 1001 000d dddd 1111 + get_d5(opcode); + _avr_set_r(avr, d, _avr_pop8(avr)); + T(uint16_t sp = _avr_sp_get(avr);) + STATE("pop %s (@%04x)[%02x]\n", avr_regname(d), sp, avr->data[sp]); + cycle++; + } break; + case 0x920f: { // PUSH -- 1001 001d dddd 1111 + get_vd5(opcode); + _avr_push8(avr, vd); + T(uint16_t sp = _avr_sp_get(avr);) + STATE("push %s[%02x] (@%04x)\n", avr_regname(d), vd, sp); + cycle++; + } break; + case 0x9400: { // COM -- One's Complement -- 1001 010d dddd 0000 + get_vd5(opcode); + uint8_t res = 0xff - vd; + STATE("com %s[%02x] = %02x\n", avr_regname(d), vd, res); + _avr_set_r(avr, d, res); + _avr_flags_znv0s(avr, res); + avr->sreg[S_C] = 1; + SREG(); + } break; + case 0x9401: { // NEG -- Two's Complement -- 1001 010d dddd 0001 + get_vd5(opcode); + uint8_t res = 0x00 - vd; + STATE("neg %s[%02x] = %02x\n", avr_regname(d), vd, res); + _avr_set_r(avr, d, res); + avr->sreg[S_H] = ((res >> 3) | (vd >> 3)) & 1; + avr->sreg[S_V] = res == 0x80; + avr->sreg[S_C] = res != 0; + _avr_flags_zns(avr, res); + SREG(); + } break; + case 0x9402: { // SWAP -- Swap Nibbles -- 1001 010d dddd 0010 + get_vd5(opcode); + uint8_t res = (vd >> 4) | (vd << 4) ; + STATE("swap %s[%02x] = %02x\n", avr_regname(d), vd, res); + _avr_set_r(avr, d, res); + } break; + case 0x9403: { // INC -- Increment -- 1001 010d dddd 0011 + get_vd5(opcode); + uint8_t res = vd + 1; + STATE("inc %s[%02x] = %02x\n", avr_regname(d), vd, res); + _avr_set_r(avr, d, res); + avr->sreg[S_V] = res == 0x80; + _avr_flags_zns(avr, res); + SREG(); + } break; + case 0x9405: { // ASR -- Arithmetic Shift Right -- 1001 010d dddd 0101 + get_vd5(opcode); + uint8_t res = (vd >> 1) | (vd & 0x80); + STATE("asr %s[%02x]\n", avr_regname(d), vd); + _avr_set_r(avr, d, res); + _avr_flags_zcnvs(avr, res, vd); + SREG(); + } break; + case 0x9406: { // LSR -- Logical Shift Right -- 1001 010d dddd 0110 + get_vd5(opcode); + uint8_t res = vd >> 1; + STATE("lsr %s[%02x]\n", avr_regname(d), vd); + _avr_set_r(avr, d, res); + avr->sreg[S_N] = 0; + _avr_flags_zcvs(avr, res, vd); + SREG(); + } break; + case 0x9407: { // ROR -- Rotate Right -- 1001 010d dddd 0111 + get_vd5(opcode); + uint8_t res = (avr->sreg[S_C] ? 0x80 : 0) | vd >> 1; + STATE("ror %s[%02x]\n", avr_regname(d), vd); + _avr_set_r(avr, d, res); + _avr_flags_zcnvs(avr, res, vd); + SREG(); + } break; + case 0x940a: { // DEC -- Decrement -- 1001 010d dddd 1010 + get_vd5(opcode); + uint8_t res = vd - 1; + STATE("dec %s[%02x] = %02x\n", avr_regname(d), vd, res); + _avr_set_r(avr, d, res); + avr->sreg[S_V] = res == 0x7f; + _avr_flags_zns(avr, res); + SREG(); + } break; + case 0x940c: + case 0x940d: { // JMP -- Long Call to sub, 32 bits -- 1001 010a aaaa 110a + avr_flashaddr_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1); + uint16_t x = _avr_flash_read16le(avr, new_pc); + a = (a << 16) | x; + STATE("jmp 0x%06x\n", a); + new_pc = a << 1; + cycle += 2; + TRACE_JUMP(); + } break; + case 0x940e: + case 0x940f: { // CALL -- Long Call to sub, 32 bits -- 1001 010a aaaa 111a + avr_flashaddr_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1); + uint16_t x = _avr_flash_read16le(avr, new_pc); + a = (a << 16) | x; + STATE("call 0x%06x\n", a); + new_pc += 2; + cycle += 1 + _avr_push_addr(avr, new_pc); + new_pc = a << 1; + TRACE_JUMP(); + STACK_FRAME_PUSH(); + } break; + + default: { + switch (opcode & 0xff00) { + case 0x9600: { // ADIW -- Add Immediate to Word -- 1001 0110 KKpp KKKK + get_vp2_k6(opcode); + uint16_t res = vp + k; + STATE("adiw %s:%s[%04x], 0x%02x\n", avr_regname(p), avr_regname(p + 1), vp, k); + _avr_set_r16le_hl(avr, p, res); + avr->sreg[S_V] = ((~vp & res) >> 15) & 1; + avr->sreg[S_C] = ((~res & vp) >> 15) & 1; + _avr_flags_zns16(avr, res); + SREG(); + cycle++; + } break; + case 0x9700: { // SBIW -- Subtract Immediate from Word -- 1001 0111 KKpp KKKK + get_vp2_k6(opcode); + uint16_t res = vp - k; + STATE("sbiw %s:%s[%04x], 0x%02x\n", avr_regname(p), avr_regname(p + 1), vp, k); + _avr_set_r16le_hl(avr, p, res); + avr->sreg[S_V] = ((vp & ~res) >> 15) & 1; + avr->sreg[S_C] = ((res & ~vp) >> 15) & 1; + _avr_flags_zns16(avr, res); + SREG(); + cycle++; + } break; + case 0x9800: { // CBI -- Clear Bit in I/O Register -- 1001 1000 AAAA Abbb + get_io5_b3mask(opcode); + uint8_t res = _avr_get_ram(avr, io) & ~mask; + STATE("cbi %s[%04x], 0x%02x = %02x\n", avr_regname(io), avr->data[io], mask, res); + _avr_set_ram(avr, io, res); + cycle++; + } break; + case 0x9900: { // SBIC -- Skip if Bit in I/O Register is Cleared -- 1001 1001 AAAA Abbb + get_io5_b3mask(opcode); + uint8_t res = _avr_get_ram(avr, io) & mask; + STATE("sbic %s[%04x], 0x%02x\t; Will%s branch\n", avr_regname(io), avr->data[io], mask, !res?"":" not"); + if (!res) { + if (_avr_is_instruction_32_bits(avr, new_pc)) { + new_pc += 4; cycle += 2; + } else { + new_pc += 2; cycle++; + } + } + } break; + case 0x9a00: { // SBI -- Set Bit in I/O Register -- 1001 1010 AAAA Abbb + get_io5_b3mask(opcode); + uint8_t res = _avr_get_ram(avr, io) | mask; + STATE("sbi %s[%04x], 0x%02x = %02x\n", avr_regname(io), avr->data[io], mask, res); + _avr_set_ram(avr, io, res); + cycle++; + } break; + case 0x9b00: { // SBIS -- Skip if Bit in I/O Register is Set -- 1001 1011 AAAA Abbb + get_io5_b3mask(opcode); + uint8_t res = _avr_get_ram(avr, io) & mask; + STATE("sbis %s[%04x], 0x%02x\t; Will%s branch\n", avr_regname(io), avr->data[io], mask, res?"":" not"); + if (res) { + if (_avr_is_instruction_32_bits(avr, new_pc)) { + new_pc += 4; cycle += 2; + } else { + new_pc += 2; cycle++; + } + } + } break; + default: + switch (opcode & 0xfc00) { + case 0x9c00: { // MUL -- Multiply Unsigned -- 1001 11rd dddd rrrr + get_vd5_vr5(opcode); + uint16_t res = vd * vr; + STATE("mul %s[%02x], %s[%02x] = %04x\n", avr_regname(d), vd, avr_regname(r), vr, res); + cycle++; + _avr_set_r16le(avr, 0, res); + avr->sreg[S_Z] = res == 0; + avr->sreg[S_C] = (res >> 15) & 1; + SREG(); + } break; + default: _avr_invalid_opcode(avr); + } + } + } break; + } + } break; + } + } break; + + case 0xb000: { + switch (opcode & 0xf800) { + case 0xb800: { // OUT A,Rr -- 1011 1AAd dddd AAAA + get_d5_a6(opcode); + STATE("out %s, %s[%02x]\n", avr_regname(A), avr_regname(d), avr->data[d]); + _avr_set_ram(avr, A, avr->data[d]); + } break; + case 0xb000: { // IN Rd,A -- 1011 0AAd dddd AAAA + get_d5_a6(opcode); + STATE("in %s, %s[%02x]\n", avr_regname(d), avr_regname(A), avr->data[A]); + _avr_set_r(avr, d, _avr_get_ram(avr, A)); + } break; + default: _avr_invalid_opcode(avr); + } + } break; + + case 0xc000: { // RJMP -- 1100 kkkk kkkk kkkk + get_o12(opcode); + STATE("rjmp .%d [%04x]\n", o >> 1, new_pc + o); + new_pc = (new_pc + o) % (avr->flashend+1); + cycle++; + TRACE_JUMP(); + } break; + + case 0xd000: { // RCALL -- 1101 kkkk kkkk kkkk + get_o12(opcode); + STATE("rcall .%d [%04x]\n", o >> 1, new_pc + o); + cycle += _avr_push_addr(avr, new_pc); + new_pc = (new_pc + o) % (avr->flashend+1); + // 'rcall .1' is used as a cheap "push 16 bits of room on the stack" + if (o != 0) { + TRACE_JUMP(); + STACK_FRAME_PUSH(); + } + } break; + + case 0xe000: { // LDI Rd, K aka SER (LDI r, 0xff) -- 1110 kkkk dddd kkkk + get_h4_k8(opcode); + STATE("ldi %s, 0x%02x\n", avr_regname(h), k); + _avr_set_r(avr, h, k); + } break; + + case 0xf000: { + switch (opcode & 0xfe00) { + case 0xf100: { /* simavr special opcodes */ + if (opcode == 0xf1f1) { // AVR_OVERFLOW_OPCODE + printf("FLASH overflow, soft reset\n"); + new_pc = 0; + TRACE_JUMP(); + } + } break; + case 0xf000: + case 0xf200: + case 0xf400: + case 0xf600: { // BRXC/BRXS -- All the SREG branches -- 1111 0Boo oooo osss + int16_t o = ((int16_t)(opcode << 6)) >> 9; // offset + uint8_t s = opcode & 7; + int set = (opcode & 0x0400) == 0; // this bit means BRXC otherwise BRXS + int branch = (avr->sreg[s] && set) || (!avr->sreg[s] && !set); + const char *names[2][8] = { + { "brcc", "brne", "brpl", "brvc", NULL, "brhc", "brtc", "brid"}, + { "brcs", "breq", "brmi", "brvs", NULL, "brhs", "brts", "brie"}, + }; + if (names[set][s]) { + STATE("%s .%d [%04x]\t; Will%s branch\n", names[set][s], o, new_pc + (o << 1), branch ? "":" not"); + } else { + STATE("%s%c .%d [%04x]\t; Will%s branch\n", set ? "brbs" : "brbc", _sreg_bit_name[s], o, new_pc + (o << 1), branch ? "":" not"); + } + if (branch) { + cycle++; // 2 cycles if taken, 1 otherwise + new_pc = new_pc + (o << 1); + } + } break; + case 0xf800: + case 0xf900: { // BLD -- Bit Store from T into a Bit in Register -- 1111 100d dddd 0bbb + get_vd5_s3_mask(opcode); + uint8_t v = (vd & ~mask) | (avr->sreg[S_T] ? mask : 0); + STATE("bld %s[%02x], 0x%02x = %02x\n", avr_regname(d), vd, mask, v); + _avr_set_r(avr, d, v); + } break; + case 0xfa00: + case 0xfb00:{ // BST -- Bit Store into T from bit in Register -- 1111 101d dddd 0bbb + get_vd5_s3(opcode) + STATE("bst %s[%02x], 0x%02x\n", avr_regname(d), vd, 1 << s); + avr->sreg[S_T] = (vd >> s) & 1; + SREG(); + } break; + case 0xfc00: + case 0xfe00: { // SBRS/SBRC -- Skip if Bit in Register is Set/Clear -- 1111 11sd dddd 0bbb + get_vd5_s3_mask(opcode) + int set = (opcode & 0x0200) != 0; + int branch = ((vd & mask) && set) || (!(vd & mask) && !set); + STATE("%s %s[%02x], 0x%02x\t; Will%s branch\n", set ? "sbrs" : "sbrc", avr_regname(d), vd, mask, branch ? "":" not"); + if (branch) { + if (_avr_is_instruction_32_bits(avr, new_pc)) { + new_pc += 4; cycle += 2; + } else { + new_pc += 2; cycle++; + } + } + } break; + default: _avr_invalid_opcode(avr); + } + } break; + + default: _avr_invalid_opcode(avr); + + } + avr->cycle += cycle; + + if ((avr->state == cpu_Running) && + (avr->run_cycle_count > cycle) && + (avr->interrupt_state == 0)) + { + avr->run_cycle_count -= cycle; + avr->pc = new_pc; + goto run_one_again; + } + + return new_pc; +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.h new file mode 100644 index 0000000..874651e --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_core.h @@ -0,0 +1,142 @@ +/* + sim_core.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __SIM_CORE_H__ +#define __SIM_CORE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef NO_COLOR + #define FONT_GREEN + #define FONT_RED + #define FONT_DEFAULT +#else + #define FONT_GREEN "\e[32m" + #define FONT_RED "\e[31m" + #define FONT_DEFAULT "\e[0m" +#endif + +/* + * Instruction decoder, run ONE instruction + */ +avr_flashaddr_t avr_run_one(avr_t * avr); + +/* + * These are for internal access to the stack (for interrupts) + */ +uint16_t _avr_sp_get(avr_t * avr); +void _avr_sp_set(avr_t * avr, uint16_t sp); +int _avr_push_addr(avr_t * avr, avr_flashaddr_t addr); + +#if CONFIG_SIMAVR_TRACE + +/* + * Get a "pretty" register name + */ +const char * avr_regname(unsigned int reg); + +/* + * DEBUG bits follow + * These will disappear when gdb arrives + */ +void avr_dump_state(avr_t * avr); + +#define DUMP_REG() { \ + for (int i = 0; i < 32; i++) printf("%s=%02x%c", avr_regname(i), avr->data[i],i==15?'\n':' ');\ + printf("\n");\ + uint16_t y = avr->data[R_YL] | (avr->data[R_YH]<<8);\ + for (int i = 0; i < 20; i++) printf("Y+%02d=%02x ", i, avr->data[y+i]);\ + printf("\n");\ + } + + +#if AVR_STACK_WATCH +#define DUMP_STACK() \ + for (int i = avr->trace_data->stack_frame_index; i; i--) {\ + int pci = i-1;\ + printf(FONT_RED "*** %04x: %-25s sp %04x\n" FONT_DEFAULT,\ + avr->trace_data->stack_frame[pci].pc, \ + avr->trace_data->codeline ? avr->trace_data->codeline[avr->trace_data->stack_frame[pci].pc>>1]->symbol : "unknown", \ + avr->trace_data->stack_frame[pci].sp);\ + } +#else +#define DUMP_STACK() +#endif + +#else /* CONFIG_SIMAVR_TRACE */ + +#define DUMP_STACK() +#define DUMP_REG(); + +#endif + +/** + * Reconstructs the SREG value from avr->sreg into dst. + */ +#define READ_SREG_INTO(avr, dst) { \ + dst = 0; \ + for (int i = 0; i < 8; i++) \ + if (avr->sreg[i] > 1) { \ + printf("** Invalid SREG!!\n"); \ + } else if (avr->sreg[i]) \ + dst |= (1 << i); \ + } + +static inline void avr_sreg_set(avr_t * avr, uint8_t flag, uint8_t ival) +{ + /* + * clear interrupt_state if disabling interrupts. + * set wait if enabling interrupts. + * no change if interrupt flag does not change. + */ + + if (flag == S_I) { + if (ival) { + if (!avr->sreg[S_I]) + avr->interrupt_state = -1; + } else + avr->interrupt_state = 0; + } + + avr->sreg[flag] = ival; +} + +/** + * Splits the SREG value from src into the avr->sreg array. + */ +#define SET_SREG_FROM(avr, src) { \ + for (int i = 0; i < 8; i++) \ + avr_sreg_set(avr, i, (src & (1 << i)) != 0); \ + } + +/* + * Opcode is sitting at the end of the flash to catch PC overflows. + * Apparently it's used by some code to simulate soft reset? + */ +#define AVR_OVERFLOW_OPCODE 0xf1f1 + +#ifdef __cplusplus +}; +#endif + +#endif /*__SIM_CORE_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.c new file mode 100644 index 0000000..93cf265 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.c @@ -0,0 +1,249 @@ +/* + sim_cycle_timers.c + + Copyright 2008-2012 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include "sim_avr.h" +#include "sim_time.h" +#include "sim_cycle_timers.h" + +#define QUEUE(__q, __e) { \ + (__e)->next = (__q); \ + (__q) = __e; \ + } +#define DETACH(__q, __l, __e) { \ + if (__l) \ + (__l)->next = (__e)->next; \ + else \ + (__q) = (__e)->next; \ + } +#define INSERT(__q, __l, __e) { \ + if (__l) { \ + (__e)->next = (__l)->next; \ + (__l)->next = (__e); \ + } else { \ + (__e)->next = (__q); \ + (__q) = (__e); \ + } \ + } + +#define DEFAULT_SLEEP_CYCLES 1000 + +void +avr_cycle_timer_reset( + struct avr_t * avr) +{ + avr_cycle_timer_pool_t * pool = &avr->cycle_timers; + memset(pool, 0, sizeof(*pool)); + // queue all slots into the free queue + for (int i = 0; i < MAX_CYCLE_TIMERS; i++) { + avr_cycle_timer_slot_p t = &pool->timer_slots[i]; + QUEUE(pool->timer_free, t); + } + avr->run_cycle_count = 1; + avr->run_cycle_limit = 1; +} + +static avr_cycle_count_t +avr_cycle_timer_return_sleep_run_cycles_limited( + avr_t *avr, + avr_cycle_count_t sleep_cycle_count) +{ + // run_cycle_count is bound to run_cycle_limit but NOT less than 1 cycle... + // this is not an error!.. unless you like deadlock. + avr_cycle_count_t run_cycle_count = ((avr->run_cycle_limit >= sleep_cycle_count) ? + sleep_cycle_count : avr->run_cycle_limit); + avr->run_cycle_count = run_cycle_count ? run_cycle_count : 1; + + // sleep cycles are returned unbounded thus preserving original behavior. + return(sleep_cycle_count); +} + +static void +avr_cycle_timer_reset_sleep_run_cycles_limited( + avr_t *avr) +{ + avr_cycle_timer_pool_t * pool = &avr->cycle_timers; + avr_cycle_count_t sleep_cycle_count = DEFAULT_SLEEP_CYCLES; + + if(pool->timer) { + if(pool->timer->when > avr->cycle) { + sleep_cycle_count = pool->timer->when - avr->cycle; + } else { + sleep_cycle_count = 0; + } + } + + avr_cycle_timer_return_sleep_run_cycles_limited(avr, sleep_cycle_count); +} + +// no sanity checks checking here, on purpose +static void +avr_cycle_timer_insert( + avr_t * avr, + avr_cycle_count_t when, + avr_cycle_timer_t timer, + void * param) +{ + avr_cycle_timer_pool_t * pool = &avr->cycle_timers; + + when += avr->cycle; + + avr_cycle_timer_slot_p t = pool->timer_free; + + if (!t) { + AVR_LOG(avr, LOG_ERROR, "CYCLE: %s: ran out of timers (%d)!\n", __func__, MAX_CYCLE_TIMERS); + return; + } + // detach head + pool->timer_free = t->next; + t->next = NULL; + t->timer = timer; + t->param = param; + t->when = when; + + // find its place in the list + avr_cycle_timer_slot_p loop = pool->timer, last = NULL; + while (loop) { + if (loop->when > when) + break; + last = loop; + loop = loop->next; + } + INSERT(pool->timer, last, t); +} + +void +avr_cycle_timer_register( + avr_t * avr, + avr_cycle_count_t when, + avr_cycle_timer_t timer, + void * param) +{ + avr_cycle_timer_pool_t * pool = &avr->cycle_timers; + + // remove it if it was already scheduled + avr_cycle_timer_cancel(avr, timer, param); + + if (!pool->timer_free) { + AVR_LOG(avr, LOG_ERROR, "CYCLE: %s: pool is full (%d)!\n", __func__, MAX_CYCLE_TIMERS); + return; + } + avr_cycle_timer_insert(avr, when, timer, param); + avr_cycle_timer_reset_sleep_run_cycles_limited(avr); +} + +void +avr_cycle_timer_register_usec( + avr_t * avr, + uint32_t when, + avr_cycle_timer_t timer, + void * param) +{ + avr_cycle_timer_register(avr, avr_usec_to_cycles(avr, when), timer, param); +} + +void +avr_cycle_timer_cancel( + avr_t * avr, + avr_cycle_timer_t timer, + void * param) +{ + avr_cycle_timer_pool_t * pool = &avr->cycle_timers; + + // find its place in the list + avr_cycle_timer_slot_p t = pool->timer, last = NULL; + while (t) { + if (t->timer == timer && t->param == param) { + DETACH(pool->timer, last, t); + QUEUE(pool->timer_free, t); + break; + } + last = t; + t = t->next; + } + avr_cycle_timer_reset_sleep_run_cycles_limited(avr); +} + +/* + * Check to see if a timer is present, if so, return the number (+1) of + * cycles left for it to fire, and if not present, return zero + */ +avr_cycle_count_t +avr_cycle_timer_status( + avr_t * avr, + avr_cycle_timer_t timer, + void * param) +{ + avr_cycle_timer_pool_t * pool = &avr->cycle_timers; + + // find its place in the list + avr_cycle_timer_slot_p t = pool->timer; + while (t) { + if (t->timer == timer && t->param == param) { + return 1 + (t->when - avr->cycle); + } + t = t->next; + } + return 0; +} + +/* + * run through all the timers, call the ones that needs it, + * clear the ones that wants it, and calculate the next + * potential cycle we could sleep for... + */ +avr_cycle_count_t +avr_cycle_timer_process( + avr_t * avr) +{ + avr_cycle_timer_pool_t * pool = &avr->cycle_timers; + + if (pool->timer) do { + avr_cycle_timer_slot_p t = pool->timer; + avr_cycle_count_t when = t->when; + + if (when > avr->cycle) + return avr_cycle_timer_return_sleep_run_cycles_limited(avr, when - avr->cycle); + + // detach from active timers + pool->timer = t->next; + t->next = NULL; + do { + avr_cycle_count_t w = t->timer(avr, when, t->param); + // make sure the return value is either zero, or greater + // than the last one to prevent infinite loop here + when = w > when ? w : 0; + } while (when && when <= avr->cycle); + + if (when) // reschedule then + avr_cycle_timer_insert(avr, when - avr->cycle, t->timer, t->param); + + // requeue this one into the free ones + QUEUE(pool->timer_free, t); + } while (pool->timer); + + // original behavior was to return 1000 cycles when no timers were present... + // run_cycles are bound to at least one cycle but no more than requested limit... + // value passed here is returned unbounded, thus preserving original behavior. + return avr_cycle_timer_return_sleep_run_cycles_limited(avr, DEFAULT_SLEEP_CYCLES); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.h new file mode 100644 index 0000000..a4dfdf7 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_cycle_timers.h @@ -0,0 +1,121 @@ +/* + sim_cycle_timers.h + + Copyright 2008-2012 Michel Pollet + + 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 . + */ + +/* + * cycle timers are callbacks that will be called when "when" cycle is reached + * these timers are one shots, then get cleared if the timer function returns zero, + * they get reset if the callback function returns a new cycle number + * + * the implementation maintains a list of 'pending' timers, sorted by when they + * should run, it allows very quick comparison with the next timer to run, and + * quick removal of then from the pile once dispatched. + */ +#ifndef __SIM_CYCLE_TIMERS_H___ +#define __SIM_CYCLE_TIMERS_H___ + +#include "sim_avr_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_CYCLE_TIMERS 64 + +typedef avr_cycle_count_t (*avr_cycle_timer_t)( + struct avr_t * avr, + avr_cycle_count_t when, + void * param); + +/* + * Each timer instance contains the absolute cycle number they + * are hoping to run at, a function pointer to call and a parameter + * + * it will NEVER be the exact cycle specified, as each instruction is + * not divisible and might take 2 or more cycles anyway. + * + * However if there was a LOT of cycle lag, the timer migth be called + * repeteadly until it 'caches up'. + */ +typedef struct avr_cycle_timer_slot_t { + struct avr_cycle_timer_slot_t *next; + avr_cycle_count_t when; + avr_cycle_timer_t timer; + void * param; +} avr_cycle_timer_slot_t, *avr_cycle_timer_slot_p; + +/* + * Timer pool contains a pool of timer slots available, they all + * start queued into the 'free' qeueue, are migrated to the + * 'active' queue when needed and are re-queued to the free one + * when done + */ +typedef struct avr_cycle_timer_pool_t { + avr_cycle_timer_slot_t timer_slots[MAX_CYCLE_TIMERS]; + avr_cycle_timer_slot_p timer_free; + avr_cycle_timer_slot_p timer; +} avr_cycle_timer_pool_t, *avr_cycle_timer_pool_p; + + +// register for calling 'timer' in 'when' cycles +void +avr_cycle_timer_register( + struct avr_t * avr, + avr_cycle_count_t when, + avr_cycle_timer_t timer, + void * param); +// register a timer to call in 'when' usec +void +avr_cycle_timer_register_usec( + struct avr_t * avr, + uint32_t when, + avr_cycle_timer_t timer, + void * param); +// cancel a previously set timer +void +avr_cycle_timer_cancel( + struct avr_t * avr, + avr_cycle_timer_t timer, + void * param); +/* + * Check to see if a timer is present, if so, return the number (+1) of + * cycles left for it to fire, and if not present, return zero + */ +avr_cycle_count_t +avr_cycle_timer_status( + struct avr_t * avr, + avr_cycle_timer_t timer, + void * param); + +// +// Private, called from the core +// +avr_cycle_count_t +avr_cycle_timer_process( + struct avr_t * avr); +void +avr_cycle_timer_reset( + struct avr_t * avr); + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_CYCLE_TIMERS_H___ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.c new file mode 100644 index 0000000..df69b81 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.c @@ -0,0 +1,499 @@ +/* + sim_elf.c + + Loads a .elf file, extract the code, the data, the eeprom and + the "mcu" specification section, also load usable code symbols + to be able to print meaningful trace information. + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sim_elf.h" +#include "sim_vcd_file.h" +#include "avr_eeprom.h" +#include "avr_ioport.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +void +avr_load_firmware( + avr_t * avr, + elf_firmware_t * firmware) +{ + if (firmware->frequency) + avr->frequency = firmware->frequency; + if (firmware->vcc) + avr->vcc = firmware->vcc; + if (firmware->avcc) + avr->avcc = firmware->avcc; + if (firmware->aref) + avr->aref = firmware->aref; +#if CONFIG_SIMAVR_TRACE && ELF_SYMBOLS + int scount = firmware->flashsize >> 1; + avr->trace_data->codeline = malloc(scount * sizeof(avr_symbol_t*)); + memset(avr->trace_data->codeline, 0, scount * sizeof(avr_symbol_t*)); + + for (int i = 0; i < firmware->symbolcount; i++) + if (firmware->symbol[i]->addr < firmware->flashsize) // code address + avr->trace_data->codeline[firmware->symbol[i]->addr >> 1] = + firmware->symbol[i]; + // "spread" the pointers for known symbols forward + avr_symbol_t * last = NULL; + for (int i = 0; i < scount; i++) { + if (!avr->trace_data->codeline[i]) + avr->trace_data->codeline[i] = last; + else + last = avr->trace_data->codeline[i]; + } +#endif + + avr_loadcode(avr, firmware->flash, + firmware->flashsize, firmware->flashbase); + avr->codeend = firmware->flashsize + + firmware->flashbase - firmware->datasize; + + if (firmware->eeprom && firmware->eesize) { + avr_eeprom_desc_t d = { + .ee = firmware->eeprom, + .offset = 0, + .size = firmware->eesize + }; + avr_ioctl(avr, AVR_IOCTL_EEPROM_SET, &d); + } + if (firmware->fuse) + memcpy(avr->fuse, firmware->fuse, firmware->fusesize); + if (firmware->lockbits) + avr->lockbits = firmware->lockbits[0]; + // load the default pull up/down values for ports + for (int i = 0; i < 8 && firmware->external_state[i].port; i++) { + avr_ioport_external_t e = { + .name = firmware->external_state[i].port, + .mask = firmware->external_state[i].mask, + .value = firmware->external_state[i].value, + }; + avr_ioctl(avr, AVR_IOCTL_IOPORT_SET_EXTERNAL(e.name), &e); + } + avr_set_command_register(avr, firmware->command_register_addr); + avr_set_console_register(avr, firmware->console_register_addr); + + // rest is initialization of the VCD file + if (firmware->tracecount == 0) + return; + avr->vcd = malloc(sizeof(*avr->vcd)); + memset(avr->vcd, 0, sizeof(*avr->vcd)); + avr_vcd_init(avr, + firmware->tracename[0] ? firmware->tracename: "gtkwave_trace.vcd", + avr->vcd, + firmware->traceperiod >= 1000 ? firmware->traceperiod : 1000); + + AVR_LOG(avr, LOG_TRACE, "Creating VCD trace file '%s'\n", + avr->vcd->filename); + + for (int ti = 0; ti < firmware->tracecount; ti++) { + if (firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_PORTPIN) { + avr_irq_t * irq = avr_io_getirq(avr, + AVR_IOCTL_IOPORT_GETIRQ(firmware->trace[ti].mask), + firmware->trace[ti].addr); + if (irq) { + char name[16]; + sprintf(name, "%c%d", firmware->trace[ti].mask, + firmware->trace[ti].addr); + avr_vcd_add_signal(avr->vcd, irq, 1, + firmware->trace[ti].name[0] ? + firmware->trace[ti].name : name); + } + } else if (firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_IRQ) { + avr_irq_t * bit = avr_get_interrupt_irq(avr, firmware->trace[ti].mask); + if (bit && firmware->trace[ti].addr < AVR_INT_IRQ_COUNT) + avr_vcd_add_signal(avr->vcd, + &bit[firmware->trace[ti].addr], + firmware->trace[ti].mask == 0xff ? 8 : 1, + firmware->trace[ti].name); + } else if (firmware->trace[ti].mask == 0xff || + firmware->trace[ti].mask == 0) { + // easy one + avr_irq_t * all = avr_iomem_getirq(avr, + firmware->trace[ti].addr, + firmware->trace[ti].name, + AVR_IOMEM_IRQ_ALL); + if (!all) { + AVR_LOG(avr, LOG_ERROR, + "ELF: %s: unable to attach trace to address %04x\n", + __FUNCTION__, firmware->trace[ti].addr); + } else { + avr_vcd_add_signal(avr->vcd, all, 8, + firmware->trace[ti].name); + } + } else { + int count = __builtin_popcount(firmware->trace[ti].mask); + // for (int bi = 0; bi < 8; bi++) + // if (firmware->trace[ti].mask & (1 << bi)) + // count++; + for (int bi = 0; bi < 8; bi++) + if (firmware->trace[ti].mask & (1 << bi)) { + avr_irq_t * bit = avr_iomem_getirq(avr, + firmware->trace[ti].addr, + firmware->trace[ti].name, + bi); + if (!bit) { + AVR_LOG(avr, LOG_ERROR, + "ELF: %s: unable to attach trace to address %04x\n", + __FUNCTION__, firmware->trace[ti].addr); + break; + } + + if (count == 1) { + avr_vcd_add_signal(avr->vcd, + bit, 1, firmware->trace[ti].name); + break; + } + char comp[128]; + sprintf(comp, "%s.%d", firmware->trace[ti].name, bi); + avr_vcd_add_signal(avr->vcd, bit, 1, comp); + } + } + } + // if the firmware has specified a command register, do NOT start the trace here + // the firmware probably knows best when to start/stop it + if (!firmware->command_register_addr) + avr_vcd_start(avr->vcd); +} + +static void +elf_parse_mmcu_section( + elf_firmware_t * firmware, + uint8_t * src, + uint32_t size) +{ +// hdump(".mmcu", src, size); + while (size) { + uint8_t tag = *src++; + uint8_t ts = *src++; + int next = size > 2 + ts ? 2 + ts : size; + // printf("elf_parse_mmcu_section %2d, size %2d / remains %3d\n", + // tag, ts, size); + switch (tag) { + case AVR_MMCU_TAG_FREQUENCY: + firmware->frequency = + src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + break; + case AVR_MMCU_TAG_NAME: + strcpy(firmware->mmcu, (char*)src); + break; + case AVR_MMCU_TAG_VCC: + firmware->vcc = + src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + break; + case AVR_MMCU_TAG_AVCC: + firmware->avcc = + src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + break; + case AVR_MMCU_TAG_AREF: + firmware->aref = + src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + break; + case AVR_MMCU_TAG_PORT_EXTERNAL_PULL: { + for (int i = 0; i < 8; i++) + if (!firmware->external_state[i].port) { + firmware->external_state[i].port = src[2]; + firmware->external_state[i].mask = src[1]; + firmware->external_state[i].value = src[0]; +#if 0 + AVR_LOG(NULL, LOG_DEBUG, + "AVR_MMCU_TAG_PORT_EXTERNAL_PULL[%d] %c:%02x:%02x\n", + i, firmware->external_state[i].port, + firmware->external_state[i].mask, + firmware->external_state[i].value); +#endif + break; + } + } break; + case AVR_MMCU_TAG_VCD_PORTPIN: + case AVR_MMCU_TAG_VCD_IRQ: + case AVR_MMCU_TAG_VCD_TRACE: { + uint8_t mask = src[0]; + uint16_t addr = src[1] | (src[2] << 8); + char * name = (char*)src + 3; + +#if 0 + AVR_LOG(NULL, LOG_DEBUG, + "VCD_TRACE %d %04x:%02x - %s\n", tag, + addr, mask, name); +#endif + firmware->trace[firmware->tracecount].kind = tag; + firmware->trace[firmware->tracecount].mask = mask; + firmware->trace[firmware->tracecount].addr = addr; + strncpy(firmware->trace[firmware->tracecount].name, name, + sizeof(firmware->trace[firmware->tracecount].name)); + firmware->tracecount++; + } break; + case AVR_MMCU_TAG_VCD_FILENAME: { + strcpy(firmware->tracename, (char*)src); + } break; + case AVR_MMCU_TAG_VCD_PERIOD: { + firmware->traceperiod = + src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + } break; + case AVR_MMCU_TAG_SIMAVR_COMMAND: { + firmware->command_register_addr = src[0] | (src[1] << 8); + } break; + case AVR_MMCU_TAG_SIMAVR_CONSOLE: { + firmware->console_register_addr = src[0] | (src[1] << 8); + } break; + } + size -= next; + src += next - 2; // already incremented + } +} + +static int +elf_copy_segment(int fd, Elf32_Phdr *php, uint8_t **dest) +{ + int rv; + + if (*dest == NULL) + *dest = malloc(php->p_filesz); + if (!*dest) + return -1; + + lseek(fd, php->p_offset, SEEK_SET); + rv = read(fd, *dest, php->p_filesz); + if (rv != php->p_filesz) { + AVR_LOG(NULL, LOG_ERROR, + "Got %d when reading %d bytes for %x at offset %d " + "from ELF file\n", + rv, php->p_filesz, php->p_vaddr, php->p_offset); + return -1; + } + AVR_LOG(NULL, LOG_DEBUG, "Loaded %d bytes at %x\n", + php->p_filesz, php->p_vaddr); + return 0; +} + +static int +elf_handle_segment(int fd, Elf32_Phdr *php, uint8_t **dest, const char *name) +{ + if (*dest) { + AVR_LOG(NULL, LOG_ERROR, + "Unexpected extra %s data: %d bytes at %x.\n", + name, php->p_filesz, php->p_vaddr); + return -1; + } else { + elf_copy_segment(fd, php, dest); + return 0; + } +} + +/* The structure *firmware must be pre-initialised to zero, then optionally + * tracing and VCD information may be added. + */ + +int +elf_read_firmware( + const char * file, + elf_firmware_t * firmware) +{ + Elf32_Ehdr elf_header; /* ELF header */ + Elf *elf = NULL; /* Our Elf pointer for libelf */ + Elf32_Phdr *php; /* Program header. */ + Elf_Scn *scn = NULL; /* Section Descriptor */ + size_t ph_count; /* Program Header entry count. */ + int fd, i; /* File Descriptor */ + + if ((fd = open(file, O_RDONLY | O_BINARY)) == -1 || + (read(fd, &elf_header, sizeof(elf_header))) < sizeof(elf_header)) { + AVR_LOG(NULL, LOG_ERROR, "could not read %s\n", file); + perror(file); + close(fd); + return -1; + } + +#if ELF_SYMBOLS + firmware->symbolcount = 0; + firmware->symbol = NULL; +#endif + + /* this is actually mandatory !! otherwise elf_begin() fails */ + if (elf_version(EV_CURRENT) == EV_NONE) { + /* library out of date - recover from error */ + return -1; + } + // Iterate through section headers again this time well stop when we find symbols + elf = elf_begin(fd, ELF_C_READ, NULL); + //printf("Loading elf %s : %p\n", file, elf); + + if (!elf) + return -1; + if (elf_kind(elf) != ELF_K_ELF) { + AVR_LOG(NULL, LOG_ERROR, "Unexpected ELF file type\n"); + return -1; + } + + /* Scan the Program Header Table. */ + + if (elf_getphdrnum(elf, &ph_count) != 0 || ph_count == 0 || + (php = elf32_getphdr(elf)) == NULL) { + AVR_LOG(NULL, LOG_ERROR, "No ELF Program Headers\n"); + return -1; + } + + for (i = 0; i < (int)ph_count; ++i, ++php) { +#if 0 + printf("Header %d type %d addr %x/%x size %d/%d flags %x\n", + i, php->p_type, php->p_vaddr, php->p_paddr, + php->p_filesz, php->p_memsz, php->p_flags); +#endif + if (php->p_type != PT_LOAD || php->p_filesz == 0) + continue; + if (php->p_vaddr < 0x800000) { + /* Explicit flash section. Load it. */ + + if (elf_handle_segment(fd, php, &firmware->flash, "Flash")) + continue; + firmware->flashsize = php->p_filesz; + firmware->flashbase = php->p_vaddr; + } else if (php->p_vaddr < 0x810000) { + /* Data space. If there are initialised variables, treat + * them as extra initialised flash. The C startup function + * understands that and will copy them to RAM. + */ + + if (firmware->flash) { + uint8_t *where; + + firmware->flash = realloc(firmware->flash, + firmware->flashsize + php->p_filesz); + if (!firmware->flash) + return -1; + where = firmware->flash + firmware->flashsize; + elf_copy_segment(fd, php, &where); + firmware->flashsize += php->p_filesz; + } else { + /* If this ever happens, add a second pass. */ + + AVR_LOG(NULL, LOG_ERROR, + "Initialialised data but no flash (%d bytes at %x)!\n", + php->p_filesz, php->p_vaddr); + return -1; + } + } else if (php->p_vaddr < 0x820000) { + /* EEPROM. */ + + if (elf_handle_segment(fd, php, &firmware->eeprom, "EEPROM")) + continue; + firmware->eesize = php->p_filesz; + } else if (php->p_vaddr < 0x830000) { + /* Fuses. */ + + if (elf_handle_segment(fd, php, &firmware->fuse, "Fuses")) + continue; + firmware->fusesize = php->p_filesz; + } else if (php->p_vaddr < 0x840000) { + /* Lock bits. */ + + elf_handle_segment(fd, php, &firmware->lockbits, "Lock bits"); + } + } + + /* Scan the section table for .mmcu magic and symbols. */ + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + GElf_Shdr shdr; /* Section Header */ + gelf_getshdr(scn, &shdr); + char * name = elf_strptr(elf, elf_header.e_shstrndx, shdr.sh_name); + // printf("Walking elf section '%s'\n", name); + + if (!strcmp(name, ".mmcu")) { + Elf_Data *s = elf_getdata(scn, NULL); + + elf_parse_mmcu_section(firmware, s->d_buf, s->d_size); + if (shdr.sh_addr < 0x860000) + AVR_LOG(NULL, LOG_WARNING, + "Warning: ELF .mmcu section at %x may be loaded.\n", + shdr.sh_addr); + // printf("%s: size %ld\n", __FUNCTION__, s->d_size); + // avr->frequency = f_cpu; + } + +#if ELF_SYMBOLS + // When we find a section header marked SHT_SYMTAB stop and get symbols + if (shdr.sh_type == SHT_SYMTAB) { + // edata points to our symbol table + Elf_Data *edata = elf_getdata(scn, NULL); + + // how many symbols are there? this number comes from the size of + // the section divided by the entry size + int symbol_count = shdr.sh_size / shdr.sh_entsize; + + // loop through to grab all symbols + for (int i = 0; i < symbol_count; i++) { + GElf_Sym sym; /* Symbol */ + // libelf grabs the symbol data using gelf_getsym() + gelf_getsym(edata, i, &sym); + + // print out the value and size + if (ELF32_ST_BIND(sym.st_info) == STB_GLOBAL || + ELF32_ST_TYPE(sym.st_info) == STT_FUNC || + ELF32_ST_TYPE(sym.st_info) == STT_OBJECT) { + const char * name = elf_strptr(elf, shdr.sh_link, sym.st_name); + + // if its a bootloader, this symbol will be the entry point we need + if (!strcmp(name, "__vectors")) + firmware->flashbase = sym.st_value; + avr_symbol_t * s = malloc(sizeof(avr_symbol_t) + strlen(name) + 1); + strcpy((char*)s->symbol, name); + s->addr = sym.st_value; + s->size = sym.st_size; + if (!(firmware->symbolcount % 8)) + firmware->symbol = realloc( + firmware->symbol, + (firmware->symbolcount + 8) * sizeof(firmware->symbol[0])); + + // insert new element, keep the array sorted + int insert = -1; + for (int si = 0; si < firmware->symbolcount && insert == -1; si++) + if (firmware->symbol[si]->addr >= s->addr) + insert = si; + if (insert == -1) + insert = firmware->symbolcount; + else + memmove(firmware->symbol + insert + 1, + firmware->symbol + insert, + (firmware->symbolcount - insert) * sizeof(firmware->symbol[0])); + firmware->symbol[insert] = s; + firmware->symbolcount++; + } + } + } +#endif // ELF_SYMBOLS + } + elf_end(elf); + close(fd); + return 0; +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.h new file mode 100644 index 0000000..37d61d7 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_elf.h @@ -0,0 +1,103 @@ +/* + sim_elf.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __SIM_ELF_H__ +#define __SIM_ELF_H__ + +#include "avr/avr_mcu_section.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ELF_SYMBOLS +#define ELF_SYMBOLS 1 +#endif + +/* these are the addresses the gnu linker uses to + * "fake" a non-Harvard addressing space for the AVR + */ +#define AVR_SEGMENT_OFFSET_FLASH 0 +#define AVR_SEGMENT_OFFSET_EEPROM 0x00810000 + +#include "sim_avr.h" + +typedef struct elf_firmware_t { + char mmcu[64]; + uint32_t frequency; + uint32_t vcc,avcc,aref; + + char tracename[128]; // trace filename + uint32_t traceperiod; + int tracecount; + struct { + uint8_t kind; + uint8_t mask; + uint16_t addr; + char name[64]; + } trace[32]; + + struct { + char port; + uint8_t mask, value; + } external_state[8]; + + // register to listen to for commands from the firmware + uint16_t command_register_addr; + uint16_t console_register_addr; + + uint32_t flashbase; // base address + uint8_t * flash; + uint32_t flashsize; + uint32_t datasize; + uint32_t bsssize; + // read the .eeprom section of the elf, too + uint8_t * eeprom; + uint32_t eesize; + uint8_t * fuse; + uint32_t fusesize; + uint8_t * lockbits; + +#if ELF_SYMBOLS + avr_symbol_t ** symbol; + uint32_t symbolcount; +#endif +} elf_firmware_t ; + +/* The structure *firmware must be pre-initialised to zero, then optionally + * with tracing and VCD information. + */ + +int +elf_read_firmware( + const char * file, + elf_firmware_t * firmware); + +void +avr_load_firmware( + avr_t * avr, + elf_firmware_t * firmware); + +#ifdef __cplusplus +}; +#endif + +#endif /*__SIM_ELF_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.c new file mode 100644 index 0000000..cd2196e --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.c @@ -0,0 +1,1026 @@ +/* + sim_gdb.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include "sim_network.h" +#include +#include +#include +#include +#include +#include +#include +#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 + // 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\n" + " \n" + " \n" + " 0x80\n" + " \n" + "", + 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(); +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.h new file mode 100644 index 0000000..2936df7 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_gdb.h @@ -0,0 +1,55 @@ +/* + sim_gdb.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __SIM_GDB_H__ +#define __SIM_GDB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Watchpoint types. + See GDB User Manual, Appendix E.2 */ +enum avr_gdb_watch_type { + AVR_GDB_BREAK_SOFT = 1 << 0, + AVR_GDB_BREAK_HARD = 1 << 1, + + AVR_GDB_WATCH_WRITE = 1 << 2, + AVR_GDB_WATCH_READ = 1 << 3, + AVR_GDB_WATCH_ACCESS = 1 << 4 +}; + +int avr_gdb_init(avr_t * avr); + +void avr_deinit_gdb(avr_t * avr); + +// call from the main AVR decoder thread +int avr_gdb_processor(avr_t * avr, int sleep); + +// Called from sim_core.c +void avr_gdb_handle_watchpoints(avr_t * g, uint16_t addr, enum avr_gdb_watch_type type); +void avr_gdb_handle_break(avr_t *); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.c new file mode 100644 index 0000000..fea67c4 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.c @@ -0,0 +1,287 @@ +/* + sim_hex.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include "sim_hex.h" +#include "sim_elf.h" + +// friendly hex dump +void hdump(const char *w, uint8_t *b, size_t l) +{ + uint32_t i; + if (l < 16) { + printf("%s: ",w); + for (i = 0; i < l; i++) printf("%02x",b[i]); + } else { + printf("%s:\n",w); + for (i = 0; i < l; i++) { + if (!(i & 0x1f)) printf(" "); + printf("%02x",b[i]); + if ((i & 0x1f) == 0x1f) { + printf(" "); + printf("\n"); + } + } + } + printf("\n"); +} + + // decode line text hex to binary +int read_hex_string(const char * src, uint8_t * buffer, int maxlen) +{ + uint8_t * dst = buffer; + int ls = 0; + uint8_t b = 0; + while (*src && maxlen) { + char c = *src++; + switch (c) { + case 'a' ... 'f': b = (b << 4) | (c - 'a' + 0xa); break; + case 'A' ... 'F': b = (b << 4) | (c - 'A' + 0xa); break; + case '0' ... '9': b = (b << 4) | (c - '0'); break; + default: + if (c > ' ') { + fprintf(stderr, "%s: huh '%c' (%s)\n", __FUNCTION__, c, src); + return -1; + } + continue; + } + if (ls & 1) { + *dst++ = b; b = 0; + maxlen--; + } + ls++; + } + + return dst - buffer; +} + +void +free_ihex_chunks( + ihex_chunk_p chunks) +{ + if (!chunks) + return; + for (int i = 0; chunks[i].size; i++) + if (chunks[i].data) + free(chunks[i].data); +} + +int +read_ihex_chunks( + const char * fname, + ihex_chunk_p * chunks ) +{ + if (!fname || !chunks) + return -1; + FILE * f = fopen(fname, "r"); + if (!f) { + perror(fname); + return -1; + } + uint32_t segment = 0; // segment address + int chunk = 0, max_chunks = 0; + *chunks = NULL; + + while (!feof(f)) { + char line[128]; + if (!fgets(line, sizeof(line)-1, f)) + continue; + if (line[0] != ':') { + fprintf(stderr, "AVR: '%s' invalid ihex format (%.4s)\n", fname, line); + break; + } + uint8_t bline[64]; + + int len = read_hex_string(line + 1, bline, sizeof(bline)); + if (len <= 0) + continue; + + uint8_t chk = 0; + { // calculate checksum + uint8_t * src = bline; + int tlen = len-1; + while (tlen--) + chk += *src++; + chk = 0x100 - chk; + } + if (chk != bline[len-1]) { + fprintf(stderr, "%s: %s, invalid checksum %02x/%02x\n", __FUNCTION__, fname, chk, bline[len-1]); + break; + } + uint32_t addr = 0; + switch (bline[3]) { + case 0: // normal data + addr = segment | (bline[1] << 8) | bline[2]; + break; + case 1: // end of file - reset segment + segment = 0; + continue; + case 2: // extended address 2 bytes + segment = ((bline[4] << 8) | bline[5]) << 4; + continue; + case 4: + segment = ((bline[4] << 8) | bline[5]) << 16; + continue; + default: + fprintf(stderr, "%s: %s, unsupported check type %02x\n", __FUNCTION__, fname, bline[3]); + continue; + } + if (chunk < max_chunks && addr != ((*chunks)[chunk].baseaddr + (*chunks)[chunk].size)) { + if ((*chunks)[chunk].size) + chunk++; + } + if (chunk >= max_chunks) { + max_chunks++; + /* Here we allocate and zero an extra chunk, to act as terminator */ + *chunks = realloc(*chunks, (1 + max_chunks) * sizeof(ihex_chunk_t)); + memset(*chunks + chunk, 0, + (1 + (max_chunks - chunk)) * sizeof(ihex_chunk_t)); + (*chunks)[chunk].baseaddr = addr; + } + (*chunks)[chunk].data = realloc((*chunks)[chunk].data, + (*chunks)[chunk].size + bline[0]); + memcpy((*chunks)[chunk].data + (*chunks)[chunk].size, + bline + 4, bline[0]); + (*chunks)[chunk].size += bline[0]; + } + fclose(f); + return max_chunks; +} + + +uint8_t * +read_ihex_file( + const char * fname, uint32_t * dsize, uint32_t * start) +{ + ihex_chunk_p chunks = NULL; + int count = read_ihex_chunks(fname, &chunks); + uint8_t * res = NULL; + + if (count > 0) { + *dsize = chunks[0].size; + *start = chunks[0].baseaddr; + res = chunks[0].data; + chunks[0].data = NULL; + } + if (count > 1) { + fprintf(stderr, "AVR: '%s' ihex contains more chunks than loaded (%d)\n", + fname, count); + } + free_ihex_chunks(chunks); + return res; +} + +/* Load a firmware file, ELF or HEX format, from filename, based at + * loadBase, returning the data in *fp ready for loading into + * the simulated MCU. Progname is the current program name for error messages. + * + * Included here as it mostly specific to HEX files. + */ + +void +sim_setup_firmware(const char * filename, uint32_t loadBase, + elf_firmware_t * fp, const char * progname) +{ + char * suffix = strrchr(filename, '.'); + + if (suffix && !strcasecmp(suffix, ".hex")) { + if (!(fp->mmcu[0] && fp->frequency > 0)) { + printf("MCU type and frequency are not set " + "when loading .hex file\n"); + } + ihex_chunk_p chunk = NULL; + int cnt = read_ihex_chunks(filename, &chunk); + if (cnt <= 0) { + fprintf(stderr, + "%s: Unable to load IHEX file %s\n", progname, filename); + exit(1); + } + printf("Loaded %d section(s) of ihex\n", cnt); + + for (int ci = 0; ci < cnt; ci++) { + if (chunk[ci].baseaddr < (1*1024*1024)) { + if (fp->flash) { + printf("Ignoring chunk %d, " + "possible flash redefinition %08x, %d\n", + ci, chunk[ci].baseaddr, chunk[ci].size); + free(chunk[ci].data); + chunk[ci].data = NULL; + continue; + } + fp->flash = chunk[ci].data; + fp->flashsize = chunk[ci].size; + fp->flashbase = chunk[ci].baseaddr; + printf("Load HEX flash %08x, %d at %08x\n", + fp->flashbase, fp->flashsize, fp->flashbase); + } else if (chunk[ci].baseaddr >= AVR_SEGMENT_OFFSET_EEPROM || + (chunk[ci].baseaddr + loadBase) >= + AVR_SEGMENT_OFFSET_EEPROM) { + // eeprom! + + if (fp->eeprom) { + + // Converting ELF with .mmcu section will do this. + + printf("Ignoring chunk %d, " + "possible eeprom redefinition %08x, %d\n", + ci, chunk[ci].baseaddr, chunk[ci].size); + free(chunk[ci].data); + chunk[ci].data = NULL; + continue; + } + fp->eeprom = chunk[ci].data; + fp->eesize = chunk[ci].size; + printf("Load HEX eeprom %08x, %d\n", + chunk[ci].baseaddr, fp->eesize); + } + } + free(chunk); + } else { + if (elf_read_firmware(filename, fp) == -1) { + fprintf(stderr, "%s: Unable to load firmware from file %s\n", + progname, filename); + exit(1); + } + } +} + +#ifdef IHEX_TEST +// gcc -std=gnu99 -Isimavr/sim simavr/sim/sim_hex.c -o sim_hex -DIHEX_TEST -Dtest_main=main +int test_main(int argc, char * argv[]) +{ + struct ihex_chunk_t chunk[4]; + + for (int fi = 1; fi < argc; fi++) { + int c = read_ihex_chunks(argv[fi], chunk, 4); + if (c == -1) { + perror(argv[fi]); + continue; + } + for (int ci = 0; ci < c; ci++) { + char n[96]; + sprintf(n, "%s[%d] = %08x", argv[fi], ci, chunk[ci].baseaddr); + hdump(n, chunk[ci].data, chunk[ci].size); + } + } +} +#endif diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.h new file mode 100644 index 0000000..e3a1d1f --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_hex.h @@ -0,0 +1,90 @@ +/* + sim_hex.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + + +#ifndef __SIM_HEX_H___ +#define __SIM_HEX_H___ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Load a firmware file, ELF or HEX format, ready for use. + +struct elf_firmware_t; // Predeclaration ... + +void +sim_setup_firmware( + const char * filename, // Firmware file + uint32_t loadBase, // Base of load region + struct elf_firmware_t * fp, // Data returned here + const char * progname); // For error messages. + +// parses a hex text string 'src' of at max 'maxlen' characters, decodes it into 'buffer' +int +read_hex_string( + const char * src, + uint8_t * buffer, + int maxlen); + +// a .hex file chunk (base address + size) +typedef struct ihex_chunk_t { + uint32_t baseaddr; // offset it started at in the .hex file + uint8_t * data; // read data + uint32_t size; // read data size +} ihex_chunk_t, *ihex_chunk_p; + +/* + * Read a .hex file, detects the various different chunks in it from their starting + * addresses and allocate an array of ihex_chunk_t returned in 'chunks'. + * Returns the number of chunks found, or -1 if an error occurs. + */ +int +read_ihex_chunks( + const char * fname, + ihex_chunk_p * chunks ); +/* Frees previously allocated chunks */ +void +free_ihex_chunks( + ihex_chunk_p chunks); + +// reads IHEX file 'fname', puts it's decoded size in *'dsize' and returns +// a newly allocated buffer with the binary data (or NULL, if error) +uint8_t * +read_ihex_file( + const char * fname, + uint32_t * dsize, + uint32_t * start); + +// hex dump from pointer 'b' for 'l' bytes with string prefix 'w' +void +hdump( + const char *w, + uint8_t *b, size_t l); + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_HEX_H___ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.c new file mode 100644 index 0000000..9e3ed9c --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.c @@ -0,0 +1,296 @@ +/* + sim_interrupts.c + + Copyright 2008-2012 Michel Pollet + + 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 . + */ + + +#include +#include +#include +#include +#include "sim_interrupts.h" +#include "sim_avr.h" +#include "sim_core.h" + +DEFINE_FIFO(avr_int_vector_p, avr_int_pending); + +void +avr_interrupt_init( + avr_t * avr ) +{ + avr_int_table_p table = &avr->interrupts; + memset(table, 0, sizeof(*table)); + + static const char *names[] = { ">avr.int.pending", ">avr.int.running" }; + avr_init_irq(&avr->irq_pool, table->irq, + 0, // base number + AVR_INT_IRQ_COUNT, names); +} + +void +avr_interrupt_reset( + avr_t * avr ) +{ + avr_int_table_p table = &avr->interrupts; + + table->running_ptr = 0; + avr_int_pending_reset(&table->pending); + avr->interrupt_state = 0; + for (int i = 0; i < table->vector_count; i++) + table->vector[i]->pending = 0; +} + +void +avr_register_vector( + avr_t *avr, + avr_int_vector_t * vector) +{ + if (!vector->vector) + return; + + avr_int_table_p table = &avr->interrupts; + + char name0[48], name1[48]; + sprintf(name0, ">avr.int.%02x.pending", vector->vector); + sprintf(name1, ">avr.int.%02x.running", vector->vector); + const char *names[2] = { name0, name1 }; + avr_init_irq(&avr->irq_pool, vector->irq, + vector->vector * 256, // base number + AVR_INT_IRQ_COUNT, names); + table->vector[table->vector_count++] = vector; + if (vector->trace) + printf("IRQ%d registered (enabled %04x:%d)\n", + vector->vector, vector->enable.reg, vector->enable.bit); + + if (!vector->enable.reg) + AVR_LOG(avr, LOG_WARNING, "IRQ%d No 'enable' bit !\n", + vector->vector); +} + +int +avr_has_pending_interrupts( + avr_t * avr) +{ + avr_int_table_p table = &avr->interrupts; + return !avr_int_pending_isempty(&table->pending); +} + +int +avr_is_interrupt_pending( + avr_t * avr, + avr_int_vector_t * vector) +{ + return vector->pending; +} + +int +avr_is_interrupt_enabled( + avr_t * avr, + avr_int_vector_t * vector) +{ + return avr_regbit_get(avr, vector->enable); +} + +int +avr_raise_interrupt( + avr_t * avr, + avr_int_vector_t * vector) +{ + if (!vector || !vector->vector) + return 0; + + if (vector->trace) + printf("IRQ%d raising (enabled %d)\n", + vector->vector, avr_regbit_get(avr, vector->enable)); + + // always mark the 'raised' flag to one, even if the interrupt is disabled + // this allow "polling" for the "raised" flag, like for non-interrupt + // driven UART and so so. These flags are often "write one to clear" + if (vector->raised.reg) + avr_regbit_set(avr, vector->raised); + + if (vector->pending) { + if (vector->trace) + printf("IRQ%d:I=%d already raised (enabled %d) (cycle %lld pc 0x%x)\n", + vector->vector, !!avr->sreg[S_I], avr_regbit_get(avr, vector->enable), + (long long int)avr->cycle, avr->pc); + + return 0; + } + + avr_raise_irq(vector->irq + AVR_INT_IRQ_PENDING, 1); + avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, vector->vector); + + // If the interrupt is enabled, attempt to wake the core + if (avr_regbit_get(avr, vector->enable)) { + // Mark the interrupt as pending + vector->pending = 1; + + avr_int_table_p table = &avr->interrupts; + + avr_int_pending_write(&table->pending, vector); + + if (avr->sreg[S_I] && avr->interrupt_state == 0) + avr->interrupt_state = 1; + if (avr->state == cpu_Sleeping) { + if (vector->trace) + printf("IRQ%d Waking CPU due to interrupt\n", + vector->vector); + avr->state = cpu_Running; // in case we were sleeping + } + } + // return 'raised' even if it was already pending + return 1; +} + +void +avr_clear_interrupt( + avr_t * avr, + avr_int_vector_t * vector) +{ + if (!vector) + return; + if (vector->trace) + printf("IRQ%d cleared\n", vector->vector); + vector->pending = 0; + + avr_raise_irq(vector->irq + AVR_INT_IRQ_PENDING, 0); + avr_raise_irq_float(avr->interrupts.irq + AVR_INT_IRQ_PENDING, + avr_has_pending_interrupts(avr) ? + avr_int_pending_read_at( + &avr->interrupts.pending, 0)->vector : 0, + avr_has_pending_interrupts(avr)); + + if (vector->raised.reg && !vector->raise_sticky) + avr_regbit_clear(avr, vector->raised); +} + +int +avr_clear_interrupt_if( + avr_t * avr, + avr_int_vector_t * vector, + uint8_t old) +{ + avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, + avr_has_pending_interrupts(avr)); + if (avr_regbit_get(avr, vector->raised)) { + avr_clear_interrupt(avr, vector); + return 1; + } + avr_regbit_setto(avr, vector->raised, old); + return 0; +} + +avr_irq_t * +avr_get_interrupt_irq( + avr_t * avr, + uint8_t v) +{ + avr_int_table_p table = &avr->interrupts; + if (v == AVR_INT_ANY) + return table->irq; + for (int i = 0; i < table->vector_count; i++) + if (table->vector[i]->vector == v) + return table->vector[i]->irq; + return NULL; +} + +/* this is called upon RETI. */ +void +avr_interrupt_reti( + struct avr_t * avr) +{ + avr_int_table_p table = &avr->interrupts; + if (table->running_ptr) { + avr_int_vector_t * vector = table->running[--table->running_ptr]; + avr_raise_irq(vector->irq + AVR_INT_IRQ_RUNNING, 0); + } + avr_raise_irq(table->irq + AVR_INT_IRQ_RUNNING, + table->running_ptr > 0 ? + table->running[table->running_ptr-1]->vector : 0); +} + +/* + * check whether interrupts are pending. If so, check if the interrupt "latency" is reached, + * and if so triggers the handlers and jump to the vector. + */ +void +avr_service_interrupts( + avr_t * avr) +{ + if (!avr->sreg[S_I] || !avr->interrupt_state) + return; + + if (avr->interrupt_state < 0) { + avr->interrupt_state++; + if (avr->interrupt_state == 0) + avr->interrupt_state = avr_has_pending_interrupts(avr); + return; + } + + avr_int_table_p table = &avr->interrupts; + + // how many are pending... + int cnt = avr_int_pending_get_read_size(&table->pending); + // locate the highest priority one + int min = 0xff; + int mini = 0; + for (int ii = 0; ii < cnt; ii++) { + avr_int_vector_t * v = avr_int_pending_read_at(&table->pending, ii); + if (v->vector < min) { + min = v->vector; + mini = ii; + } + } + avr_int_vector_t * vector = avr_int_pending_read_at(&table->pending, mini); + + // it's possible that the vector being serviced is not at the front of the fifo, because we process interrupts based + // on vector priority rather than position in the fifo. if this is the case, we need to manually swap the vector + // being serviced with the vector at the front of the fifo so that the vector at the front of the fifo can be + // serviced in a following iteration. + avr_int_vector_p fifo_front = avr_int_pending_read(&table->pending); + if (fifo_front->vector != vector->vector) { + // the read into fifo_front above has incremented pending.read, so now mini points 1 beyond the desired + // destination for the swap. + table->pending.buffer[(table->pending.read + mini - 1) % avr_int_pending_fifo_size] = fifo_front; + } + + // if that single interrupt is masked, ignore it and continue + // could also have been disabled, or cleared + if (!avr_regbit_get(avr, vector->enable) || !vector->pending) { + vector->pending = 0; + avr->interrupt_state = avr_has_pending_interrupts(avr); + } else { + if (vector->trace) + printf("IRQ%d calling\n", vector->vector); + _avr_push_addr(avr, avr->pc); + avr_sreg_set(avr, S_I, 0); + avr->pc = vector->vector * avr->vector_size; + + avr_raise_irq(vector->irq + AVR_INT_IRQ_RUNNING, 1); + avr_raise_irq(table->irq + AVR_INT_IRQ_RUNNING, vector->vector); + if (table->running_ptr == ARRAY_SIZE(table->running)) { + AVR_LOG(avr, LOG_ERROR, "%s run out of nested stack!", __func__); + } else { + table->running[table->running_ptr++] = vector; + } + avr_clear_interrupt(avr, vector); + } +} + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.h new file mode 100644 index 0000000..bebf614 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_interrupts.h @@ -0,0 +1,134 @@ +/* + sim_interrupts.h + + Copyright 2008-2012 Michel Pollet + + 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 . + */ + +#ifndef __SIM_INTERRUPTS_H__ +#define __SIM_INTERRUPTS_H__ + +#include "sim_avr_types.h" +#include "sim_irq.h" +#include "fifo_declare.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AVR_INT_IRQ_PENDING = 0, + AVR_INT_IRQ_RUNNING, + AVR_INT_IRQ_COUNT, + AVR_INT_ANY = 0xff, // for avr_get_interrupt_irq() +}; +// interrupt vector for the IO modules +typedef struct avr_int_vector_t { + uint8_t vector; // vector number, zero (reset) is reserved + avr_regbit_t enable; // IO register index for the "interrupt enable" flag for this vector + avr_regbit_t raised; // IO register index for the register where the "raised" flag is (optional) + + uint8_t mask; // Mask for PCINTs. this is needed for chips like the 2560 where PCINT do not align with IRQs + int8_t shift; // PCINT8 = E0, PCINT9-15 are on J0-J6. Shift shifts down (<0) or up (>0) for alignment with IRQ#. + + // 'pending' IRQ, and 'running' status as signaled here + avr_irq_t irq[AVR_INT_IRQ_COUNT]; + uint8_t pending : 1, // 1 while scheduled in the fifo + trace : 1, // only for debug of a vector + raise_sticky : 1; // 1 if the interrupt flag (= the raised regbit) is not cleared + // by the hardware when executing the interrupt routine (see TWINT) +} avr_int_vector_t, *avr_int_vector_p; + +// Size needs to be >= max number of vectors, and a power of two +DECLARE_FIFO(avr_int_vector_p, avr_int_pending, 64); + +// interrupt vectors, and their enable/clear registers +typedef struct avr_int_table_t { + avr_int_vector_t * vector[64]; + uint8_t vector_count; + avr_int_pending_t pending; + uint8_t running_ptr; + avr_int_vector_t *running[64]; // stack of nested interrupts + // global status for pending + running in interrupt context + avr_irq_t irq[AVR_INT_IRQ_COUNT]; +} avr_int_table_t, *avr_int_table_p; + +/* + * Interrupt Helper Functions + */ +// register an interrupt vector. It's only needed if you want to use the "r_raised" flags +void +avr_register_vector( + struct avr_t *avr, + avr_int_vector_t * vector); +// raise an interrupt (if enabled). The interrupt is latched and will be called later +// return non-zero if the interrupt was raised and is now pending +int +avr_raise_interrupt( + struct avr_t * avr, + avr_int_vector_t * vector); +// return non-zero if the AVR core has any pending interrupts +int +avr_has_pending_interrupts( + struct avr_t * avr); +// return nonzero if a specific interrupt vector is pending +int +avr_is_interrupt_pending( + struct avr_t * avr, + avr_int_vector_t * vector); +// clear the "pending" status of an interrupt +void +avr_clear_interrupt( + struct avr_t * avr, + avr_int_vector_t * vector); +// called by the core at each cycle to check whether an interrupt is pending +void +avr_service_interrupts( + struct avr_t * avr); +// called by the core when RETI opcode is ran +void +avr_interrupt_reti( + struct avr_t * avr); +// clear the interrupt (inc pending) if "raised" flag is 1 +int +avr_clear_interrupt_if( + struct avr_t * avr, + avr_int_vector_t * vector, + uint8_t old); + +// return the IRQ that is raised when the vector is enabled and called/cleared +// this allows tracing of pending interrupts +avr_irq_t * +avr_get_interrupt_irq( + struct avr_t * avr, + uint8_t v); + +// Initializes the interrupt table +void +avr_interrupt_init( + struct avr_t * avr ); + +// reset the interrupt table and the fifo +void +avr_interrupt_reset( + struct avr_t * avr ); + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_INTERRUPTS_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.c new file mode 100644 index 0000000..548f030 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.c @@ -0,0 +1,298 @@ +/* + sim_io.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + + +#include +#include +#include +#include +#include +#include "sim_io.h" + +int +avr_ioctl( + avr_t *avr, + uint32_t ctl, + void * io_param) +{ + avr_io_t * port = avr->io_port; + int res = -1; + while (port && res == -1) { + if (port->ioctl) + res = port->ioctl(port, ctl, io_param); + port = port->next; + } + return res; +} + +void +avr_register_io( + avr_t *avr, + avr_io_t * io) +{ + io->next = avr->io_port; + io->avr = avr; + avr->io_port = io; +} + +void +avr_register_io_read( + avr_t *avr, + avr_io_addr_t addr, + avr_io_read_t readp, + void * param) +{ + avr_io_addr_t a = AVR_DATA_TO_IO(addr); + if (avr->io[a].r.param || avr->io[a].r.c) { + if (avr->io[a].r.param != param || avr->io[a].r.c != readp) { + AVR_LOG(avr, LOG_ERROR, + "IO: %s(): Already registered, refusing to override.\n", + __func__); + AVR_LOG(avr, LOG_ERROR, + "IO: %s(%04x : %p/%p): %p/%p\n", + __func__, a, + avr->io[a].r.c, avr->io[a].r.param, readp, param); + abort(); + } + } + avr->io[a].r.param = param; + avr->io[a].r.c = readp; +} + +static void +_avr_io_mux_write( + avr_t * avr, + avr_io_addr_t addr, + uint8_t v, + void * param) +{ + int io = (intptr_t)param; + for (int i = 0; i < avr->io_shared_io[io].used; i++) { + avr_io_write_t c = avr->io_shared_io[io].io[i].c; + if (c) + c(avr, addr, v, avr->io_shared_io[io].io[i].param); + } +} + +void +avr_register_io_write( + avr_t *avr, + avr_io_addr_t addr, + avr_io_write_t writep, + void * param) +{ + avr_io_addr_t a = AVR_DATA_TO_IO(addr); + + if (a >= MAX_IOs) { + AVR_LOG(avr, LOG_ERROR, + "IO: %s(): IO address 0x%04x out of range (max 0x%04x).\n", + __func__, a, MAX_IOs); + abort(); + } + /* + * Verifying that some other piece of code is not installed to watch write + * on this address. If there is, this code installs a "dispatcher" callback + * instead to handle multiple clients, otherwise, it continues as usual + */ + if (avr->io[a].w.param || avr->io[a].w.c) { + if (avr->io[a].w.param != param || avr->io[a].w.c != writep) { + // if the muxer not already installed, allocate a new slot + if (avr->io[a].w.c != _avr_io_mux_write) { + int no = avr->io_shared_io_count++; + if (avr->io_shared_io_count > ARRAY_SIZE(avr->io_shared_io)) { + AVR_LOG(avr, LOG_ERROR, + "IO: %s(): Too many shared IO registers.\n", __func__); + abort(); + } + AVR_LOG(avr, LOG_TRACE, + "IO: %s(%04x): Installing muxer on register.\n", + __func__, addr); + avr->io_shared_io[no].used = 1; + avr->io_shared_io[no].io[0].param = avr->io[a].w.param; + avr->io_shared_io[no].io[0].c = avr->io[a].w.c; + avr->io[a].w.param = (void*)(intptr_t)no; + avr->io[a].w.c = _avr_io_mux_write; + } + int no = (intptr_t)avr->io[a].w.param; + int d = avr->io_shared_io[no].used++; + if (avr->io_shared_io[no].used > ARRAY_SIZE(avr->io_shared_io[0].io)) { + AVR_LOG(avr, LOG_ERROR, + "IO: %s(): Too many callbacks on %04x.\n", + __func__, addr); + abort(); + } + avr->io_shared_io[no].io[d].param = param; + avr->io_shared_io[no].io[d].c = writep; + return; + } + } + + avr->io[a].w.param = param; + avr->io[a].w.c = writep; +} + +avr_irq_t * +avr_io_getirq( + avr_t * avr, + uint32_t ctl, + int index) +{ + avr_io_t * port = avr->io_port; + while (port) { + if (port->irq && port->irq_ioctl_get == ctl && port->irq_count > index) + return port->irq + index; + port = port->next; + } + return NULL; +} + +avr_irq_t * +avr_iomem_getirq( + avr_t * avr, + avr_io_addr_t addr, + const char * name, + int index) +{ + if (index > 8) + return NULL; + avr_io_addr_t a = AVR_DATA_TO_IO(addr); + if (avr->io[a].irq == NULL) { + /* + * Prepare an array of names for the io IRQs. Ideally we'd love to have + * a proper name for these, but it's not possible at this time. + */ + char names[9 * 20]; + char * d = names; + const char * namep[9]; + for (int ni = 0; ni < 9; ni++) { + if (ni < 8) + sprintf(d, "=avr.io.%04x.%d", addr, ni); + else + sprintf(d, "8=avr.io.%04x.all", addr); + namep[ni] = d; + d += strlen(d) + 1; + } + avr->io[a].irq = avr_alloc_irq(&avr->irq_pool, 0, 9, namep); + // mark the pin ones as filtered, so they only are raised when changing + for (int i = 0; i < 8; i++) + avr->io[a].irq[i].flags |= IRQ_FLAG_FILTERED; + } + // if given a name, replace the default one... + if (name) { + int l = strlen(name); + char n[l + 10]; + sprintf(n, "avr.io.%s", name); + free((void*)avr->io[a].irq[index].name); + avr->io[a].irq[index].name = strdup(n); + } + return avr->io[a].irq + index; +} + +avr_irq_t * +avr_io_setirqs( + avr_io_t * io, + uint32_t ctl, + int count, + avr_irq_t * irqs ) +{ + // allocate this module's IRQ + io->irq_count = count; + + if (!irqs) { + const char ** irq_names = NULL; + + if (io->irq_names) { + irq_names = malloc(count * sizeof(char*)); + memset(irq_names, 0, count * sizeof(char*)); + char buf[64]; + for (int i = 0; i < count; i++) { + /* + * this bit takes the io module 'kind' ("port") + * the IRQ name ("=0") and the last character of the ioctl ('p','o','r','A') + * to create a full name "=porta.0" + */ + char * dst = buf; + // copy the 'flags' of the name out + const char * kind = io->irq_names[i]; + while (isdigit(*kind)) + *dst++ = *kind++; + while (!isalpha(*kind)) + *dst++ = *kind++; + // add avr name +// strcpy(dst, io->avr->mmcu); + strcpy(dst, "avr"); + dst += strlen(dst); + *dst ++ = '.'; + // add module 'kind' + strcpy(dst, io->kind); + dst += strlen(dst); + // add port name, if any + if ((ctl & 0xff) > ' ') + *dst ++ = tolower(ctl & 0xff); + *dst ++ = '.'; + // add the rest of the irq name + strcpy(dst, kind); + dst += strlen(dst); + *dst = 0; + +// printf("%s\n", buf); + irq_names[i] = strdup(buf); + } + } + irqs = avr_alloc_irq(&io->avr->irq_pool, 0, + count, irq_names); + if (irq_names) { + for (int i = 0; i < count; i++) + free((char*)irq_names[i]); + free((char*)irq_names); + } + } + + io->irq = irqs; + io->irq_ioctl_get = ctl; + return io->irq; +} + +static void +avr_deallocate_io( + avr_io_t * io) +{ + if (io->dealloc) + io->dealloc(io); + avr_free_irq(io->irq, io->irq_count); + io->irq_count = 0; + io->irq_ioctl_get = 0; + io->avr = NULL; + io->next = NULL; +} + +void +avr_deallocate_ios( + avr_t * avr) +{ + avr_io_t * port = avr->io_port; + while (port) { + avr_io_t * next = port->next; + avr_deallocate_io(port); + port = next; + } + avr->io_port = NULL; +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.h new file mode 100644 index 0000000..cfd2fdb --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_io.h @@ -0,0 +1,132 @@ +/* + sim_io.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __SIM_IO_H__ +#define __SIM_IO_H__ + +#include "sim_avr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * used by the ioports to implement their own features + * see avr_eeprom.* for an example, and avr_ioctl(). + */ +#define AVR_IOCTL_DEF(_a,_b,_c,_d) \ + (((_a) << 24)|((_b) << 16)|((_c) << 8)|((_d))) + +/* + * IO module base struct + * Modules uses that as their first member in their own struct + */ +typedef struct avr_io_t { + struct avr_io_t * next; + avr_t * avr; // avr we are attached to + const char * kind; // pretty name, for debug + + const char ** irq_names; // IRQ names + + uint32_t irq_ioctl_get; // used to get irqs from this module + int irq_count; // number of (optional) irqs + struct avr_irq_t * irq; // optional external IRQs + // called at reset time + void (*reset)(struct avr_io_t *io); + // called externally. allow access to io modules and so on + int (*ioctl)(struct avr_io_t *io, uint32_t ctl, void *io_param); + + // optional, a function to free up allocated system resources + void (*dealloc)(struct avr_io_t *io); +} avr_io_t; + +/* + * IO modules helper functions + */ + +// registers an IO module, so it's run(), reset() etc are called +// this is called by the AVR core init functions, you /could/ register an external +// one after instantiation, for whatever purpose... +void +avr_register_io( + avr_t *avr, + avr_io_t * io); +// Sets an IO module "official" IRQs and the ioctl used to get to them. if 'irqs' is NULL, +// 'count' will be allocated +avr_irq_t * +avr_io_setirqs( + avr_io_t * io, + uint32_t ctl, + int count, + avr_irq_t * irqs ); + +// register a callback for when IO register "addr" is read +void +avr_register_io_read( + avr_t *avr, + avr_io_addr_t addr, + avr_io_read_t read, + void * param); +// register a callback for when the IO register is written. callback has to set the memory itself +void +avr_register_io_write( + avr_t *avr, + avr_io_addr_t addr, + avr_io_write_t write, + void * param); +// call every IO modules until one responds to this +int +avr_ioctl( + avr_t *avr, + uint32_t ctl, + void * io_param); +// get the specific irq for a module, check AVR_IOCTL_IOPORT_GETIRQ for example +struct avr_irq_t * +avr_io_getirq( + avr_t * avr, + uint32_t ctl, + int index); + +// get the IRQ for an absolute IO address +// this allows any code to hook an IRQ in any io address, for example +// tracing changes of values into a register +// Note that the values do not "magically" change, they change only +// when the AVR code attempt to read and write at that address +// +// the "index" is a bit number, or ALL bits if index == 8 +#define AVR_IOMEM_IRQ_ALL 8 +avr_irq_t * +avr_iomem_getirq( + avr_t * avr, + avr_io_addr_t addr, + const char * name /* Optional, if NULL, "ioXXXX" will be used */ , + int index); + +// Terminates all IOs and remove from them from the io chain +void +avr_deallocate_ios( + avr_t *avr); + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_IO_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.c new file mode 100644 index 0000000..6d175cf --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.c @@ -0,0 +1,294 @@ +/* + sim_irq.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include "sim_irq.h" + +// internal structure for a hook, never seen by the notify procs +typedef struct avr_irq_hook_t { + struct avr_irq_hook_t * next; + int busy; // prevent reentrance of callbacks + + struct avr_irq_t * chain; // raise the IRQ on this too - optional if "notify" is on + avr_irq_notify_t notify; // called when IRQ is raised - optional if "chain" is on + void * param; // "notify" parameter +} avr_irq_hook_t; + +static void +_avr_irq_pool_add( + avr_irq_pool_t * pool, + avr_irq_t * irq) +{ + int insert = 0; + /* lookup a slot */ + for (; insert < pool->count && pool->irq[insert]; insert++) + ; + if (insert == pool->count) { + if ((pool->count & 0xf) == 0) { + pool->irq = (avr_irq_t**)realloc(pool->irq, + (pool->count + 16) * sizeof(avr_irq_t *)); + } + pool->count++; + } + pool->irq[insert] = irq; + irq->pool = pool; +} + +static void +_avr_irq_pool_remove( + avr_irq_pool_t * pool, + avr_irq_t * irq) +{ + for (int i = 0; i < pool->count; i++) + if (pool->irq[i] == irq) { + pool->irq[i] = 0; + return; + } +} + +void +avr_init_irq( + avr_irq_pool_t * pool, + avr_irq_t * irq, + uint32_t base, + uint32_t count, + const char ** names /* optional */) +{ + memset(irq, 0, sizeof(avr_irq_t) * count); + + for (int i = 0; i < count; i++) { + irq[i].irq = base + i; + irq[i].flags = IRQ_FLAG_INIT; + if (pool) + _avr_irq_pool_add(pool, &irq[i]); + if (names && names[i]) + irq[i].name = strdup(names[i]); + else { + printf("WARNING %s() with NULL name for irq %d.\n", __func__, irq[i].irq); + } + } +} + +avr_irq_t * +avr_alloc_irq( + avr_irq_pool_t * pool, + uint32_t base, + uint32_t count, + const char ** names /* optional */) +{ + avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count); + avr_init_irq(pool, irq, base, count, names); + for (int i = 0; i < count; i++) + irq[i].flags |= IRQ_FLAG_ALLOC; + return irq; +} + +static avr_irq_hook_t * +_avr_alloc_irq_hook( + avr_irq_t * irq) +{ + avr_irq_hook_t *hook = malloc(sizeof(avr_irq_hook_t)); + memset(hook, 0, sizeof(avr_irq_hook_t)); + hook->next = irq->hook; + irq->hook = hook; + return hook; +} + +void +avr_free_irq( + avr_irq_t * irq, + uint32_t count) +{ + if (!irq || !count) + return; + for (int i = 0; i < count; i++) { + avr_irq_t * iq = irq + i; + if (iq->pool) + _avr_irq_pool_remove(iq->pool, iq); + if (iq->name) + free((char*)iq->name); + iq->name = NULL; + // purge hooks + avr_irq_hook_t *hook = iq->hook; + while (hook) { + avr_irq_hook_t * next = hook->next; + free(hook); + hook = next; + } + iq->hook = NULL; + } + // if that irq list was allocated by us, free it + if (irq->flags & IRQ_FLAG_ALLOC) + free(irq); +} + +void +avr_irq_register_notify( + avr_irq_t * irq, + avr_irq_notify_t notify, + void * param) +{ + if (!irq || !notify) + return; + + avr_irq_hook_t *hook = irq->hook; + while (hook) { + if (hook->notify == notify && hook->param == param) + return; // already there + hook = hook->next; + } + hook = _avr_alloc_irq_hook(irq); + hook->notify = notify; + hook->param = param; +} + +void +avr_irq_unregister_notify( + avr_irq_t * irq, + avr_irq_notify_t notify, + void * param) +{ + avr_irq_hook_t *hook, *prev; + if (!irq || !notify) + return; + + hook = irq->hook; + prev = NULL; + while (hook) { + if (hook->notify == notify && hook->param == param) { + if ( prev ) + prev->next = hook->next; + else + irq->hook = hook->next; + free(hook); + return; + } + prev = hook; + hook = hook->next; + } +} + +void +avr_raise_irq_float( + avr_irq_t * irq, + uint32_t value, + int floating) +{ + if (!irq) + return ; + uint32_t output = (irq->flags & IRQ_FLAG_NOT) ? !value : value; + // if value is the same but it's the first time, raise it anyway + if (irq->value == output && + (irq->flags & IRQ_FLAG_FILTERED) && !(irq->flags & IRQ_FLAG_INIT)) + return; + irq->flags &= ~(IRQ_FLAG_INIT | IRQ_FLAG_FLOATING); + if (floating) + irq->flags |= IRQ_FLAG_FLOATING; + avr_irq_hook_t *hook = irq->hook; + while (hook) { + avr_irq_hook_t * next = hook->next; + // prevents reentrance / endless calling loops + if (hook->busy == 0) { + hook->busy++; + if (hook->notify) + hook->notify(irq, output, hook->param); + if (hook->chain) + avr_raise_irq_float(hook->chain, output, floating); + hook->busy--; + } + hook = next; + } + // the value is set after the callbacks are called, so the callbacks + // can themselves compare for old/new values between their parameter + // they are passed (new value) and the previous irq->value + irq->value = output; +} + +void +avr_raise_irq( + avr_irq_t * irq, + uint32_t value) +{ + avr_raise_irq_float(irq, value, !!(irq->flags & IRQ_FLAG_FLOATING)); +} + +void +avr_connect_irq( + avr_irq_t * src, + avr_irq_t * dst) +{ + if (!src || !dst || src == dst) { + fprintf(stderr, "error: %s invalid irq %p/%p", __FUNCTION__, src, dst); + return; + } + avr_irq_hook_t *hook = src->hook; + while (hook) { + if (hook->chain == dst) + return; // already there + hook = hook->next; + } + hook = _avr_alloc_irq_hook(src); + hook->chain = dst; +} + +void +avr_unconnect_irq( + avr_irq_t * src, + avr_irq_t * dst) +{ + avr_irq_hook_t *hook, *prev; + + if (!src || !dst || src == dst) { + fprintf(stderr, "error: %s invalid irq %p/%p", __FUNCTION__, src, dst); + return; + } + hook = src->hook; + prev = NULL; + while (hook) { + if (hook->chain == dst) { + if ( prev ) + prev->next = hook->next; + else + src->hook = hook->next; + free(hook); + return; + } + prev = hook; + hook = hook->next; + } +} + +uint8_t +avr_irq_get_flags( + avr_irq_t * irq ) +{ + return irq->flags; +} + +void +avr_irq_set_flags( + avr_irq_t * irq, + uint8_t flags ) +{ + irq->flags = flags; +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.h new file mode 100644 index 0000000..b96fd47 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_irq.h @@ -0,0 +1,150 @@ +/* + sim_irq.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __SIM_IRQ_H__ +#define __SIM_IRQ_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Internal IRQ system + * + * This subsystem allows any piece of code to "register" a hook to be called when an IRQ is + * raised. The IRQ definition is up to the module defining it, for example a IOPORT pin change + * might be an IRQ in which case any piece of code can be notified when a pin has changed state + * + * The notify hooks are chained, and duplicates are filtered out so you can't register a + * notify hook twice on one particular IRQ + * + * IRQ calling order is not defined, so don't rely on it. + * + * IRQ hook needs to be registered in reset() handlers, ie after all modules init() bits + * have been called, to prevent race condition of the initialization order. + */ +struct avr_irq_t; + +typedef void (*avr_irq_notify_t)( + struct avr_irq_t * irq, + uint32_t value, + void * param); + + +enum { + IRQ_FLAG_NOT = (1 << 0), //!< change polarity of the IRQ + IRQ_FLAG_FILTERED = (1 << 1), //!< do not "notify" if "value" is the same as previous raise + IRQ_FLAG_ALLOC = (1 << 2), //!< this irq structure was malloced via avr_alloc_irq + IRQ_FLAG_INIT = (1 << 3), //!< this irq hasn't been used yet + IRQ_FLAG_FLOATING = (1 << 4), //!< this 'pin'/signal is floating + IRQ_FLAG_USER = (1 << 5), //!< Can be used by irq users +}; + +/* + * IRQ Pool structure + */ +typedef struct avr_irq_pool_t { + int count; //!< number of irqs living in the pool + struct avr_irq_t ** irq; //!< irqs belonging in this pool +} avr_irq_pool_t; + +/*! + * Public IRQ structure + */ +typedef struct avr_irq_t { + struct avr_irq_pool_t * pool; + const char * name; + uint32_t irq; //!< any value the user needs + uint32_t value; //!< current value + uint8_t flags; //!< IRQ_* flags + struct avr_irq_hook_t * hook; //!< list of hooks to be notified +} avr_irq_t; + +//! allocates 'count' IRQs, initializes their "irq" starting from 'base' and increment +avr_irq_t * +avr_alloc_irq( + avr_irq_pool_t * pool, + uint32_t base, + uint32_t count, + const char ** names /* optional */); +void +avr_free_irq( + avr_irq_t * irq, + uint32_t count); + +//! init 'count' IRQs, initializes their "irq" starting from 'base' and increment +void +avr_init_irq( + avr_irq_pool_t * pool, + avr_irq_t * irq, + uint32_t base, + uint32_t count, + const char ** names /* optional */); +//! Returns the current IRQ flags +uint8_t +avr_irq_get_flags( + avr_irq_t * irq ); +//! Sets this irq's flags +void +avr_irq_set_flags( + avr_irq_t * irq, + uint8_t flags ); +//! 'raise' an IRQ. Ie call their 'hooks', and raise any chained IRQs, and set the new 'value' +void +avr_raise_irq( + avr_irq_t * irq, + uint32_t value); +//! Same as avr_raise_irq(), but also allow setting the float status +void +avr_raise_irq_float( + avr_irq_t * irq, + uint32_t value, + int floating); +//! this connects a "source" IRQ to a "destination" IRQ +void +avr_connect_irq( + avr_irq_t * src, + avr_irq_t * dst); +void +avr_unconnect_irq( + avr_irq_t * src, + avr_irq_t * dst); + +//! register a notification 'hook' for 'irq' -- 'param' is anything that your want passed back as argument +void +avr_irq_register_notify( + avr_irq_t * irq, + avr_irq_notify_t notify, + void * param); + +void +avr_irq_unregister_notify( + avr_irq_t * irq, + avr_irq_notify_t notify, + void * param); + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_IRQ_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_network.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_network.h new file mode 100644 index 0000000..da4600a --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_network.h @@ -0,0 +1,89 @@ +/* + sim_network.h + + Copyright 2012 Stephan Veigl + + 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 . + */ + +#ifndef __SIM_NETWORK_H__ +#define __SIM_NETWORK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __MINGW32__ + +// Windows with MinGW + +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +static inline int network_init(void) +{ + // nothing to do + return 0; +} + +static inline void network_release(void) +{ + // nothing to do +} + +#endif + +#ifdef __cplusplus +}; +#endif + +#endif /*__SIM_NETWORK_H__*/ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_regbit.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_regbit.h new file mode 100644 index 0000000..862f490 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_regbit.h @@ -0,0 +1,199 @@ +/* + sim_regbit.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __SIM_REGBIT_H__ +#define __SIM_REGBIT_H__ + +#include "sim_avr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARRAY_SIZE(_aa) (sizeof(_aa) / sizeof((_aa)[0])) + + +/* + * These accessors are inlined and are used to perform the operations on + * avr_regbit_t definitions. This is the "official" way to access bits into registers + * The small footprint costs brings much better versatility for functions/bits that are + * not always defined in the same place on real AVR cores + */ +/* + * set/get/clear io register bits in one operation + */ +static inline uint8_t +avr_regbit_set( + avr_t * avr, + avr_regbit_t rb) +{ + uint16_t a = rb.reg; + uint8_t m; + + if (!a) + return 0; + m = (uint8_t)(rb.mask << rb.bit); + avr_core_watch_write(avr, a, (uint8_t)(avr->data[a] | m)); + return (uint8_t)((avr->data[a] >> rb.bit) & rb.mask); +} + +static inline uint8_t +avr_regbit_setto( + avr_t * avr, + avr_regbit_t rb, + uint8_t v) +{ + uint16_t a = rb.reg; + uint8_t m; + + if (!a) + return 0; + m = (uint8_t)(rb.mask << rb.bit); + avr_core_watch_write(avr, a, + (uint8_t)((avr->data[a] & ~(m)) | + ((v << rb.bit) & m))); + return (uint8_t)((avr->data[a] >> rb.bit) & rb.mask); +} + +/* + * Set the 'raw' bits, if 'v' is the unshifted value of the bits + */ +static inline uint8_t +avr_regbit_setto_raw( + avr_t * avr, + avr_regbit_t rb, + uint8_t v) +{ + uint16_t a = rb.reg; + uint8_t m; + + if (!a) + return 0; + m = (uint8_t)(rb.mask << rb.bit); + avr_core_watch_write(avr, a, + (uint8_t)((avr->data[a] & ~(m)) | ((v) & m))); + return (uint8_t)((avr->data[a]) & (rb.mask << rb.bit)); +} + +static inline uint8_t +avr_regbit_get( + avr_t * avr, + avr_regbit_t rb) +{ + uint16_t a = rb.reg; + if (!a) + return 0; + //uint8_t m = rb.mask << rb.bit; + return (uint8_t)((avr->data[a] >> rb.bit) & rb.mask); +} + +/* + * Using regbit from value eliminates some of the + * set to test then clear register operations. + * makes cheking register bits before setting easier. + */ +static inline uint8_t +avr_regbit_from_value( + avr_t * avr __attribute__((unused)), + avr_regbit_t rb, + uint8_t value) +{ + uint16_t a = rb.reg; + if (!a) + return 0; + return (uint8_t)((value >> rb.bit) & rb.mask); +} + +/* + * Return the bit(s) 'in position' instead of zero based + */ +static inline uint8_t +avr_regbit_get_raw( + avr_t * avr, + avr_regbit_t rb) +{ + uint16_t a = rb.reg; + if (!a) + return 0; + //uint8_t m = rb.mask << rb.bit; + return (uint8_t)((avr->data[a]) & (rb.mask << rb.bit)); +} + +static inline uint8_t +avr_regbit_clear( + avr_t * avr, + avr_regbit_t rb) +{ + uint16_t a = rb.reg; + if (!a) + return 0; + uint8_t m = (uint8_t)(rb.mask << rb.bit); + avr_core_watch_write(avr, a, (uint8_t)(avr->data[a] & ~m)); + return avr->data[a]; +} + + +/* + * This reads the bits for an array of avr_regbit_t, make up a "byte" with them. + * This allows reading bits like CS0, CS1, CS2 etc even if they are not in the same + * physical IO register. + */ +static inline uint8_t +avr_regbit_get_array( + avr_t * avr, + avr_regbit_t *rb, + int count) +{ + uint8_t res = 0; + int i; + + for (i = 0; i < count; i++, rb++) if (rb->reg) { + uint16_t a = rb->reg; + res |= (uint8_t)(((avr->data[a] >> rb->bit) & rb->mask) << i); + } + return res; +} + +/* + * Does the reverse of avr_regbit_get_array + */ +static inline void +avr_regbit_set_array_from_value( + avr_t * avr, + avr_regbit_t * rb, + uint8_t count, + uint8_t value) +{ + int i; + for (i = 0; i < count; i++, rb++) if (rb->reg) { + uint8_t rbv = (value >> (count - i)) & 1; + avr_regbit_setto(avr, *rb, rbv); + } +} + +#define AVR_IO_REGBIT(_io, _bit) { . reg = (_io), .bit = (_bit), .mask = 1 } +#define AVR_IO_REGBITS(_io, _bit, _mask) { . reg = (_io), .bit = (_bit), .mask = (_mask) } + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_REGBIT_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_time.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_time.h new file mode 100644 index 0000000..4b81f43 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_time.h @@ -0,0 +1,64 @@ +/* + sim_time.h + + Copyright 2008-2012 Michel Pollet + + 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 . + */ + + +#ifndef __SIM_TIME_H___ +#define __SIM_TIME_H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim_avr.h" + +// converts a number of usec to a number of machine cycles, at current speed +static inline avr_cycle_count_t +avr_usec_to_cycles(struct avr_t * avr, uint32_t usec) +{ + return avr->frequency * (avr_cycle_count_t)usec / 1000000; +} + +// converts back a number of cycles to usecs (for usleep) +static inline uint32_t +avr_cycles_to_usec(struct avr_t * avr, avr_cycle_count_t cycles) +{ + return 1000000L * cycles / avr->frequency; +} + +// converts back a number of cycles to nsecs +static inline uint64_t +avr_cycles_to_nsec(struct avr_t * avr, avr_cycle_count_t cycles) +{ + return (uint64_t)1E6 * (uint64_t)cycles / (avr->frequency/1000); +} + +// converts a number of hz (to megahertz etc) to a number of cycle +static inline avr_cycle_count_t +avr_hz_to_cycles(avr_t * avr, uint32_t hz) +{ + return avr->frequency / hz; +} + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_TIME_H___ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.c new file mode 100644 index 0000000..eeb4986 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.c @@ -0,0 +1,68 @@ +/* + sim_utils.c + + Implements a Value Change Dump file outout to generate + traces & curves and display them in gtkwave. + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include + +#include "sim_utils.h" + +static argv_p +argv_realloc( + argv_p argv, + uint32_t size ) +{ + argv = realloc(argv, + sizeof(argv_t) + (size * sizeof(argv->argv[0]))); + argv->size = size; + return argv; +} + +argv_p +argv_parse( + argv_p argv, + char * line ) +{ + if (!argv) + argv = argv_realloc(argv, 8); + argv->argc = 0; + + /* strip end of lines and trailing spaces */ + char *d = line + strlen(line); + while ((d - line) > 0 && *(--d) <= ' ') + *d = 0; + /* stop spaces + tabs */ + char *s = line; + while (*s && *s <= ' ') + s++; + argv->line = s; + char * a = NULL; + do { + if (argv->argc == argv->size) + argv = argv_realloc(argv, argv->size + 8); + if ((a = strsep(&s, " \t")) != NULL) + argv->argv[argv->argc++] = a; + } while (a); + argv->argv[argv->argc] = NULL; + return argv; +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.h new file mode 100644 index 0000000..44cb2c5 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_utils.h @@ -0,0 +1,51 @@ +/* + sim_utils.h + + Implements a Value Change Dump file outout to generate + traces & curves and display them in gtkwave. + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __SIM_UTILS_H__ +#define __SIM_UTILS_H__ + +#include + +typedef struct argv_t { + uint32_t size, argc; + char * line; + char * argv[]; +} argv_t, *argv_p; + +/* + * Allocate a argv_t structure, split 'line' into words (destructively) + * and fills up argc, and argv fields with pointers to the individual + * words. The line is stripped of any \r\n as well + * You can pass an already allocated argv_t for it to be reused (and + * grown to fit). + * + * You are still responsible, as the caller, to (free) the resulting + * pointer, and the 'line' text, if appropriate, no duplication is made + */ +argv_p +argv_parse( + argv_p argv, + char * line ); + +#endif /* __SIM_UTILS_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.c new file mode 100644 index 0000000..16e4d25 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.c @@ -0,0 +1,630 @@ +/* + sim_vcd_file.c + + Implements a Value Change Dump file outout to generate + traces & curves and display them in gtkwave. + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include +#include +#include +#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: + * #[\n][| + * b[x/z/0/1]?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 [_] */ + if (strlen(vcd->signal[i].name) >= 4) { + char *dup = strdupa(vcd->signal[i].name); + char *ioctl = strsep(&dup, "_"); + int index = 0; + if (dup) + index = atoi(dup); + if (strlen(ioctl) == 4) { + uint32_t ioc = AVR_IOCTL_DEF( + ioctl[0], ioctl[1], ioctl[2], ioctl[3]); + avr_irq_t * irq = avr_io_getirq(vcd->avr, ioc, index); + if (irq) { + vcd->signal[i].irq.flags = IRQ_FLAG_INIT; + avr_connect_irq(&vcd->signal[i].irq, irq); + } else { + AVR_LOG(vcd->avr, LOG_WARNING, + "%s IRQ was not found\n", + vcd->signal[i].name); + } + continue; + } + AVR_LOG(vcd->avr, LOG_WARNING, + "%s is an invalid IRQ format\n", + vcd->signal[i].name); + } + } + return 0; +} + +void +avr_vcd_close( + avr_vcd_t * vcd) +{ + avr_vcd_stop(vcd); + + /* dispose of any link and hooks */ + for (int i = 0; i < vcd->signal_count; i++) { + avr_vcd_signal_t * s = &vcd->signal[i]; + + avr_free_irq(&s->irq, 1); + } + + if (vcd->filename) { + free(vcd->filename); + vcd->filename = NULL; + } +} + +static char * +_avr_vcd_get_float_signal_text( + avr_vcd_signal_t * s, + char * out) +{ + char * dst = out; + + if (s->size > 1) + *dst++ = 'b'; + + for (int i = s->size; i > 0; i--) + *dst++ = 'x'; + if (s->size > 1) + *dst++ = ' '; + *dst++ = s->alias; + *dst = 0; + return out; +} + +static char * +_avr_vcd_get_signal_text( + avr_vcd_signal_t * s, + char * out, + uint32_t value) +{ + char * dst = out; + + if (s->size > 1) + *dst++ = 'b'; + + for (int i = s->size; i > 0; i--) + *dst++ = value & (1 << (i-1)) ? '1' : '0'; + if (s->size > 1) + *dst++ = ' '; + *dst++ = s->alias; + *dst = 0; + return out; +} + +/* Write queued output to the VCD file. */ + +static void +avr_vcd_flush_log( + avr_vcd_t * vcd) +{ +#if AVR_VCD_MAX_SIGNALS > 32 + uint64_t seen = 0; +#else + uint32_t seen = 0; +#endif + uint64_t oldbase = 0; // make sure it's different + char out[48]; + + if (avr_vcd_fifo_isempty(&vcd->log) || !vcd->output) + return; + + while (!avr_vcd_fifo_isempty(&vcd->log)) { + avr_vcd_log_t l = avr_vcd_fifo_read(&vcd->log); + // 10ns base -- 100MHz should be enough + uint64_t base = avr_cycles_to_nsec(vcd->avr, l.when - vcd->start) / 10; + + /* + * if that trace was seen in this nsec already, we fudge the + * base time to make sure the new value is offset by one nsec, + * to make sure we get at least a small pulse on the waveform. + * + * This is a bit of a fudge, but it is the only way to represent + * very short "pulses" that are still visible on the waveform. + */ + if (base == oldbase && + (seen & (1 << l.sigindex))) + base++; // this forces a new timestamp + + if (base > oldbase || !seen) { + seen = 0; + fprintf(vcd->output, "#%" PRIu64 "\n", base); + oldbase = base; + } + // mark this trace as seen for this timestamp + seen |= (1 << l.sigindex); + fprintf(vcd->output, "%s\n", + l.floating ? + _avr_vcd_get_float_signal_text( + &vcd->signal[l.sigindex], + out) : + _avr_vcd_get_signal_text( + &vcd->signal[l.sigindex], + out, l.value)); + } +} + +/* Cycle timer for writing queued output. */ + +static avr_cycle_count_t +_avr_vcd_timer( + struct avr_t * avr, + avr_cycle_count_t when, + void * param) +{ + avr_vcd_t * vcd = param; + avr_vcd_flush_log(vcd); + return when + vcd->period; +} + +/* Called for an IRQ that is being recorded. */ + +static void +_avr_vcd_notify( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + avr_vcd_t * vcd = (avr_vcd_t *)param; + + if (!vcd->output) { + AVR_LOG(vcd->avr, LOG_WARNING, + "%s: no output\n", + __FUNCTION__); + return; + } + + avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq; + avr_vcd_log_t l = { + .sigindex = s->irq.irq, + .when = vcd->avr->cycle, + .value = value, + .floating = !!(avr_irq_get_flags(irq) & IRQ_FLAG_FLOATING), + }; + if (avr_vcd_fifo_isfull(&vcd->log)) { + AVR_LOG(vcd->avr, LOG_WARNING, + "%s FIFO Overload, flushing!\n", + __func__); + /* Decrease period by a quarter, for next time */ + vcd->period -= vcd->period >> 2; + avr_vcd_flush_log(vcd); + } + avr_vcd_fifo_write(&vcd->log, l); +} + +/* Register an IRQ whose value is to be logged. */ + +int +avr_vcd_add_signal( + avr_vcd_t * vcd, + avr_irq_t * signal_irq, + int signal_bit_size, + const char * name ) +{ + if (vcd->signal_count == AVR_VCD_MAX_SIGNALS) { + AVR_LOG(vcd->avr, LOG_ERROR, + " %s: unable add signal '%s'\n", + __FUNCTION__, name); + return -1; + } + int index = vcd->signal_count++; + avr_vcd_signal_t * s = &vcd->signal[index]; + strncpy(s->name, name, sizeof(s->name)); + s->size = signal_bit_size; + s->alias = ' ' + vcd->signal_count ; + + /* manufacture a nice IRQ name */ + int l = strlen(name); + char iname[10 + l + 1]; + if (signal_bit_size > 1) + sprintf(iname, "%d>vcd.%s", signal_bit_size, name); + else + sprintf(iname, ">vcd.%s", name); + + const char * names[1] = { iname }; + avr_init_irq(&vcd->avr->irq_pool, &s->irq, index, 1, names); + avr_irq_register_notify(&s->irq, _avr_vcd_notify, vcd); + + avr_connect_irq(signal_irq, &s->irq); + return 0; +} + +/* Open the VCD output file and write header. Does nothing for input. */ + +int +avr_vcd_start( + avr_vcd_t * vcd) +{ + time_t now; + + vcd->start = vcd->avr->cycle; + avr_vcd_fifo_reset(&vcd->log); + + if (vcd->input) { + /* + * nothing to do here, the first cycle timer will take care + * if it. + */ + return 0; + } + if (vcd->output) + avr_vcd_stop(vcd); + vcd->output = fopen(vcd->filename, "w"); + if (vcd->output == NULL) { + perror(vcd->filename); + return -1; + } + + time(&now); + fprintf(vcd->output, "$date %s$end\n", ctime(&now)); + fprintf(vcd->output, + "$version Simavr " CONFIG_SIMAVR_VERSION " $end\n"); + fprintf(vcd->output, "$timescale 10ns $end\n"); // 10ns base, aka 100MHz + fprintf(vcd->output, "$scope module logic $end\n"); + + for (int i = 0; i < vcd->signal_count; i++) { + fprintf(vcd->output, "$var wire %d %c %s $end\n", + vcd->signal[i].size, vcd->signal[i].alias, vcd->signal[i].name); + } + + fprintf(vcd->output, "$upscope $end\n"); + fprintf(vcd->output, "$enddefinitions $end\n"); + + fprintf(vcd->output, "$dumpvars\n"); + for (int i = 0; i < vcd->signal_count; i++) { + avr_vcd_signal_t * s = &vcd->signal[i]; + char out[48]; + fprintf(vcd->output, "%s\n", + _avr_vcd_get_float_signal_text(s, out)); + } + fprintf(vcd->output, "$end\n"); + avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd); + return 0; +} + +int +avr_vcd_stop( + avr_vcd_t * vcd) +{ + avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd); + avr_cycle_timer_cancel(vcd->avr, _avr_vcd_input_timer, vcd); + + avr_vcd_flush_log(vcd); + + if (vcd->input_line) + free(vcd->input_line); + vcd->input_line = NULL; + if (vcd->input) + fclose(vcd->input); + vcd->input = NULL; + if (vcd->output) + fclose(vcd->output); + vcd->output = NULL; + return 0; +} + + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.h new file mode 100644 index 0000000..2dd6219 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simavr/sim/sim_vcd_file.h @@ -0,0 +1,131 @@ +/* + sim_vcd_file.c + + Implements a Value Change Dump file outout to generate + traces & curves and display them in gtkwave. + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#ifndef __SIM_VCD_FILE_H__ +#define __SIM_VCD_FILE_H__ + +#include +#include "sim_irq.h" +#include "fifo_declare.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Value Change dump module for simavr. + * + * This structure registers IRQ change hooks to various "source" IRQs + * and dumps their values (if changed) at certain intervals into the VCD + * file. + * + * It can also do the reverse, load a VCD file generated by for example + * sigrock signal analyzer, and 'replay' digital input with the proper + * timing. + * + * TODO: Add support for 'looping' a VCD input. + */ + +#define AVR_VCD_MAX_SIGNALS 64 + +typedef struct avr_vcd_signal_t { + /* + * For VCD output this is the IRQ we receive new values from. + * For VCD input, this is the IRQ we broadcast the values to + */ + avr_irq_t irq; + char alias; // vcd one character alias + uint8_t size; // in bits + char name[32]; // full human name +} avr_vcd_signal_t, *avr_vcd_signal_p; + +typedef struct avr_vcd_log_t { + uint64_t when; // Cycles for output, + // nS for input. + uint64_t sigindex : 8, // index in signal table + floating : 1, + value : 32; +} avr_vcd_log_t, *avr_vcd_log_p; + +DECLARE_FIFO(avr_vcd_log_t, avr_vcd_fifo, 256); + +struct argv_t; + +typedef struct avr_vcd_t { + struct avr_t * avr; // AVR we are attaching timers to.. + + char * filename; // .vcd filename + /* can be input OR output, not both */ + FILE * output; + FILE * input; + struct argv_t * input_line; + + int signal_count; + avr_vcd_signal_t signal[AVR_VCD_MAX_SIGNALS]; + + uint64_t start; + uint64_t period; // for output cycles + uint64_t vcd_to_ns; // for input unit mapping + + avr_vcd_fifo_t log; +} avr_vcd_t; + +// initializes a new VCD trace file, and returns zero if all is well +int +avr_vcd_init( + struct avr_t * avr, + const char * filename, // filename to write + avr_vcd_t * vcd, // vcd struct to initialize + uint32_t period ); // file flushing period is in usec +int +avr_vcd_init_input( + struct avr_t * avr, + const char * filename, // filename to read + avr_vcd_t * vcd ); // vcd struct to initialize +void +avr_vcd_close( + avr_vcd_t * vcd ); + +// Add a trace signal to the vcd file. Must be called before avr_vcd_start() +int +avr_vcd_add_signal( + avr_vcd_t * vcd, + avr_irq_t * signal_irq, + int signal_bit_size, + const char * name ); + +// Starts recording the signal value into the file +int +avr_vcd_start( + avr_vcd_t * vcd); +// stops recording signal values into the file +int +avr_vcd_stop( + avr_vcd_t * vcd); + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIM_VCD_FILE_H__ */ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/simuc b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/simuc new file mode 100755 index 0000000..966bd1f Binary files /dev/null and b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/simuc differ diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/main.cpp b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/main.cpp new file mode 100644 index 0000000..0af4178 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/main.cpp @@ -0,0 +1,190 @@ +#include +#include +#include + +#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 for status and:"); + for (long unsigned int i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { + printf(" %s", commands[i]); + } + printf("\n"); + continue; + } + + } catch (std::exception& e) { + printf("ERROR\n"); + } + + } + + } + while (1) { + struct SimAvrEvent ev = waitForEvent(); + printf("event %s (%d) received\n", eventText((EnumSimEvent)ev.event), ev.event); + if (ev.event == EventShutdown) { + break; + } + } + + usleep(10000); + return 0; +} \ No newline at end of file diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.cpp b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.cpp new file mode 100644 index 0000000..f59ddbd --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.cpp @@ -0,0 +1,18 @@ +#include "error.h" +#include + +std::string error (const char *location, const char *format, ...) { + va_list args; + va_start (args, format); + int length = std::vsnprintf (NULL, 0, format, args); + va_end (args); + + va_start (args, format); + char* str = new char[length + 1]; // one more character for null terminator + std::vsnprintf (str, length + 1, format, args); + std::string rv = "Error at " + std::string(location) + " -> " + str; + delete[] str; + va_end (args); + + return rv; +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.h new file mode 100644 index 0000000..80d5682 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/error.h @@ -0,0 +1,11 @@ +#ifndef ERROR_H +#define ERROR_H + +#include + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define AT __FILE__ ":" TOSTRING(__LINE__) +std::string error (const char *location, const char *format, ...); + +#endif // ERROR_H diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.cpp b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.cpp new file mode 100644 index 0000000..32a23b6 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.cpp @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 gdbFromUartBuffer; +std::vector gdbToUartBuffer; + +__attribute__((unused)) static void fromGdbUart (uint8_t b) { + static int cnt = 0; + if (b == '$') { + cnt = 1; + } else if (cnt > 0 && b == '#') { + cnt = -1; + } else if (cnt < 0) { + cnt--; + } + gdbFromUartBuffer.push_back(b); + + if (cnt <= -3 || (cnt == 0 && b == '+')) { + printf("\n\rgdb-uart OUT -> "); + for (uint8_t c : gdbFromUartBuffer) { + putchar(c); + } + printf("\n\r"); + gdbFromUartBuffer.clear(); + } +} + +__attribute__((unused)) static void toGdbUart (uint8_t b) { + static int cnt = 0; + if (b == '$') { + cnt = 1; + } else if (cnt > 0 && b == '#') { + cnt = -1; + } else if (cnt < 0) { + cnt--; + } + gdbToUartBuffer.push_back(b); + + if (cnt <= -3 || (cnt == 0 && b == '+')) { + printf("\n\rgdb-uart IN <-- "); + for (uint8_t c : gdbToUartBuffer) { + putchar(c); + } + printf("\n\r"); + gdbToUartBuffer.clear(); + } + +} + +void init (struct StartParameters *param) { + try { + // switchStdOut(NULL); + // switchStdErr(NULL); + // std::cout.rdbuf(0); + // std::cerr.rdbuf(0); + const char *filename = param->filename != NULL + ? param->filename + // : "../gdb-stub-sm_atmega324p/sim/dist/gdb-stub-sm_atmega324p.axf"; + : "../gdb-stub-sm_atmega324p/sim/dist/gdb-stub-sm_atmega324p.axf"; + printf("firmware file \"%s\"\n", filename); + simavr.load(param); + if (strcmp(simavr.getTargetDeviceName(), "atmega324p") != 0) { + std::logic_error(error(AT, "invalid target device %s", simavr.getTargetDeviceName())); + } + + simavr.setUartDumpEnabled(false); + // simavr.registerIoWrite(PORTB, handleWritePortB); + simavr.setUartPtyEnabled(0, true); + int uartCnt = 1; + if (strcmp(simavr.getTargetDeviceName(), "atmega324p") == 0) { + simavr.setUartPtyEnabled(1, true); + simavr.setUartPtyHook(1, fromGdbUart, toGdbUart); + uartCnt = 2; + } + // printf("uart0 -> picocom --imap lfcrlf -b 115200 %s\n", simavr.getUartPtyDeviceName(0)); + // printf("uart1 -> picocom --imap lfcrlf -b 115200 %s\n", simavr.getUartPtyDeviceName(1)); + + printf("device %s\n", simavr.getTargetDeviceName()); + printf("frequency %.2lfMHz\n", simavr.getTargetFrequency() / 1E6); + + for (int i = 0; i < uartCnt; i++) { + char s[128]; + snprintf(s, 128, "/tmp/sim-megaavr-%s-uart%1d", simavr.getTargetDeviceName(), i); + FILE *f = fopen(s, "w"); + if (f == NULL) { + std::logic_error(error(AT, "cannot write file %s", s)); + } + printf("uart%1d -> picocom %s (see file %s)\n", i, simavr.getUartPtyDeviceName(i), s); + fprintf(f, "%s\n", simavr.getUartPtyDeviceName(i)); + fclose(f); + } + + } catch (std::exception& e) { + lastErrorMessage = "init() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void shutdown () { + try { + simavr.shutdown(); + } catch (std::exception& e) { + lastErrorMessage = "shutdown() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void start () { + try { + simavr.start(); + + } catch (std::exception& e) { + lastErrorMessage = "start() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void stop () { + try { + simavr.stop(); + + } catch (std::exception& e) { + lastErrorMessage = "stop() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void setCommand (EnumSimAvrCommand cmd, void *param) { + try { + simavr.setCommand(cmd, param); + + } catch (std::exception& e) { + lastErrorMessage = "setCommand(..) fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + throw e; + } + +} + +void addEvent (EnumSimEvent event) { + try { + simavr.addEvent(event); + + } catch (std::exception& e) { + lastErrorMessage = "addEvent(..) fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void setTimeSync (bool sync) { + try { + simavr.setTimeSync(sync); + + } catch (std::exception& e) { + lastErrorMessage = "setTimeSync() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +struct SimAvrStatus getStatus () { + return simavr.getStatus();; +} + +struct SimAvrEvent waitForEvent () { + return simavr.waitForEvent(); +} + +const char *eventText (EnumSimEvent event) { + const char *rv = simavr.eventText((EnumSimAvrEvent)event); + if (rv != NULL) { + return rv; + } else { + switch (event) { + case EventLedOff: return "LedOff"; + case EventLedOn: return "LedOn"; + default: return "?"; + } + } +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.h new file mode 100644 index 0000000..d980f26 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/sim/sim.h @@ -0,0 +1,43 @@ +#ifndef SIM_H +#define SIM_H + +#include "../simavr/simavr.h" + +// #define _AVR_IO_H_ +// #define _SFR_IO8(reg) (0x20 + reg) +// #include "iom324p.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + EventInterrupt = 1, + EventLedOff = 10, + EventLedOn = 11 +} EnumSimEvent; + + + +extern void init (struct StartParameters *param); +extern void shutdown (); +extern const char * lastError (); +extern void start (); +extern void stop (); +extern void setCommand (EnumSimAvrCommand cmd, void *param); +extern void addEvent (EnumSimEvent event); +extern void setTimeSync (bool sync); +extern struct SimAvrStatus getStatus (); +extern struct SimAvrEvent waitForEvent (); +extern const char *eventText (EnumSimEvent event); + +typedef void (*NotificationListener)(char *, int); +void callbackTrigger(const NotificationListener l); +void getDeviceRandomStatus(char *answer, int sizeOfChars); +int randNum( int min, int max); + +#ifdef __cplusplus +} +#endif + +#endif // SIM_H diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/fifo_declare.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/fifo_declare.h new file mode 100644 index 0000000..8a3b2fb --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/fifo_declare.h @@ -0,0 +1,189 @@ +/* + fido_declare.h + Copyright (C) 2003-2012 Michel Pollet + + 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 + +#if __AVR__ +#define FIFO_CURSOR_TYPE uint8_t +#define FIFO_BOOL_TYPE char +#define FIFO_INLINE +#define FIFO_SYNC +#endif + +#ifndef FIFO_CURSOR_TYPE +#define FIFO_CURSOR_TYPE uint16_t +#endif +#ifndef FIFO_BOOL_TYPE +#define FIFO_BOOL_TYPE int +#endif +#ifndef FIFO_INLINE +#define FIFO_INLINE inline +#endif + +/* We should not need volatile */ +#ifndef FIFO_VOLATILE +#define FIFO_VOLATILE +#endif +#ifndef FIFO_SYNC +#define FIFO_SYNC __sync_synchronize() +#endif + +#ifndef FIFO_ZERO_INIT +#define FIFO_ZERO_INIT {0} +#endif +#define FIFO_NULL { FIFO_ZERO_INIT, 0, 0, 0 } + +/* New compilers don't like unused static functions. However, + * we do like 'static inlines' for these small accessors, + * so we mark them as 'unused'. It stops it complaining */ +#ifdef __GNUC__ +#define FIFO_DECL static __attribute__ ((unused)) +#else +#define FIFO_DECL static +#endif + +#define DECLARE_FIFO(__type, __name, __size) \ +enum { __name##_overflow_f = (1 << 0) }; \ +enum { __name##_fifo_size = (__size) }; \ +typedef struct __name##_t { \ + __type buffer[__name##_fifo_size]; \ + FIFO_VOLATILE FIFO_CURSOR_TYPE read; \ + FIFO_VOLATILE FIFO_CURSOR_TYPE write; \ + FIFO_VOLATILE uint8_t flags; \ +} __name##_t + +#define DEFINE_FIFO(__type, __name) \ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\ +{\ + FIFO_CURSOR_TYPE now = c->write;\ + FIFO_CURSOR_TYPE next = (now + 1) & (__name##_fifo_size-1);\ + if (c->read != next) { \ + c->buffer[now] = b;\ + FIFO_SYNC; \ + c->write = next;\ + return 1;\ + }\ + return 0;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\ +{\ + FIFO_CURSOR_TYPE next = (c->write + 1) & (__name##_fifo_size-1);\ + return c->read == next;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\ +{\ + return c->read == c->write;\ +}\ +FIFO_DECL FIFO_INLINE __type __name##_read(__name##_t * c)\ +{\ + __type res = FIFO_ZERO_INIT; \ + FIFO_CURSOR_TYPE read = c->read;\ + if (read == c->write)\ + return res;\ + res = c->buffer[read];\ + FIFO_SYNC; \ + c->read = (read + 1) & (__name##_fifo_size-1);\ + return res;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\ +{\ + return ((c->write + __name##_fifo_size) - c->read) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_write_size(__name##_t *c)\ +{\ + return (__name##_fifo_size-1) - __name##_get_read_size(c);\ +}\ +FIFO_DECL FIFO_INLINE void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + FIFO_SYNC; \ + c->read = (c->read + o) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + return c->buffer[(c->read + o) & (__name##_fifo_size-1)];\ +}\ +FIFO_DECL FIFO_INLINE void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\ +{\ + c->buffer[(c->write + o) & (__name##_fifo_size-1)] = b;\ +}\ +FIFO_DECL FIFO_INLINE void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + FIFO_SYNC; \ + c->write = (c->write + o) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE void __name##_reset(__name##_t *c)\ +{\ + FIFO_SYNC; \ + c->read = c->write = c->flags = 0;\ +}\ +struct __name##_t + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.c b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.c new file mode 100644 index 0000000..a283fef --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.c @@ -0,0 +1,371 @@ +/* + uart_pty.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#elif defined (__FreeBSD__) +#include +#include +#include +#include +#else +#include +#endif + +#include "uart_pty.h" +#include +#include +#include + +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] = "8avr = avr; + p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names); + avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p); + + const int hastap = (getenv("SIMAVR_UART_TAP") && atoi(getenv("SIMAVR_UART_TAP"))) || + (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))); + p->hastap = hastap; + p->hook_from_uart = NULL; + p->hook_to_uart = NULL; + + for (int ti = 0; ti < 1 + hastap; ti++) { + int m, s; + + if (openpty(&m, &s, p->port[ti].slavename, NULL, NULL) < 0) { + fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno)); + return; + } + struct termios tio; + tcgetattr(m, &tio); + cfmakeraw(&tio); + tio.c_cflag &= ~(ECHO | ECHONL); // no input echo + // tio.c_oflag |= (OPOST | ONLCR); // LF -> CRLF + tcsetattr(m, TCSANOW, &tio); + p->port[ti].s = m; + p->port[ti].tap = ti != 0; + // p->port[ti].crlf = ti != 0; + p->port[ti].crlf = 1; + p->port[ti].hook_from_uart = NULL; + p->port[ti].hook_to_uart = NULL; + if (ti > 0) { + printf("uart_pty_init %s on port *** %s ***\n", + ti == 0 ? "bridge" : "tap", p->port[ti].slavename); + } + } + + pthread_create(&p->thread, NULL, uart_pty_thread, p); + +} + +void +uart_pty_stop( + uart_pty_t * p) +{ + puts(__func__); + pthread_kill(p->thread, SIGINT); + for (int ti = 0; ti < 2; ti++) + if (p->port[ti].s) + close(p->port[ti].s); + void * ret; + pthread_join(p->thread, &ret); +} + +void +uart_pty_connect( + uart_pty_t * p, + char uart) +{ + // disable the stdio dump, as we are sending binary there + uint32_t f = 0; + avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f); + f &= ~AVR_UART_FLAG_STDIO; + avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f); + + avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT); + avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT); + avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON); + avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF); + + if (src && dst) { + avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN); + avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst); + } + if (xon) + avr_irq_register_notify(xon, uart_pty_xon_hook, p); + if (xoff) + avr_irq_register_notify(xoff, uart_pty_xoff_hook, p); + + for (int ti = 0; ti < 1+(p->hastap?1:0); ti++) if (p->port[ti].s) { + char link[128]; + snprintf(link, sizeof(link), "/tmp/simavr-uart%c%s", uart, ti == 1 ? "-tap" : ""); + unlink(link); + if (symlink(p->port[ti].slavename, link) != 0) { + fprintf(stderr, "WARN %s: Can't create %s: %s", __func__, link, strerror(errno)); + } else { + // printf("%s: %s now points to %s\n", __func__, link, p->port[ti].slavename); + } + } + if (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) { + char cmd[256]; + sprintf(cmd, "xterm -e picocom -b 115200 %s >/dev/null 2>&1 &", + p->tap.slavename); + system(cmd); + } else { + // printf("note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal\n"); + } +} diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.h new file mode 100644 index 0000000..482de7d --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/parts/uart_pty.h @@ -0,0 +1,90 @@ +/* + uart_pty.h + + Copyright 2012 Michel Pollet + + 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 . + */ + + +#ifndef __UART_PTY_H___ +#define __UART_PTY_H___ + +#include +#include +#include "fifo_declare.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + IRQ_UART_PTY_BYTE_IN = 0, + IRQ_UART_PTY_BYTE_OUT, + IRQ_UART_PTY_COUNT +}; + +DECLARE_FIFO(uint8_t,uart_pty_fifo, 512); + +typedef struct uart_pty_port_t { + unsigned int tap : 1, crlf : 1; + int s; // socket we chat on + char slavename[64]; + uart_pty_fifo_t in; + uart_pty_fifo_t out; + uint8_t buffer[512]; + size_t buffer_len, buffer_done; + void (*hook_from_uart)(uint8_t); + void (*hook_to_uart)(uint8_t); +} uart_pty_port_t, *uart_pty_port_p; + +typedef struct uart_pty_t { + avr_irq_t * irq; // irq list + struct avr_t *avr; // keep it around so we can pause it + + pthread_t thread; + int xon; + int hastap; + + union { + struct { + uart_pty_port_t pty; + uart_pty_port_t tap; + }; + uart_pty_port_t port[2]; + }; + void (*hook_from_uart)(uint8_t); + void (*hook_to_uart)(uint8_t); +} uart_pty_t; + +void +uart_pty_init( + struct avr_t * avr, + uart_pty_t * b); +void +uart_pty_stop(uart_pty_t * p); + +void +uart_pty_connect( + uart_pty_t * p, + char uart); + +#ifdef __cplusplus +} +#endif + +#endif /* __UART_PTY_H___ */ + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/semaphore.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/semaphore.h new file mode 100644 index 0000000..289a45d --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/semaphore.h @@ -0,0 +1,32 @@ +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#include +#include + +class Semaphore { + public: + Semaphore (int count_ = 0) : count(count_) { + } + + inline void notify () { + std::unique_lock lock(mtx); + count++; + //notify the waiting thread + cv.notify_one(); + } + inline void wait () { + std::unique_lock lock(mtx); + while(count == 0) { + //wait on the mutex until notify is called + cv.wait(lock); + } + count--; + } + private: + std::mutex mtx; + std::condition_variable cv; + int count; +}; + +#endif // SEMAPHORE_H diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.cpp b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.cpp new file mode 100644 index 0000000..ba63390 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.cpp @@ -0,0 +1,749 @@ +#include "simavr.h" +#include "../sim/error.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +SimAvr::SimAvr () { + memset(&status, 0, sizeof(status)); +} + +SimAvr::~SimAvr () { + if (firmware != NULL) { + free(firmware); + firmware = NULL; + } + if (avr != NULL) { + free(avr); + avr = NULL; + } +} + +void SimAvr::shutdown () { + // if (pthread_mutex_lock(&lock)) { + // throw std::logic_error(error(AT, "shutdown() fails caused by mutex error")); + // } + // cancelThread = true; + // isShutdown = true; + // pthread_mutex_unlock(&lock); + addEvent(EventShutdown); +} + + +void SimAvr::load (struct StartParameters *params) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "load() fails caused by mutex error")); + } + try { + if (isShutdown) { + throw std::logic_error(error(AT, "cannot load after simavr shutdown")); + } + if (firmware != NULL) { + throw std::logic_error(error(AT, "firmware already loaded")); + } + + startParameters = params; + firmware = (elf_firmware_t *) malloc(sizeof(elf_firmware_t)); + std::string filename = std::string(params->filename); + if (firmware == NULL || elf_read_firmware(filename.c_str(), firmware) != 0) { + throw std::logic_error(error(AT, "elf_read_firmware() from %s fails", filename.c_str())); + } + if (params->mmcu != NULL) { + strncpy(firmware->mmcu, params->mmcu, sizeof(firmware->mmcu)); + } + if (params->frequency > 0) { + firmware->frequency = (uint32_t)params->frequency; + } + if (params->vcc > 0) { + firmware->vcc = (uint32_t)params->vcc; + } + if (params->avcc > 0) { + firmware->avcc = (uint32_t)params->avcc; + } + if (params->aref > 0) { + firmware->aref = (uint32_t)params->aref; + } + + // strncpy(firmware->mmcu, "atmega324p", sizeof(firmware->mmcu)); + // firmware->frequency = 20000000L; + if (firmware->frequency == 0) { + firmware->frequency = 8000000L; + } + if (firmware->vcc == 0) { + firmware->vcc = 5000; + } + if (firmware->avcc == 0) { + firmware->avcc = 5000; + } + if (firmware->aref == 0) { + firmware->aref = 2500; + } + if (firmware->mmcu[0] == 0) { + throw std::logic_error(error(AT, "missing cpu type, use --mmcu ... to set mmcu manually)", firmware->mmcu)); + } + + avr = avr_make_mcu_by_name(firmware->mmcu); + if (!avr) { + throw std::logic_error(error(AT, "avr_make_mcu_by_name() fails (mmcu=%s)", firmware->mmcu)); + } + if (params->gdbPort > 0) { + avr->gdb_port = params->gdbPort; + } else { + avr->gdb_port = 1234; + } + printf("init with port=%d, mmcu=%s, f=%u, vcc=%d, avcc=%d, aref=%d, pc=0x%04x\n", + avr->gdb_port, firmware->mmcu, firmware->frequency, firmware->vcc, firmware->avcc, firmware->aref, params->pc < 0 ? 0 : params->pc); + status.freqHz = firmware->frequency; + + if (avr_init(avr) != 0) { + throw std::logic_error(error(AT, "avr_init() fails")); + } + + + firmware->eeprom = (uint8_t *)malloc(1024); + for (int i = 0; i < 1024; i++) { + firmware->eeprom[i] = 0xff; + } + firmware->eeprom[0] = 0xf0; + firmware->eesize = 1024; + + avr_load_firmware(avr, firmware); + status.state = StateLoaded; + + avr->fuse[AVR_FUSE_LOW] = 0xe7; + avr->fuse[AVR_FUSE_HIGH] = 0xd8; + avr->fuse[AVR_FUSE_EXT] = 0xff; + avr->lockbits = 0xff; + + avr->gdb_port = 1234; + avr_gdb_init(avr); + if (params->pc >= 0) { + avr->pc = params->pc; + } + + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + addEvent(EventLoad); +} + +// enabled/disable character output on stdout in case of avr uart write +void SimAvr::setUartDumpEnabled (bool enabled) { + if (avr == NULL) { + throw std::logic_error(error(AT, "setUartDumpEnabled() fails because no avr available")); + } + uint32_t f = 0; + avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('0'), &f); + f = enabled ? f | AVR_UART_FLAG_STDIO : f & ~AVR_UART_FLAG_STDIO; + avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('0'), &f); + avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('1'), &f); + f = enabled ? f | AVR_UART_FLAG_STDIO : f & ~AVR_UART_FLAG_STDIO; + avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('1'), &f); +} + +void SimAvr::registerIoWrite (avr_io_addr_t addr, avrsim_io_write_callback_t callback) { + avr_register_io_write(avr, addr, (avr_io_write_t)callback, this); +} + +void SimAvr::setUartPtyEnabled (int uart, bool enable) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + uart_pty_t *p = NULL; + try { + if (uart < 0 || uart > 1) { + throw std::logic_error(error(AT, "setUartPtyEnabled() fails (invalid uart %d)", uart)); + } + if (enable && uartPty[uart] != NULL) { + throw std::logic_error(error(AT, "enableUartPty() fails (uart %d already enabled)", uart)); + } + if (!enable && uartPty[uart] == NULL) { + throw std::logic_error(error(AT, "enableUartPty() fails (uart %d not enabled)", uart)); + } + if (avr == NULL) { + throw std::logic_error(error(AT, "enableUartPty() fails (avr not ready)")); + } + if (enable) { + p = (uart_pty_t *)malloc(sizeof(uart_pty_t)); + if (p == NULL) { + throw std::logic_error(error(AT, "enableUartPty() fails (malloc fails)")); + } + memset(p, 0, sizeof(uart_pty_t)); + // setenv("SIMAVR_UART_TAP", "1", 1); + uart_pty_init(avr, p); + uart_pty_connect(p, uart + '0'); + uartPty[uart] = p; + } else { + uart_pty_stop(uartPty[uart]); + free(uartPty[uart]); + uartPty[uart] = NULL; + } + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + if (p != NULL) { + free(p); + } + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +void SimAvr::setUartPtyHook (int uart, void (*fromUart)(uint8_t), void (*toUart)(uint8_t)) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "setUartPtyHook() fails caused by mutex error")); + } + try { + if (uart < 0 || uart > 1) { + throw std::logic_error(error(AT, "setUartPtyHook() fails (invalid uart %d)", uart)); + } + if (uartPty[uart] == NULL) { + throw std::logic_error(error(AT, "setUartPtyHook() fails (uart %d not enabled)", uart)); + } + uartPty[uart]->hook_from_uart = fromUart; + uartPty[uart]->hook_to_uart = toUart; + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +const char *SimAvr::getUartPtyDeviceName (int uart) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + try { + if (uart < 0 || uart > 1) { + throw std::logic_error(error(AT, "getUartPtyDeviceName() fails (invalid uart %d)", uart)); + } + uart_pty_t *p = uartPty[uart]; + pthread_mutex_unlock(&lock); + return p == NULL ? NULL : p->port[0].slavename; + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +const char *SimAvr::getTargetDeviceName () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + try { + if (firmware == NULL) { + throw std::logic_error(error(AT, "getTargetDeviceName() fails (no firmware loaded)")); + } + pthread_mutex_unlock(&lock); + return firmware->mmcu; + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +uint32_t SimAvr::getTargetFrequency () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + try { + if (firmware == NULL) { + throw std::logic_error(error(AT, "getTargetDeviceName() fails (no firmware loaded)")); + } + pthread_mutex_unlock(&lock); + return firmware->frequency; + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +void SimAvr::start () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + try { + if (isShutdown) { + throw std::logic_error("start() not allowed after shutdown"); + } + if (avrRunThread != NULL) { + throw std::logic_error("simavr already started"); + } + if (avr == NULL) { + throw std::logic_error("avr not ready (check if load done)"); + } + syncTime[0] = !syncTime[1]; + avrRunThread = new std::thread(&SimAvr::avrRun, this); + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + addEvent(EventStart); +} + + +void SimAvr::stop () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "stop() fails caused by mutex error")); + } + try { + if (avrRunThread == NULL) { + throw std::logic_error("simavr not started"); + } + cancelThread = true; + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + addEvent(EventStop); +} + +void SimAvr::setCommand (EnumSimAvrCommand cmd, void *param) { + if (command != ReadyForNewCommand) { + throw std::logic_error("another command pending"); + } + command = cmd; +} + +void SimAvr::addEvent (int event) { + struct timeval tp; + gettimeofday(&tp, NULL); + SimAvrEvent *p = (SimAvrEvent *)malloc(sizeof(SimAvrEvent)); + if (p != NULL) { + p->epochMillis = tp.tv_sec * 1000 + (long)tp.tv_usec / 1000; + p->event = event; + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "addEvent() fails caused by mutex error")); + } + try { + p->cycle = avr->cycle; + if (!isShutdown) { + events.push_back(p); + eventCount.notify(); + } + pthread_mutex_unlock(&lock); + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + } +} + + +void SimAvr::setTimeSync (bool sync) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "setTimeSync() fails caused by mutex error")); + } + try { + syncTime[1] = sync; + pthread_mutex_unlock(&lock); + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + + +struct SimAvrEvent SimAvr::waitForEvent () { + eventCount.wait(); + + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "waitForEvent() fails caused by mutex error")); + } + try { + struct SimAvrEvent rv = { epochMillis: 0, event: EventUnknown }; + struct SimAvrEvent *p = NULL; + if (events.size() > 0) { + p = events.front(); + rv = *p; + if (p->event != EventShutdown) { + events.pop_front(); + free(p); + } + if (p->event == EventShutdown) { + cancelThread = true; + isShutdown = true; + } + } + pthread_mutex_unlock(&lock); + return rv; + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +const char *SimAvr::eventText (EnumSimAvrEvent event) { + switch (event) { + case EventUnknown: return "Unknown"; + case EventShutdown: return "Shutdown"; + case EventLoad: return "Load"; + case EventStart: return "Start"; + case EventStop: return "Stop"; + default: return NULL; + } +} + +struct SimAvrStatus SimAvr::getStatus () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "getStatus() fails caused by mutex error")); + } + struct SimAvrStatus rv = status; + pthread_mutex_unlock(&lock); + return rv; +} + +void SimAvr::printCyclesAndElapsedTime () { + uint64_t time = avr->cycle * 1000000000L / avr->frequency; + uint16_t nanos = time % 1000; time = time / 1000; + uint16_t micros = time % 1000; time = time / 1000; + uint16_t millis = time % 1000; time = time / 1000; + uint16_t seconds = time % 1000; time = time / 1000; + printf("cycle %" PRIu64 " = %ds %03dms %03dµs %03dns", avr->cycle, seconds, millis, micros, nanos); +} + +void SimAvr::avrRun () { + long cnt = 0; + int lastAvrState = -1; + _avr_sp_set(avr, 0); + while (1) { + try { + if (avr->state != lastAvrState) { + switch (avr->state) { + case cpu_Stopped: { + printf("\ncpu stopped at 0x%04x on ", avr->pc); + printCyclesAndElapsedTime(); + char sfr[9]; + sfr[7] = avr->sreg[0] ? 'C' : '-'; + sfr[6] = avr->sreg[1] ? 'Z' : '-'; + sfr[5] = avr->sreg[2] ? 'N' : '-'; + sfr[4] = avr->sreg[3] ? 'V' : '-'; + sfr[3] = avr->sreg[4] ? 'S' : '-'; + sfr[2] = avr->sreg[5] ? 'H' : '-'; + sfr[1] = avr->sreg[6] ? 'T' : '-'; + sfr[0] = avr->sreg[7] ? 'I' : '-'; + sfr[8] = 0; + printf(" SFR: %s\n ", sfr); + uint16_t x = 0, y = 0, z = 0; + for (int i = 0; i < 32; i++) { + uint8_t b = avr->data[i]; + printf(" r%02d=%02x", i, b); + switch (i) { + case 0x0f: printf("\n "); break; + case 0x1a: x = (x & 0xff00) | b; break; + case 0x1b: x = (x & 0x00ff) | (b << 8); break; + case 0x1c: y = (y & 0xff00) | b; break; + case 0x1d: y = (y & 0x00ff) | (b << 8); break; + case 0x1e: z = (z & 0xff00) | b; break; + case 0x1f: z = (z & 0x00ff) | (b << 8); break; + } + } + printf("\n X=0x%04x Y=0x%04x Z=0x%04x\n", x, y, z); + + uint16_t sp = _avr_sp_get(avr); + printf(" Stack (SP=0x%04x)", sp); + int printHeader = 1; + for (int i = 0; i < 16; i++) { + uint16_t addr = sp + 1 + i; + if (addr <= avr->ioend) { continue; } + if (addr > avr->ramend) { break; } + if (printHeader) { + printf(" -> SRAM 0x%04x:", addr); + printHeader = 0; + } + uint8_t b = avr_core_watch_read(avr, addr); + printf(" %02x", b); + + } + printf(printHeader ? "\n" : "\n"); + + printf(" Arduino LED L: "); + printf((avr->data[0x24] & 0x20) && (avr->data[0x25] & 0x20) ? "ON\n" : "OFF\n"); + + printf("\n"); + break; + } + case cpu_Sleeping: printf("cpu enter sleep mode at cycle %" PRIu64 "\n", avr->cycle); break; + case cpu_Running: printf("cpu starts running at cycle %" PRIu64 "\n", avr->cycle); break; + case cpu_Step: printf("cpu step\n"); break; + case cpu_StepDone: printf("cpu step done\n"); break; + case cpu_Done: printf("cpu done\n"); break; + case cpu_Crashed: printf("cpu crashed\n"); break; + default: printf("cpu enter unknown mode at cycle %" PRIu64 "\n", avr->cycle); break; + } + lastAvrState = avr->state; + } + + if (startParameters != NULL) { + switch (startParameters->board) { + case BoardNano: { + static int8_t ledL = -1; // pin floating + uint8_t ddrb = avr->data[0x24]; + uint8_t portb = avr->data[0x25]; + int8_t nextLedL = -1; + if (ddrb & 0x20) { + nextLedL = portb & 0x20 ? 1 : 0; + } + if (nextLedL != ledL) { + ledL = nextLedL; + printCyclesAndElapsedTime(); + printf(": LED L = %c\n", ledL ? 'X' : '.'); + } + break; + } + case BoardSure: { + static int8_t led[4] = { -1, -1, -1, -1 }; // pin floating + uint8_t ddra = avr->data[0x3a]; + uint8_t porta = avr->data[0x3b]; + int change = 0; + for (int i = 0; i < 4; i++) { + int8_t nextLed = -1; + if (ddra & (1 << i)) { + nextLed = porta & (1 << i) ? 0 : 1; + } + if (nextLed != led[i]) { + change = 1; + led[i] = nextLed; + } + } + if (change) { + printCyclesAndElapsedTime(); printf(": "); + printf(" LED PA[3210] ="); + for (int i = 3; i >= 0; i--) { + printf(" %c", led[i] ? 'X' : '.'); + } + printf("\n"); + } + break; + } + case BoardEWS1: { + static int8_t led = -1; // pin floating + uint8_t ddrb = avr->data[0x24]; + uint8_t portb = avr->data[0x25]; + int8_t nextLed = -1; + if (ddrb & 0x01) { + nextLed = portb & 0x01 ? 1 : 0; + } + if (nextLed != led) { + led = nextLed; + printCyclesAndElapsedTime(); + printf(": LED1 (PB0) = %c\n", led ? 'X' : '.'); + } + break; + } + default: break; + } + } + + if (cnt <= 0) { + // usleep(10000); + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (1)")); + } + try { + status.cycle = avr->cycle; + status.state = StateRunning; + status.running = true; + switch (avr->state) { + case cpu_Done: status.state = StateDone; break; + case cpu_Running: status.state = StateRunning; break; + case cpu_Crashed: status.state = StateCrashed; break; + default: status.state = StateOthers; break; + } + if (cancelThread) { + cnt = -1; + cancelThread = false; + } else { + if (syncTime[0] != syncTime[1]) { + syncTime[0] = syncTime[1]; + if (syncTime[0]) { + cyclesOnSyncStart = avr->cycle; + struct timeval tp; + gettimeofday(&tp, NULL); + timeMicrosOnSyncStart = tp.tv_usec + (uint64_t)tp.tv_sec * 1000000L; + } else { + cyclesOnSyncStart = -1; + timeMicrosOnSyncStart = 0; + } + } + if (syncTime[0]) { + uint64_t ucMicros = (uint64_t)(status.cycle - cyclesOnSyncStart) * 1000000L / status.freqHz ; + struct timeval tp; + gettimeofday(&tp, NULL); + const uint64_t timeMicros = tp.tv_usec + (uint64_t)tp.tv_sec * 1000000L - timeMicrosOnSyncStart; + + if (ucMicros > 3000000) { + ucMicros++; + } + + int64_t dt = ucMicros - timeMicros; + if (dt > 0) { + int us = dt > INT_MAX ? INT_MAX : (int)dt; + if (us > 1000) { + timeval ts; + ts.tv_sec = 0; + ts.tv_usec = us; + select(0, 0, 0, 0, &ts); + } + } + } + } + if (avr->state == cpu_Done) { + cnt = -1; + } else if (avr->state == cpu_Crashed) { + throw std::logic_error(error(AT, "avr state is cpu_Crashed")); + } + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + } + + if (avr->state == cpu_Stopped) { + timeval ts; + ts.tv_sec = 0; + ts.tv_usec = 1000; + select(0, 0, 0, 0, &ts); + } + + if (cnt < 0) { + cyclesOnSyncStart = -1; + timeMicrosOnSyncStart = 0; + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (2)")); + } + try { + if (avrRunThread != NULL) { + avrRunThread->detach(); + delete avrRunThread; + avrRunThread = NULL; + } + pthread_mutex_unlock(&lock); + return; + } catch (std::exception& e) { + pthread_mutex_unlock(&lock); + status.state = StateError; + throw; + } + + } else if (++cnt >= 100000) { + cnt = 0; + } + + if (command != ReadyForNewCommand) { + switch (command) { + case CommandStatus: { + printCyclesAndElapsedTime(); + printf("\n"); + break; + } + case CommandInterrupt: { + if (avr->state == cpu_Running) { + avr->state = cpu_Stopped; + } + break; + } + case CommandContinue: { + if (avr->state == cpu_Stopped) { + avr->state = cpu_Running; + } + break; + } + case CommandStack: { + uint16_t sp = _avr_sp_get(avr); + printf("Stack: SP=0x%04x:\n", sp); + for (uint16_t addr = ((sp + 1) / 16) * 16; addr <= avr->ramend; addr++) { + if (addr % 16 == 0) { + printf(" 0x%04x:", addr); + } + if (addr % 4 == 0) { + printf(" "); + } + if (addr <= sp) { + printf(" "); + } else { + uint8_t b = avr_core_watch_read(avr, addr); + printf(" %02x", b); + } + if (addr % 16 == 15) { + printf("\n"); + } + } + printf("\n"); + break; + } + + default: break; + } + command = ReadyForNewCommand; + addEvent(EventCommandExecuted); + } + + avr_run(avr); + + } catch (std::exception& e) { + status.state = StateError; + cyclesOnSyncStart = -1; + timeMicrosOnSyncStart = 0; + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (2)")); + } + try { + if (avrRunThread != NULL) { + avrRunThread->detach(); + delete avrRunThread; + avrRunThread = NULL; + } + pthread_mutex_unlock(&lock); + throw; + } catch (std::exception& e) { + pthread_mutex_unlock(&lock); + status.state = StateError; + throw; + } + + } + } +} + diff --git a/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.h b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.h new file mode 100644 index 0000000..e1455b7 --- /dev/null +++ b/examples/simuc/dpkg/htl-simuc_version_arch/usr/share/htl-simuc/simuc/src/simavr/simavr.h @@ -0,0 +1,132 @@ +#ifndef SIMAVR_H +#define SIMAVR_H + +#include "semaphore.h" +#include "parts/uart_pty.h" + +#include +#include +#include + +#include +#include +#include + +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 events; + EnumSimAvrCommand command; + struct SimAvrStatus status; + bool cancelThread = false; + bool isShutdown = false; + bool syncTime[2] = { false, true }; + std::thread *avrRunThread = NULL; + long cyclesOnSyncStart = -1; + uint64_t timeMicrosOnSyncStart = 0; + Semaphore eventCount; + uart_pty_t *uartPty[2] = { NULL, NULL }; + void printCyclesAndElapsedTime (); +}; + + + + +#endif // SIMAVR_H \ No newline at end of file diff --git a/examples/simuc/sim/vcd/.gitkeep b/examples/simuc/sim/vcd/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/simuc/src/main.cpp b/examples/simuc/src/main.cpp new file mode 100644 index 0000000..0af4178 --- /dev/null +++ b/examples/simuc/src/main.cpp @@ -0,0 +1,190 @@ +#include +#include +#include + +#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 for status and:"); + for (long unsigned int i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { + printf(" %s", commands[i]); + } + printf("\n"); + continue; + } + + } catch (std::exception& e) { + printf("ERROR\n"); + } + + } + + } + while (1) { + struct SimAvrEvent ev = waitForEvent(); + printf("event %s (%d) received\n", eventText((EnumSimEvent)ev.event), ev.event); + if (ev.event == EventShutdown) { + break; + } + } + + usleep(10000); + return 0; +} \ No newline at end of file diff --git a/examples/simuc/src/sim/error.cpp b/examples/simuc/src/sim/error.cpp new file mode 100644 index 0000000..f59ddbd --- /dev/null +++ b/examples/simuc/src/sim/error.cpp @@ -0,0 +1,18 @@ +#include "error.h" +#include + +std::string error (const char *location, const char *format, ...) { + va_list args; + va_start (args, format); + int length = std::vsnprintf (NULL, 0, format, args); + va_end (args); + + va_start (args, format); + char* str = new char[length + 1]; // one more character for null terminator + std::vsnprintf (str, length + 1, format, args); + std::string rv = "Error at " + std::string(location) + " -> " + str; + delete[] str; + va_end (args); + + return rv; +} diff --git a/examples/simuc/src/sim/error.h b/examples/simuc/src/sim/error.h new file mode 100644 index 0000000..80d5682 --- /dev/null +++ b/examples/simuc/src/sim/error.h @@ -0,0 +1,11 @@ +#ifndef ERROR_H +#define ERROR_H + +#include + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define AT __FILE__ ":" TOSTRING(__LINE__) +std::string error (const char *location, const char *format, ...); + +#endif // ERROR_H diff --git a/examples/simuc/src/sim/sim.cpp b/examples/simuc/src/sim/sim.cpp new file mode 100644 index 0000000..32a23b6 --- /dev/null +++ b/examples/simuc/src/sim/sim.cpp @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 gdbFromUartBuffer; +std::vector gdbToUartBuffer; + +__attribute__((unused)) static void fromGdbUart (uint8_t b) { + static int cnt = 0; + if (b == '$') { + cnt = 1; + } else if (cnt > 0 && b == '#') { + cnt = -1; + } else if (cnt < 0) { + cnt--; + } + gdbFromUartBuffer.push_back(b); + + if (cnt <= -3 || (cnt == 0 && b == '+')) { + printf("\n\rgdb-uart OUT -> "); + for (uint8_t c : gdbFromUartBuffer) { + putchar(c); + } + printf("\n\r"); + gdbFromUartBuffer.clear(); + } +} + +__attribute__((unused)) static void toGdbUart (uint8_t b) { + static int cnt = 0; + if (b == '$') { + cnt = 1; + } else if (cnt > 0 && b == '#') { + cnt = -1; + } else if (cnt < 0) { + cnt--; + } + gdbToUartBuffer.push_back(b); + + if (cnt <= -3 || (cnt == 0 && b == '+')) { + printf("\n\rgdb-uart IN <-- "); + for (uint8_t c : gdbToUartBuffer) { + putchar(c); + } + printf("\n\r"); + gdbToUartBuffer.clear(); + } + +} + +void init (struct StartParameters *param) { + try { + // switchStdOut(NULL); + // switchStdErr(NULL); + // std::cout.rdbuf(0); + // std::cerr.rdbuf(0); + const char *filename = param->filename != NULL + ? param->filename + // : "../gdb-stub-sm_atmega324p/sim/dist/gdb-stub-sm_atmega324p.axf"; + : "../gdb-stub-sm_atmega324p/sim/dist/gdb-stub-sm_atmega324p.axf"; + printf("firmware file \"%s\"\n", filename); + simavr.load(param); + if (strcmp(simavr.getTargetDeviceName(), "atmega324p") != 0) { + std::logic_error(error(AT, "invalid target device %s", simavr.getTargetDeviceName())); + } + + simavr.setUartDumpEnabled(false); + // simavr.registerIoWrite(PORTB, handleWritePortB); + simavr.setUartPtyEnabled(0, true); + int uartCnt = 1; + if (strcmp(simavr.getTargetDeviceName(), "atmega324p") == 0) { + simavr.setUartPtyEnabled(1, true); + simavr.setUartPtyHook(1, fromGdbUart, toGdbUart); + uartCnt = 2; + } + // printf("uart0 -> picocom --imap lfcrlf -b 115200 %s\n", simavr.getUartPtyDeviceName(0)); + // printf("uart1 -> picocom --imap lfcrlf -b 115200 %s\n", simavr.getUartPtyDeviceName(1)); + + printf("device %s\n", simavr.getTargetDeviceName()); + printf("frequency %.2lfMHz\n", simavr.getTargetFrequency() / 1E6); + + for (int i = 0; i < uartCnt; i++) { + char s[128]; + snprintf(s, 128, "/tmp/sim-megaavr-%s-uart%1d", simavr.getTargetDeviceName(), i); + FILE *f = fopen(s, "w"); + if (f == NULL) { + std::logic_error(error(AT, "cannot write file %s", s)); + } + printf("uart%1d -> picocom %s (see file %s)\n", i, simavr.getUartPtyDeviceName(i), s); + fprintf(f, "%s\n", simavr.getUartPtyDeviceName(i)); + fclose(f); + } + + } catch (std::exception& e) { + lastErrorMessage = "init() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void shutdown () { + try { + simavr.shutdown(); + } catch (std::exception& e) { + lastErrorMessage = "shutdown() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void start () { + try { + simavr.start(); + + } catch (std::exception& e) { + lastErrorMessage = "start() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void stop () { + try { + simavr.stop(); + + } catch (std::exception& e) { + lastErrorMessage = "stop() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void setCommand (EnumSimAvrCommand cmd, void *param) { + try { + simavr.setCommand(cmd, param); + + } catch (std::exception& e) { + lastErrorMessage = "setCommand(..) fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + throw e; + } + +} + +void addEvent (EnumSimEvent event) { + try { + simavr.addEvent(event); + + } catch (std::exception& e) { + lastErrorMessage = "addEvent(..) fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +void setTimeSync (bool sync) { + try { + simavr.setTimeSync(sync); + + } catch (std::exception& e) { + lastErrorMessage = "setTimeSync() fails, caused by " + std::string(e.what()); + std::cerr << lastErrorMessage << "\n"; + errno = 1; + } +} + +struct SimAvrStatus getStatus () { + return simavr.getStatus();; +} + +struct SimAvrEvent waitForEvent () { + return simavr.waitForEvent(); +} + +const char *eventText (EnumSimEvent event) { + const char *rv = simavr.eventText((EnumSimAvrEvent)event); + if (rv != NULL) { + return rv; + } else { + switch (event) { + case EventLedOff: return "LedOff"; + case EventLedOn: return "LedOn"; + default: return "?"; + } + } +} diff --git a/examples/simuc/src/sim/sim.h b/examples/simuc/src/sim/sim.h new file mode 100644 index 0000000..d980f26 --- /dev/null +++ b/examples/simuc/src/sim/sim.h @@ -0,0 +1,43 @@ +#ifndef SIM_H +#define SIM_H + +#include "../simavr/simavr.h" + +// #define _AVR_IO_H_ +// #define _SFR_IO8(reg) (0x20 + reg) +// #include "iom324p.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + EventInterrupt = 1, + EventLedOff = 10, + EventLedOn = 11 +} EnumSimEvent; + + + +extern void init (struct StartParameters *param); +extern void shutdown (); +extern const char * lastError (); +extern void start (); +extern void stop (); +extern void setCommand (EnumSimAvrCommand cmd, void *param); +extern void addEvent (EnumSimEvent event); +extern void setTimeSync (bool sync); +extern struct SimAvrStatus getStatus (); +extern struct SimAvrEvent waitForEvent (); +extern const char *eventText (EnumSimEvent event); + +typedef void (*NotificationListener)(char *, int); +void callbackTrigger(const NotificationListener l); +void getDeviceRandomStatus(char *answer, int sizeOfChars); +int randNum( int min, int max); + +#ifdef __cplusplus +} +#endif + +#endif // SIM_H diff --git a/examples/simuc/src/simavr/parts/fifo_declare.h b/examples/simuc/src/simavr/parts/fifo_declare.h new file mode 100644 index 0000000..8a3b2fb --- /dev/null +++ b/examples/simuc/src/simavr/parts/fifo_declare.h @@ -0,0 +1,189 @@ +/* + fido_declare.h + Copyright (C) 2003-2012 Michel Pollet + + 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 + +#if __AVR__ +#define FIFO_CURSOR_TYPE uint8_t +#define FIFO_BOOL_TYPE char +#define FIFO_INLINE +#define FIFO_SYNC +#endif + +#ifndef FIFO_CURSOR_TYPE +#define FIFO_CURSOR_TYPE uint16_t +#endif +#ifndef FIFO_BOOL_TYPE +#define FIFO_BOOL_TYPE int +#endif +#ifndef FIFO_INLINE +#define FIFO_INLINE inline +#endif + +/* We should not need volatile */ +#ifndef FIFO_VOLATILE +#define FIFO_VOLATILE +#endif +#ifndef FIFO_SYNC +#define FIFO_SYNC __sync_synchronize() +#endif + +#ifndef FIFO_ZERO_INIT +#define FIFO_ZERO_INIT {0} +#endif +#define FIFO_NULL { FIFO_ZERO_INIT, 0, 0, 0 } + +/* New compilers don't like unused static functions. However, + * we do like 'static inlines' for these small accessors, + * so we mark them as 'unused'. It stops it complaining */ +#ifdef __GNUC__ +#define FIFO_DECL static __attribute__ ((unused)) +#else +#define FIFO_DECL static +#endif + +#define DECLARE_FIFO(__type, __name, __size) \ +enum { __name##_overflow_f = (1 << 0) }; \ +enum { __name##_fifo_size = (__size) }; \ +typedef struct __name##_t { \ + __type buffer[__name##_fifo_size]; \ + FIFO_VOLATILE FIFO_CURSOR_TYPE read; \ + FIFO_VOLATILE FIFO_CURSOR_TYPE write; \ + FIFO_VOLATILE uint8_t flags; \ +} __name##_t + +#define DEFINE_FIFO(__type, __name) \ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\ +{\ + FIFO_CURSOR_TYPE now = c->write;\ + FIFO_CURSOR_TYPE next = (now + 1) & (__name##_fifo_size-1);\ + if (c->read != next) { \ + c->buffer[now] = b;\ + FIFO_SYNC; \ + c->write = next;\ + return 1;\ + }\ + return 0;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\ +{\ + FIFO_CURSOR_TYPE next = (c->write + 1) & (__name##_fifo_size-1);\ + return c->read == next;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\ +{\ + return c->read == c->write;\ +}\ +FIFO_DECL FIFO_INLINE __type __name##_read(__name##_t * c)\ +{\ + __type res = FIFO_ZERO_INIT; \ + FIFO_CURSOR_TYPE read = c->read;\ + if (read == c->write)\ + return res;\ + res = c->buffer[read];\ + FIFO_SYNC; \ + c->read = (read + 1) & (__name##_fifo_size-1);\ + return res;\ +}\ +FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\ +{\ + return ((c->write + __name##_fifo_size) - c->read) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_write_size(__name##_t *c)\ +{\ + return (__name##_fifo_size-1) - __name##_get_read_size(c);\ +}\ +FIFO_DECL FIFO_INLINE void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + FIFO_SYNC; \ + c->read = (c->read + o) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + return c->buffer[(c->read + o) & (__name##_fifo_size-1)];\ +}\ +FIFO_DECL FIFO_INLINE void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\ +{\ + c->buffer[(c->write + o) & (__name##_fifo_size-1)] = b;\ +}\ +FIFO_DECL FIFO_INLINE void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\ +{\ + FIFO_SYNC; \ + c->write = (c->write + o) & (__name##_fifo_size-1);\ +}\ +FIFO_DECL FIFO_INLINE void __name##_reset(__name##_t *c)\ +{\ + FIFO_SYNC; \ + c->read = c->write = c->flags = 0;\ +}\ +struct __name##_t + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/examples/simuc/src/simavr/parts/uart_pty.c b/examples/simuc/src/simavr/parts/uart_pty.c new file mode 100644 index 0000000..a283fef --- /dev/null +++ b/examples/simuc/src/simavr/parts/uart_pty.c @@ -0,0 +1,371 @@ +/* + uart_pty.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#elif defined (__FreeBSD__) +#include +#include +#include +#include +#else +#include +#endif + +#include "uart_pty.h" +#include +#include +#include + +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] = "8avr = avr; + p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names); + avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p); + + const int hastap = (getenv("SIMAVR_UART_TAP") && atoi(getenv("SIMAVR_UART_TAP"))) || + (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))); + p->hastap = hastap; + p->hook_from_uart = NULL; + p->hook_to_uart = NULL; + + for (int ti = 0; ti < 1 + hastap; ti++) { + int m, s; + + if (openpty(&m, &s, p->port[ti].slavename, NULL, NULL) < 0) { + fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno)); + return; + } + struct termios tio; + tcgetattr(m, &tio); + cfmakeraw(&tio); + tio.c_cflag &= ~(ECHO | ECHONL); // no input echo + // tio.c_oflag |= (OPOST | ONLCR); // LF -> CRLF + tcsetattr(m, TCSANOW, &tio); + p->port[ti].s = m; + p->port[ti].tap = ti != 0; + // p->port[ti].crlf = ti != 0; + p->port[ti].crlf = 1; + p->port[ti].hook_from_uart = NULL; + p->port[ti].hook_to_uart = NULL; + if (ti > 0) { + printf("uart_pty_init %s on port *** %s ***\n", + ti == 0 ? "bridge" : "tap", p->port[ti].slavename); + } + } + + pthread_create(&p->thread, NULL, uart_pty_thread, p); + +} + +void +uart_pty_stop( + uart_pty_t * p) +{ + puts(__func__); + pthread_kill(p->thread, SIGINT); + for (int ti = 0; ti < 2; ti++) + if (p->port[ti].s) + close(p->port[ti].s); + void * ret; + pthread_join(p->thread, &ret); +} + +void +uart_pty_connect( + uart_pty_t * p, + char uart) +{ + // disable the stdio dump, as we are sending binary there + uint32_t f = 0; + avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f); + f &= ~AVR_UART_FLAG_STDIO; + avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f); + + avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT); + avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT); + avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON); + avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF); + + if (src && dst) { + avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN); + avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst); + } + if (xon) + avr_irq_register_notify(xon, uart_pty_xon_hook, p); + if (xoff) + avr_irq_register_notify(xoff, uart_pty_xoff_hook, p); + + for (int ti = 0; ti < 1+(p->hastap?1:0); ti++) if (p->port[ti].s) { + char link[128]; + snprintf(link, sizeof(link), "/tmp/simavr-uart%c%s", uart, ti == 1 ? "-tap" : ""); + unlink(link); + if (symlink(p->port[ti].slavename, link) != 0) { + fprintf(stderr, "WARN %s: Can't create %s: %s", __func__, link, strerror(errno)); + } else { + // printf("%s: %s now points to %s\n", __func__, link, p->port[ti].slavename); + } + } + if (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) { + char cmd[256]; + sprintf(cmd, "xterm -e picocom -b 115200 %s >/dev/null 2>&1 &", + p->tap.slavename); + system(cmd); + } else { + // printf("note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal\n"); + } +} diff --git a/examples/simuc/src/simavr/parts/uart_pty.h b/examples/simuc/src/simavr/parts/uart_pty.h new file mode 100644 index 0000000..482de7d --- /dev/null +++ b/examples/simuc/src/simavr/parts/uart_pty.h @@ -0,0 +1,90 @@ +/* + uart_pty.h + + Copyright 2012 Michel Pollet + + 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 . + */ + + +#ifndef __UART_PTY_H___ +#define __UART_PTY_H___ + +#include +#include +#include "fifo_declare.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + IRQ_UART_PTY_BYTE_IN = 0, + IRQ_UART_PTY_BYTE_OUT, + IRQ_UART_PTY_COUNT +}; + +DECLARE_FIFO(uint8_t,uart_pty_fifo, 512); + +typedef struct uart_pty_port_t { + unsigned int tap : 1, crlf : 1; + int s; // socket we chat on + char slavename[64]; + uart_pty_fifo_t in; + uart_pty_fifo_t out; + uint8_t buffer[512]; + size_t buffer_len, buffer_done; + void (*hook_from_uart)(uint8_t); + void (*hook_to_uart)(uint8_t); +} uart_pty_port_t, *uart_pty_port_p; + +typedef struct uart_pty_t { + avr_irq_t * irq; // irq list + struct avr_t *avr; // keep it around so we can pause it + + pthread_t thread; + int xon; + int hastap; + + union { + struct { + uart_pty_port_t pty; + uart_pty_port_t tap; + }; + uart_pty_port_t port[2]; + }; + void (*hook_from_uart)(uint8_t); + void (*hook_to_uart)(uint8_t); +} uart_pty_t; + +void +uart_pty_init( + struct avr_t * avr, + uart_pty_t * b); +void +uart_pty_stop(uart_pty_t * p); + +void +uart_pty_connect( + uart_pty_t * p, + char uart); + +#ifdef __cplusplus +} +#endif + +#endif /* __UART_PTY_H___ */ + diff --git a/examples/simuc/src/simavr/semaphore.h b/examples/simuc/src/simavr/semaphore.h new file mode 100644 index 0000000..289a45d --- /dev/null +++ b/examples/simuc/src/simavr/semaphore.h @@ -0,0 +1,32 @@ +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#include +#include + +class Semaphore { + public: + Semaphore (int count_ = 0) : count(count_) { + } + + inline void notify () { + std::unique_lock lock(mtx); + count++; + //notify the waiting thread + cv.notify_one(); + } + inline void wait () { + std::unique_lock lock(mtx); + while(count == 0) { + //wait on the mutex until notify is called + cv.wait(lock); + } + count--; + } + private: + std::mutex mtx; + std::condition_variable cv; + int count; +}; + +#endif // SEMAPHORE_H diff --git a/examples/simuc/src/simavr/simavr.cpp b/examples/simuc/src/simavr/simavr.cpp new file mode 100644 index 0000000..ba63390 --- /dev/null +++ b/examples/simuc/src/simavr/simavr.cpp @@ -0,0 +1,749 @@ +#include "simavr.h" +#include "../sim/error.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +SimAvr::SimAvr () { + memset(&status, 0, sizeof(status)); +} + +SimAvr::~SimAvr () { + if (firmware != NULL) { + free(firmware); + firmware = NULL; + } + if (avr != NULL) { + free(avr); + avr = NULL; + } +} + +void SimAvr::shutdown () { + // if (pthread_mutex_lock(&lock)) { + // throw std::logic_error(error(AT, "shutdown() fails caused by mutex error")); + // } + // cancelThread = true; + // isShutdown = true; + // pthread_mutex_unlock(&lock); + addEvent(EventShutdown); +} + + +void SimAvr::load (struct StartParameters *params) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "load() fails caused by mutex error")); + } + try { + if (isShutdown) { + throw std::logic_error(error(AT, "cannot load after simavr shutdown")); + } + if (firmware != NULL) { + throw std::logic_error(error(AT, "firmware already loaded")); + } + + startParameters = params; + firmware = (elf_firmware_t *) malloc(sizeof(elf_firmware_t)); + std::string filename = std::string(params->filename); + if (firmware == NULL || elf_read_firmware(filename.c_str(), firmware) != 0) { + throw std::logic_error(error(AT, "elf_read_firmware() from %s fails", filename.c_str())); + } + if (params->mmcu != NULL) { + strncpy(firmware->mmcu, params->mmcu, sizeof(firmware->mmcu)); + } + if (params->frequency > 0) { + firmware->frequency = (uint32_t)params->frequency; + } + if (params->vcc > 0) { + firmware->vcc = (uint32_t)params->vcc; + } + if (params->avcc > 0) { + firmware->avcc = (uint32_t)params->avcc; + } + if (params->aref > 0) { + firmware->aref = (uint32_t)params->aref; + } + + // strncpy(firmware->mmcu, "atmega324p", sizeof(firmware->mmcu)); + // firmware->frequency = 20000000L; + if (firmware->frequency == 0) { + firmware->frequency = 8000000L; + } + if (firmware->vcc == 0) { + firmware->vcc = 5000; + } + if (firmware->avcc == 0) { + firmware->avcc = 5000; + } + if (firmware->aref == 0) { + firmware->aref = 2500; + } + if (firmware->mmcu[0] == 0) { + throw std::logic_error(error(AT, "missing cpu type, use --mmcu ... to set mmcu manually)", firmware->mmcu)); + } + + avr = avr_make_mcu_by_name(firmware->mmcu); + if (!avr) { + throw std::logic_error(error(AT, "avr_make_mcu_by_name() fails (mmcu=%s)", firmware->mmcu)); + } + if (params->gdbPort > 0) { + avr->gdb_port = params->gdbPort; + } else { + avr->gdb_port = 1234; + } + printf("init with port=%d, mmcu=%s, f=%u, vcc=%d, avcc=%d, aref=%d, pc=0x%04x\n", + avr->gdb_port, firmware->mmcu, firmware->frequency, firmware->vcc, firmware->avcc, firmware->aref, params->pc < 0 ? 0 : params->pc); + status.freqHz = firmware->frequency; + + if (avr_init(avr) != 0) { + throw std::logic_error(error(AT, "avr_init() fails")); + } + + + firmware->eeprom = (uint8_t *)malloc(1024); + for (int i = 0; i < 1024; i++) { + firmware->eeprom[i] = 0xff; + } + firmware->eeprom[0] = 0xf0; + firmware->eesize = 1024; + + avr_load_firmware(avr, firmware); + status.state = StateLoaded; + + avr->fuse[AVR_FUSE_LOW] = 0xe7; + avr->fuse[AVR_FUSE_HIGH] = 0xd8; + avr->fuse[AVR_FUSE_EXT] = 0xff; + avr->lockbits = 0xff; + + avr->gdb_port = 1234; + avr_gdb_init(avr); + if (params->pc >= 0) { + avr->pc = params->pc; + } + + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + addEvent(EventLoad); +} + +// enabled/disable character output on stdout in case of avr uart write +void SimAvr::setUartDumpEnabled (bool enabled) { + if (avr == NULL) { + throw std::logic_error(error(AT, "setUartDumpEnabled() fails because no avr available")); + } + uint32_t f = 0; + avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('0'), &f); + f = enabled ? f | AVR_UART_FLAG_STDIO : f & ~AVR_UART_FLAG_STDIO; + avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('0'), &f); + avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('1'), &f); + f = enabled ? f | AVR_UART_FLAG_STDIO : f & ~AVR_UART_FLAG_STDIO; + avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('1'), &f); +} + +void SimAvr::registerIoWrite (avr_io_addr_t addr, avrsim_io_write_callback_t callback) { + avr_register_io_write(avr, addr, (avr_io_write_t)callback, this); +} + +void SimAvr::setUartPtyEnabled (int uart, bool enable) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + uart_pty_t *p = NULL; + try { + if (uart < 0 || uart > 1) { + throw std::logic_error(error(AT, "setUartPtyEnabled() fails (invalid uart %d)", uart)); + } + if (enable && uartPty[uart] != NULL) { + throw std::logic_error(error(AT, "enableUartPty() fails (uart %d already enabled)", uart)); + } + if (!enable && uartPty[uart] == NULL) { + throw std::logic_error(error(AT, "enableUartPty() fails (uart %d not enabled)", uart)); + } + if (avr == NULL) { + throw std::logic_error(error(AT, "enableUartPty() fails (avr not ready)")); + } + if (enable) { + p = (uart_pty_t *)malloc(sizeof(uart_pty_t)); + if (p == NULL) { + throw std::logic_error(error(AT, "enableUartPty() fails (malloc fails)")); + } + memset(p, 0, sizeof(uart_pty_t)); + // setenv("SIMAVR_UART_TAP", "1", 1); + uart_pty_init(avr, p); + uart_pty_connect(p, uart + '0'); + uartPty[uart] = p; + } else { + uart_pty_stop(uartPty[uart]); + free(uartPty[uart]); + uartPty[uart] = NULL; + } + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + if (p != NULL) { + free(p); + } + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +void SimAvr::setUartPtyHook (int uart, void (*fromUart)(uint8_t), void (*toUart)(uint8_t)) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "setUartPtyHook() fails caused by mutex error")); + } + try { + if (uart < 0 || uart > 1) { + throw std::logic_error(error(AT, "setUartPtyHook() fails (invalid uart %d)", uart)); + } + if (uartPty[uart] == NULL) { + throw std::logic_error(error(AT, "setUartPtyHook() fails (uart %d not enabled)", uart)); + } + uartPty[uart]->hook_from_uart = fromUart; + uartPty[uart]->hook_to_uart = toUart; + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +const char *SimAvr::getUartPtyDeviceName (int uart) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + try { + if (uart < 0 || uart > 1) { + throw std::logic_error(error(AT, "getUartPtyDeviceName() fails (invalid uart %d)", uart)); + } + uart_pty_t *p = uartPty[uart]; + pthread_mutex_unlock(&lock); + return p == NULL ? NULL : p->port[0].slavename; + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +const char *SimAvr::getTargetDeviceName () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + try { + if (firmware == NULL) { + throw std::logic_error(error(AT, "getTargetDeviceName() fails (no firmware loaded)")); + } + pthread_mutex_unlock(&lock); + return firmware->mmcu; + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +uint32_t SimAvr::getTargetFrequency () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + try { + if (firmware == NULL) { + throw std::logic_error(error(AT, "getTargetDeviceName() fails (no firmware loaded)")); + } + pthread_mutex_unlock(&lock); + return firmware->frequency; + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +void SimAvr::start () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "start() fails caused by mutex error")); + } + try { + if (isShutdown) { + throw std::logic_error("start() not allowed after shutdown"); + } + if (avrRunThread != NULL) { + throw std::logic_error("simavr already started"); + } + if (avr == NULL) { + throw std::logic_error("avr not ready (check if load done)"); + } + syncTime[0] = !syncTime[1]; + avrRunThread = new std::thread(&SimAvr::avrRun, this); + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + addEvent(EventStart); +} + + +void SimAvr::stop () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "stop() fails caused by mutex error")); + } + try { + if (avrRunThread == NULL) { + throw std::logic_error("simavr not started"); + } + cancelThread = true; + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + addEvent(EventStop); +} + +void SimAvr::setCommand (EnumSimAvrCommand cmd, void *param) { + if (command != ReadyForNewCommand) { + throw std::logic_error("another command pending"); + } + command = cmd; +} + +void SimAvr::addEvent (int event) { + struct timeval tp; + gettimeofday(&tp, NULL); + SimAvrEvent *p = (SimAvrEvent *)malloc(sizeof(SimAvrEvent)); + if (p != NULL) { + p->epochMillis = tp.tv_sec * 1000 + (long)tp.tv_usec / 1000; + p->event = event; + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "addEvent() fails caused by mutex error")); + } + try { + p->cycle = avr->cycle; + if (!isShutdown) { + events.push_back(p); + eventCount.notify(); + } + pthread_mutex_unlock(&lock); + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + } +} + + +void SimAvr::setTimeSync (bool sync) { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "setTimeSync() fails caused by mutex error")); + } + try { + syncTime[1] = sync; + pthread_mutex_unlock(&lock); + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + + +struct SimAvrEvent SimAvr::waitForEvent () { + eventCount.wait(); + + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "waitForEvent() fails caused by mutex error")); + } + try { + struct SimAvrEvent rv = { epochMillis: 0, event: EventUnknown }; + struct SimAvrEvent *p = NULL; + if (events.size() > 0) { + p = events.front(); + rv = *p; + if (p->event != EventShutdown) { + events.pop_front(); + free(p); + } + if (p->event == EventShutdown) { + cancelThread = true; + isShutdown = true; + } + } + pthread_mutex_unlock(&lock); + return rv; + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } +} + +const char *SimAvr::eventText (EnumSimAvrEvent event) { + switch (event) { + case EventUnknown: return "Unknown"; + case EventShutdown: return "Shutdown"; + case EventLoad: return "Load"; + case EventStart: return "Start"; + case EventStop: return "Stop"; + default: return NULL; + } +} + +struct SimAvrStatus SimAvr::getStatus () { + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "getStatus() fails caused by mutex error")); + } + struct SimAvrStatus rv = status; + pthread_mutex_unlock(&lock); + return rv; +} + +void SimAvr::printCyclesAndElapsedTime () { + uint64_t time = avr->cycle * 1000000000L / avr->frequency; + uint16_t nanos = time % 1000; time = time / 1000; + uint16_t micros = time % 1000; time = time / 1000; + uint16_t millis = time % 1000; time = time / 1000; + uint16_t seconds = time % 1000; time = time / 1000; + printf("cycle %" PRIu64 " = %ds %03dms %03dµs %03dns", avr->cycle, seconds, millis, micros, nanos); +} + +void SimAvr::avrRun () { + long cnt = 0; + int lastAvrState = -1; + _avr_sp_set(avr, 0); + while (1) { + try { + if (avr->state != lastAvrState) { + switch (avr->state) { + case cpu_Stopped: { + printf("\ncpu stopped at 0x%04x on ", avr->pc); + printCyclesAndElapsedTime(); + char sfr[9]; + sfr[7] = avr->sreg[0] ? 'C' : '-'; + sfr[6] = avr->sreg[1] ? 'Z' : '-'; + sfr[5] = avr->sreg[2] ? 'N' : '-'; + sfr[4] = avr->sreg[3] ? 'V' : '-'; + sfr[3] = avr->sreg[4] ? 'S' : '-'; + sfr[2] = avr->sreg[5] ? 'H' : '-'; + sfr[1] = avr->sreg[6] ? 'T' : '-'; + sfr[0] = avr->sreg[7] ? 'I' : '-'; + sfr[8] = 0; + printf(" SFR: %s\n ", sfr); + uint16_t x = 0, y = 0, z = 0; + for (int i = 0; i < 32; i++) { + uint8_t b = avr->data[i]; + printf(" r%02d=%02x", i, b); + switch (i) { + case 0x0f: printf("\n "); break; + case 0x1a: x = (x & 0xff00) | b; break; + case 0x1b: x = (x & 0x00ff) | (b << 8); break; + case 0x1c: y = (y & 0xff00) | b; break; + case 0x1d: y = (y & 0x00ff) | (b << 8); break; + case 0x1e: z = (z & 0xff00) | b; break; + case 0x1f: z = (z & 0x00ff) | (b << 8); break; + } + } + printf("\n X=0x%04x Y=0x%04x Z=0x%04x\n", x, y, z); + + uint16_t sp = _avr_sp_get(avr); + printf(" Stack (SP=0x%04x)", sp); + int printHeader = 1; + for (int i = 0; i < 16; i++) { + uint16_t addr = sp + 1 + i; + if (addr <= avr->ioend) { continue; } + if (addr > avr->ramend) { break; } + if (printHeader) { + printf(" -> SRAM 0x%04x:", addr); + printHeader = 0; + } + uint8_t b = avr_core_watch_read(avr, addr); + printf(" %02x", b); + + } + printf(printHeader ? "\n" : "\n"); + + printf(" Arduino LED L: "); + printf((avr->data[0x24] & 0x20) && (avr->data[0x25] & 0x20) ? "ON\n" : "OFF\n"); + + printf("\n"); + break; + } + case cpu_Sleeping: printf("cpu enter sleep mode at cycle %" PRIu64 "\n", avr->cycle); break; + case cpu_Running: printf("cpu starts running at cycle %" PRIu64 "\n", avr->cycle); break; + case cpu_Step: printf("cpu step\n"); break; + case cpu_StepDone: printf("cpu step done\n"); break; + case cpu_Done: printf("cpu done\n"); break; + case cpu_Crashed: printf("cpu crashed\n"); break; + default: printf("cpu enter unknown mode at cycle %" PRIu64 "\n", avr->cycle); break; + } + lastAvrState = avr->state; + } + + if (startParameters != NULL) { + switch (startParameters->board) { + case BoardNano: { + static int8_t ledL = -1; // pin floating + uint8_t ddrb = avr->data[0x24]; + uint8_t portb = avr->data[0x25]; + int8_t nextLedL = -1; + if (ddrb & 0x20) { + nextLedL = portb & 0x20 ? 1 : 0; + } + if (nextLedL != ledL) { + ledL = nextLedL; + printCyclesAndElapsedTime(); + printf(": LED L = %c\n", ledL ? 'X' : '.'); + } + break; + } + case BoardSure: { + static int8_t led[4] = { -1, -1, -1, -1 }; // pin floating + uint8_t ddra = avr->data[0x3a]; + uint8_t porta = avr->data[0x3b]; + int change = 0; + for (int i = 0; i < 4; i++) { + int8_t nextLed = -1; + if (ddra & (1 << i)) { + nextLed = porta & (1 << i) ? 0 : 1; + } + if (nextLed != led[i]) { + change = 1; + led[i] = nextLed; + } + } + if (change) { + printCyclesAndElapsedTime(); printf(": "); + printf(" LED PA[3210] ="); + for (int i = 3; i >= 0; i--) { + printf(" %c", led[i] ? 'X' : '.'); + } + printf("\n"); + } + break; + } + case BoardEWS1: { + static int8_t led = -1; // pin floating + uint8_t ddrb = avr->data[0x24]; + uint8_t portb = avr->data[0x25]; + int8_t nextLed = -1; + if (ddrb & 0x01) { + nextLed = portb & 0x01 ? 1 : 0; + } + if (nextLed != led) { + led = nextLed; + printCyclesAndElapsedTime(); + printf(": LED1 (PB0) = %c\n", led ? 'X' : '.'); + } + break; + } + default: break; + } + } + + if (cnt <= 0) { + // usleep(10000); + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (1)")); + } + try { + status.cycle = avr->cycle; + status.state = StateRunning; + status.running = true; + switch (avr->state) { + case cpu_Done: status.state = StateDone; break; + case cpu_Running: status.state = StateRunning; break; + case cpu_Crashed: status.state = StateCrashed; break; + default: status.state = StateOthers; break; + } + if (cancelThread) { + cnt = -1; + cancelThread = false; + } else { + if (syncTime[0] != syncTime[1]) { + syncTime[0] = syncTime[1]; + if (syncTime[0]) { + cyclesOnSyncStart = avr->cycle; + struct timeval tp; + gettimeofday(&tp, NULL); + timeMicrosOnSyncStart = tp.tv_usec + (uint64_t)tp.tv_sec * 1000000L; + } else { + cyclesOnSyncStart = -1; + timeMicrosOnSyncStart = 0; + } + } + if (syncTime[0]) { + uint64_t ucMicros = (uint64_t)(status.cycle - cyclesOnSyncStart) * 1000000L / status.freqHz ; + struct timeval tp; + gettimeofday(&tp, NULL); + const uint64_t timeMicros = tp.tv_usec + (uint64_t)tp.tv_sec * 1000000L - timeMicrosOnSyncStart; + + if (ucMicros > 3000000) { + ucMicros++; + } + + int64_t dt = ucMicros - timeMicros; + if (dt > 0) { + int us = dt > INT_MAX ? INT_MAX : (int)dt; + if (us > 1000) { + timeval ts; + ts.tv_sec = 0; + ts.tv_usec = us; + select(0, 0, 0, 0, &ts); + } + } + } + } + if (avr->state == cpu_Done) { + cnt = -1; + } else if (avr->state == cpu_Crashed) { + throw std::logic_error(error(AT, "avr state is cpu_Crashed")); + } + pthread_mutex_unlock(&lock); + + } catch (std::exception& e) { + status.state = StateError; + pthread_mutex_unlock(&lock); + throw; + } + } + + if (avr->state == cpu_Stopped) { + timeval ts; + ts.tv_sec = 0; + ts.tv_usec = 1000; + select(0, 0, 0, 0, &ts); + } + + if (cnt < 0) { + cyclesOnSyncStart = -1; + timeMicrosOnSyncStart = 0; + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (2)")); + } + try { + if (avrRunThread != NULL) { + avrRunThread->detach(); + delete avrRunThread; + avrRunThread = NULL; + } + pthread_mutex_unlock(&lock); + return; + } catch (std::exception& e) { + pthread_mutex_unlock(&lock); + status.state = StateError; + throw; + } + + } else if (++cnt >= 100000) { + cnt = 0; + } + + if (command != ReadyForNewCommand) { + switch (command) { + case CommandStatus: { + printCyclesAndElapsedTime(); + printf("\n"); + break; + } + case CommandInterrupt: { + if (avr->state == cpu_Running) { + avr->state = cpu_Stopped; + } + break; + } + case CommandContinue: { + if (avr->state == cpu_Stopped) { + avr->state = cpu_Running; + } + break; + } + case CommandStack: { + uint16_t sp = _avr_sp_get(avr); + printf("Stack: SP=0x%04x:\n", sp); + for (uint16_t addr = ((sp + 1) / 16) * 16; addr <= avr->ramend; addr++) { + if (addr % 16 == 0) { + printf(" 0x%04x:", addr); + } + if (addr % 4 == 0) { + printf(" "); + } + if (addr <= sp) { + printf(" "); + } else { + uint8_t b = avr_core_watch_read(avr, addr); + printf(" %02x", b); + } + if (addr % 16 == 15) { + printf("\n"); + } + } + printf("\n"); + break; + } + + default: break; + } + command = ReadyForNewCommand; + addEvent(EventCommandExecuted); + } + + avr_run(avr); + + } catch (std::exception& e) { + status.state = StateError; + cyclesOnSyncStart = -1; + timeMicrosOnSyncStart = 0; + if (pthread_mutex_lock(&lock)) { + throw std::logic_error(error(AT, "avrRun() fails caused by mutex error (2)")); + } + try { + if (avrRunThread != NULL) { + avrRunThread->detach(); + delete avrRunThread; + avrRunThread = NULL; + } + pthread_mutex_unlock(&lock); + throw; + } catch (std::exception& e) { + pthread_mutex_unlock(&lock); + status.state = StateError; + throw; + } + + } + } +} + diff --git a/examples/simuc/src/simavr/simavr.h b/examples/simuc/src/simavr/simavr.h new file mode 100644 index 0000000..e1455b7 --- /dev/null +++ b/examples/simuc/src/simavr/simavr.h @@ -0,0 +1,132 @@ +#ifndef SIMAVR_H +#define SIMAVR_H + +#include "semaphore.h" +#include "parts/uart_pty.h" + +#include +#include +#include + +#include +#include +#include + +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 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