summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
17 files changed, 800 insertions, 0 deletions
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)))