From ec2e1fd5c6271bdf45ce22b3bb5cd3b690d92d5b Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Sat, 23 Jan 2021 14:25:04 -0500 Subject: [PATCH] increase signal buffers; fix oversample --- .../os/hal/ports/STM32/STM32H7xx/hal_lld.c | 10 +- Makefile | 2 +- STM32H723xG.ld | 25 +- cfg/halconf.h | 8 +- cfg/mcuconf.h | 4 +- gui/stmdsp.hpp | 2 +- gui/wxmain.cpp | 2 +- source/adc.cpp | 2 +- source/common.hpp | 15 +- source/error.hpp | 4 + source/main.cpp | 770 +++++++++--------- 11 files changed, 419 insertions(+), 425 deletions(-) diff --git a/ChibiOS_20.3.2/os/hal/ports/STM32/STM32H7xx/hal_lld.c b/ChibiOS_20.3.2/os/hal/ports/STM32/STM32H7xx/hal_lld.c index e9ffb25..20992be 100644 --- a/ChibiOS_20.3.2/os/hal/ports/STM32/STM32H7xx/hal_lld.c +++ b/ChibiOS_20.3.2/os/hal/ports/STM32/STM32H7xx/hal_lld.c @@ -168,11 +168,14 @@ void hal_lld_init(void) { /* MPU initialization.*/ #if (STM32_NOCACHE_SRAM1_SRAM2 == TRUE) || (STM32_NOCACHE_SRAM3 == TRUE) || \ - (STM32_NOCACHE_SRAM4 == TRUE) + (STM32_NOCACHE_ALLSRAM == TRUE) { uint32_t base, size; -#if (STM32_NOCACHE_SRAM1_SRAM2 == TRUE) && (STM32_NOCACHE_SRAM3 == TRUE) +#if (STM32_NOCACHE_ALLSRAM == TRUE) + base = 0x30000000U; + size = MPU_RASR_SIZE_256M; +#elif (STM32_NOCACHE_SRAM1_SRAM2 == TRUE) && (STM32_NOCACHE_SRAM3 == TRUE) base = 0x30000000U; size = MPU_RASR_SIZE_512K; #elif (STM32_NOCACHE_SRAM1_SRAM2 == TRUE) && (STM32_NOCACHE_SRAM3 == FALSE) @@ -181,9 +184,6 @@ void hal_lld_init(void) { #elif (STM32_NOCACHE_SRAM1_SRAM2 == FALSE) && (STM32_NOCACHE_SRAM3 == TRUE) base = 0x30040000U; size = MPU_RASR_SIZE_16K; -#elif (STM32_NOCACHE_SRAM4 == TRUE) - base = 0x38000000U; - size = MPU_RASR_SIZE_16K; #else #error "invalid constants used in mcuconf.h" #endif diff --git a/Makefile b/Makefile index 5e57974..a609c42 100644 --- a/Makefile +++ b/Makefile @@ -148,7 +148,7 @@ CPPWARN = -Wall -Wextra -Wundef # # List all user C define here, like -D_DEBUG=1 -UDEFS = #-DSTM32_ENFORCE_H7_REV_V # Must be removed for non-Rev-V devices. +UDEFS = -DCORTEX_ENABLE_WFI_IDLE=FALSE # Define ASM defines here UADEFS = diff --git a/STM32H723xG.ld b/STM32H723xG.ld index 065430a..eb3d63b 100644 --- a/STM32H723xG.ld +++ b/STM32H723xG.ld @@ -18,30 +18,31 @@ * STM32H743xI generic setup. * * AXI SRAM - BSS, Data, Heap. - * SRAM1+SRAM2 - None. - * SRAM4 - NOCACHE. + * SRAM1 - SIGGEN. + * SRAM2 - DAC. + * SRAM4 - ADC. * DTCM-RAM - Main Stack, Process Stack. * ITCM-RAM - STMDSP Algorithm. * BCKP SRAM - None. */ MEMORY { - flash0 (rx) : org = 0x08000000, len = 1M /* Flash bank1+bank2 */ - flash1 (rx) : org = 0x08000000, len = 512K /* Flash bank 1 */ - flash2 (rx) : org = 0x08080000, len = 512K /* Flash bank 2 */ + flash0 (rx) : org = 0x08000000, len = 1M /* Flash bank1+bank2 */ + flash1 (rx) : org = 0x08000000, len = 512K /* Flash bank 1 */ + flash2 (rx) : org = 0x08080000, len = 512K /* Flash bank 2 */ flash3 (rx) : org = 0x00000000, len = 0 flash4 (rx) : org = 0x00000000, len = 0 flash5 (rx) : org = 0x00000000, len = 0 flash6 (rx) : org = 0x00000000, len = 0 flash7 (rx) : org = 0x00000000, len = 0 ram0 (wx) : org = 0x24000000, len = 320k /* AXI SRAM */ - ram1 (wx) : org = 0x30000000, len = 32k /* AHB SRAM1+SRAM2 */ + ram1 (wx) : org = 0x30000000, len = 16k /* AHB SRAM1 */ ram2 (wx) : org = 0x30004000, len = 16k /* AHB SRAM2 */ ram3 (wx) : org = 0x38000000, len = 16k /* AHB SRAM4 */ ram4 (wx) : org = 0x00000000, len = 0 - ram5 (wx) : org = 0x20000000, len = 128k /* DTCM-RAM */ - ram6 (wx) : org = 0x00000000, len = 64k /* ITCM-RAM */ - ram7 (wx) : org = 0x38800000, len = 4k /* BCKP SRAM */ + ram5 (wx) : org = 0x20000000, len = 128k /* DTCM-RAM */ + ram6 (wx) : org = 0x00000000, len = 64k /* ITCM-RAM */ + ram7 (wx) : org = 0x38800000, len = 4k /* BCKP SRAM */ } /* For each data/text section two region are defined, a virtual region @@ -97,7 +98,7 @@ INCLUDE rules_stacks.ld /*===========================================================================*/ /* RAM region to be used for nocache segment.*/ -REGION_ALIAS("NOCACHE_RAM", ram3); +/*REGION_ALIAS("NOCACHE_RAM", ram3);*/ /* RAM region to be used for eth segment.*/ /*REGION_ALIAS("ETH_RAM", ram3);*/ @@ -105,7 +106,7 @@ REGION_ALIAS("NOCACHE_RAM", ram3); SECTIONS { /* Special section for non cache-able areas.*/ - .nocache (NOLOAD) : ALIGN(4) + /*.nocache (NOLOAD) : ALIGN(4) { __nocache_base__ = .; *(.nocache) @@ -113,7 +114,7 @@ SECTIONS *(.bss.__nocache_*) . = ALIGN(4); __nocache_end__ = .; - } > NOCACHE_RAM + } > NOCACHE_RAM*/ /* Special section for Ethernet DMA non cache-able areas.*/ /*.eth (NOLOAD) : ALIGN(4) diff --git a/cfg/halconf.h b/cfg/halconf.h index 1c77c25..24c6fde 100644 --- a/cfg/halconf.h +++ b/cfg/halconf.h @@ -230,7 +230,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) -#define ADC_USE_WAIT TRUE +#define ADC_USE_WAIT FALSE #endif /** @@ -238,7 +238,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define ADC_USE_MUTUAL_EXCLUSION TRUE +#define ADC_USE_MUTUAL_EXCLUSION FALSE #endif /*===========================================================================*/ @@ -290,7 +290,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__) -#define DAC_USE_WAIT TRUE +#define DAC_USE_WAIT FALSE #endif /** @@ -298,7 +298,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define DAC_USE_MUTUAL_EXCLUSION TRUE +#define DAC_USE_MUTUAL_EXCLUSION FALSE #endif /*===========================================================================*/ diff --git a/cfg/mcuconf.h b/cfg/mcuconf.h index af176ad..27a2939 100644 --- a/cfg/mcuconf.h +++ b/cfg/mcuconf.h @@ -46,8 +46,8 @@ */ #define STM32_NOCACHE_MPU_REGION MPU_REGION_6 #define STM32_NOCACHE_SRAM1_SRAM2 FALSE -#define STM32_NOCACHE_SRAM3 FALSE // keep -#define STM32_NOCACHE_SRAM4 TRUE +#define STM32_NOCACHE_SRAM3 FALSE +#define STM32_NOCACHE_ALLSRAM TRUE /* * PWR system settings. diff --git a/gui/stmdsp.hpp b/gui/stmdsp.hpp index 4551483..9db9df6 100644 --- a/gui/stmdsp.hpp +++ b/gui/stmdsp.hpp @@ -19,7 +19,7 @@ namespace stmdsp { - constexpr unsigned int SAMPLES_MAX = 3000; + constexpr unsigned int SAMPLES_MAX = 4000; class scanner { diff --git a/gui/wxmain.cpp b/gui/wxmain.cpp index dd32127..6991610 100644 --- a/gui/wxmain.cpp +++ b/gui/wxmain.cpp @@ -47,7 +47,7 @@ static const std::array srateNums { static const char *makefile_text = R"make( all: @arm-none-eabi-g++ -x c++ -Os -fno-exceptions -fno-rtti \ - -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 \ + -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 -mtune=cortex-m7 \ -nostartfiles \ -Wl,-Ttext-segment=0x00000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry \ $0 -o $0.o diff --git a/source/adc.cpp b/source/adc.cpp index 3bd6b39..3f334ac 100644 --- a/source/adc.cpp +++ b/source/adc.cpp @@ -24,7 +24,7 @@ ADCConversionGroup ADC::m_group_config = { .end_cb = ADC::conversionCallback, .error_cb = nullptr, .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13), /* TIM6_TRGO */ - .cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1, // Oversampling 2x + .cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x .ccr = 0, .pcsel = 0, .ltr1 = 0, .htr1 = 0x0FFF, diff --git a/source/common.hpp b/source/common.hpp index f6a8cd0..876cf74 100644 --- a/source/common.hpp +++ b/source/common.hpp @@ -1,12 +1,12 @@ #include #include -//#define ENABLE_SIGGEN - -constexpr unsigned int MAX_SAMPLE_BUFFER_SIZE = 4000; - using Sample = uint16_t; +// gives 8000 +constexpr unsigned int MAX_SAMPLE_BUFFER_BYTESIZE = 16384; +constexpr unsigned int MAX_SAMPLE_BUFFER_SIZE = MAX_SAMPLE_BUFFER_BYTESIZE / sizeof(Sample); + class SampleBuffer { public: @@ -14,12 +14,6 @@ public: m_buffer(buffer) {} void clear() { - /*static const Sample ref[21] = { - 100, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, - 2000, 2200, 2400, 2600, 2800, 3000, 3200, 3400, 3600, 3800, 4095 - }; - for (unsigned int i = 0; i < m_size; i++) - m_buffer[i] = ref[i % 21];*/ std::fill(m_buffer, m_buffer + m_size, 2048); } void modify(Sample *data, unsigned int srcsize) { @@ -60,7 +54,6 @@ public: } private: - //std::array m_buffer; Sample *m_buffer = nullptr; unsigned int m_size = MAX_SAMPLE_BUFFER_SIZE; Sample *m_modified = nullptr; diff --git a/source/error.hpp b/source/error.hpp index 699c746..6911792 100644 --- a/source/error.hpp +++ b/source/error.hpp @@ -27,6 +27,10 @@ public: return condition; } + bool hasError() { + return m_index > 0; + } + Error pop() { return m_index == 0 ? Error::None : m_queue[--m_index]; } diff --git a/source/main.cpp b/source/main.cpp index cc3138f..dd98980 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,387 +1,383 @@ -/** - * @file main.cpp - * @brief Program entry point. - * - * Copyright (C) 2020 Clyne Sullivan - * - * Distributed under the GNU GPL v3 or later. You should have received a copy of - * the GNU General Public License along with this program. - * If not, see . - */ - -#include "ch.h" -#include "hal.h" - -static_assert(sizeof(adcsample_t) == sizeof(uint16_t)); -static_assert(sizeof(dacsample_t) == sizeof(uint16_t)); - -#include "common.hpp" -#include "error.hpp" - -#include "adc.hpp" -#include "dac.hpp" -#include "elf_load.hpp" -#include "sclock.hpp" -#include "usbserial.hpp" - -#include - -constexpr unsigned int MAX_ELF_FILE_SIZE = 8 * 1024; - -enum class RunStatus : char -{ - Idle = '1', - Running -}; -static RunStatus run_status = RunStatus::Idle; - -#define MSG_CONVFIRST (1) -#define MSG_CONVSECOND (2) -#define MSG_CONVFIRST_MEASURE (3) -#define MSG_CONVSECOND_MEASURE (4) - -#define MSG_FOR_FIRST(m) (m & 1) -#define MSG_FOR_MEASURE(m) (m > 2) - -static msg_t conversionMBBuffer[4]; -static MAILBOX_DECL(conversionMB, conversionMBBuffer, 4); - -static THD_WORKING_AREA(conversionThreadWA, 2048); -static THD_FUNCTION(conversionThread, arg); - -static time_measurement_t conversion_time_measurement; - -static ErrorManager EM; - -static SampleBuffer samplesIn (reinterpret_cast(0x38000000)); -static SampleBuffer samplesOut (reinterpret_cast(0x38002000)); -#ifdef ENABLE_SIGGEN -static SampleBuffer samplesSigGen; -#endif - -static unsigned char elf_file_store[MAX_ELF_FILE_SIZE]; -static ELF::Entry elf_entry = nullptr; - -static void signal_operate(adcsample_t *buffer, size_t count); -static void signal_operate_measure(adcsample_t *buffer, size_t count); -static void main_loop(); - -static THD_WORKING_AREA(waThread1, 128); -static THD_FUNCTION(Thread1, arg); - -int main() -{ - // Initialize the RTOS - halInit(); - chSysInit(); - - //SCB_DisableDCache(); - - // Enable FPU - SCB->CPACR |= 0xF << 20; - - ADC::begin(); - DAC::begin(); - SClock::begin(); - USBSerial::begin(); - - SClock::setRate(SClock::Rate::R32K); - ADC::setRate(SClock::Rate::R32K); - - // Start the conversion manager thread - chTMObjectInit(&conversion_time_measurement); - chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, - nullptr); - chThdCreateStatic(conversionThreadWA, sizeof(conversionThreadWA), - NORMALPRIO, conversionThread, nullptr); - - main_loop(); -} - -void main_loop() -{ - - while (1) { - if (USBSerial::isActive()) { - // Attempt to receive a command packet - if (unsigned char cmd[3]; USBSerial::read(&cmd[0], 1) > 0) { - // Packet received, first byte represents the desired command/action - switch (cmd[0]) { - - case 'a': - USBSerial::write(samplesIn.bytedata(), samplesIn.bytesize()); - break; - case 'A': - USBSerial::read(samplesIn.bytedata(), samplesIn.bytesize()); - break; - - case 'B': - if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) && - EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) - { - unsigned int count = (cmd[1] | (cmd[2] << 8)) * 2; - if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) { - samplesIn.setSize(count); - samplesOut.setSize(count); - } - } - break; - - case 'd': - USBSerial::write(samplesOut.bytedata(), samplesOut.bytesize()); - break; -#ifdef ENABLE_SIGGEN - case 'D': - if (EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) { - unsigned int count = cmd[1] | (cmd[2] << 8); - if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) { - samplesSigGen.setSize(count); - USBSerial::read(samplesSigGen.bytedata(), samplesSigGen.bytesize()); - } - } - break; -#endif - - // 'E' - Reads in and loads the compiled conversion code binary from USB. - case 'E': - if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) && - EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) - { - // Only load the binary if it can fit in the memory reserved for it. - unsigned int size = cmd[1] | (cmd[2] << 8); - if (EM.assert(size < sizeof(elf_file_store), Error::BadUserCodeSize)) { - USBSerial::read(elf_file_store, size); - elf_entry = ELF::load(elf_file_store); - - EM.assert(elf_entry != nullptr, Error::BadUserCodeLoad); - } - } - break; - - // 'e' - Unloads the currently loaded conversion code - case 'e': - elf_entry = nullptr; - break; - - // 'i' - Sends an identifying string to confirm that this is the stmdsp device. - case 'i': - USBSerial::write(reinterpret_cast("stmdsp"), 6); - break; - - // 'I' - Sends the current run status. - case 'I': - { - unsigned char buf[2] = { - static_cast(run_status), - static_cast(EM.pop()) - }; - USBSerial::write(buf, sizeof(buf)); - } - break; - - // 'M' - Begins continuous sampling, but measures the execution time of the first - // sample processing. This duration can be later read through 'm'. - case 'M': - if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) { - run_status = RunStatus::Running; - samplesOut.clear(); - ADC::start(samplesIn.data(), samplesIn.size(), signal_operate_measure); - DAC::start(0, samplesOut.data(), samplesOut.size()); - } - break; - - // 'm' - Returns the last measured sample processing time, presumably in processor - // ticks. - case 'm': - USBSerial::write(reinterpret_cast(&conversion_time_measurement.last), - sizeof(rtcnt_t)); - break; - - // 'R' - Begin continuous sampling/conversion of the ADC. Samples will go through - // the conversion code, and will be sent out over the DAC. - case 'R': - if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) { - run_status = RunStatus::Running; - samplesOut.clear(); - ADC::start(samplesIn.data(), samplesIn.size(), signal_operate); - DAC::start(0, samplesOut.data(), samplesOut.size()); - } - break; - - case 'r': - if (EM.assert(USBSerial::read(&cmd[1], 1) == 1, Error::BadParamSize)) { - if (cmd[1] == 0xFF) { - unsigned char r = SClock::getRate(); - USBSerial::write(&r, 1); - } else { - auto r = static_cast(cmd[1]); - SClock::setRate(r); - ADC::setRate(r); - } - } - break; - - // 'S' - Stops the continuous sampling/conversion. - case 'S': - if (run_status == RunStatus::Running) { - DAC::stop(0); - ADC::stop(); - run_status = RunStatus::Idle; - } - break; - - case 's': - if (auto samps = samplesOut.modified(); samps != nullptr) { - unsigned char buf[2] = { - static_cast(samplesOut.size() / 2 & 0xFF), - static_cast(((samplesOut.size() / 2) >> 8) & 0xFF) - }; - USBSerial::write(buf, 2); - unsigned int total = samplesOut.bytesize() / 2; - unsigned int offset = 0; - unsigned char unused; - while (total > 512) { - USBSerial::write(reinterpret_cast(samps) + offset, 512); - while (USBSerial::read(&unused, 1) == 0); - offset += 512; - total -= 512; - } - USBSerial::write(reinterpret_cast(samps) + offset, total); - while (USBSerial::read(&unused, 1) == 0); - } else { - USBSerial::write(reinterpret_cast("\0\0"), 2); - } - break; - -#ifdef ENABLE_SIGGEN - case 'W': - DAC::start(1, samplesSigGen.data(), samplesSigGen.size()); - break; - case 'w': - DAC::stop(1); - break; -#endif - - default: - break; - } - } - } - - chThdSleepMicroseconds(100); - } -} - -void conversion_abort() -{ - elf_entry = nullptr; - DAC::stop(0); - ADC::stop(); - EM.add(Error::ConversionAborted); -} - -THD_FUNCTION(conversionThread, arg) -{ - (void)arg; - - while (1) { - msg_t message; - if (chMBFetchTimeout(&conversionMB, &message, TIME_INFINITE) == MSG_OK) { - auto samples = MSG_FOR_FIRST(message) ? samplesIn.data() : samplesIn.middata(); - auto size = samplesIn.size() / 2; - - if (elf_entry) { - if (!MSG_FOR_MEASURE(message)) { - samples = elf_entry(samples, size); - } else { - chTMStartMeasurementX(&conversion_time_measurement); - samples = elf_entry(samples, size); - chTMStopMeasurementX(&conversion_time_measurement); - } - } - - if (MSG_FOR_FIRST(message)) - samplesOut.modify(samples, size); - else - samplesOut.midmodify(samples, size); - } - } -} - -void signal_operate(adcsample_t *buffer, size_t) -{ - chSysLockFromISR(); - - if (chMBGetUsedCountI(&conversionMB) > 1) { - chSysUnlockFromISR(); - conversion_abort(); - } else { - chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST : MSG_CONVSECOND); - chSysUnlockFromISR(); - } -} - -void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count) -{ - chSysLockFromISR(); - chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST_MEASURE : MSG_CONVSECOND_MEASURE); - chSysUnlockFromISR(); - - ADC::setOperation(signal_operate); -} - -THD_FUNCTION(Thread1, arg) -{ - (void)arg; - while (1) { - palSetLine(LINE_LED1); - chThdSleepMilliseconds(70); - palSetLine(LINE_LED2); - chThdSleepMilliseconds(70); - palSetLine(LINE_LED3); - chThdSleepMilliseconds(240); - palClearLine(LINE_LED1); - chThdSleepMilliseconds(70); - palClearLine(LINE_LED2); - chThdSleepMilliseconds(70); - palClearLine(LINE_LED3); - chThdSleepMilliseconds(240); - } -} - -extern "C" { - -__attribute__((naked)) -void HardFault_Handler() -{ - while (1); -// //asm("push {lr}"); -// -// uint32_t *stack; -// uint32_t lr; -// asm("\ -// tst lr, #4; \ -// ite eq; \ -// mrseq %0, msp; \ -// mrsne %0, psp; \ -// mov %1, lr; \ -// " : "=r" (stack), "=r" (lr)); -// //stack++; -// stack[7] |= (1 << 24); // Keep Thumb mode enabled -// -// conversion_abort(); -// -// // TODO test lr and decide how to recover -// -// //if (run_status == RunStatus::Converting) { -// stack[6] = stack[5]; // Escape from elf_entry code -// //} else /*if (run_status == RunStatus::Recovered)*/ { -// // stack[6] = (uint32_t)main_loop & ~1; // Return to safety -// //} -// -// //asm("pop {lr}; bx lr"); -// asm("bx lr"); -} - -} // extern "C" - +/** + * @file main.cpp + * @brief Program entry point. + * + * Copyright (C) 2020 Clyne Sullivan + * + * Distributed under the GNU GPL v3 or later. You should have received a copy of + * the GNU General Public License along with this program. + * If not, see . + */ + +#include "ch.h" +#include "hal.h" + +static_assert(sizeof(adcsample_t) == sizeof(uint16_t)); +static_assert(sizeof(dacsample_t) == sizeof(uint16_t)); + +#include "common.hpp" +#include "error.hpp" + +#include "adc.hpp" +#include "dac.hpp" +#include "elf_load.hpp" +#include "sclock.hpp" +#include "usbserial.hpp" + +#include + +constexpr unsigned int MAX_ELF_FILE_SIZE = 8 * 1024; + +enum class RunStatus : char +{ + Idle = '1', + Running +}; +static RunStatus run_status = RunStatus::Idle; + +#define MSG_CONVFIRST (1) +#define MSG_CONVSECOND (2) +#define MSG_CONVFIRST_MEASURE (3) +#define MSG_CONVSECOND_MEASURE (4) + +#define MSG_FOR_FIRST(m) (m & 1) +#define MSG_FOR_MEASURE(m) (m > 2) + +static msg_t conversionMBBuffer[4]; +static MAILBOX_DECL(conversionMB, conversionMBBuffer, 4); + +static THD_WORKING_AREA(conversionThreadWA, 2048); +static THD_FUNCTION(conversionThread, arg); + +static time_measurement_t conversion_time_measurement; + +static ErrorManager EM; + +static SampleBuffer samplesIn (reinterpret_cast(0x38000000)); // 16k +static SampleBuffer samplesOut (reinterpret_cast(0x30004000)); // 16k +static SampleBuffer samplesSigGen (reinterpret_cast(0x30000000)); // 16k + +static unsigned char elf_file_store[MAX_ELF_FILE_SIZE]; +static ELF::Entry elf_entry = nullptr; + +static void signal_operate(adcsample_t *buffer, size_t count); +static void signal_operate_measure(adcsample_t *buffer, size_t count); +static void main_loop(); + +static THD_WORKING_AREA(waThread1, 128); +static THD_FUNCTION(Thread1, arg); + +int main() +{ + // Initialize the RTOS + halInit(); + chSysInit(); + + // Enable FPU + SCB->CPACR |= 0xF << 20; + + ADC::begin(); + DAC::begin(); + SClock::begin(); + USBSerial::begin(); + + SClock::setRate(SClock::Rate::R32K); + ADC::setRate(SClock::Rate::R32K); + + chTMObjectInit(&conversion_time_measurement); + chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, nullptr); + chThdCreateStatic(conversionThreadWA, sizeof(conversionThreadWA), + NORMALPRIO, conversionThread, nullptr); + + main_loop(); +} + +void main_loop() +{ + + while (1) { + if (USBSerial::isActive()) { + // Attempt to receive a command packet + if (unsigned char cmd[3]; USBSerial::read(&cmd[0], 1) > 0) { + // Packet received, first byte represents the desired command/action + switch (cmd[0]) { + + case 'a': + USBSerial::write(samplesIn.bytedata(), samplesIn.bytesize()); + break; + case 'A': + USBSerial::read(samplesIn.bytedata(), samplesIn.bytesize()); + break; + + case 'B': + if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) && + EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) + { + unsigned int count = (cmd[1] | (cmd[2] << 8)) * 2; + if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) { + samplesIn.setSize(count); + samplesOut.setSize(count); + } + } + break; + + case 'd': + USBSerial::write(samplesOut.bytedata(), samplesOut.bytesize()); + break; + case 'D': + if (EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) { + unsigned int count = cmd[1] | (cmd[2] << 8); + if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) { + samplesSigGen.setSize(count); + USBSerial::read(samplesSigGen.bytedata(), samplesSigGen.bytesize()); + } + } + break; + + // 'E' - Reads in and loads the compiled conversion code binary from USB. + case 'E': + if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) && + EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) + { + // Only load the binary if it can fit in the memory reserved for it. + unsigned int size = cmd[1] | (cmd[2] << 8); + if (EM.assert(size < sizeof(elf_file_store), Error::BadUserCodeSize)) { + USBSerial::read(elf_file_store, size); + elf_entry = ELF::load(elf_file_store); + + EM.assert(elf_entry != nullptr, Error::BadUserCodeLoad); + } + } + break; + + // 'e' - Unloads the currently loaded conversion code + case 'e': + elf_entry = nullptr; + break; + + // 'i' - Sends an identifying string to confirm that this is the stmdsp device. + case 'i': + USBSerial::write(reinterpret_cast("stmdsp"), 6); + break; + + // 'I' - Sends the current run status. + case 'I': + { + unsigned char buf[2] = { + static_cast(run_status), + static_cast(EM.pop()) + }; + USBSerial::write(buf, sizeof(buf)); + } + break; + + // 'M' - Begins continuous sampling, but measures the execution time of the first + // sample processing. This duration can be later read through 'm'. + case 'M': + if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) { + run_status = RunStatus::Running; + samplesOut.clear(); + ADC::start(samplesIn.data(), samplesIn.size(), signal_operate_measure); + DAC::start(0, samplesOut.data(), samplesOut.size()); + } + break; + + // 'm' - Returns the last measured sample processing time, presumably in processor + // ticks. + case 'm': + USBSerial::write(reinterpret_cast(&conversion_time_measurement.last), + sizeof(rtcnt_t)); + break; + + // 'R' - Begin continuous sampling/conversion of the ADC. Samples will go through + // the conversion code, and will be sent out over the DAC. + case 'R': + if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) { + run_status = RunStatus::Running; + samplesOut.clear(); + ADC::start(samplesIn.data(), samplesIn.size(), signal_operate); + DAC::start(0, samplesOut.data(), samplesOut.size()); + } + break; + + case 'r': + if (EM.assert(USBSerial::read(&cmd[1], 1) == 1, Error::BadParamSize)) { + if (cmd[1] == 0xFF) { + unsigned char r = SClock::getRate(); + USBSerial::write(&r, 1); + } else { + auto r = static_cast(cmd[1]); + SClock::setRate(r); + ADC::setRate(r); + } + } + break; + + // 'S' - Stops the continuous sampling/conversion. + case 'S': + if (run_status == RunStatus::Running) { + DAC::stop(0); + ADC::stop(); + run_status = RunStatus::Idle; + } + break; + + case 's': + if (auto samps = samplesOut.modified(); samps != nullptr) { + unsigned char buf[2] = { + static_cast(samplesOut.size() / 2 & 0xFF), + static_cast(((samplesOut.size() / 2) >> 8) & 0xFF) + }; + USBSerial::write(buf, 2); + unsigned int total = samplesOut.bytesize() / 2; + unsigned int offset = 0; + unsigned char unused; + while (total > 512) { + USBSerial::write(reinterpret_cast(samps) + offset, 512); + while (USBSerial::read(&unused, 1) == 0); + offset += 512; + total -= 512; + } + USBSerial::write(reinterpret_cast(samps) + offset, total); + while (USBSerial::read(&unused, 1) == 0); + } else { + USBSerial::write(reinterpret_cast("\0\0"), 2); + } + break; + + case 'W': + DAC::start(1, samplesSigGen.data(), samplesSigGen.size()); + break; + case 'w': + DAC::stop(1); + break; + + default: + break; + } + } + } + + chThdSleepMicroseconds(100); + } +} + +void conversion_abort() +{ + elf_entry = nullptr; + DAC::stop(0); + ADC::stop(); + EM.add(Error::ConversionAborted); +} + +THD_FUNCTION(conversionThread, arg) +{ + (void)arg; + + while (1) { + msg_t message; + if (chMBFetchTimeout(&conversionMB, &message, TIME_INFINITE) == MSG_OK) { + auto samples = MSG_FOR_FIRST(message) ? samplesIn.data() : samplesIn.middata(); + auto size = samplesIn.size() / 2; + + if (elf_entry) { + if (!MSG_FOR_MEASURE(message)) { + samples = elf_entry(samples, size); + } else { + chTMStartMeasurementX(&conversion_time_measurement); + samples = elf_entry(samples, size); + chTMStopMeasurementX(&conversion_time_measurement); + } + } + + if (MSG_FOR_FIRST(message)) + samplesOut.modify(samples, size); + else + samplesOut.midmodify(samples, size); + } + } +} + +void signal_operate(adcsample_t *buffer, size_t) +{ + chSysLockFromISR(); + + if (chMBGetUsedCountI(&conversionMB) > 1) { + chSysUnlockFromISR(); + conversion_abort(); + } else { + chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST : MSG_CONVSECOND); + chSysUnlockFromISR(); + } +} + +void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count) +{ + chSysLockFromISR(); + chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST_MEASURE : MSG_CONVSECOND_MEASURE); + chSysUnlockFromISR(); + + ADC::setOperation(signal_operate); +} + +THD_FUNCTION(Thread1, arg) +{ + (void)arg; + + bool erroron = false; + while (1) { + bool isidle = run_status == RunStatus::Idle; + auto led = isidle ? LINE_LED_GREEN : LINE_LED_YELLOW; + auto delay = isidle ? 500 : 250; + + palSetLine(led); + chThdSleepMilliseconds(delay); + palClearLine(led); + chThdSleepMilliseconds(delay); + + if (auto err = EM.hasError(); err ^ erroron) { + erroron = err; + if (err) + palSetLine(LINE_LED_RED); + else + palClearLine(LINE_LED_RED); + } + } +} + +extern "C" { + +__attribute__((naked)) +void HardFault_Handler() +{ + while (1); +// //asm("push {lr}"); +// +// uint32_t *stack; +// uint32_t lr; +// asm("\ +// tst lr, #4; \ +// ite eq; \ +// mrseq %0, msp; \ +// mrsne %0, psp; \ +// mov %1, lr; \ +// " : "=r" (stack), "=r" (lr)); +// //stack++; +// stack[7] |= (1 << 24); // Keep Thumb mode enabled +// +// conversion_abort(); +// +// // TODO test lr and decide how to recover +// +// //if (run_status == RunStatus::Converting) { +// stack[6] = stack[5]; // Escape from elf_entry code +// //} else /*if (run_status == RunStatus::Recovered)*/ { +// // stack[6] = (uint32_t)main_loop & ~1; // Return to safety +// //} +// +// //asm("pop {lr}; bx lr"); +// asm("bx lr"); +} + +} // extern "C" +