diff --git a/arm/app.ld b/arm/app.ld index aff330d..3820254 100644 --- a/arm/app.ld +++ b/arm/app.ld @@ -7,9 +7,12 @@ SECTIONS { .text : { + _text_begin = .; KEEP(*(.app_init)) *(.text*) + _text_end = .; + *(.rodata*) } > ICTM @@ -30,5 +33,8 @@ SECTIONS __exidx_end = .; } > ICTM + _heap_begin = .; + _initial_stack_pointer = 16384; + _heap_end = _initial_stack_pointer - 1024; } diff --git a/arm/async.cc b/arm/async.cc new file mode 100644 index 0000000..54307fd --- /dev/null +++ b/arm/async.cc @@ -0,0 +1,132 @@ +#include "async.h" + +#include +#include +#include + +namespace async { +namespace { + +using namespace std::literals::chrono_literals; + +struct Stuff { + std::coroutine_handle<> h; + std::chrono::system_clock::time_point expiration; + + Stuff* next; +}; + +std::atomic work; +std::array, static_cast(AwaitableType::kNumTypes)> + queues; + +} // namespace + +void schedule(std::coroutine_handle<> h, int ms) { + std::chrono::system_clock::time_point exp = + std::chrono::system_clock::now() + std::chrono::milliseconds(ms); + Stuff* news = new Stuff{ + .h = h, + .expiration = exp, + }; + + Stuff* stuff = work; + + if (!stuff || stuff->expiration > exp) { + news->next = stuff; + work = news; + return; + } + + Stuff* s = stuff; + while (s->next && s->next->expiration <= exp) { + s = s->next; + } + + news->next = s->next; + s->next = news; +} + +void main_loop(void (*idle_function)()) { + while (1) { + if (idle_function != nullptr) { + idle_function(); + } + Stuff* stuff = work; + if (stuff == nullptr) { + continue; // busyloop + } + + auto now = std::chrono::system_clock::now(); + auto dt = stuff->expiration - now; + + if (dt > 0ms) { + continue; + } + + stuff->h(); + if (stuff->h.done()) { + stuff->h.destroy(); + } + + Stuff* oldstuff = stuff; + work = stuff->next; + + delete oldstuff; + } +} + +void enqueue(std::coroutine_handle<> h, AwaitableType type) { + Stuff* item = new Stuff{.h = h}; + auto ttype = static_cast(type); + Stuff* stuff = queues[ttype]; + if (stuff == nullptr) { + queues[ttype] = item; + return; + } + while (stuff->next != nullptr) { + stuff = stuff->next; + } + stuff->next = item; +} + +void resume(AwaitableType type) { + auto ttype = static_cast(type); + Stuff* stuff = queues[ttype]; + if (stuff == nullptr) { + return; + } + + queues[ttype] = stuff->next; + schedule(stuff->h); + delete stuff; +} + +} // namespace async + +#if 0 + +task readline() { + int size = 0; + char c; + + buffer buff = buffer::make(32); + +// fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK); + + while (true) { + int n = read(0, &c, 1); + if (n < 1) { + co_await co_waitio(); + continue; + } + buff.data[size++] = static_cast(c); + + if (c == '\n') { + buff.data = buff.data.subspan(0, size); + co_return buff; + } + } +} + +#endif // 0 diff --git a/arm/async.h b/arm/async.h new file mode 100644 index 0000000..a813c16 --- /dev/null +++ b/arm/async.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include + +namespace async { + +template +struct task { + struct promise_type; + using handle_type = std::coroutine_handle; + + struct maybe_suspend { + bool await_ready() noexcept(true) { return !parent; } + void await_suspend(std::coroutine_handle<>) noexcept(true) { + if (parent) { + parent(); + } + } + void await_resume() noexcept(true) { } + + std::coroutine_handle<> parent; + }; + + struct promise_type { + task get_return_object() { return {.h = handle_type::from_promise(*this)}; } + std::suspend_always initial_suspend() noexcept { return {}; } + maybe_suspend final_suspend() noexcept { return { .parent = parent }; } + void return_value(T&& value) { ret_value = std::move(value); } + void unhandled_exception() {} + + T ret_value; + std::coroutine_handle<> parent; + }; + + // awaitable + bool await_ready() { return h.done(); } + void await_suspend(std::coroutine_handle<> ha) { + h(); + h.promise().parent = ha; + } + T await_resume() { return std::move(h.promise().ret_value); } + + std::coroutine_handle h; +}; + +template<> +struct task { + struct promise_type; + using handle_type = std::coroutine_handle; + + struct maybe_suspend { + bool await_ready() noexcept(true) { return !parent; } + void await_suspend(std::coroutine_handle<>) noexcept(true) { + if (parent) { + parent(); + } + } + void await_resume() noexcept(true) { } + + std::coroutine_handle<> parent; + }; + + struct promise_type { + task get_return_object() { return {.h = handle_type::from_promise(*this)}; } + std::suspend_always initial_suspend() noexcept { return {}; } + maybe_suspend final_suspend() noexcept { return { .parent = parent }; } + void return_void() {} + void unhandled_exception() {} + + std::coroutine_handle<> parent; + }; + + // awaitable + bool await_ready() { return h.done(); } + void await_suspend(std::coroutine_handle<> ha) { + h(); + h.promise().parent = ha; + } + void await_resume() {} + + std::coroutine_handle h; +}; + +enum class AwaitableType { + kUnknown = 0, + kUartRx = 1, + + kNumTypes +}; + +void schedule(std::coroutine_handle<> h, int ms = 0); +void enqueue(std::coroutine_handle<> h, AwaitableType type); +void resume(AwaitableType type); // typically called from an ISR + +void main_loop(void (*idle_function)()); + +inline auto await(AwaitableType type) { + struct awaitable { + AwaitableType type; + + bool await_ready() { return false; }; + void await_suspend(std::coroutine_handle<> h) { + enqueue(h, type); + } + void await_resume() {} + }; + + return awaitable{type}; +} + +inline auto delay(int ms) { + struct awaitable { + int ms; + + bool await_ready() { return false; }; + void await_suspend(std::coroutine_handle<> h) { + schedule(h, ms); + } + void await_resume() {} + }; + + return awaitable{ms}; +} + +} // namespace async diff --git a/arm/buffer.h b/arm/buffer.h new file mode 100644 index 0000000..9cbcc8b --- /dev/null +++ b/arm/buffer.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +struct buffer { + std::span data; + + buffer() = default; + buffer(std::span d) : data(d) {} + + static buffer make(size_t size) { + return buffer({new std::byte[size], size}); + } + + buffer(buffer& other) = delete; + buffer& operator=(buffer& other) = delete; + + buffer(buffer&& other) : data(std::exchange(other.data, {})) {} + buffer& operator=(buffer&& other) { data = std::exchange(other.data, {}); return *this; } + + ~buffer() { if (data.data()) { delete[] data.data(); }; } +}; diff --git a/arm/crash.cc b/arm/crash.cc new file mode 100644 index 0000000..b3ca8e3 --- /dev/null +++ b/arm/crash.cc @@ -0,0 +1,112 @@ +#include + +#include "gpio.h" +#include "itoa.h" +#include "sleep.h" +#include "uart.h" + +extern "C" uint32_t _initial_stack_pointer, _text_begin, _text_end; + +namespace crash { +namespace { +[[maybe_unused]] void CrashHexDump(uint32_t* begin, uint32_t* end, + int direction = 1) { + char number[] = "00000000 "; + char addr[] = "00000000: "; + int i = 0; + itoa(reinterpret_cast(begin), addr); + UartSendCrash(addr); + for (uint32_t* ptr = begin; direction > 0 ? ptr < end : ptr > end; + ptr += direction, i++) { + itoa(*ptr, number); + UartSendCrash(number); + if (i % 4 == 3) { + UartSendCrash("\r\n"); + itoa(reinterpret_cast(ptr + 1), addr); + UartSendCrash(addr); + } + } + if (i % 4 != 3) { + UartSendCrash("\r\n"); + } +} + +void StackTrace(uint32_t* sp) { + char number[] = "00000000\r\n"; + for (uint32_t* ptr = sp; ptr < &_initial_stack_pointer; ptr++) { + auto addr = reinterpret_cast(*ptr); + if (addr < &_text_begin || addr >= &_text_end || (*ptr & 0x1) == 0) { + continue; + } + itoa(*ptr, number); + UartSendCrash(number); + } +} + +struct Armv6mRegs { + uintptr_t r4; + uintptr_t r5; + uintptr_t r6; + uintptr_t r7; + uintptr_t r8; + uintptr_t r9; + uintptr_t r10; + uintptr_t r11; + uintptr_t sp; + + // saved by the CPU + uintptr_t r0; + uintptr_t r1; + uintptr_t r2; + uintptr_t r3; + uintptr_t r12; + uintptr_t lr; + uintptr_t pc; + uintptr_t xpsr; +}; + +void CrashHandler(Armv6mRegs* regs) { + char number[] = "00000000\r\n"; + + UartSendCrash("\r\n\r\nCra$h!!\r\n- xpsr: 0x"); + itoa(regs->xpsr, number); + UartSendCrash(number); + UartSendCrash("- pc: 0x"); + itoa(regs->pc, number); + UartSendCrash(number); + UartSendCrash("- lr: 0x"); + itoa(regs->lr, number); + UartSendCrash(number); + UartSendCrash("- Stack trace:\r\n"); + StackTrace(reinterpret_cast(regs->sp)); + + while (1) { + gpio0->data = 0x55; + sleep(100); + gpio0->data = 0xaa; + sleep(100); + } +} +} // namespace + +__attribute__((naked)) void HardFaultHandler() { + asm volatile( + // set those leds first + "mov r0, %0 \n" + "mov r1, #0xa5 \n" + "str r1, [r0] \n" + + "mov r0, r8 \n" + "mov r1, r9 \n" + "mov r2, r10 \n" + "mov r3, r11 \n" + "mrs lr, msp \n" + "push {r0-r3, lr} \n" + "push {r4-r7} \n" + "mrs r0, msp \n" + "mov r1, %1 \n" + "blx r1 \n" + : + : "r"(gpio0), "r"(CrashHandler)); +} +} // namespace crash diff --git a/arm/crash.h b/arm/crash.h new file mode 100644 index 0000000..c1feee7 --- /dev/null +++ b/arm/crash.h @@ -0,0 +1,5 @@ +#pragma once + +namespace crash { +void HardFaultHandler(); +} // namespace crash diff --git a/arm/gpio.h b/arm/gpio.h index d825f25..59051da 100644 --- a/arm/gpio.h +++ b/arm/gpio.h @@ -7,3 +7,21 @@ struct Gpio { }; #define gpio0 ((Gpio*) 0x40000000) + +inline void ToggleLed(int which) { + uint8_t data = gpio0->data; + data ^= (0x1 << which); + gpio0->data = data; +} + +inline void SetLed(int which) { + uint8_t data = gpio0->data; + data |= (0x1 << which); + gpio0->data = data; +} + +inline void ClearLed(int which) { + uint8_t data = gpio0->data; + data &= ~(0x1 << which); + gpio0->data = data; +} diff --git a/arm/hal/cmsis/aum1_cm1.h b/arm/hal/cmsis/aum1_cm1.h new file mode 100644 index 0000000..d06d9da --- /dev/null +++ b/arm/hal/cmsis/aum1_cm1.h @@ -0,0 +1,111 @@ +#pragma once + +#ifdef __cplusplus + extern "C" { +#endif + + +/* ------------------------- Interrupt Number Definition ------------------------ */ + +typedef enum IRQn +{ +/* ------------------- Cortex-M0 Processor Exceptions Numbers ------------------- */ + NonMaskableInt_IRQn = -14, /* 2 Non Maskable Interrupt */ + HardFault_IRQn = -13, /* 3 HardFault Interrupt */ + + + +//SVCall_IRQn = -5, /* 11 SV Call Interrupt */ + +//PendSV_IRQn = -2, /* 14 Pend SV Interrupt */ +//SysTick_IRQn = -1, /* 15 System Tick Interrupt */ + + +/* ---------------------- ARTY_CM1 Specific Interrupt Numbers ------------------- */ + Unused0_IRQn = 0, + Uart0_IRQn = 1, /* GPIO 0 Interrupt */ + Timer0_IRQn = 2, /* Timer 0 Interrupt */ + Unused3_IRQn = 3, + Unused4_IRQn = 4, + Unused5_IRQn = 5, + Unused6_IRQn = 6, + Unused7_IRQn = 7, + Unused8_IRQn = 8, + Unused9_IRQn = 9, + Unused10_IRQn = 10, + Unused11_IRQn = 11, + Unused12_IRQn = 12, + Unused13_IRQn = 13, + Unused14_IRQn = 14, + Unused15_IRQn = 15, + Unused16_IRQn = 16, + Unused17_IRQn = 17, + Unused18_IRQn = 18, + Unused19_IRQn = 19, + Unused20_IRQn = 20, + Unused21_IRQn = 21, + Unused22_IRQn = 22, + Unused23_IRQn = 23, + Unused24_IRQn = 24, + Unused25_IRQn = 25, + Unused26_IRQn = 26, + Unused27_IRQn = 27, + Unused28_IRQn = 28, + Unused29_IRQn = 29, + Unused30_IRQn = 30, + Unused31_IRQn = 31, +} IRQn_Type; + + +/* ================================================================================ */ +/* ================ Processor and Core Peripheral Section ================ */ +/* ================================================================================ */ + +/* -------- Configuration of the Cortex-M0 Processor and Core Peripherals ------- */ +#define __CM1_REV 0x0000 /* Core revision r0p0 */ +#define __MPU_PRESENT 0 /* MPU present or not */ +#define __NVIC_PRIO_BITS 2 /* Number of Bits used for Priority Levels */ +#define __Vendor_SysTickConfig 1 /* Set to 1 if different SysTick Config is used */ + +#include /* Processor and core peripherals */ + +/* ================================================================================ */ +/* ================ Device Specific Peripheral Section ================ */ +/* ================================================================================ */ + +/* ------------------- Start of section using anonymous unions ------------------ */ +#if defined ( __CC_ARM ) + #pragma push +#pragma anon_unions +#elif defined(__ICCARM__) + #pragma language=extended +#elif defined(__GNUC__) + /* anonymous unions are enabled by default */ +#elif defined(__TMS470__) +/* anonymous unions are enabled by default */ +#elif defined(__TASKING__) + #pragma warning 586 +#else + #warning Not supported compiler type +#endif +/* -------------------- End of section using anonymous unions ------------------- */ + +#if defined ( __CC_ARM ) + #pragma pop +#elif defined(__ICCARM__) + /* leave anonymous unions enabled */ +#elif defined(__GNUC__) + /* anonymous unions are enabled by default */ +#elif defined(__TMS470__) + /* anonymous unions are enabled by default */ +#elif defined(__TASKING__) + #pragma warning restore +#else + #warning Not supported compiler type +#endif + + + +#ifdef __cplusplus +} +#endif diff --git a/arm/hal/cmsis/core_cm1.h b/arm/hal/cmsis/core_cm1.h new file mode 100644 index 0000000..4727ee8 --- /dev/null +++ b/arm/hal/cmsis/core_cm1.h @@ -0,0 +1,692 @@ +/**************************************************************************//** + * @file core_cm1.h + * @brief CMSIS Cortex-M1 Core Peripheral Access Layer Header File + * @version V3.20 + * @date 19. April 2018 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2018 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef __CORE_CM1_H_GENERIC +#define __CORE_CM1_H_GENERIC + +/** \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** \ingroup Cortex_M0 + @{ + */ + +/* CMSIS CM1 definitions */ +#define __CM1_CMSIS_VERSION_MAIN (0x03) /*!< [31:16] CMSIS HAL main version */ +#define __CM1_CMSIS_VERSION_SUB (0x20) /*!< [15:0] CMSIS HAL sub version */ +#define __CM1_CMSIS_VERSION ((__CM1_CMSIS_VERSION_MAIN << 16) | \ + __CM1_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x00) /*!< Cortex-M Core */ + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#endif + +/** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all +*/ +#define __FPU_USED 0 + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif +#endif + +#include /* standard types definitions */ +#include /* Core Instruction Access */ +#include /* Core Function Access */ + +#endif /* __CORE_CM1_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM1_H_DEPENDANT +#define __CORE_CM1_H_DEPENDANT + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM1_REV + #define __CM1_REV 0x0000 + #warning "__CM1_REV not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 2 + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0 + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/*@} end of group Cortex_M0 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + ******************************************************************************/ +/** \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ +#else + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ +#endif + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + + +/** \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + + +/** \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ +#else + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ +#endif + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + + +/** \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/*@} end of group CMSIS_CORE */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IO uint32_t ISER[1]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[31]; + __IO uint32_t ICER[1]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[31]; + __IO uint32_t ISPR[1]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[31]; + __IO uint32_t ICPR[1]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[31]; + uint32_t RESERVED4[64]; + __IO uint32_t IP[8]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ +} NVIC_Type; + +/*@} end of group CMSIS_NVIC */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + uint32_t RESERVED0; + __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + uint32_t RESERVED1; + __IO uint32_t SHP[2]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ + __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24 /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20 /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16 /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4 /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0 /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL << SCB_CPUID_REVISION_Pos) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31 /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28 /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27 /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26 /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25 /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23 /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22 /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12 /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0 /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL << SCB_ICSR_VECTACTIVE_Pos) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16 /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15 /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2 /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1 /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4 /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2 /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1 /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9 /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3 /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_SVCALLPENDED_Pos 15 /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Cortex-M1 Core Debug Registers (DCB registers, SHCSR, and DFSR) + are only accessible over DAP and not via processor. Therefore + they are not covered by the Cortex-M1 header file. + @{ + */ +/*@} end of group CMSIS_CoreDebug */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M1 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Register Access Functions + ******************************************************************************/ +/** \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/* Interrupt Priorities are WORD accessible only under ARMv6M */ +/* The following MACROS handle generation of the register offset and byte masks */ +#define _BIT_SHIFT(IRQn) ( (((uint32_t)(IRQn) ) & 0x03) * 8 ) +#define _SHP_IDX(IRQn) ( ((((uint32_t)(IRQn) & 0x0F)-8) >> 2) ) +#define _IP_IDX(IRQn) ( ((uint32_t)(IRQn) >> 2) ) + + +/** \brief Enable External Interrupt + + The function enables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC->ISER[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + + +/** \brief Disable External Interrupt + + The function disables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + + +/** \brief Get Pending Interrupt + + The function reads the pending register in the NVIC and returns the pending bit + for the specified interrupt. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->ISPR[0] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); +} + + +/** \brief Set Pending Interrupt + + The function sets the pending bit of an external interrupt. + + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + + +/** \brief Clear Pending Interrupt + + The function clears the pending bit of an external interrupt. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ +} + + +/** \brief Clear All Pending Interrupts + + The function clears all pending bits. + */ +__STATIC_INLINE void NVIC_ClearAllPendingIRQ(void) +{ + NVIC->ICPR[0] = 0xFFFFFFFF; /* Clear all pending interrupts */ +} + + +/** \brief Set Interrupt Priority + + The function sets the priority of an interrupt. + + \note The priority cannot be set for every core interrupt. + + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if(IRQn < 0) { + SCB->SHP[_SHP_IDX(IRQn)] = (SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) | + (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); } + else { + NVIC->IP[_IP_IDX(IRQn)] = (NVIC->IP[_IP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) | + (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); } +} + + +/** \brief Get Interrupt Priority + + The function reads the priority of an interrupt. The interrupt + number can be positive to specify an external (device specific) + interrupt, or negative to specify an internal (core) interrupt. + + + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented + priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if(IRQn < 0) { + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & 0xFF) >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M0 system interrupts */ + else { + return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & 0xFF) >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ +} + + +/** \brief System Reset + + The function initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | + SCB_AIRCR_SYSRESETREQ_Msk); + __DSB(); /* Ensure completion of memory access */ + while(1); /* wait until reset */ +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0) + +/** \brief System Tick Configuration + + The function initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + + \param [in] ticks Number of ticks between two interrupts. + + \return 0 Function succeeded. + \return 1 Function failed. + + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ + + SysTick->LOAD = ticks - 1; /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + + +#endif /* __CORE_CM1_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ + +#ifdef __cplusplus +} +#endif diff --git a/arm/hal/cmsis/core_cmFunc.h b/arm/hal/cmsis/core_cmFunc.h new file mode 100644 index 0000000..8c72230 --- /dev/null +++ b/arm/hal/cmsis/core_cmFunc.h @@ -0,0 +1,636 @@ +/**************************************************************************//** + * @file core_cmFunc.h + * @brief CMSIS Cortex-M Core Function Access Header File + * @version V3.20 + * @date 25. February 2013 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2013 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#ifndef __CORE_CMFUNC_H +#define __CORE_CMFUNC_H + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +#if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ +/* ARM armcc specific functions */ + +#if (__ARMCC_VERSION < 400677) + #error "Please use ARM Compiler Toolchain V4.0.677 or later!" +#endif + +/* intrinsic void __enable_irq(); */ +/* intrinsic void __disable_irq(); */ + +/** \brief Get Control Register + + This function returns the content of the Control Register. + + \return Control Register value + */ +__STATIC_INLINE uint32_t __get_CONTROL(void) +{ + uint32_t __regControl __ASM("control"); + return(__regControl); +} + + +/** \brief Set Control Register + + This function writes the given value to the Control Register. + + \param [in] control Control Register value to set + */ +__STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + uint32_t __regControl __ASM("control"); + __regControl = control; +} + + +/** \brief Get IPSR Register + + This function returns the content of the IPSR Register. + + \return IPSR Register value + */ +__STATIC_INLINE uint32_t __get_IPSR(void) +{ + uint32_t __regIPSR __ASM("ipsr"); + return(__regIPSR); +} + + +/** \brief Get APSR Register + + This function returns the content of the APSR Register. + + \return APSR Register value + */ +__STATIC_INLINE uint32_t __get_APSR(void) +{ + uint32_t __regAPSR __ASM("apsr"); + return(__regAPSR); +} + + +/** \brief Get xPSR Register + + This function returns the content of the xPSR Register. + + \return xPSR Register value + */ +__STATIC_INLINE uint32_t __get_xPSR(void) +{ + uint32_t __regXPSR __ASM("xpsr"); + return(__regXPSR); +} + + +/** \brief Get Process Stack Pointer + + This function returns the current value of the Process Stack Pointer (PSP). + + \return PSP Register value + */ +__STATIC_INLINE uint32_t __get_PSP(void) +{ + uint32_t __regProcessStackPointer __ASM("psp"); + return(__regProcessStackPointer); +} + + +/** \brief Set Process Stack Pointer + + This function assigns the given value to the Process Stack Pointer (PSP). + + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + uint32_t __regProcessStackPointer __ASM("psp"); + __regProcessStackPointer = topOfProcStack; +} + + +/** \brief Get Main Stack Pointer + + This function returns the current value of the Main Stack Pointer (MSP). + + \return MSP Register value + */ +__STATIC_INLINE uint32_t __get_MSP(void) +{ + uint32_t __regMainStackPointer __ASM("msp"); + return(__regMainStackPointer); +} + + +/** \brief Set Main Stack Pointer + + This function assigns the given value to the Main Stack Pointer (MSP). + + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + uint32_t __regMainStackPointer __ASM("msp"); + __regMainStackPointer = topOfMainStack; +} + + +/** \brief Get Priority Mask + + This function returns the current state of the priority mask bit from the Priority Mask Register. + + \return Priority Mask value + */ +__STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + uint32_t __regPriMask __ASM("primask"); + return(__regPriMask); +} + + +/** \brief Set Priority Mask + + This function assigns the given value to the Priority Mask Register. + + \param [in] priMask Priority Mask + */ +__STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + uint32_t __regPriMask __ASM("primask"); + __regPriMask = (priMask); +} + + +#if (__CORTEX_M >= 0x03) + +/** \brief Enable FIQ + + This function enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +#define __enable_fault_irq __enable_fiq + + +/** \brief Disable FIQ + + This function disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +#define __disable_fault_irq __disable_fiq + + +/** \brief Get Base Priority + + This function returns the current value of the Base Priority register. + + \return Base Priority register value + */ +__STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + uint32_t __regBasePri __ASM("basepri"); + return(__regBasePri); +} + + +/** \brief Set Base Priority + + This function assigns the given value to the Base Priority register. + + \param [in] basePri Base Priority value to set + */ +__STATIC_INLINE void __set_BASEPRI(uint32_t basePri) +{ + uint32_t __regBasePri __ASM("basepri"); + __regBasePri = (basePri & 0xff); +} + + +/** \brief Get Fault Mask + + This function returns the current value of the Fault Mask register. + + \return Fault Mask register value + */ +__STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t __regFaultMask __ASM("faultmask"); + return(__regFaultMask); +} + + +/** \brief Set Fault Mask + + This function assigns the given value to the Fault Mask register. + + \param [in] faultMask Fault Mask value to set + */ +__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + uint32_t __regFaultMask __ASM("faultmask"); + __regFaultMask = (faultMask & (uint32_t)1); +} + +#endif /* (__CORTEX_M >= 0x03) */ + + +#if (__CORTEX_M == 0x04) + +/** \brief Get FPSCR + + This function returns the current value of the Floating Point Status/Control register. + + \return Floating Point Status/Control register value + */ +__STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + uint32_t __regfpscr __ASM("fpscr"); + return(__regfpscr); +#else + return(0); +#endif +} + + +/** \brief Set FPSCR + + This function assigns the given value to the Floating Point Status/Control register. + + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + uint32_t __regfpscr __ASM("fpscr"); + __regfpscr = (fpscr); +#endif +} + +#endif /* (__CORTEX_M == 0x04) */ + + +#elif defined ( __ICCARM__ ) /*------------------ ICC Compiler -------------------*/ +/* IAR iccarm specific functions */ + +#include + + +#elif defined ( __TMS470__ ) /*---------------- TI CCS Compiler ------------------*/ +/* TI CCS specific functions */ + +#include + + +#elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ +/* GNU gcc specific functions */ + +/** \brief Enable IRQ Interrupts + + This function enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** \brief Disable IRQ Interrupts + + This function disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** \brief Get Control Register + + This function returns the content of the Control Register. + + \return Control Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +/** \brief Set Control Register + + This function writes the given value to the Control Register. + + \param [in] control Control Register value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); +} + + +/** \brief Get IPSR Register + + This function returns the content of the IPSR Register. + + \return IPSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get APSR Register + + This function returns the content of the APSR Register. + + \return APSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get xPSR Register + + This function returns the content of the xPSR Register. + + \return xPSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get Process Stack Pointer + + This function returns the current value of the Process Stack Pointer (PSP). + + \return PSP Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); + return(result); +} + + +/** \brief Set Process Stack Pointer + + This function assigns the given value to the Process Stack Pointer (PSP). + + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : "sp"); +} + + +/** \brief Get Main Stack Pointer + + This function returns the current value of the Main Stack Pointer (MSP). + + \return MSP Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); + return(result); +} + + +/** \brief Set Main Stack Pointer + + This function assigns the given value to the Main Stack Pointer (MSP). + + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : "sp"); +} + + +/** \brief Get Priority Mask + + This function returns the current state of the priority mask bit from the Priority Mask Register. + + \return Priority Mask value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +/** \brief Set Priority Mask + + This function assigns the given value to the Priority Mask Register. + + \param [in] priMask Priority Mask + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (__CORTEX_M >= 0x03) + +/** \brief Enable FIQ + + This function enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** \brief Disable FIQ + + This function disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** \brief Get Base Priority + + This function returns the current value of the Base Priority register. + + \return Base Priority register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_max" : "=r" (result) ); + return(result); +} + + +/** \brief Set Base Priority + + This function assigns the given value to the Base Priority register. + + \param [in] basePri Base Priority value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI(uint32_t value) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); +} + + +/** \brief Get Fault Mask + + This function returns the current value of the Fault Mask register. + + \return Fault Mask register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +/** \brief Set Fault Mask + + This function assigns the given value to the Fault Mask register. + + \param [in] faultMask Fault Mask value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + +#endif /* (__CORTEX_M >= 0x03) */ + + +#if (__CORTEX_M == 0x04) + +/** \brief Get FPSCR + + This function returns the current value of the Floating Point Status/Control register. + + \return Floating Point Status/Control register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + uint32_t result; + + /* Empty asm statement works as a scheduling barrier */ + __ASM volatile (""); + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + __ASM volatile (""); + return(result); +#else + return(0); +#endif +} + + +/** \brief Set FPSCR + + This function assigns the given value to the Floating Point Status/Control register. + + \param [in] fpscr Floating Point Status/Control value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + /* Empty asm statement works as a scheduling barrier */ + __ASM volatile (""); + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); + __ASM volatile (""); +#endif +} + +#endif /* (__CORTEX_M == 0x04) */ + + +#elif defined ( __TASKING__ ) /*------------------ TASKING Compiler --------------*/ +/* TASKING carm specific functions */ + +/* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all instrinsics, + * Including the CMSIS ones. + */ + +#endif + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +#endif /* __CORE_CMFUNC_H */ diff --git a/arm/hal/cmsis/core_cmInstr.h b/arm/hal/cmsis/core_cmInstr.h new file mode 100644 index 0000000..d213f0e --- /dev/null +++ b/arm/hal/cmsis/core_cmInstr.h @@ -0,0 +1,688 @@ +/**************************************************************************//** + * @file core_cmInstr.h + * @brief CMSIS Cortex-M Core Instruction Access Header File + * @version V3.20 + * @date 05. March 2013 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2013 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#ifndef __CORE_CMINSTR_H +#define __CORE_CMINSTR_H + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +#if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ +/* ARM armcc specific functions */ + +#if (__ARMCC_VERSION < 400677) + #error "Please use ARM Compiler Toolchain V4.0.677 or later!" +#endif + + +/** \brief No Operation + + No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __nop + + +/** \brief Wait For Interrupt + + Wait For Interrupt is a hint instruction that suspends execution + until one of a number of events occurs. + */ +#define __WFI __wfi + + +/** \brief Wait For Event + + Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __wfe + + +/** \brief Send Event + + Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __sev + + +/** \brief Instruction Synchronization Barrier + + Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or + memory, after the instruction has been completed. + */ +#define __ISB() __isb(0xF) + + +/** \brief Data Synchronization Barrier + + This function acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __dsb(0xF) + + +/** \brief Data Memory Barrier + + This function ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __dmb(0xF) + + +/** \brief Reverse byte order (32 bit) + + This function reverses the byte order in integer value. + + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV __rev + + +/** \brief Reverse byte order (16 bit) + + This function reverses the byte order in two unsigned short values. + + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(uint32_t value) +{ + rev16 r0, r0 + bx lr +} +#endif + +/** \brief Reverse byte order in signed short value + + This function reverses the byte order in a signed short value with sign extension to integer. + + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(int32_t value) +{ + revsh r0, r0 + bx lr +} +#endif + + +/** \brief Rotate Right in unsigned value (32 bit) + + This function Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + + \param [in] value Value to rotate + \param [in] value Number of Bits to rotate + \return Rotated value + */ +#define __ROR __ror + + +/** \brief Breakpoint + + This function causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __breakpoint(value) + + +#if (__CORTEX_M >= 0x03) + +/** \brief Reverse bit order of value + + This function reverses the bit order of the given value. + + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __rbit + + +/** \brief LDR Exclusive (8 bit) + + This function performs a exclusive LDR command for 8 bit value. + + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB(ptr) ((uint8_t ) __ldrex(ptr)) + + +/** \brief LDR Exclusive (16 bit) + + This function performs a exclusive LDR command for 16 bit values. + + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH(ptr) ((uint16_t) __ldrex(ptr)) + + +/** \brief LDR Exclusive (32 bit) + + This function performs a exclusive LDR command for 32 bit values. + + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW(ptr) ((uint32_t ) __ldrex(ptr)) + + +/** \brief STR Exclusive (8 bit) + + This function performs a exclusive STR command for 8 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB(value, ptr) __strex(value, ptr) + + +/** \brief STR Exclusive (16 bit) + + This function performs a exclusive STR command for 16 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH(value, ptr) __strex(value, ptr) + + +/** \brief STR Exclusive (32 bit) + + This function performs a exclusive STR command for 32 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW(value, ptr) __strex(value, ptr) + + +/** \brief Remove the exclusive lock + + This function removes the exclusive lock which is created by LDREX. + + */ +#define __CLREX __clrex + + +/** \brief Signed Saturate + + This function saturates a signed value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __ssat + + +/** \brief Unsigned Saturate + + This function saturates an unsigned value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __usat + + +/** \brief Count leading zeros + + This function counts the number of leading zeros of a data value. + + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +#define __CLZ __clz + +#endif /* (__CORTEX_M >= 0x03) */ + + + +#elif defined ( __ICCARM__ ) /*------------------ ICC Compiler -------------------*/ +/* IAR iccarm specific functions */ + +#include + + +#elif defined ( __TMS470__ ) /*---------------- TI CCS Compiler ------------------*/ +/* TI CCS specific functions */ + +#include + + +#elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ +/* GNU gcc specific functions */ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constrant "l" + * Otherwise, use general registers, specified by constrant "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** \brief No Operation + + No Operation does nothing. This instruction can be used for code alignment purposes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __NOP(void) +{ + __ASM volatile ("nop"); +} + + +/** \brief Wait For Interrupt + + Wait For Interrupt is a hint instruction that suspends execution + until one of a number of events occurs. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __WFI(void) +{ + __ASM volatile ("wfi"); +} + + +/** \brief Wait For Event + + Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __WFE(void) +{ + __ASM volatile ("wfe"); +} + + +/** \brief Send Event + + Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __SEV(void) +{ + __ASM volatile ("sev"); +} + + +/** \brief Instruction Synchronization Barrier + + Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or + memory, after the instruction has been completed. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __ISB(void) +{ + __ASM volatile ("isb"); +} + + +/** \brief Data Synchronization Barrier + + This function acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __DSB(void) +{ + __ASM volatile ("dsb"); +} + + +/** \brief Data Memory Barrier + + This function ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __DMB(void) +{ + __ASM volatile ("dmb"); +} + + +/** \brief Reverse byte order (32 bit) + + This function reverses the byte order in integer value. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +#endif +} + + +/** \brief Reverse byte order (16 bit) + + This function reverses the byte order in two unsigned short values. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** \brief Reverse byte order in signed short value + + This function reverses the byte order in a signed short value with sign extension to integer. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __REVSH(int32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (short)__builtin_bswap16(value); +#else + uint32_t result; + + __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +#endif +} + + +/** \brief Rotate Right in unsigned value (32 bit) + + This function Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + + \param [in] value Value to rotate + \param [in] value Number of Bits to rotate + \return Rotated value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + return (op1 >> op2) | (op1 << (32 - op2)); +} + + +/** \brief Breakpoint + + This function causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +#if (__CORTEX_M >= 0x03) + +/** \brief Reverse bit order of value + + This function reverses the bit order of the given value. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + + +/** \brief LDR Exclusive (8 bit) + + This function performs a exclusive LDR command for 8 bit value. + + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return(result); +} + + +/** \brief LDR Exclusive (16 bit) + + This function performs a exclusive LDR command for 16 bit values. + + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return(result); +} + + +/** \brief LDR Exclusive (32 bit) + + This function performs a exclusive LDR command for 32 bit values. + + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** \brief STR Exclusive (8 bit) + + This function performs a exclusive STR command for 8 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** \brief STR Exclusive (16 bit) + + This function performs a exclusive STR command for 16 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** \brief STR Exclusive (32 bit) + + This function performs a exclusive STR command for 32 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** \brief Remove the exclusive lock + + This function removes the exclusive lock which is created by LDREX. + + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + + +/** \brief Signed Saturate + + This function saturates a signed value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** \brief Unsigned Saturate + + This function saturates an unsigned value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** \brief Count leading zeros + + This function counts the number of leading zeros of a data value. + + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint8_t __CLZ(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("clz %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + +#endif /* (__CORTEX_M >= 0x03) */ + + + + +#elif defined ( __TASKING__ ) /*------------------ TASKING Compiler --------------*/ +/* TASKING carm specific functions */ + +/* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + +#endif + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + +#endif /* __CORE_CMINSTR_H */ diff --git a/arm/itoa.h b/arm/itoa.h new file mode 100644 index 0000000..9b1b35f --- /dev/null +++ b/arm/itoa.h @@ -0,0 +1,13 @@ +#pragma once + +// out must be at least 8 bytes long +inline void itoa(int val, char* out) { + for (int i = 0; i < 8; i++) { + uint8_t digit = (val >> (28 - 4 * i)) & 0xf; + if (digit < 0xa) { + out[i] = '0' + digit; + } else { + out[i] = 'a' + digit - 0xa; + } + } +} diff --git a/arm/lock.h b/arm/lock.h new file mode 100644 index 0000000..dc97a49 --- /dev/null +++ b/arm/lock.h @@ -0,0 +1,18 @@ +#pragma once + +#include "aum1_cm1.h" + +struct InterruptLock { + bool was_locked; + + InterruptLock() + : was_locked(__get_PRIMASK() != 0) { + __disable_irq(); + } + + ~InterruptLock() { + if (!was_locked) { + __enable_irq(); + } + } +}; diff --git a/arm/main.cc b/arm/main.cc index 55d01f5..8c6cc2a 100644 --- a/arm/main.cc +++ b/arm/main.cc @@ -1,32 +1,83 @@ +#include "async.h" +#include "aum1_cm1.h" +#include "buffer.h" +#include "crash.h" #include "gpio.h" +#include "timer.h" +#include "trace.h" +#include "uart.h" namespace { +using async::AwaitableType; -[[maybe_unused]] -void sleep(int ms) { - for (int i = 0; i < ms; i++) { - // sleep for 1 ms - for (int j = 0; j < 22000; j++) { - asm(""); - } - } +void HandleUartRxFromIsr(void*, unsigned int) { + tracing::trace(tracing::TraceEvent::kUartRxCb); + async::resume(AwaitableType::kUartRx); } +void Uart0Isr() { + tracing::trace(tracing::TraceEvent::kUartIsr); + XUartLite_InterruptHandler(uart0); +} + +void Timer0Isr() { + tracing::dump(); + __builtin_trap(); +} + +void SetupUart() { + InitUarts(); + + auto* vector_table = reinterpret_cast(0x0); + vector_table[16 + HardFault_IRQn] = + reinterpret_cast(crash::HardFaultHandler); + + vector_table[16 + Uart0_IRQn] = reinterpret_cast(Uart0Isr); + vector_table[16 + Timer0_IRQn] = reinterpret_cast(Timer0Isr); + NVIC_SetPriority(Uart0_IRQn, 3); + NVIC_EnableIRQ(Uart0_IRQn); + + XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr); + XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr); + XUartLite_EnableInterrupt(uart0); +} + +void SetupTimer() { + NVIC_EnableIRQ(Timer0_IRQn); + timer0->SetupAsWdt(100000 * 4000); + timer0->EnableT1(); +} } // namespace -extern "C" int main() { - uint8_t cnt = 0; - - for (int i = 0; i < 256; i++) { - gpio0->data = cnt; - cnt++; - - sleep(125); +async::task UartRead(int size) { + auto buff = buffer::make(size); + auto* data = reinterpret_cast(buff.data.data()); + size_t received = XUartLite_Recv(uart0, data, buff.data.size()); + if (received < buff.data.size()) { + co_await async::await(AwaitableType::kUartRx); } - - auto* airc = reinterpret_cast(0xe000ed0c); - uint32_t a = *airc; - *airc = a | 0x04; - - while (1) {} + co_return buff; +} + +async::task<> echo() { + while (1) { + buffer buff = co_await UartRead(1); + UartSend(buff.data); + } +} + +int main() { + SetupUart(); + SetupTimer(); + + async::schedule(echo().h); + + async::main_loop([]() { + static int cnt = 0; + timer0->Pet(); + if ((cnt++ % 100000) == 0) { + ToggleLed(0); + } + }); + // should never get here } diff --git a/arm/makefile b/arm/makefile index d56afef..5366cbc 100644 --- a/arm/makefile +++ b/arm/makefile @@ -15,14 +15,16 @@ LDFLAGS = -march=armv6-m \ -Wl,--gc-sections -Os \ -Wl,--print-memory-usage -flto +includes += -Ihal/cmsis + sources += hal/lib/common/xil_assert.c includes += -Ihal/lib/common -sources += hal/uart/xuartlite.c hal/uart/xuartlite_stats.c +sources += hal/uart/xuartlite.c hal/uart/xuartlite_stats.c hal/uart/xuartlite_intr.c includes += -Ihal/uart bootloader_objects = uart.o bootloader.o vector_table.o $(sources:.c=.o) -app_objects = app_init.o main.o +app_objects = app_init.o crash.o main.o uart.o stdlib.o async.o trace.o $(sources:.c=.o) CPPFLAGS += $(includes) @@ -37,10 +39,21 @@ bootloader.elf: $(bootloader_objects) bootloader.ld app.elf: $(app_objects) app.ld $(LD) -Wl,-Tapp.ld $(LDFLAGS) -o $@ $(app_objects) -%.elf: - $(LD) -Wl,-Tapp.ld $(LDFLAGS) -o $@ $^ +.PHONY: clean test -.PHONY: clean +deps = $(app_objects:.o=.d) $(bootloader_objects:.o=.d) clean: - rm -rf *.elf *.bin $(app_objects) $(bootloader_objects) + rm -rf *.elf *.bin $(tests) $(app_objects) $(bootloader_objects) $(deps) + +HOSTCXX = clang++ + +tests = ring_buffer_test + +test: $(tests) + +ring_buffer_test: ring_buffer_test.cc + $(HOSTCXX) -std=c++2a -o $@ $< -lgmock -lgtest -lgtest_main + ./$@ + +-include $(deps) diff --git a/arm/ring_buffer.h b/arm/ring_buffer.h new file mode 100644 index 0000000..56e8d37 --- /dev/null +++ b/arm/ring_buffer.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include + +struct RingBuffer { + std::span buffer; + + std::atomic read_ptr = 0; + std::atomic write_ptr = 0; + std::atomic full = 0; + + bool Store(std::span data) { + if (data.size() > FreeSpace()) { + return false; + } + const size_t to_copy = std::min(buffer.size() - write_ptr, data.size()); + std::copy(data.begin(), data.begin() + to_copy, buffer.begin() + write_ptr); + if (to_copy < data.size()) { + std::copy(data.begin() + to_copy, data.end(), buffer.begin()); + } + Push(data.size()); + + return true; + } + + bool Load(std::span out) { + if (out.size() > AvailableData()) { + return false; + } + const size_t to_copy = std::min(buffer.size() - read_ptr, out.size()); + std::copy(buffer.begin() + read_ptr, buffer.begin() + read_ptr + to_copy, out.begin()); + if (to_copy < out.size()) { + std::copy(buffer.begin(), buffer.begin() + out.size() - to_copy, out.begin() + to_copy); + } + Pop(out.size()); + return true; + } + + bool Push(size_t amount) { + if (amount > FreeSpace()) { + return false; + } + write_ptr = (write_ptr + amount) % buffer.size(); + if (read_ptr == write_ptr) { + full = true; + } + return true; + } + + bool Pop(size_t amount) { + if (amount > AvailableData()) { + return false; + } + read_ptr = (read_ptr + amount) % buffer.size(); + if (amount > 0) { + full = false; + } + return true; + } + + size_t FreeSpace() const { + return buffer.size() - AvailableData(); + } + + size_t AvailableData() const { + if (read_ptr == write_ptr) { + return full ? buffer.size() : 0; + } + return (buffer.size() + write_ptr - read_ptr) % buffer.size(); + } + + uint8_t* RawReadPointer() const { + return reinterpret_cast(buffer.data() + read_ptr); + } + + size_t ContiguousAvailableData() const { + if (read_ptr < write_ptr) { + return AvailableData(); + } + if (full) { + return 0; + } + + return buffer.size() - read_ptr; + } +}; diff --git a/arm/ring_buffer_test.cc b/arm/ring_buffer_test.cc new file mode 100644 index 0000000..62888d1 --- /dev/null +++ b/arm/ring_buffer_test.cc @@ -0,0 +1,42 @@ +#include "ring_buffer.h" + +#include +#include + +#include + +using namespace ::testing; + +TEST(RingBuffer, BasicStoreLoad) { + std::array arr; + RingBuffer rb{.buffer = std::as_writable_bytes(std::span{arr})}; + + std::array a0 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::array a1 = {}; + + ASSERT_TRUE(rb.Store(std::as_bytes(std::span{a0}))); + ASSERT_TRUE(rb.Load(std::as_writable_bytes(std::span{a1}))); + + EXPECT_THAT(a1, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); +} + +TEST(RingBuffer, StoreOverflow) { + std::array arr; + RingBuffer rb{.buffer = std::as_writable_bytes(std::span{arr})}; + + std::array a0 = {0, 1, 2, 3, 4, 5, 6, 7}; + std::array a1 = {}; + + ASSERT_TRUE(rb.Store(std::as_bytes(std::span{a0}))); + ASSERT_FALSE(rb.Store(std::as_bytes(std::span{a0}))); + + EXPECT_EQ(rb.AvailableData(), 8); + + ASSERT_TRUE(rb.Pop(7)); + EXPECT_EQ(rb.AvailableData(), 1); + + EXPECT_TRUE(rb.Store(std::as_bytes(std::span{a0}))); + ASSERT_TRUE(rb.Load(std::as_writable_bytes(std::span{a1}))); + // one byte leftover from first run, then 7 more + EXPECT_THAT(a1, ElementsAre(7, 0, 1, 2, 3, 4, 5, 6)); +} diff --git a/arm/sleep.h b/arm/sleep.h new file mode 100644 index 0000000..018906c --- /dev/null +++ b/arm/sleep.h @@ -0,0 +1,10 @@ +#pragma once + +inline void sleep(int ms) { + for (int i = 0; i < ms; i++) { + // sleep for 1 ms + for (int j = 0; j < 22000; j++) { + asm(""); + } + } +} diff --git a/arm/stdlib.cc b/arm/stdlib.cc new file mode 100644 index 0000000..b3fe6c9 --- /dev/null +++ b/arm/stdlib.cc @@ -0,0 +1,53 @@ +#include + +#include + +#include "itoa.h" +#include "lock.h" +#include "timer.h" +#include "uart.h" + +extern unsigned char _heap_begin, _heap_end; + +namespace { +void LogStats(unsigned char* heap) { + char number[] = "00000000\r\n"; + UartSend("Program break now at 0x"); + itoa(reinterpret_cast(heap), number); + UartSend(number); +} +} // namespace + +extern "C" void* _sbrk(int increment) { + static unsigned char* heap = &_heap_begin; + unsigned char* prev_heap = heap; + if (heap + increment >= &_heap_end) { + UartSend("Heap overflow!\r\n"); + return reinterpret_cast(-1); + } + heap += increment; + LogStats(heap); + return prev_heap; +} + +extern "C" int _gettimeofday(struct timeval* tv, void* tzvp) { + uint32_t ticks = timer0->GetT1Ticks(); + tv->tv_sec = ticks / 100000000; + tv->tv_usec = (ticks % 100000000) / 100; + + return 0; +} + +extern "C" uint8_t __atomic_exchange_1(volatile void* ptr, uint8_t val, + int memorder) { + auto* dest = reinterpret_cast(ptr); + bool ret; + + { + InterruptLock lock; + ret = *dest; + *dest = val; + } + + return ret; +} diff --git a/arm/timer.h b/arm/timer.h new file mode 100644 index 0000000..c746eee --- /dev/null +++ b/arm/timer.h @@ -0,0 +1,63 @@ +#pragma once + +struct TimerControl { + union { + struct { + uint32_t MDT0 : 1; + uint32_t UDT0 : 1; + uint32_t GENT0 : 1; + uint32_t CAPT0 : 1; + uint32_t ARHT0 : 1; + uint32_t LOAD0 : 1; + uint32_t ENIT0 : 1; + uint32_t ENT0 : 1; + uint32_t T0INT : 1; + uint32_t PWMA0 : 1; + uint32_t ENALL : 1; + uint32_t CASC : 1; + + uint32_t reserved : 20; + }; + uint32_t raw; + }; +}; + +struct Timer { + volatile TimerControl TCSR0; + volatile uint32_t TLR0; + volatile uint32_t TCR0; + + uint32_t _reserved; + + volatile TimerControl TCSR1; + volatile uint32_t TLR1; + volatile uint32_t TCR1; + + void EnableT1() { + TCSR1.ARHT0 = 1; + TCSR1.ENT0 = 1; + } + + uint32_t GetT1Ticks() { + return TCR1; + } + + void SetupAsWdt(uint32_t timeout_ticks) { + TLR0 = timeout_ticks; + TCSR0.LOAD0 = 1; // reset counter + TCSR0.UDT0 = 1; // count backwards from the load value + TCSR0.ENIT0 = 1; // enable interrupt + + TCSR0.LOAD0 = 0; // allow counter to run + TCSR0.ENT0 = 1; // enable timer + } + + void Pet() { + TCSR0.ENT0 = 0; + TCSR0.LOAD0 = 1; + TCSR0.LOAD0 = 0; + TCSR0.ENT0 = 1; + } +}; + +#define timer0 ((Timer*) 0x40002000) diff --git a/arm/trace.cc b/arm/trace.cc new file mode 100644 index 0000000..a47ee09 --- /dev/null +++ b/arm/trace.cc @@ -0,0 +1,38 @@ +#include "trace.h" + +#include + +#include "itoa.h" +#include "lock.h" +#include "uart.h" + +namespace tracing { +namespace { + +constexpr size_t kTraceBufferSize = 256; +std::array buffer; +size_t write_ptr = 0; + +} // namespace + +void trace(int raw_event) { trace(static_cast(raw_event)); } + +void trace(TraceEvent event) { + InterruptLock lock; + + buffer[write_ptr] = event; + + write_ptr = (write_ptr + 1) % buffer.size(); +} + +void dump() { + std::rotate(buffer.begin(), buffer.begin() + write_ptr, buffer.end()); + + char number[] = "00000000\r\n"; + + for (TraceEvent event : buffer) { + itoa(static_cast(event), number); + UartSendCrash(number); + } +} +} // namespace tracing diff --git a/arm/trace.h b/arm/trace.h new file mode 100644 index 0000000..f548fc0 --- /dev/null +++ b/arm/trace.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace tracing { +enum class TraceEvent : uint8_t { + kUnknown = 0, + + kUartIsr, + kUartRxCb, + kUartTxCb, +}; + +void trace(TraceEvent event); +void trace(int raw_event); +void dump(); +} // namespace tracing diff --git a/arm/uart.cc b/arm/uart.cc index fb9ecb7..f8c4eea 100644 --- a/arm/uart.cc +++ b/arm/uart.cc @@ -1,5 +1,9 @@ #include "uart.h" +#include "gpio.h" +#include "ring_buffer.h" +#include "trace.h" + namespace { constexpr uintptr_t kUart0BaseAddress = 0x40001000; @@ -12,6 +16,10 @@ XUartLite_Config uart0_config = { .DataBits = 8, }; +constexpr size_t kUartTxBufferSize = 256; +std::array tx_buffer = {}; +RingBuffer tx_ring_buffer{.buffer = tx_buffer}; + } // namespace XUartLite* uart0 = &uart0_inst; @@ -19,3 +27,39 @@ XUartLite* uart0 = &uart0_inst; void InitUarts() { XUartLite_CfgInitialize(uart0, &uart0_config, uart0_config.RegBaseAddr); } + +extern "C" uint8_t XUartLite_GetSR(XUartLite* InstancePtr); +#define XUL_SR_TX_FIFO_EMPTY 0x04 /* transmit FIFO empty */ + +void UartSendCrash(std::span data) { + while (data.size() > 0) { + while ((XUartLite_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) { + } + auto* dat = + reinterpret_cast(const_cast(data.data())); + uint8_t sent = XUartLite_Send(uart0, dat, data.size()); + data = data.subspan(sent); + } + while ((XUartLite_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) { + } +} + +// blocking +void UartSend(std::span data) { + while (!tx_ring_buffer.Store(data)) { + } + + if (!XUartLite_IsSending(uart0)) { + XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), + tx_ring_buffer.ContiguousAvailableData()); + } +} + +void HandleUartTxFromIsr(void*, unsigned int transmitted) { + tracing::trace(tracing::TraceEvent::kUartTxCb); + tx_ring_buffer.Pop(transmitted); + if (tx_ring_buffer.AvailableData() > 0) { + XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), + tx_ring_buffer.ContiguousAvailableData()); + } +} diff --git a/arm/uart.h b/arm/uart.h index 9c6dae7..d7b0a28 100644 --- a/arm/uart.h +++ b/arm/uart.h @@ -1,7 +1,22 @@ #pragma once +#include +#include + #include "xuartlite.h" extern XUartLite* uart0; void InitUarts(); + +// blocking +void UartSend(std::span data); +inline void UartSend(std::string_view s) { + return UartSend(std::as_bytes(std::span{s.data(), s.size()})); +} +void UartSendCrash(std::span data); +inline void UartSendCrash(std::string_view s) { + return UartSendCrash(std::as_bytes(std::span{s.data(), s.size()})); +} + +void HandleUartTxFromIsr(void*, unsigned int transmitted); diff --git a/arm/vector_table.cc b/arm/vector_table.cc index 1c6b572..faade0e 100644 --- a/arm/vector_table.cc +++ b/arm/vector_table.cc @@ -23,7 +23,7 @@ void ResetHandler() { } // namespace -__attribute__((section(".vector_table"))) +__attribute__((section(".vector_table"), used)) uint32_t vector_table[16] = { [StackPointer] = reinterpret_cast(&_initial_stack_pointer), [Reset] = reinterpret_cast(ResetHandler),