diff --git a/Makefile.rules b/Makefile.rules index 661a6a0..9ac5a0b 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -28,6 +28,9 @@ ALL_CXXFLAGS = $(ALL_CFLAGS) -fno-exceptions LDFLAGS = $(TARGET_LDFLAGS) -Wl,--gc-sections -Wl,-Map=$@.map,--cref +.PRECIOUS: $(TARGETDIR)/%.o + +ifneq ($(OVERRIDE_RULES),1) $(TARGETDIR)/%.o: %.cpp mkdir -p $(TARGETDIR) $(CXX) $(ALL_CXXFLAGS) -c $^ -o $@ @@ -35,6 +38,7 @@ $(TARGETDIR)/%.o: %.cpp $(TARGETDIR)/%.o: %.c mkdir -p $(TARGETDIR) $(CXX) $(ALL_CXXFLAGS) -c $^ -o $@ +endif $(TARGETDIR)/%.s: %.cpp mkdir -p $(TARGETDIR) diff --git a/Makefile.rules.i686-linux-gnu b/Makefile.rules.i686-linux-gnu index 388549c..6da5d8c 100644 --- a/Makefile.rules.i686-linux-gnu +++ b/Makefile.rules.i686-linux-gnu @@ -1,2 +1,4 @@ OBJDUMP = objdump #TARGET_CFLAGS = -S +GCC_VER = -4.8 +TARGET_CFLAGS = -std=c++11 diff --git a/Makefile.rules.msp430-llvm b/Makefile.rules.msp430-llvm new file mode 100644 index 0000000..7936b79 --- /dev/null +++ b/Makefile.rules.msp430-llvm @@ -0,0 +1,30 @@ +LLVM_PATH = /home/pfalcon/projects-3rdparty/llvm-3.0.src/Debug/bin +OPT = $(LLVM_PATH)/opt +LLC = $(LLVM_PATH)/llc +CROSS_COMPILE = msp430- + +MCU = msp430g2553 +TARGET_FLAGS = -mmcu=$(MCU) +# Can select specific device if multiple connected at the same time, +# format "-s " or "-U ", see mspdebug --help +DEVICE_SELECT ?= + +CLANGFLAGS = -ccc-host-triple msp430 -D__MSP430G2553__ -I/usr/msp430/include -m32 -emit-llvm +OPTFLAGS = -std-compile-opts -strip-debug -disable-simplify-libcalls +LLCFLAGS = -march=msp430 + +OVERRIDE_RULES = 1 + +$(TARGETDIR)/%.o: %.cpp + mkdir -p $(TARGETDIR) + clang $(ALL_CXXFLAGS) $(CLANGFLAGS) -S -c $^ -o $@.ll + $(OPT) $(OPTFLAGS) -S $@.ll -f -o $@.opt.ll + + $(LLC) $(LLCFLAGS) $@.opt.ll -o $@.opt.s_ + + awk '/.globl\tmain/ { print "\t.section\t.init9,\"ax\",@progbits"; } \ + { print }' $@.opt.s_ > $@.opt.s + msp430-as $@.opt.s -o $@ + +deploy-%: $(TARGETDIR)/% + mspdebug $(DEVICE_SELECT) rf2500 "prog $^" diff --git a/Makefile.rules.msp430-llvm-c b/Makefile.rules.msp430-llvm-c new file mode 100644 index 0000000..44d242b --- /dev/null +++ b/Makefile.rules.msp430-llvm-c @@ -0,0 +1,33 @@ +LLVM_PATH = /home/pfalcon/projects-3rdparty/llvm-3.0.src/Debug/bin +OPT = $(LLVM_PATH)/opt +LLC = $(LLVM_PATH)/llc +CROSS_COMPILE = msp430- + +MCU = msp430g2553 +TARGET_FLAGS = -mmcu=$(MCU) +# Can select specific device if multiple connected at the same time, +# format "-s " or "-U ", see mspdebug --help +DEVICE_SELECT ?= + +CLANGFLAGS = -ccc-host-triple msp430 -D__MSP430G2553__ -I/usr/msp430/include -m32 -emit-llvm +OPTFLAGS = -std-compile-opts -strip-debug -disable-simplify-libcalls +LLCFLAGS = -march=msp430 + +OVERRIDE_RULES = 1 + +$(TARGETDIR)/%.o: %.cpp + mkdir -p $(TARGETDIR) + clang $(ALL_CXXFLAGS) $(CLANGFLAGS) -S -c $^ -o $@.ll + $(OPT) $(OPTFLAGS) -S $@.ll -f -o $@.opt.ll + + $(LLC) -march=c $@.opt.ll -o $@.cbackend.c + clang $(ALL_CXXFLAGS) $(CLANGFLAGS) -S -c $@.cbackend.c -o $@.cbackend.ll + $(OPT) $(OPTFLAGS) $@.cbackend.ll -f -S -o $@.cbackend.opt.ll + $(LLC) $(LLCFLAGS) $@.cbackend.opt.ll -o $@.opt.s_ + + awk '/.globl\tmain/ { print "\t.section\t.init9,\"ax\",@progbits"; } \ + { print }' $@.opt.s_ > $@.opt.s + msp430-as $@.opt.s -o $@ + +deploy-%: $(TARGETDIR)/% + mspdebug $(DEVICE_SELECT) rf2500 "prog $^" diff --git a/docs/RFCs_and_Best_Practices.markdown b/docs/RFCs_and_Best_Practices.markdown new file mode 100644 index 0000000..b431f70 --- /dev/null +++ b/docs/RFCs_and_Best_Practices.markdown @@ -0,0 +1,101 @@ +RFCs and Best Practices +======================= + +This document discusses various topics and usage patterns of PTL. As +PTL is work in progress, different sections have different status: +some of them describe fully implemented, proven, and recommended +solutions (Best Practices), some just provide some thoughts or +speculations on some topic, waiting for more elaboration, feedback, +and actual implementation. + +Pin Function Multiplexing aka Pin Alternative Functions aka Pinmux aka Altfunc +----------------------------------------------------------------------------- +Currently, PTL lacks abstraction for setting a particular pin to one +of the functions as available in specific hardware. It would be nice +to provide some abstraction, but the main point about altfunc settings +is that they should never be done in "application" code. Instead, +specific implementation of an abtract MCU block should set needed altfunc +for pins in its init() method. So, altfunc handling is private interface +of a particular hardware implementation. Again, it would be nice to +make it consistent across implementations, but that's lower priority +than some other tasks. + +If you would like to see examples how PTL handles this so far, look +at `include/msp430/spi_msp430.hpp` and `include/tm4/uart_tm4.hpp`. + + +RFC: Time Multiplexing of Pin Functions/Setup +--------------------------------------------- + +Sometimes, it is required to use different functions on the same pins at +different times. A perhaps stretched example is that on SPI pins you +have a LED and a button. Most of the time the pins handle LED and button, +but sometimes you switch them to SPI mode to do transfers to another device, +with the idea that a transfer is very short, so user won't see LED blinking, +and you just hope a user won't press a buttom during the transfer (yes, that's +why example is stretched). Another, more realistic example is that two +or more devices share same SPI bus (but different chip selects obviously), +but they need different speed settings. + +As PTL aim is efficiency, it doesn't support limited-scope solutions to +do that "automatically". PTL just doesn't know when specific settings +should be used and for what duration. For example, if it allowed kind +of "event handlers" for SPI byte transfer, to allow to set up particular +settings before transfer, and then undo them after, then there would be +lot of unneeded flip-flopping for a block transfer. Apply them on block +transfer level? Then byte transfers won't be covered. Let them apply +to both (it already gets messy), then what if you need few transfers +in row? So, again, PTL can't know access patterns for pins/blocks, only +you may know. So, PTL avoids "Inversion of Control" pattern in this case, +and the way to handle this is to seprate such transfers into complete +high-level transactions, and apply needed settings explicitly to such +high-level transactions. Of course, they rather be represented as classes +for encapsulation and reuse: + + +Case 1: + +~~~~ +template +class DeviceProtocol { +public: + static void transact_foo() { + spi::setup_pins(); + spi::read(); + spi::write_block(); + spi::read_block(); + ... + spi::release_pins(); + } +}; +~~~~ + +Case 2: +~~~~ +template +class Device1Protocol { +public: + static void transact_foo() { + spi::speed(100 KHz); + device1cs::assert(); + spi::read(); + ... + device1cs::deassert(); + } +}; + +template +class Device2Protocol { +public: + static void transact_bar() { + spi::speed(8 MHz); + device2cs::assert(); + spi::read(); + ... + device2cs::deassert(); + } +}; +~~~~ + +Note: spi setup_pins()/release_pins()/speed() methods are given as example +and may need to be added on actual demand for the scenarios described. diff --git a/docs/alt_metaprog_example.txt b/docs/alt_metaprog_example.txt new file mode 100644 index 0000000..02a5cb7 --- /dev/null +++ b/docs/alt_metaprog_example.txt @@ -0,0 +1,35 @@ +ParBus<[Pin, Pin]> bus; + +def pins_same_port(pins): + port = pins[0].port + for p in pins[1:]: + if p.port != port: + return False + return True + +def consecutive_pins(pins): + if not pins_same_port(pins): + return False + bit = pins[0].bit.shift + for p in pins[1:]: + bit -= 1 + if p.bit.shift != bit: + return False + return True + +@constraint(pins_same_port, consecutive_pins) +@let shift = bus.pins[-1].bit.shift +@let mask = (1 << len(bus.pins)) - 1 +void bus::set(int val) +{ + bus::port::set_masked(val << shift, mask << shift); +} + +@default +void bus::write(int val) +{ +@for p in reverse(bus::pins): + p::set(v & 1); + v >>= 1; +@endfor +} diff --git a/docs/contributing.txt b/docs/contributing.txt index e906f57..d63aa88 100644 --- a/docs/contributing.txt +++ b/docs/contributing.txt @@ -1,5 +1,9 @@ Peripheral Template Library is currently licensed under the terms of LGPLv3, -though it's consider to either add linking exception clause or switch to +though it's considered to either add linking exception clause or switch to BSD-like license. To facilitate such change in the future, only BSD/MIT -licensed third-party code is merged. If you have suggestion regarding -licensing, please feel free to bring them up. +licensed third-party code is merged, and contributors are asked to license +their patches under dual LGPL3/BSD license (or alternatively, just under BSD +license). + +If you have suggestions regarding licensing, please feel free to bring them +up. diff --git a/examples/Makefile b/examples/Makefile index ed53414..c9375da 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -6,7 +6,7 @@ ALL = blink blink_static blink_dynamic blink_timer blink_ticks \ uart_echo uart_echo_irq uart_echo_async \ uart_printf \ spi i2c_24cxx 1wire adc \ - timer_irq timer_irq_dispatch irq_dispatch \ + timer_irq timer_irq_dispatch irq_dispatch irq_dispatch_simple \ flash \ perf_counter lcd lcd_nokia \ msp430_parse_tlv @@ -32,6 +32,7 @@ i2c_24cxx: $(TARGETDIR)/i2c_24cxx adc: $(TARGETDIR)/adc timer_irq: $(TARGETDIR)/timer_irq timer_irq_dispatch: $(TARGETDIR)/timer_irq_dispatch +irq_dispatch_simple: $(TARGETDIR)/irq_dispatch_simple irq_dispatch: $(TARGETDIR)/irq_dispatch flash: $(TARGETDIR)/flash perf_counter: $(TARGETDIR)/perf_counter @@ -74,3 +75,6 @@ $(TARGETDIR)/adc.o: adc.cpp $(TARGETDIR)/lcd: $(TARGETDIR)/lcd.o $(TARGETDIR)/lcd.o: lcd.cpp + +$(TARGETDIR)/irq_dispatch: $(TARGETDIR)/irq_dispatch.o +$(TARGETDIR)/irq_dispatch_simple: $(TARGETDIR)/irq_dispatch_simple.o diff --git a/examples/hw_config_stm32.hpp b/examples/hw_config_stm32.hpp new file mode 100644 index 0000000..53081a1 --- /dev/null +++ b/examples/hw_config_stm32.hpp @@ -0,0 +1 @@ +using namespace PTL; diff --git a/examples/hw_config_lm4.hpp b/examples/hw_config_tm4.hpp similarity index 75% rename from examples/hw_config_lm4.hpp rename to examples/hw_config_tm4.hpp index 713eb7d..e87891e 100644 --- a/examples/hw_config_lm4.hpp +++ b/examples/hw_config_tm4.hpp @@ -1,3 +1,5 @@ +#include + using namespace PTL; typedef UART<16 MHZ, 115200, UART0_> uart; diff --git a/examples/irq_dispatch.cpp b/examples/irq_dispatch.cpp index 016752a..0db73d4 100644 --- a/examples/irq_dispatch.cpp +++ b/examples/irq_dispatch.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include HW_CONFIG @@ -35,9 +35,7 @@ class myspi : public spi } }; -// Explicit instantiation of IRQ dispatcher class -template class IrqDispatch; - +IRQ_DISPATCH(mytimer, myuart, myspi); int main() { diff --git a/examples/irq_dispatch_simple.cpp b/examples/irq_dispatch_simple.cpp new file mode 100644 index 0000000..5fce1f4 --- /dev/null +++ b/examples/irq_dispatch_simple.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +#include HW_CONFIG + +class mytimer : public timer +{ +public: + static void irq_reset() + { + board::LED::toggle(); + } +}; + +IRQ_DISPATCH(mytimer); + +int main() +{ + cpu::init(cpu::DEFAULT); + board::LED::port::enable(); + board::LED::output(); + mytimer::free_run(); + mytimer::enable_irq(); + cpu::enable_irq(); + while (true); +} diff --git a/include/algo/nearptr.hpp b/include/algo/nearptr.hpp new file mode 100644 index 0000000..907adae --- /dev/null +++ b/include/algo/nearptr.hpp @@ -0,0 +1,77 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * 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 . + */ +#ifndef _NEAR_PTR_HPP +#define _NEAR_PTR_HPP +// As C++ doesn't support overloading templates by parameters, we should name +// all templates differently. Also, "const char *" template parameter cannot +// be cast to int. So, we have 3 templates with the same implementation, +// difgering only in type of base template parameter. + +#define IMPL(name) \ +public: \ + storage_t off; \ + name() { off = 0; } \ + name(T *p) { off = ((int)p - (int)base) >> align; } \ + T& operator*() const { return *(T*)((int)base + (off << align)); } \ + operator T*() const { return (T*)((int)base + (off << align)); } + +// NearPtr against absolute base. This is what would be used on +// MCU platform. +// Params: +// base - base of the memoty region within which pointer will point +// storage_t - size of pointer value (uint8_t, uint16_t, etc.); can be +// signed type too if needed +// T - type to point to +// align - alignment of pointer, as power of 2 +// For example, 16-bit NearPtr with alignment of 16 can address 1MB of memory +template class AbsNearPtr +{ + IMPL(AbsNearPtr) +}; + +// NearPtr against statically allocated array (note: must have external +// linkage, i.e. defined globally w/o static keyword). +template class ArrayNearPtr +{ + IMPL(ArrayNearPtr) +}; + +// NearPtr against dynamic base store in a variable, for example, received +// from malloc(). +template class VarNearPtr +{ + IMPL(VarNearPtr) +}; + +#if 0 +// Example usage: + +const int base1 = 0x20000000; +char base2[256] = "foobar"; +char *base3 = (char*)malloc(256); + +template using Ptr1 = AbsNearPtr; +template using Ptr2 = ArrayNearPtr; +template using Ptr3 = VarNearPtr; + +Ptr2 p = &base2[3]; +printf("%c", *p); // Produces "b" +#endif + +#endif // _NEAR_PTR_HPP diff --git a/include/cortex-m/cpu_cortexm.hpp b/include/cortex-m/cpu_cortexm.hpp index e9a7314..587d53f 100644 --- a/include/cortex-m/cpu_cortexm.hpp +++ b/include/cortex-m/cpu_cortexm.hpp @@ -16,6 +16,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ +#ifndef _CPU_CORTEXM_HPP +#define _CPU_CORTEXM_HPP + #include #include // Declare Cortex-M IRQ handlers @@ -25,6 +28,9 @@ namespace PTL { class CortexMCPU {}; +// Various blocks of MCU +enum { CORTEXM_NONE, CORTEXM_SYSTICK }; + template <> class CPU : public ICPU { @@ -40,3 +46,5 @@ class CPU : public ICPU }; } // namespace + +#endif // _CPU_CORTEXM_HPP diff --git a/include/cortex-m/irq_dispatch_cortexm.hpp b/include/cortex-m/irq_dispatch_cortexm.hpp new file mode 100644 index 0000000..8f9a4fd --- /dev/null +++ b/include/cortex-m/irq_dispatch_cortexm.hpp @@ -0,0 +1,66 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * 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 . + */ +#ifndef _IRQ_DISPATCH_CORTEXM_HPP +#define _IRQ_DISPATCH_CORTEXM_HPP + +#include + +namespace PTL { + +/** + ** Device-specific classes and macros for interrupt dispatching + **/ + +/* Implementation of function which will dispatch harware IRQ to all + blocks which have corresponding PTL handler. + What to change: name, condition, implementation. */ +IRQ_DISPATCH_HELPER(do_systick, block::block_type == CORTEXM_SYSTICK) +{ + block::irq_reset(); +} + +/* Class which encapsulates all hardware -> PTL dispatchers. Mostly + used to allow variable list of blocks to dispatch to. */ +template +class IrqDispatch +{ +public: + HANDLER(SysTick_, do_systick); +}; + +/* Dispatch IRQ from MCU and compiler specific "IRQ vector" function. This + is expected to be fully optimized out by inlining. + What to change: name/attributes of function defined. */ +#define CALL_HANDLER(dispatcher, vector) \ + void vector##Handler() \ + { \ + dispatcher::vector##Handler(); \ + } + +/* Macros to dispatch from MCU/compiler specific "IRQ vector function" + to IrqDispatch class. The latter should be passed in as a param. */ +#define IRQ_DISPATCH(blocks...) \ + template class IrqDispatch; \ + typedef IrqDispatch _irq_dispatch; \ + CALL_HANDLER(_irq_dispatch, SysTick_); + + +} // namespace + +#endif // _IRQ_DISPATCH_CORTEXM_HPP diff --git a/include/cortex-m/timer_cortexm_systick.hpp b/include/cortex-m/timer_cortexm_systick.hpp index 8c678dd..e8a3e5b 100644 --- a/include/cortex-m/timer_cortexm_systick.hpp +++ b/include/cortex-m/timer_cortexm_systick.hpp @@ -20,6 +20,7 @@ #define _TIMER_CORTEXM_SYSTICK_HPP #include +#include #include namespace PTL { @@ -27,19 +28,22 @@ namespace PTL { class CSysTick : public ITimer { public: + const static int block_type = CORTEXM_SYSTICK; typedef uint32_t width; static width value() { return SysTick->VAL; } static void free_run() { SysTick->LOAD = 0xffffff; - // Set CLKSOURCE to get same clock as CPU core. Clock in case - // CLKSOURCE unset is implementation-dependent actually. + // Set CLKSOURCE to get same clock as CPU core. In case CLKSOURCE + // is unset, the clock used is implementation-dependent. SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; } static void enable_irq() { SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; } static void disable_irq() { SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; } + + static void irq_reset() {} }; typedef CSysTick timer; diff --git a/include/cpu.hpp b/include/cpu.hpp index 0964347..26f8e0c 100644 --- a/include/cpu.hpp +++ b/include/cpu.hpp @@ -24,6 +24,8 @@ #include #elif defined(__EFM32__) #include +#elif defined(__TM4__) +#include #elif defined(__thumb__) || defined(__thumb2__) #include namespace PTL { diff --git a/include/irq_dispatch.hpp b/include/irq_dispatch.hpp new file mode 100644 index 0000000..39ff482 --- /dev/null +++ b/include/irq_dispatch.hpp @@ -0,0 +1,25 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * 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 . + */ +#ifdef __MSP430__ +#include +#elif defined(__ARM_ARCH_7M__) +#include +#else +#error Unknown platform in irq_dispatch.hpp +#endif diff --git a/include/irq_dispatch_base.hpp b/include/irq_dispatch_base.hpp new file mode 100644 index 0000000..840250f --- /dev/null +++ b/include/irq_dispatch_base.hpp @@ -0,0 +1,79 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * 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 . + */ +#ifndef _IRQ_DISPATCH_BASE_HPP +#define _IRQ_DISPATCH_BASE_HPP + +namespace PTL { + +/** + ** Device-independent helper macros/classes for interrupt dispatching + **/ + +/* Null MCU device block, used as a placeholder. */ +class NullBlock +{ +public: + const static int block_type = 0; +}; + +/* + * We want to declare a function whose body will be executed only + * when (static) condition is true. We have to use metaprog conditionals + * here, because if condition is false, func body may not compile at all + * (for example because class it opeartes on lacks specific methods). + * So, we use 2 complementary implementations of func: one with empty + * body for case of condition == false, and one with the given body + * in case it's true. All is wrapped with macros for readability. + */ + +#define IRQ_DISPATCH_HELPER(func_name, cond) \ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(!(cond))>::type* = 0) {} \ +\ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(cond)>::type* = 0) \ + +/* Apply templated function to list of template args. Note: this and other similar + funcs depend on particular number of arguments passed and must be updated to + pass more. */ +#define APPLY_TO_ALL(func, b1, b2, b3) { func(); func(); func(); } +/* Apply templated function to list of template args, also passing (runtime) argument + to a function. */ +#define APPLY_TO_ALL_ARG(func, arg, b1, b2, b3) { func(arg); func(arg); func(arg); } + +/* Define handler function for particular hardware IRQ. */ +#define HANDLER(vector, dispatch_func) \ + static void vector##Handler() \ + { \ + APPLY_TO_ALL(dispatch_func, b1, b2, b3); \ + } + +/* Define handler function for particular hardware IRQ, caching value + of a register allowing to demux multiplexed IRQs. This cached value + is passed to each PTL handler. */ +#define HANDLER_CACHE_REG(vector, dispatch_func, reg) \ + static void vector##Handler() \ + { \ + uint8_t cache = reg; \ + APPLY_TO_ALL_ARG(dispatch_func, cache, b1, b2, b3); \ + } + +} // namespace + +#endif // _IRQ_DISPATCH_BASE_HPP diff --git a/include/msp430/delay_static_msp430.hpp b/include/msp430/delay_static_msp430.hpp index bd689f3..628ca69 100644 --- a/include/msp430/delay_static_msp430.hpp +++ b/include/msp430/delay_static_msp430.hpp @@ -72,6 +72,7 @@ static ALWAYS_INLINE void __delay_cycles2(long delay) quot = (delay - 4) / 4 - 1; // cycles stops on 0 -> -1 transition, i.e. 1 extra iteration rem = (delay - 4) % 4; +#ifndef __clang__ asm( "mov %A0, r14 \n" "mov %B0, r15 \n" @@ -81,7 +82,17 @@ static ALWAYS_INLINE void __delay_cycles2(long delay) "jc 1b \n" // 2 cycles, msp430 has weird C flag value for substracts : : "ir" (quot) : "r14", "r15" ); - +#else + asm( + "mov %0, r14 \n" + "mov %1, r15 \n" + "1: \n" + "sub #1, r14 \n" // 1 cycle + "subc #0, r15 \n" // 1 cycle + "jc 1b \n" // 2 cycles, msp430 has weird C flag value for substracts + : : "ir" ((uint16_t)(quot & 0xffff)), "ir" ((uint16_t)(quot >> 16)) : "r14", "r15" + ); +#endif // It might be possible to optimize these, but it's boring uint16_t low = (uint16_t)quot; uint16_t hiw = quot >> 16; diff --git a/include/msp430/irq_dispatch_msp430.hpp b/include/msp430/irq_dispatch_msp430.hpp index 2ae535e..58906e7 100644 --- a/include/msp430/irq_dispatch_msp430.hpp +++ b/include/msp430/irq_dispatch_msp430.hpp @@ -22,32 +22,12 @@ #include // interrupt(X) define. TODO: get rid of? #include +#include namespace PTL { -class NullBlock -{ -public: - const static int block_type = 0; -}; - -/* - * We want to declare a function here whose body will be executed only - * when condition is true. We have to use metaprog conditionals here, - * because if condition is false, func body may not compile at all - * (for example because class it opeartes on lacks specific methods). - * So, we use 2 complementary implementations of func: one with empty - * body for case of condition == false, and one with the given body - * in case it's true. All is wrapped with macros for readability. - */ - -#define IRQ_DISPATCH_HELPER(func_name, cond) \ -template \ -inline void func_name(uint8_t val = 0, typename meta::enable_if<(!(cond))>::type* = 0) {} \ -\ -template \ -inline void func_name(uint8_t val = 0, typename meta::enable_if<(cond)>::type* = 0) \ - +/* Functions to dispatch specific hardware IRQ down to a specific + MCU block and its PTL irq events. */ IRQ_DISPATCH_HELPER(do_timer_main, block::block_type == MSP430_TIMER) { @@ -94,23 +74,8 @@ IRQ_DISPATCH_HELPER(do_usci_tx, block::block_type == MSP430_USCI) block::irq_tx(); } - -#define APPLY_TO_ALL(func, b1, b2, b3) { func(); func(); func(); } -#define APPLY_TO_ALL_ARG(func, arg, b1, b2, b3) { func(arg); func(arg); func(arg); } - -#define HANDLER(vector, dispatch_func) \ - static interrupt(vector) vector##_handler() \ - { \ - APPLY_TO_ALL(dispatch_func, b1, b2, b3); \ - } - -#define HANDLER_CACHE_REG(vector, dispatch_func, reg) \ - static interrupt(vector) vector##_handler() \ - { \ - uint8_t cache = reg; \ - APPLY_TO_ALL_ARG(dispatch_func, cache, b1, b2, b3); \ - } - +/* Class which encapsulates all hardware -> PTL dispatchers. Mostly + used to allow variable list of blocks to dispatch to. */ template class IrqDispatch { @@ -121,6 +86,30 @@ class IrqDispatch HANDLER_CACHE_REG(USCIAB0TX_VECTOR, do_usci_tx, IFG2); }; + +/* Dispatch IRQ from MCU and compiler specific "IRQ vector" function. This + is expected to be fully optimized out by inlining. + What to change when porting: name/attributes of function defined. */ +#define CALL_HANDLER(dispatcher, vector) \ + interrupt(vector) vector##Handler() \ + { \ + dispatcher::vector##Handler(); \ + } + +/* Global IRQ dispatch declaration macro - that's what user uses in the + application code. Note that there can be at most one IRQ_DISPATCH + per application. Specifying all irq-receiving classes at once allows + to suffle/sort/group irq handlers to produce optimal code - that's + what all fucntions/classes/macros above do actually. + What to change when porting: list of CALL_HANDLER calls. */ +#define IRQ_DISPATCH(blocks...) \ + template class IrqDispatch; \ + typedef IrqDispatch _irq_dispatch; \ + CALL_HANDLER(_irq_dispatch, TIMER0_A1_VECTOR); \ + CALL_HANDLER(_irq_dispatch, TIMER0_A0_VECTOR); \ + CALL_HANDLER(_irq_dispatch, USCIAB0RX_VECTOR); \ + CALL_HANDLER(_irq_dispatch, USCIAB0TX_VECTOR); + } // namespace #endif // _IRQ_DISPATCH_MSP430_HPP diff --git a/include/msp430/spi_msp430.hpp b/include/msp430/spi_msp430.hpp index 4612bd2..6ce9079 100644 --- a/include/msp430/spi_msp430.hpp +++ b/include/msp430/spi_msp430.hpp @@ -50,6 +50,7 @@ class SPI : public ISPI, public SPIBlockXfer< SPI >, public MSP4 UCB0BR0 = 1; UCB0BR1 = 0; + // Set SPI altfuncs for pins P1SEL |= SCLK | SDI | SDO; P1SEL2 |= SCLK | SDI | SDO; // Per MSP430 manual, direction of SCLK/MOSI/MISO is controlled @@ -60,6 +61,7 @@ class SPI : public ISPI, public SPIBlockXfer< SPI >, public MSP4 static void disable() { UCB0CTL1 = UCSWRST; + // Unset SPI altfuncs, back to GPIO mode P1SEL &= ~(SCLK | SDI | SDO); P1SEL2 &= ~(SCLK | SDI | SDO); } diff --git a/include/rtos/circular.hpp b/include/rtos/circular.hpp index 502303c..05a1482 100644 --- a/include/rtos/circular.hpp +++ b/include/rtos/circular.hpp @@ -102,6 +102,11 @@ class CircularBuffer return 0; return pop(); } + + /* Read/write interface */ + void write(elem b) { push_checked(b); } + // Checking that buffer contains enough elements is on the caller + elem read() { return pop(); } }; } // namespace diff --git a/include/tm4/cpu_tm4.hpp b/include/tm4/cpu_tm4.hpp new file mode 100644 index 0000000..a0037fb --- /dev/null +++ b/include/tm4/cpu_tm4.hpp @@ -0,0 +1,35 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012 Paul Sokolovsky + * + * 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 . + */ +#pragma once +#include + +namespace PTL { + +class TM4CPU {}; + +enum { TM4_UART }; + +template <> +class CPU : public CPU +{ +}; + +typedef CPU cpu; + +} // namespace diff --git a/include/tm4/uart_tm4.hpp b/include/tm4/uart_tm4.hpp index 9c3f8ca..7657298 100644 --- a/include/tm4/uart_tm4.hpp +++ b/include/tm4/uart_tm4.hpp @@ -20,6 +20,7 @@ #define _UART_LM4_HPP #include +#include namespace PTL { @@ -35,6 +36,8 @@ class UART typedef Pin txpin; public: + const static int block_type = TM4_UART; + static void init() { SYSCTL->RCGCUART |= Bit0::value; diff --git a/tests/Makefile b/tests/Makefile index 259cb08..b8037f1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,7 +1,7 @@ PTL_PATH = .. include $(PTL_PATH)/Makefile.rules -ALL = sub_mod circular +ALL = sub_mod circular nearptr .PHONY: $(ALL) .PRECIOUS: $(TARGETDIR)/%.o @@ -10,3 +10,4 @@ all: $(ALL) sub_mod: $(TARGETDIR)/sub_mod circular: $(TARGETDIR)/circular +nearptr: $(TARGETDIR)/nearptr diff --git a/tests/nearptr.cpp b/tests/nearptr.cpp new file mode 100644 index 0000000..6812f2a --- /dev/null +++ b/tests/nearptr.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +const int base1 = 0x20000000; +char base2[256] = "foobar"; +char *base3 = (char*)malloc(256); + +//template using Ptr = AbsNearPtr; +template using Ptr = ArrayNearPtr; +//template using Ptr = VarNearPtr; + +void func(Ptr p) +{ + assert(sizeof(p) == 2); + assert((char*)p == base2 + 3); + assert(*p == 'b'); + assert(p[1] == 'a'); +} + +int main() +{ + Ptr p = &base2[3]; + assert(sizeof(p) == 2); + // Pass into a function to avoid inlining optimizations + func(p); +}