summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2018-12-11 10:09:37 +0100
committerDaniel Friesel <derf@finalrewind.org>2018-12-11 10:09:37 +0100
commitc1027b36455cf47d336d42e2e42a63f096f7e772 (patch)
tree8d4590b6493c73ab9f85d78a9aa384495e103e7b
parent00205e4996df209dc43664af7c171d34c2e97cda (diff)
New architecture: msp430fr5994lp (MSP430FR5994 Launchpad)
Almost exclusively copypasted from msp430fr5969lp. May be replaced by symlinks later on.
-rw-r--r--include/arch/msp430fr5994lp/driver/adc.h17
-rw-r--r--include/arch/msp430fr5994lp/driver/counter.h27
-rw-r--r--include/arch/msp430fr5994lp/driver/gpio.h202
-rw-r--r--include/arch/msp430fr5994lp/driver/i2c.h19
-rw-r--r--include/arch/msp430fr5994lp/driver/spi_a1.h18
-rw-r--r--include/arch/msp430fr5994lp/driver/spi_b.h18
-rw-r--r--include/arch/msp430fr5994lp/driver/stdin.h24
-rw-r--r--include/arch/msp430fr5994lp/driver/stdout.h19
-rw-r--r--include/arch/msp430fr5994lp/driver/timer.h75
-rw-r--r--include/arch/msp430fr5994lp/driver/uptime.h30
-rw-r--r--src/arch/msp430fr5994lp/Makefile.inc107
-rw-r--r--src/arch/msp430fr5994lp/arch.cc172
-rw-r--r--src/arch/msp430fr5994lp/driver/adc.cc71
-rw-r--r--src/arch/msp430fr5994lp/driver/counter.cc7
-rw-r--r--src/arch/msp430fr5994lp/driver/cpufreq.cc24
-rw-r--r--src/arch/msp430fr5994lp/driver/gpio.cc3
-rw-r--r--src/arch/msp430fr5994lp/driver/gpio.su0
-rw-r--r--src/arch/msp430fr5994lp/driver/i2c.cc128
-rw-r--r--src/arch/msp430fr5994lp/driver/spi_a1.cc61
-rw-r--r--src/arch/msp430fr5994lp/driver/spi_b.cc75
-rw-r--r--src/arch/msp430fr5994lp/driver/stdin.cc31
-rw-r--r--src/arch/msp430fr5994lp/driver/stdout.cc61
-rw-r--r--src/arch/msp430fr5994lp/driver/stdout.su5
-rw-r--r--src/arch/msp430fr5994lp/driver/timer.cc3
-rw-r--r--src/arch/msp430fr5994lp/driver/uptime.cc4
-rw-r--r--src/arch/msp430fr5994lp/driver/uptime.su0
-rwxr-xr-xsrc/arch/msp430fr5994lp/model.py48
27 files changed, 1249 insertions, 0 deletions
diff --git a/include/arch/msp430fr5994lp/driver/adc.h b/include/arch/msp430fr5994lp/driver/adc.h
new file mode 100644
index 0000000..d93aed4
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/adc.h
@@ -0,0 +1,17 @@
+#ifndef ADC_H
+#define ADC_H
+
+class ADC {
+ private:
+ ADC(ADC const &copy);
+
+ public:
+ ADC() {}
+
+ float getTemp();
+ float getVCC();
+};
+
+extern ADC adc;
+
+#endif
diff --git a/include/arch/msp430fr5994lp/driver/counter.h b/include/arch/msp430fr5994lp/driver/counter.h
new file mode 100644
index 0000000..e7f0507
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/counter.h
@@ -0,0 +1,27 @@
+#include <msp430.h>
+#include <stdint.h>
+
+class Counter {
+ private:
+ Counter(const Counter &copy);
+
+ public:
+ uint16_t value;
+ uint8_t overflowed;
+
+ Counter() : overflowed(0) {}
+
+ inline void start() {
+ overflowed = 0;
+ TA2CTL = TASSEL__SMCLK | ID__1 | MC__CONTINUOUS;
+ TA2EX0 = 0;
+ TA2CTL |= TACLR;
+ }
+
+ inline void stop() {
+ TA2CTL = 0;
+ value = TA2R;
+ }
+};
+
+extern Counter counter;
diff --git a/include/arch/msp430fr5994lp/driver/gpio.h b/include/arch/msp430fr5994lp/driver/gpio.h
new file mode 100644
index 0000000..ef681db
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/gpio.h
@@ -0,0 +1,202 @@
+#ifndef GPIO_H
+#define GPIO_H
+
+#include <msp430.h>
+
+class GPIO {
+ private:
+ GPIO(const GPIO &copy);
+
+ public:
+ GPIO () {}
+
+ enum Pin : unsigned char {
+ p1_0 = 0, p1_1, p1_2, p1_3, p1_4, p1_5, p1_6, p1_7,
+ p2_0, p2_1, p2_2, p2_3, p2_4, p2_5, p2_6, p2_7,
+ p3_0, p3_1, p3_2, p3_3, p3_4, p3_5, p3_6, p3_7,
+ p4_0, p4_1, p4_2, p4_3, p4_4, p4_5, p4_6, p4_7,
+ p5_0, p5_1, p5_2, p5_3, p5_4, p5_5, p5_6, p5_7,
+ p6_0, p6_1, p6_2, p6_3, p6_4, p6_5, p6_6, p6_7,
+ p7_0, p7_1, p7_2, p7_3, p7_4, p7_5, p7_6, p7_7,
+ p8_0, p8_1, p8_2, p8_3, p8_4, p8_5, p8_6, p8_7,
+ pj_0, pj_1, pj_2, pj_3, pj_4, pj_5, pj_6, pj_7,
+ PIN_INVALID
+ };
+
+ inline void setup() {
+ P1OUT = 0;
+ P2OUT = 0;
+ P3OUT = 0;
+ P4OUT = 0;
+ P5OUT = 0;
+ P6OUT = 0;
+ P7OUT = 0;
+ P8OUT = 0;
+ PJOUT = 0;
+ P1DIR = BIT0 | BIT1 | 0xff; // red LED, green LED
+ P2DIR = 0xff ^ (BIT0 | BIT1); // UART
+ P3DIR = 0xff;
+ P4DIR = 0xff;
+ P5DIR = 0xff;
+ P6DIR = 0xff;
+ P7DIR = 0xff;
+ P8DIR = 0x0f;
+ PJDIR = BIT6 | BIT7; // HFXT (not populated)
+ }
+ inline void led_on(unsigned char id) {
+ if (id == 0) {
+ P1OUT |= BIT0;
+ } else {
+ P1OUT |= BIT1;
+ }
+ }
+ inline void led_off(unsigned char id) {
+ if (id == 0) {
+ P1OUT &= ~BIT0;
+ } else {
+ P1OUT &= ~BIT1;
+ }
+ }
+ inline void led_toggle(unsigned char id) {
+ if (id == 0) {
+ P1OUT ^= BIT0;
+ } else {
+ P1OUT ^= BIT1;
+ }
+ }
+ inline void input(unsigned char const pin) {
+ if (pin < p2_0) {
+ P1DIR &= ~(1 << pin);
+ } else if (pin < p3_0) {
+ P2DIR &= ~(1 << (pin - p2_0));
+ } else if (pin < p4_0) {
+ P3DIR &= ~(1 << (pin - p3_0));
+ } else if (pin < pj_0) {
+ P4DIR &= ~(1 << (pin - p4_0));
+ } else if (pin < PIN_INVALID) {
+ PJDIR &= ~(1 << (pin - pj_0));
+ }
+ }
+ inline void input(unsigned char const pin, unsigned char const pull) {
+ if (pin < p2_0) {
+ P1DIR &= ~(1 << pin);
+ P1OUT = pull ? (P1OUT | (1 << pin)) : (P1OUT & ~(1 << pin));
+ P1REN |= (1 << pin);
+ } else if (pin < p3_0) {
+ P2DIR &= ~(1 << (pin - p2_0));
+ P2OUT = pull ? (P2OUT | (1 << (pin - p2_0))) : (P2OUT & ~(1 << (pin - p2_0)));
+ P2REN |= (1 << (pin - p2_0));
+ } else if (pin < p4_0) {
+ P3DIR &= ~(1 << (pin - p3_0));
+ P3OUT = pull ? (P3OUT | (1 << (pin - p3_0))) : (P3OUT & ~(1 << (pin - p3_0)));
+ P3REN |= (1 << (pin - p3_0));
+ } else if (pin < pj_0) {
+ P4DIR &= ~(1 << (pin - p4_0));
+ P4OUT = pull ? (P4OUT | (1 << (pin - p4_0))) : (P4OUT & ~(1 << (pin - p4_0)));
+ P4REN |= (1 << (pin - p4_0));
+ } else if (pin < PIN_INVALID) {
+ PJDIR &= ~(1 << (pin - pj_0));
+ PJOUT = pull ? (PJOUT | (1 << (pin - pj_0))) : (PJOUT & ~(1 << (pin - pj_0)));
+ PJREN |= (1 << (pin - pj_0));
+ }
+ }
+ inline void output(unsigned char const pin) {
+ if (pin < p2_0) {
+ P1DIR |= (1 << pin);
+ } else if (pin < p3_0) {
+ P2DIR |= (1 << (pin - p2_0));
+ } else if (pin < p4_0) {
+ P3DIR |= (1 << (pin - p3_0));
+ } else if (pin < pj_0) {
+ P4DIR |= (1 << (pin - p4_0));
+ } else if (pin < PIN_INVALID) {
+ PJDIR |= (1 << (pin - pj_0));
+ }
+ }
+ inline void output(unsigned char const pin, unsigned char const value) {
+ if (pin < p2_0) {
+ P1OUT = value ? (P1OUT | (1 << pin)) : (P1OUT & ~(1 << pin));
+ P1DIR |= (1 << pin);
+ } else if (pin < p3_0) {
+ P2OUT = value ? (P2OUT | (1 << (pin - p2_0))) : (P2OUT & ~(1 << (pin - p2_0)));
+ P2DIR |= (1 << (pin - p2_0));
+ } else if (pin < p4_0) {
+ P3OUT = value ? (P3OUT | (1 << (pin - p3_0))) : (P3OUT & ~(1 << (pin - p3_0)));
+ P3DIR |= (1 << (pin - p3_0));
+ } else if (pin < pj_0) {
+ P4OUT = value ? (P4OUT | (1 << (pin - p4_0))) : (P4OUT & ~(1 << (pin - p4_0)));
+ P4DIR |= (1 << (pin - p4_0));
+ } else if (pin < PIN_INVALID) {
+ PJOUT = value ? (PJOUT | (1 << (pin - pj_0))) : (PJOUT & ~(1 << (pin - pj_0)));
+ PJDIR |= (1 << (pin - pj_0));
+ }
+ }
+ inline unsigned char read(unsigned char const pin) {
+ if (pin < p2_0) {
+ return P1IN & (1 << pin);
+ } else if (pin < p3_0) {
+ return P2IN & (1 << (pin - p2_0));
+ } else if (pin < p4_0) {
+ return P3IN & (1 << (pin - p3_0));
+ } else if (pin < pj_0) {
+ return P4IN & (1 << (pin - p4_0));
+ } else if (pin < PIN_INVALID) {
+ return PJIN & (1 << (pin - pj_0));
+ }
+ return 0;
+ }
+ inline void write(unsigned char const pin, unsigned char value) {
+ if (pin < p2_0) {
+ if (value) {
+ P1OUT |= (1 << pin);
+ } else {
+ P1OUT &= ~(1 << pin);
+ }
+ } else if (pin < p3_0) {
+ if (value) {
+ P2OUT |= (1 << (pin - p2_0));
+ } else {
+ P2OUT &= ~(1 << (pin - p2_0));
+ }
+ } else if (pin < p4_0) {
+ if (value) {
+ P3OUT |= (1 << (pin - p3_0));
+ } else {
+ P3OUT &= ~(1 << (pin - p3_0));
+ }
+ } else if (pin < pj_0) {
+ if (value) {
+ P4OUT |= (1 << (pin - p4_0));
+ } else {
+ P4OUT &= ~(1 << (pin - p4_0));
+ }
+ } else if (pin < PIN_INVALID) {
+ if (value) {
+ PJOUT |= (1 << (pin - pj_0));
+ } else {
+ PJOUT &= ~(1 << (pin - pj_0));
+ }
+ }
+ }
+ inline void write_mask(unsigned char const pin_base, unsigned char set_mask, unsigned char clear_mask) {
+ if (pin_base < p2_0) {
+ P1OUT = (P1OUT | set_mask) & ~clear_mask;
+ }
+ if (pin_base < p3_0) {
+ P2OUT = (P2OUT | set_mask) & ~clear_mask;
+ }
+ if (pin_base < p4_0) {
+ P3OUT = (P3OUT | set_mask) & ~clear_mask;
+ }
+ if (pin_base < pj_0) {
+ P4OUT = (P4OUT | set_mask) & ~clear_mask;
+ }
+ if (pin_base < PIN_INVALID) {
+ PJOUT = (PJOUT | set_mask) & ~clear_mask;
+ }
+ }
+};
+
+extern GPIO gpio;
+
+#endif
diff --git a/include/arch/msp430fr5994lp/driver/i2c.h b/include/arch/msp430fr5994lp/driver/i2c.h
new file mode 100644
index 0000000..6d6ea66
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/i2c.h
@@ -0,0 +1,19 @@
+#ifndef I2C_H
+#define I2C_H
+
+class I2C {
+ private:
+ I2C(const I2C &copy);
+
+ public:
+ I2C () {}
+ signed char setup();
+ void scan(unsigned int *results);
+ signed char xmit(unsigned char address,
+ unsigned char tx_len, unsigned char *tx_buf,
+ unsigned char rx_len, unsigned char *rx_buf);
+};
+
+extern I2C i2c;
+
+#endif
diff --git a/include/arch/msp430fr5994lp/driver/spi_a1.h b/include/arch/msp430fr5994lp/driver/spi_a1.h
new file mode 100644
index 0000000..8c593ee
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/spi_a1.h
@@ -0,0 +1,18 @@
+#ifndef SPI_H
+#define SPI_H
+
+class SPI {
+ private:
+ SPI(const SPI &copy);
+
+ public:
+ SPI () {}
+ signed char setup();
+ signed char xmit(
+ unsigned char tx_len, unsigned char *tx_buf,
+ unsigned char rx_len, unsigned char *rx_buf);
+};
+
+extern SPI spi;
+
+#endif
diff --git a/include/arch/msp430fr5994lp/driver/spi_b.h b/include/arch/msp430fr5994lp/driver/spi_b.h
new file mode 100644
index 0000000..4be7346
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/spi_b.h
@@ -0,0 +1,18 @@
+#ifndef SPI_H
+#define SPI_H
+
+class SPI {
+ private:
+ SPI(const SPI &copy);
+
+ public:
+ SPI () {}
+ void setup();
+ signed char xmit(
+ unsigned char tx_len, unsigned char *tx_buf,
+ unsigned char rx_len, unsigned char *rx_buf);
+};
+
+extern SPI spi;
+
+#endif
diff --git a/include/arch/msp430fr5994lp/driver/stdin.h b/include/arch/msp430fr5994lp/driver/stdin.h
new file mode 100644
index 0000000..6462e0c
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/stdin.h
@@ -0,0 +1,24 @@
+#ifndef STANDARDINPUT_H
+#define STANDARDINPUT_H
+
+class StandardInput {
+ private:
+ StandardInput(const StandardInput &copy);
+ char buffer[8];
+ unsigned char write_pos, read_pos;
+
+ public:
+ StandardInput() : write_pos(0), read_pos(0) {}
+ void setup();
+ bool hasKey();
+ char getKey();
+
+ inline void addKey(char key) {
+ buffer[write_pos++] = key;
+ write_pos %= 8;
+ }
+};
+
+extern StandardInput kin;
+
+#endif
diff --git a/include/arch/msp430fr5994lp/driver/stdout.h b/include/arch/msp430fr5994lp/driver/stdout.h
new file mode 100644
index 0000000..2eb669d
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/stdout.h
@@ -0,0 +1,19 @@
+#ifndef STANDARDOUTPUT_H
+#define STANDARDOUTPUT_H
+
+#include "object/outputstream.h"
+
+class StandardOutput : public OutputStream {
+ private:
+ StandardOutput(const StandardOutput &copy);
+
+ public:
+ StandardOutput () {}
+ void setup();
+
+ virtual void put(char c) override;
+};
+
+extern StandardOutput kout;
+
+#endif
diff --git a/include/arch/msp430fr5994lp/driver/timer.h b/include/arch/msp430fr5994lp/driver/timer.h
new file mode 100644
index 0000000..cb37da2
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/timer.h
@@ -0,0 +1,75 @@
+#include <msp430.h>
+#include <stdint.h>
+
+#define ON_TIMER_INTERRUPT_head __attribute__((interrupt(TIMER0_A1_VECTOR))) __attribute__((wakeup)) void handle_timer0_overflow() { if (TA0IV == 0x0e) {
+#define ON_TIMER_INTERRUPT_tail } }
+
+#if F_CPU == 16000000UL
+#define _TA0_MAIN_DIV ID__8
+#elif F_CPU == 8000000UL
+#define _TA0_MAIN_DIV ID__4
+#elif F_CPU == 4000000UL
+#define _TA0_MAIN_DIV ID__2
+#elif F_CPU == 1000000UL
+#define _TA0_MAIN_DIV ID__1
+#else
+#error Unsupported F_CPU
+#endif
+
+class Timer {
+ private:
+ Timer(const Timer &copy);
+
+ public:
+ Timer() {}
+
+#if F_CPU == 1000000UL
+ inline void setup_khz(uint16_t const frequency) {
+ TA0CTL = TASSEL__SMCLK | ID__1; // -> 1 MHz base
+ TA0EX0 = 0;
+ TA0CCR0 = 1000UL / frequency;
+ TA0CTL |= TACLR;
+ }
+
+ inline void setup_hz(uint16_t const frequency) { // 1 MHz base
+ if (frequency < 20) {
+ TA0CTL = TASSEL__SMCLK | ID__8; // /8
+ TA0EX0 = 1; // /2 -> /16 -> 62500 Hz
+ TA0CCR0 = 62500UL / frequency;
+ } else {
+ TA0CTL = TASSEL__SMCLK | ID__1;
+ TA0EX0 = 0;
+ TA0CCR0 = 1000000UL / frequency;
+ }
+ TA0CTL |= TACLR;
+ }
+#else
+ inline void setup_khz(uint16_t const frequency) {
+ TA0CTL = TASSEL__SMCLK | _TA0_MAIN_DIV; // -> 2 MHz base
+ TA0EX0 = 0;
+ TA0CCR0 = 2000UL / frequency;
+ TA0CTL |= TACLR;
+ }
+
+ inline void setup_hz(uint16_t const frequency) { // 2 MHz base
+ TA0CTL = TASSEL__SMCLK | _TA0_MAIN_DIV;
+ TA0EX0 = 0;
+ TA0CCR0 = 2000000UL / frequency;
+ TA0CTL |= TACLR;
+ }
+#endif
+
+ inline void start(unsigned char const interrupt) {
+ if (interrupt) {
+ TA0CTL |= MC__UP | TACLR | TAIE;
+ } else {
+ TA0CTL |= MC__UP | TACLR;
+ }
+ }
+
+ inline void stop() {
+ TA0CTL &= ~MC__UP;
+ }
+};
+
+extern Timer timer;
diff --git a/include/arch/msp430fr5994lp/driver/uptime.h b/include/arch/msp430fr5994lp/driver/uptime.h
new file mode 100644
index 0000000..3a52840
--- /dev/null
+++ b/include/arch/msp430fr5994lp/driver/uptime.h
@@ -0,0 +1,30 @@
+#ifndef UPTIME_H
+#define UPTIME_H
+
+#include <msp430.h>
+#include <stdint.h>
+
+class Uptime {
+ private:
+ Uptime(const Uptime &copy);
+#ifdef TIMER_S
+ uint16_t seconds;
+#endif
+
+ public:
+#ifdef TIMER_S
+ Uptime () : seconds(0) {}
+#else
+ Uptime () {}
+#endif
+ inline uint16_t get_us() { return TA0R; }
+ inline uint16_t get_cycles() { return TA2R; }
+#ifdef TIMER_S
+ inline uint16_t get_s() { return seconds; }
+ inline void tick_s() { seconds++; }
+#endif
+};
+
+extern Uptime uptime;
+
+#endif
diff --git a/src/arch/msp430fr5994lp/Makefile.inc b/src/arch/msp430fr5994lp/Makefile.inc
new file mode 100644
index 0000000..8c78d1b
--- /dev/null
+++ b/src/arch/msp430fr5994lp/Makefile.inc
@@ -0,0 +1,107 @@
+# vim:ft=make
+
+CPU = 430x
+MCU = msp430fr5994
+
+cpu_freq ?= 16000000
+
+INCLUDES += -I/opt/msp430/ti/msp430-gcc-full-linux-5.0.0.36/include
+COMMON_FLAGS += -mcpu=${CPU} -mmcu=${MCU} -DMULTIPASS_ARCH_msp430fr5994lp
+COMMON_FLAGS += -DMULTIPASS_ARCH_HAS_I2C
+
+# LTO seems to be broken.
+
+CC = /opt/msp430/ti/msp430-gcc-full-linux-5.0.0.36/bin/msp430-elf-gcc
+CXX = /opt/msp430/ti/msp430-gcc-full-linux-5.0.0.36/bin/msp430-elf-g++
+OBJCOPY = /opt/msp430/ti/msp430-gcc-full-linux-5.0.0.36/bin/msp430-elf-objcopy
+OBJDUMP = /opt/msp430/ti/msp430-gcc-full-linux-5.0.0.36/bin/msp430-elf-objdump
+
+ARCH_SHORTNAME = msp430
+
+CXX_TARGETS += src/arch/msp430fr5994lp/arch.cc
+
+ifeq (${aspectc}, 1)
+ CXX = ag++ -r build/repo.acp -v 0 --c_compiler /opt/msp430/ti/msp430-gcc-full-linux-5.0.0.36/bin/msp430-elf-g++ -p . --Xcompiler
+endif
+
+ifneq ($(findstring adc,${arch_drivers}), )
+ CXX_TARGETS += src/arch/msp430fr5994lp/driver/adc.cc
+endif
+
+CXX_TARGETS += src/arch/msp430fr5994lp/driver/gpio.cc
+CXX_TARGETS += src/arch/msp430fr5994lp/driver/stdout.cc
+CXX_TARGETS += src/arch/msp430fr5994lp/driver/uptime.cc
+
+ifneq ($(findstring stdin,${arch_drivers}), )
+ CXX_TARGETS += src/arch/msp430fr5994lp/driver/stdin.cc
+endif
+
+ifneq ($(findstring softi2c,${drivers}), )
+else ifneq ($(findstring i2c,${arch_drivers}), )
+ CXX_TARGETS += src/arch/msp430fr5994lp/driver/i2c.cc
+ COMMON_FLAGS += -DDRIVER_I2C
+endif
+
+ifneq ($(findstring spi_a1,${arch_drivers}), )
+ CXX_TARGETS += src/arch/msp430fr5994lp/driver/spi_a1.cc
+endif
+
+ifneq ($(findstring spi_b,${arch_drivers}), )
+ CXX_TARGETS += src/arch/msp430fr5994lp/driver/spi_b.cc
+endif
+
+ifneq ($(findstring timer,${arch_drivers}), )
+ CXX_TARGETS += src/arch/msp430fr5994lp/driver/timer.cc
+endif
+
+ifneq ($(findstring counter,${arch_drivers}), )
+ CXX_TARGETS += src/arch/msp430fr5994lp/driver/counter.cc
+endif
+
+ifneq (${cpu_freq}, )
+ COMMON_FLAGS += -DF_CPU=${cpu_freq}UL
+else
+ COMMON_FLAGS += -DF_CPU=16000000UL
+endif
+
+
+OBJECTS = ${CXX_TARGETS:.cc=.o} ${C_TARGETS:.c=.o}
+
+.cc.o:
+ ${QUIET}${CXX} ${INCLUDES} ${COMMON_FLAGS} ${CXXFLAGS} -c -o $@ ${@:.o=.cc}
+
+.c.o:
+ ${QUIET}${CC} ${INCLUDES} ${COMMON_FLAGS} ${CFLAGS} -c -o $@ ${@:.o=.c}
+
+build/system.elf: ${OBJECTS}
+ ${QUIET}${CXX} ${INCLUDES} ${COMMON_FLAGS} ${CXXFLAGS} \
+ -Wl,--library-path=/opt/msp430/ti/msp430-gcc-full-linux-5.0.0.36/include/ \
+ -Wl,--gc-sections \
+ -o $@ ${OBJECTS}
+
+build/system.hex: build/system.elf
+ ${QUIET}${OBJCOPY} -O ihex ${@:.hex=.elf} $@
+
+program: build/system.hex
+ ${QUIET}LD_LIBRARY_PATH=/home/derf/var/projects/msp430/MSP430Flasher_1.3.15 \
+ /home/derf/var/projects/msp430/MSP430Flasher_1.3.15/MSP430Flasher \
+ -w build/system.hex -v -g -z '[VCC]'
+
+arch_clean:
+ ${QUIET}rm -f ${OBJECTS}
+ ${QUIET}rm -f build/system.hex
+
+monitor:
+ ${QUIET}screen /dev/ttyACM1 115200
+
+arch_help:
+ @echo "msp430fR5994lp specific flags:"
+ @echo " - none -"
+
+arch_info:
+ @echo "CPU Freq: ${cpu_freq} Hz"
+ @echo "Timer Freq: ${timer_freq} Hz -> $(shell src/arch/msp430fr5994lp/model.py f_timer "${cpu_freq}" "${timer_freq}")"
+ @echo "I2C Freq: ${i2c_freq} Hz"
+ @echo "Monitor: /dev/ttyACM1 115200"
+
+.PHONY: arch_clean arch_help arch_info monitor program
diff --git a/src/arch/msp430fr5994lp/arch.cc b/src/arch/msp430fr5994lp/arch.cc
new file mode 100644
index 0000000..1eb34b5
--- /dev/null
+++ b/src/arch/msp430fr5994lp/arch.cc
@@ -0,0 +1,172 @@
+#include "arch.h"
+#include <msp430.h>
+
+void Arch::setup(void)
+{
+ WDTCTL = WDTPW | WDTHOLD;
+
+ PJSEL0 = BIT4 | BIT5;
+
+ PM5CTL0 &= ~LOCKLPM5;
+
+ /*
+ * Note: arch drivers assume SMCLK freq == F_CPU
+ */
+
+#if F_CPU == 16000000UL
+ FRCTL0 = FWPW; // unlock FRAM Control
+ FRCTL0_L = 0x10; // one wait state before FRAM access (required for 8MHz < F_CPU <= 16 MHz)
+ FRCTL0_H = 0xff; // lock FRAM control by writing an invalid password
+
+ // 16MHz DCO
+ CSCTL0_H = CSKEY >> 8;
+ CSCTL1 = DCORSEL | DCOFSEL_4;
+#elif F_CPU == 8000000UL
+ // 8MHz DCO
+ CSCTL0_H = CSKEY >> 8;
+ CSCTL1 = DCOFSEL_6;
+#elif F_CPU == 4000000UL
+ // 8MHz DCO
+ CSCTL0_H = CSKEY >> 8;
+ CSCTL1 = DCOFSEL_3;
+#elif F_CPU == 1000000UL
+ // 8MHz DCO
+ CSCTL0_H = CSKEY >> 8;
+ CSCTL1 = DCOFSEL_0;
+#else
+#error Unsupported F_CPU
+#endif
+
+#ifdef WITH_LOOP
+ CSCTL2 = SELA__LFXTCLK | SELS__DCOCLK | SELM__DCOCLK;
+#else
+ CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;
+#endif
+ CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;
+ CSCTL0_H = 0;
+
+
+#ifdef WITH_LOOP
+ // enable LXFT for RTC
+ CSCTL0_H = CSKEY >> 8;
+ CSCTL4 &= ~LFXTOFF;
+ while (SFRIFG1 & OFIFG) {
+ CSCTL5 &= ~LFXTOFFG;
+ SFRIFG1 &= ~OFIFG;
+ }
+ CSCTL0_H = 0;
+
+ __delay_cycles(1000000);
+#endif
+
+#ifdef TIMER_US
+#if F_CPU == 16000000UL
+ TA0CTL = TASSEL__SMCLK | ID__8 | MC__CONTINUOUS; // /8
+ TA0EX0 = 1; // /2 -> /16
+#elif F_CPU == 8000000UL
+ TA0CTL = TASSEL__SMCLK | ID__8 | MC__CONTINUOUS; // /8
+ TA0EX0 = 0; // /1 -> /8
+#elif F_CPU == 4000000UL
+ TA0CTL = TASSEL__SMCLK | ID__4 | MC__CONTINUOUS; // /4
+ TA0EX0 = 0; // /1 -> /8
+#elif F_CPU == 1000000UL
+ TA0CTL = TASSEL__SMCLK | ID__1 | MC__CONTINUOUS; // /1
+ TA0EX0 = 0; // /1 -> /8
+#else
+#error Unsupported F_CPU
+#endif /* F_CPU */
+ TA0CTL |= TACLR;
+#endif /* TIMER_US */
+
+#if defined(WITH_LOOP) || defined(TIMER_S)
+ // 1s per wakeup for loop. Independent of SMCLK/F_CPU
+ TA1CTL = TASSEL__ACLK | ID__8 | MC__UP;
+ TA1EX0 = 0;
+ TA1CCR0 = 4096;
+ TA1CTL |= TACLR | TAIE;
+#endif
+
+#ifdef TIMER_CYCLES
+ TA2CTL = TASSEL__SMCLK | ID__1 | MC__CONTINUOUS;
+ TA2EX0 = 0;
+ TA2CTL |= TACLR;
+#endif
+}
+
+#ifdef WITH_WAKEUP
+extern void wakeup();
+#endif
+
+#if defined(WITH_LOOP)
+extern void loop();
+volatile char run_loop = 0;
+#endif
+
+void Arch::delay_us(unsigned int const us)
+{
+ if (us < 10) {
+ for (unsigned int i = 0; i < us; i++) {
+ __delay_cycles(F_CPU / 1000000UL);
+ }
+ } else {
+ for (unsigned int i = 0; i < us/10; i++) {
+ __delay_cycles(F_CPU / 100000UL);
+ }
+ }
+}
+void Arch::delay_ms(unsigned int const ms)
+{
+ for (unsigned int i = 0; i < ms; i++) {
+ __delay_cycles(F_CPU / 1000UL);
+ }
+}
+
+void Arch::idle_loop(void)
+{
+ while (1) {
+ asm volatile("nop");
+ __bis_SR_register(GIE | LPM2_bits);
+ asm volatile("nop");
+ __dint();
+#if defined(WITH_LOOP)
+ if (run_loop) {
+ loop();
+ run_loop = 0;
+ }
+#endif
+#ifdef WITH_WAKEUP
+ wakeup();
+#endif
+ }
+}
+
+void Arch::idle(void)
+{
+ asm volatile("nop");
+ __bis_SR_register(GIE | LPM2_bits);
+ asm volatile("nop");
+ __dint();
+#ifdef WITH_WAKEUP
+ wakeup();
+#endif
+}
+
+Arch arch;
+
+#if defined(WITH_LOOP) || defined(TIMER_S)
+
+#include "driver/uptime.h"
+
+__attribute__((interrupt(TIMER1_A1_VECTOR))) __attribute__((wakeup)) void handle_timer1_overflow()
+{
+ if (TA1IV == 0x0e) {
+#ifdef WITH_LOOP
+ run_loop = 1;
+#endif
+#ifdef TIMER_S
+ uptime.tick_s();
+#endif
+ }
+}
+
+#endif /* defined(WITH_LOOP) || defined(TIMER_S) */
diff --git a/src/arch/msp430fr5994lp/driver/adc.cc b/src/arch/msp430fr5994lp/driver/adc.cc
new file mode 100644
index 0000000..5a044a6
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/adc.cc
@@ -0,0 +1,71 @@
+#include "driver/adc.h"
+#include <msp430.h>
+
+#define CALADC12_12V_30C *((unsigned int *)0x1A1A)
+#define CALADC12_12V_85C *((unsigned int *)0x1A1C)
+
+float ADC::getTemp()
+{
+ float ret;
+
+ while(REFCTL0 & REFGENBUSY);
+
+ REFCTL0 = REFVSEL_0 | REFON;
+ ADC12CTL0 &= ~ADC12ENC;
+ ADC12CTL0 = ADC12SHT0_8 | ADC12ON;
+ ADC12CTL1 = ADC12SHP;
+ ADC12CTL3 = ADC12TCMAP;
+ ADC12MCTL0 = ADC12VRSEL_1 | ADC12INCH_30;
+ while(!(REFCTL0 & REFGENRDY));
+
+ ADC12CTL0 |= ADC12ENC;
+ ADC12CTL0 |= ADC12SC;
+ while (ADC12CTL1 & ADC12BUSY);
+
+ ret = (float)((long)ADC12MEM0 - CALADC12_12V_30C) * (85 - 30) /
+ (CALADC12_12V_85C - CALADC12_12V_30C) + 30.0f;
+
+ // Disable ADC
+ ADC12CTL0 &= ~ADC12ENC; // disable any conversion to allow ADC configuration
+ ADC12CTL0 &= ~ADC12ON; // Turn off ADC
+
+ // Disable internal 2V reference
+ while(REFCTL0 & REFGENBUSY);
+ REFCTL0 &= ~REFON;
+
+ return ret;
+}
+
+float ADC::getVCC()
+{
+ float ret;
+
+ while(REFCTL0 & REFGENBUSY);
+
+ REFCTL0 = REFVSEL_1 | REFON;
+ ADC12CTL0 &= ~ADC12ENC;
+ ADC12CTL0 = ADC12SHT0_8 | ADC12ON;
+ ADC12CTL1 = ADC12SHP;
+ ADC12CTL3 = ADC12BATMAP;
+ ADC12MCTL0 = ADC12VRSEL_1 | ADC12INCH_31;
+ while(!(REFCTL0 & REFGENRDY));
+
+ ADC12CTL0 |= ADC12ENC;
+ ADC12CTL0 |= ADC12SC;
+ while (ADC12CTL1 & ADC12BUSY);
+
+ ret = (float)ADC12MEM0 / 4096 * 2 * 2;
+ return ret;
+
+ // Disable ADC
+ ADC12CTL0 &= ~ADC12ENC; // disable any conversion to allow ADC configuration
+ ADC12CTL0 &= ~ADC12ON; // Turn off ADC
+
+ // Disable internal 2V reference
+ while(REFCTL0 & REFGENBUSY);
+ REFCTL0 &= ~REFON;
+
+ return ret;
+}
+
+ADC adc;
diff --git a/src/arch/msp430fr5994lp/driver/counter.cc b/src/arch/msp430fr5994lp/driver/counter.cc
new file mode 100644
index 0000000..62ac778
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/counter.cc
@@ -0,0 +1,7 @@
+#include "driver/counter.h"
+
+#if defined(TIMER_CYCLES)
+#warn "timer_cycles and counter are mutually exclusive. Expect odd behaviour."
+#endif
+
+Counter counter;
diff --git a/src/arch/msp430fr5994lp/driver/cpufreq.cc b/src/arch/msp430fr5994lp/driver/cpufreq.cc
new file mode 100644
index 0000000..55a74b5
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/cpufreq.cc
@@ -0,0 +1,24 @@
+void CPUFreq::set(unsigned int freq_khz)
+{
+ /*
+ * Note: arch drivers assume SMCLK freq == F_CPU
+ */
+
+ if (freq_khz == 16000) {
+ FRCTL0 = FWPW; // unlock FRAM Control
+ FRCTL0_L = 0x10; // one wait state before FRAM access (required for 8MHz < F_CPU <= 16 MHz)
+ FRCTL0_H = 0xff; // lock FRAM control by writing an invalid password
+ }
+
+ CSCTL0_H = CSKEY >> 8;
+ if (freq_khz == 16000) {
+ CSCTL1 = DCORSEL | DCOFSEL_4;
+ } else if (freq_khz == 8000) {
+ CSCTL1 = DCOFSEL_6;
+ } else if (freq_khz == 4000) {
+ CSCTL1 = DCOFSEL_3;
+ } else if (freq_khz == 1000) {
+ CSCTL1 = DCOFSEL_0;
+ }
+ CSCTL0_H = 0;
+}
diff --git a/src/arch/msp430fr5994lp/driver/gpio.cc b/src/arch/msp430fr5994lp/driver/gpio.cc
new file mode 100644
index 0000000..1403aed
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/gpio.cc
@@ -0,0 +1,3 @@
+#include "driver/gpio.h"
+
+GPIO gpio;
diff --git a/src/arch/msp430fr5994lp/driver/gpio.su b/src/arch/msp430fr5994lp/driver/gpio.su
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/gpio.su
diff --git a/src/arch/msp430fr5994lp/driver/i2c.cc b/src/arch/msp430fr5994lp/driver/i2c.cc
new file mode 100644
index 0000000..fe5b37b
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/i2c.cc
@@ -0,0 +1,128 @@
+#include "driver/i2c.h"
+#include "arch.h"
+#include <msp430.h>
+
+#ifndef F_I2C
+#define F_I2C 100000
+#endif
+
+volatile unsigned short old_ifg = 0;
+
+#if (F_CPU / F_I2C) < 45
+inline void await_i2c_int(unsigned int ie_flags, unsigned int ifg_flags) {
+ while (!(UCB0IFG & ifg_flags)) ;
+ if (UCB0IFG & (UCNACKIFG | UCCLTOIFG)) {
+ UCB0IFG &= ~(UCNACKIFG | UCCLTOIFG);
+ }
+}
+#else
+inline void await_i2c_int(unsigned int ie_flags, unsigned int ifg_flags)
+{
+ UCB0IFG = 0;
+ old_ifg = 0;
+ UCB0IE = ie_flags;
+ do {
+ arch.idle();
+ } while (!(old_ifg & ifg_flags));
+ UCB0IE = 0;
+}
+#endif
+
+signed char I2C::setup()
+{
+#ifdef I2C_PULLUP_FIXED_GPIO
+ P1DIR |= BIT4 | BIT5;
+ P1OUT |= BIT4 | BIT5;
+#endif
+ UCB0CTL1 = UCSWRST;
+ UCB0CTLW0 = UCMODE_3 | UCMST | UCSYNC | UCSSEL_2 | UCSWRST | UCCLTO_1;
+ UCB0BRW = (F_CPU / F_I2C) - 1;
+ P1DIR &= ~(BIT6 | BIT7);
+ P1SEL0 &= ~(BIT6 | BIT7);
+ P1SEL1 |= BIT6 | BIT7;
+
+ UCB0CTL1 &= ~UCSWRST;
+ UCB0I2CSA = 0;
+
+ arch.delay_us(100);
+
+ if (UCB0STAT & UCBBUSY)
+ return -1;
+
+ return 0;
+}
+
+void I2C::scan(unsigned int *results)
+{
+ for (unsigned char address = 0; address < 128; address++) {
+ UCB0I2CSA = address;
+ UCB0CTL1 |= UCTR | UCTXSTT | UCTXSTP;
+
+ while (UCB0CTL1 & UCTXSTP);
+
+ if (UCB0IFG & UCNACKIFG) {
+ UCB0IFG &= ~UCNACKIFG;
+ } else {
+ results[address / (8 * sizeof(unsigned int))] |= 1 << (address % (8 * sizeof(unsigned int)));
+ }
+ }
+ UCB0IFG = 0;
+}
+
+signed char I2C::xmit(unsigned char address,
+ unsigned char tx_len, unsigned char *tx_buf,
+ unsigned char rx_len, unsigned char *rx_buf)
+{
+ unsigned char i;
+ UCB0I2CSA = address;
+ if (tx_len) {
+ UCB0CTL1 |= UCTR | UCTXSTT;
+ for (i = 0; i < tx_len; i++) {
+ await_i2c_int(UCTXIE0 | UCNACKIE | UCCLTOIE, UCTXIFG0 | UCNACKIFG | UCCLTOIFG);
+ if (old_ifg & (UCNACKIFG | UCCLTOIFG)) {
+ UCB0CTL1 |= UCTXSTP;
+ return -1;
+ }
+ old_ifg = 0;
+ UCB0TXBUF = tx_buf[i];
+ }
+ await_i2c_int(UCTXIE0 | UCNACKIE | UCCLTOIE, UCTXIFG0 | UCNACKIFG | UCCLTOIFG);
+ //if (UCB0IFG & (UCNACKIFG | UCCLTOIFG)) {
+ // UCB0IFG &= ~UCNACKIFG;
+ // UCB0IFG &= ~UCCLTOIFG;
+ // UCB0CTL1 |= UCTXSTP;
+ // return -1;
+ //}
+ }
+ if (rx_len) {
+ UCB0I2CSA = address;
+ UCB0IFG = 0;
+ UCB0CTL1 &= ~UCTR;
+ UCB0CTL1 |= UCTXSTT;
+
+ while (UCB0CTL1 & UCTXSTT);
+ UCB0IFG &= ~UCTXIFG0;
+
+ for (i = 0; i < rx_len; i++) {
+ if (i == rx_len - 1)
+ UCB0CTL1 |= UCTXSTP;
+ await_i2c_int(UCRXIE | UCNACKIE | UCCLTOIE, UCRXIFG0 | UCNACKIFG | UCCLTOIFG);
+ rx_buf[i] = UCB0RXBUF;
+ UCB0IFG &= ~UCRXIFG0;
+ }
+ UCB0IFG &= ~UCRXIFG0;
+ }
+
+ UCB0CTL1 |= UCTXSTP;
+
+ while (UCB0CTL1 & UCTXSTP);
+ return 0;
+}
+
+__attribute__((interrupt(USCI_B0_VECTOR))) __attribute__((wakeup)) void handle_usci_b0()
+{
+ old_ifg = UCB0IFG;
+ UCB0IFG = 0;
+}
+
+I2C i2c;
diff --git a/src/arch/msp430fr5994lp/driver/spi_a1.cc b/src/arch/msp430fr5994lp/driver/spi_a1.cc
new file mode 100644
index 0000000..981bc53
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/spi_a1.cc
@@ -0,0 +1,61 @@
+#include "driver/spi.h"
+#include <msp430.h>
+
+signed char SPI::setup()
+{
+ /* UCA1CLK Pin 2.4 */
+ P2SEL0 &= ~BIT4;
+ P2SEL1 |= BIT4;
+ P2DIR |= BIT4;
+
+ /* UCA1SIMO Pin 2.5 */
+ P2SEL0 &= ~BIT5;
+ P2SEL1 |= BIT5;
+ P2DIR |= BIT5;
+
+ /* UCA1SOMI Pin 2.6 */
+ P2SEL0 &= ~BIT6;
+ P2SEL1 |= BIT6;
+ P2DIR &= ~BIT6;
+ //P2REN |= BIT6;
+
+ UCA1CTLW0 |= UCSWRST;
+ UCA1MCTLW = 0; // no modulation
+
+ UCA1CTLW0 = UCCKPH | UCMSB | UCMST | UCSYNC | UCMODE_0 | UCSSEL__SMCLK | UCSWRST;
+ UCA1BRW = 15; // /16 -> 1MHz
+ UCA1CTLW0 &= ~UCSWRST;
+}
+
+signed char SPI::xmit(unsigned char tx_len, unsigned char *tx_buf,
+ unsigned char rx_len, unsigned char *rx_buf)
+{
+ volatile char rxbuf_cache;
+ if (tx_len < 1) {
+ return -1;
+ }
+
+ UCA1IE &= ~(UCTXIE | UCRXIE);
+
+ while (UCA1STATW & UCBUSY) ;
+
+ rxbuf_cache = UCA1RXBUF;
+ UCA1TXBUF = tx_buf[0];
+
+ unsigned char tx_pos = 1;
+ unsigned char rx_pos = 0;
+
+ while (tx_pos < tx_len || rx_pos < rx_len) {
+ if ((tx_pos < tx_len) && (UCA1IF & UCTXIFG)) {
+ UCBA1TXBUF = tx_buf[tx_pos++];
+ }
+ if (UCA1IFG & UCRXIFG) {
+ if (rx_pos < rx_len) {
+ rx_buf[rx_pos] = UCA1RXBUF;
+ } else {
+ rxbuf_cache = UCA1RXBUF;
+ }
+ }
+ rx_pos++;
+ }
+}
diff --git a/src/arch/msp430fr5994lp/driver/spi_b.cc b/src/arch/msp430fr5994lp/driver/spi_b.cc
new file mode 100644
index 0000000..0fa71da
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/spi_b.cc
@@ -0,0 +1,75 @@
+#include "driver/spi_b.h"
+#include <msp430.h>
+
+#ifndef F_I2C
+#define F_I2C 1000000UL
+#endif
+
+void SPI::setup()
+{
+ UCB0CTLW0 |= UCSWRST;
+
+ /* UCB0CLK Pin 2.2 */
+ P2SEL0 &= ~BIT2;
+ P2SEL1 |= BIT2;
+ P2DIR |= BIT2;
+
+ /* UCB0SIMO Pin 1.6 */
+ P1SEL0 &= ~BIT6;
+ P1SEL1 |= BIT6;
+ P1DIR |= BIT6;
+
+ /* UCB0SOMI Pin 1.7 */
+ P1SEL0 &= ~BIT7;
+ P1SEL1 |= BIT7;
+ P1DIR &= ~BIT7;
+ //P1REN |= BIT6;
+
+ UCB0CTLW0 = UCCKPH | UCMSB | UCMST | UCSYNC | UCMODE_0 | UCSSEL__SMCLK | UCSWRST;
+ UCB0BRW = (F_CPU/F_I2C)-1; // /16 -> 1MHz
+ // UCB0BRW = (F_CPU / F_I2C) - 1
+ UCB0CTLW0 &= ~UCSWRST;
+}
+
+static inline unsigned char clean_rxbuf()
+{
+ return UCB0RXBUF;
+}
+
+signed char SPI::xmit(unsigned char tx_len, unsigned char *tx_buf,
+ unsigned char rx_len, unsigned char *rx_buf)
+{
+ if (tx_len < 1) {
+ return -1;
+ }
+
+ while (UCB0STATW & UCBUSY) ;
+
+ if (!(UCB0IFG & UCTXIFG)) {
+ return -1;
+ }
+
+ UCB0IFG &= ~UCRXIFG;
+ UCB0TXBUF = tx_buf[0];
+
+ unsigned char tx_pos = 1;
+ unsigned char rx_pos = 0;
+
+ while (tx_pos < tx_len || rx_pos < rx_len) {
+ if ((tx_pos < tx_len) && (UCB0IFG & UCTXIFG)) {
+ UCB0TXBUF = tx_buf[tx_pos++];
+ }
+ if (UCB0IFG & UCRXIFG) {
+ if (rx_pos < rx_len) {
+ rx_buf[rx_pos] = UCB0RXBUF;
+ } else {
+ UCB0IFG &= ~UCRXIFG;
+ }
+ rx_pos++;
+ }
+ }
+ while (UCB0STATW & UCBUSY) ;
+ return 0;
+}
+
+SPI spi;
diff --git a/src/arch/msp430fr5994lp/driver/stdin.cc b/src/arch/msp430fr5994lp/driver/stdin.cc
new file mode 100644
index 0000000..cc6e586
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/stdin.cc
@@ -0,0 +1,31 @@
+#include "driver/stdin.h"
+#include <msp430.h>
+
+void StandardInput::setup()
+{
+ UCA0IE |= UCRXIE;
+}
+
+bool StandardInput::hasKey()
+{
+ if (write_pos != read_pos) {
+ return true;
+ }
+ return false;
+}
+
+char StandardInput::getKey()
+{
+ char ret = buffer[read_pos++];
+ read_pos %= 8;
+ return ret;
+}
+
+StandardInput kin;
+
+__attribute__((interrupt(USCI_A0_VECTOR))) __attribute__((wakeup)) void handle_stdin()
+{
+ if (UCA0IFG & UCRXIFG) {
+ kin.addKey(UCA0RXBUF);
+ }
+}
diff --git a/src/arch/msp430fr5994lp/driver/stdout.cc b/src/arch/msp430fr5994lp/driver/stdout.cc
new file mode 100644
index 0000000..b3e8b4d
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/stdout.cc
@@ -0,0 +1,61 @@
+#include "driver/stdout.h"
+#include <msp430.h>
+
+/*
+ * Baud rate calculation according to datasheet:
+ * N := f_{BRCLK} / Baudrate = F_CPU / 115200 in our case
+ * if N <= 16: OS16 = 0, UCBR0 = int(N)
+ * if N > 16: OS16 = 1, UCBR0 = int(N/16), UCBRF0 = int(((n/16) - int(n/16)) * 16) = int(N)%16
+ * Set UCBRS0 according to table 21-4
+ */
+
+void StandardOutput::setup()
+{
+ UCA0CTLW0 |= UCSWRST;
+#if F_CPU == 16000000UL
+ // 16M / 115200 == 138.88889 -> UCOS16 = 1, UCBR0 = 16, UCBRF0 = 10, UCBRS0 = 0xf7 ("0.8751")
+ UCA0CTLW0 = UCSWRST | UCSSEL__SMCLK;
+ UCA0MCTLW = UCOS16 | (10<<4) | 0xF700;
+ UCA0BR0 = 8;
+#elif F_CPU == 8000000UL
+ // 8M / 115200 == 69.444444 -> UCOS16 = 1, UCBR0 = 4, UCBRF0 = 5, UCBRS0 = 0x55 ("0.4378")
+ UCA0CTLW0 = UCSWRST | UCSSEL__SMCLK;
+ UCA0MCTLW = UCOS16 | (5<<4) | 0x5500;
+ UCA0BR0 = 4;
+#elif F_CPU == 4000000UL
+ // 4M / 115200 == 34.722222 -> UCOS16 = 1, UCBR0 = 2, UCBRF0 = 2, UCBRS0 = 0xbb ("0.7147")
+ UCA0CTLW0 = UCSWRST | UCSSEL__SMCLK;
+ UCA0MCTLW = UCOS16 | (2<<4) | 0xbb00;
+ UCA0BR0 = 2;
+#elif F_CPU == 1000000UL
+ // 1M / 115200 == 8.6805556 -> UCOS16 = 0, UCBR0 = 8, UCBRF0 = 0, UCBRS0 = 0xd6 ("0.6667")
+ UCA0CTLW0 = UCSWRST | UCSSEL__SMCLK;
+ UCA0MCTLW = 0x5500;
+ UCA0BR0 = 8;
+#else
+#error Unsupported F_CPU
+#endif
+
+ UCA0IRCTL = 0;
+ UCA0ABCTL = 0;
+
+ P2SEL0 &= ~(BIT0 | BIT1);
+ P2SEL1 |= BIT0 | BIT1;
+ P2DIR |= BIT0;
+
+ UCA0CTLW0 &= ~UCSWRST;
+
+ //UCA0IE |= UCRXIE;
+}
+
+void StandardOutput::put(char c)
+{
+ while (!(UCA0IFG & UCTXIFG));
+ UCA0TXBUF = c;
+
+ if (c == '\n') {
+ put('\r');
+ }
+}
+
+StandardOutput kout;
diff --git a/src/arch/msp430fr5994lp/driver/stdout.su b/src/arch/msp430fr5994lp/driver/stdout.su
new file mode 100644
index 0000000..671f61b
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/stdout.su
@@ -0,0 +1,5 @@
+outputstream.h:21:15:virtual void OutputStream::write(const char*) 6 static
+outputstream.h:27:15:virtual void OutputStream::flush() 2 static
+stdout.cc:51:6:virtual void StandardOutput::put(char) 2 static
+stdout.cc:12:6:void StandardOutput::setup() 2 static
+stdout.cc:61:20:cc) 2 static
diff --git a/src/arch/msp430fr5994lp/driver/timer.cc b/src/arch/msp430fr5994lp/driver/timer.cc
new file mode 100644
index 0000000..4f2d6d1
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/timer.cc
@@ -0,0 +1,3 @@
+#include "driver/timer.h"
+
+Timer timer;
diff --git a/src/arch/msp430fr5994lp/driver/uptime.cc b/src/arch/msp430fr5994lp/driver/uptime.cc
new file mode 100644
index 0000000..05154f9
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/uptime.cc
@@ -0,0 +1,4 @@
+#include "driver/uptime.h"
+#include <msp430.h>
+
+Uptime uptime;
diff --git a/src/arch/msp430fr5994lp/driver/uptime.su b/src/arch/msp430fr5994lp/driver/uptime.su
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/arch/msp430fr5994lp/driver/uptime.su
diff --git a/src/arch/msp430fr5994lp/model.py b/src/arch/msp430fr5994lp/model.py
new file mode 100755
index 0000000..4747b68
--- /dev/null
+++ b/src/arch/msp430fr5994lp/model.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+
+import numpy as np
+import sys
+
+# include/arch/msp430fr5969lp/driver/timer.h
+def get_timer_frequency(f_cpu, f_timer):
+ if f_cpu == 16000000:
+ ta0_main_div = 8
+ elif f_cpu == 8000000:
+ ta0_main_div = 4
+ elif f_cpu == 4000000:
+ ta0_main_div = 2
+ elif f_cpu == 1000000:
+ ta0_main_div = 1
+ else:
+ raise ValueError("Invalid f_cpu")
+
+ if f_cpu == 1000000:
+ if f_timer >= 1000:
+ divisor = 1
+ counter = 1000 / (f_timer / 1000)
+ elif f_timer >= 20:
+ divisor = 1
+ counter = 1000000 / f_timer
+ else:
+ divisor = 8 * 2
+ counter = 62500 / f_timer
+ else:
+ if f_timer >= 1000:
+ divisor = ta0_main_div * 1
+ counter = 2000 / (f_timer / 1000)
+ else:
+ divisor = ta0_main_div * 1
+ counter = 2000000 / f_timer
+
+ return f_cpu / divisor / int(counter)
+
+module = sys.argv[1]
+
+if module == 'f_timer' and len(sys.argv) == 4:
+ try:
+ f_cpu = int(sys.argv[2])
+ f_timer = int(sys.argv[3])
+ except:
+ sys.exit(0)
+ if f_cpu != 0 and f_timer != 0:
+ print('{:.2f}'.format(get_timer_frequency(f_cpu, f_timer)))