]> code.bitgloo.com Git - clyne/stmdsp.git/commitdiff
reorganized source, wip
authorClyne Sullivan <clyne@clyne-lp.lan>
Sat, 31 Jul 2021 14:47:00 +0000 (10:47 -0400)
committerClyne Sullivan <clyne@clyne-lp.lan>
Sat, 31 Jul 2021 14:47:00 +0000 (10:47 -0400)
45 files changed:
Makefile
openocd.cfg
source/adc.cpp [deleted file]
source/adc.hpp [deleted file]
source/board/board_h7.c
source/board/board_l4.c
source/board/l4/board.h
source/communication.cpp [new file with mode: 0644]
source/communication.hpp [new file with mode: 0644]
source/conversion.cpp [new file with mode: 0644]
source/conversion.hpp [new file with mode: 0644]
source/cordic.cpp [deleted file]
source/cordic.hpp [deleted file]
source/dac.cpp [deleted file]
source/dac.hpp [deleted file]
source/elf.h [new file with mode: 0644]
source/elf_format.hpp [deleted file]
source/elf_load.cpp [deleted file]
source/elf_load.hpp [deleted file]
source/elfload.cpp [new file with mode: 0644]
source/elfload.hpp [new file with mode: 0644]
source/error.cpp [new file with mode: 0644]
source/error.hpp
source/handlers.cpp [new file with mode: 0644]
source/handlers.hpp [new file with mode: 0644]
source/main.cpp
source/monitor.cpp [new file with mode: 0644]
source/monitor.hpp [new file with mode: 0644]
source/periph/adc.cpp [new file with mode: 0644]
source/periph/adc.hpp [new file with mode: 0644]
source/periph/cordic.cpp [new file with mode: 0644]
source/periph/cordic.hpp [new file with mode: 0644]
source/periph/dac.cpp [new file with mode: 0644]
source/periph/dac.hpp [new file with mode: 0644]
source/periph/usbcfg.c [new file with mode: 0644]
source/periph/usbcfg.h [new file with mode: 0644]
source/periph/usbserial.cpp [new file with mode: 0644]
source/periph/usbserial.hpp [new file with mode: 0644]
source/runstatus.hpp [new file with mode: 0644]
source/samples.cpp [new file with mode: 0644]
source/samples.hpp [new file with mode: 0644]
source/usbcfg.c [deleted file]
source/usbcfg.h [deleted file]
source/usbserial.cpp [deleted file]
source/usbserial.hpp [deleted file]

index bec53097e453dec3268a4d4f58ccd67ff1032ffc..e308e1eb08058ec9a5f55168c42665e4f9212e8c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -128,8 +128,8 @@ include $(CHIBIOS)/os/rt/rt.mk
 include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk
 # Auto-build files in ./source recursively.
 #include $(CHIBIOS)/tools/mk/autobuild.mk
-ALLCSRC += $(wildcard source/*.c)
-ALLCPPSRC += $(wildcard source/*.cpp)
+ALLCSRC += $(wildcard source/*.c) $(wildcard source/periph/*.c)
+ALLCPPSRC += $(wildcard source/*.cpp) $(wildcard source/periph/*.cpp)
 ALLASMSRC += $(wildcard source/*.s)
 # Other files (optional).
 #include $(CHIBIOS)/test/lib/test.mk
@@ -158,7 +158,8 @@ ASMSRC = $(ALLASMSRC)
 ASMXSRC = $(ALLXASMSRC)
 
 # Inclusion directories.
-INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC)
+INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) \
+         source source/periph
 
 # Define C warning options here.
 CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes -pedantic
index 05991652b8e4589c79101cab38f2959aea6b980e..47e6c7d28ff8565c55795e21f4bf025b5c1ba125 100644 (file)
@@ -1,3 +1,3 @@
 source [find interface/stlink.cfg]
-source [find target/stm32h7x.cfg]
+source [find target/stm32l4x.cfg]
 
diff --git a/source/adc.cpp b/source/adc.cpp
deleted file mode 100644 (file)
index 2a4fa38..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/**
- * @file adc.cpp
- * @brief Manages signal reading through the ADC.
- *
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-#include "adc.hpp"
-
-#if defined(TARGET_PLATFORM_L4)
-ADCDriver *ADC::m_driver = &ADCD1;
-ADCDriver *ADC::m_driver2 = &ADCD3;
-#else
-ADCDriver *ADC::m_driver = &ADCD3;
-//ADCDriver *ADC::m_driver2 = &ADCD1; // TODO
-#endif
-
-const ADCConfig ADC::m_config = {
-    .difsel = 0,
-#if defined(TARGET_PLATFORM_H7)
-    .calibration = 0,
-#endif
-};
-
-const ADCConfig ADC::m_config2 = {
-    .difsel = 0,
-#if defined(TARGET_PLATFORM_H7)
-    .calibration = 0,
-#endif
-};
-
-ADCConversionGroup ADC::m_group_config = {
-    .circular = true,
-    .num_channels = 1,
-    .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_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
-#if defined(TARGET_PLATFORM_H7)
-    .ccr = 0,
-    .pcsel = 0,
-    .ltr1 = 0, .htr1 = 4095,
-    .ltr2 = 0, .htr2 = 4095,
-    .ltr3 = 0, .htr3 = 4095,
-#else
-    .tr1 = ADC_TR(0, 4095),
-    .tr2 = ADC_TR(0, 4095),
-    .tr3 = ADC_TR(0, 4095),
-    .awd2cr = 0,
-    .awd3cr = 0,
-#endif
-    .smpr = {
-        ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5), 0
-    },
-    .sqr = {
-        ADC_SQR1_SQ1_N(ADC_CHANNEL_IN5),
-        0, 0, 0
-    },
-};
-
-static bool readAltDone = false;
-static void readAltCallback(ADCDriver *)
-{
-    readAltDone = true;
-}
-ADCConversionGroup ADC::m_group_config2 = {
-    .circular = false,
-    .num_channels = 2,
-    .end_cb = readAltCallback,
-    .error_cb = nullptr,
-    .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13),  /* TIM6_TRGO */
-    .cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
-#if defined(TARGET_PLATFORM_H7)
-    .ccr = 0,
-    .pcsel = 0,
-    .ltr1 = 0, .htr1 = 4095,
-    .ltr2 = 0, .htr2 = 4095,
-    .ltr3 = 0, .htr3 = 4095,
-#else
-    .tr1 = ADC_TR(0, 4095),
-    .tr2 = ADC_TR(0, 4095),
-    .tr3 = ADC_TR(0, 4095),
-    .awd2cr = 0,
-    .awd3cr = 0,
-#endif
-    .smpr = {
-        ADC_SMPR1_SMP_AN1(ADC_SMPR_SMP_12P5) | ADC_SMPR1_SMP_AN2(ADC_SMPR_SMP_12P5), 0
-    },
-    .sqr = {
-        ADC_SQR1_SQ1_N(ADC_CHANNEL_IN1) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN2),
-        0, 0, 0
-    },
-};
-
-adcsample_t *ADC::m_current_buffer = nullptr;
-size_t ADC::m_current_buffer_size = 0;
-ADC::Operation ADC::m_operation = nullptr;
-
-void ADC::begin()
-{
-#if defined(TARGET_PLATFORM_H7)
-    palSetPadMode(GPIOF, 3, PAL_MODE_INPUT_ANALOG);
-#else
-    palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG); // Algorithm in
-    palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG); // Potentiometer 1
-    palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG); // Potentiometer 2
-#endif
-
-    adcStart(m_driver, &m_config);
-    adcStart(m_driver2, &m_config2);
-}
-
-void ADC::start(adcsample_t *buffer, size_t count, Operation operation)
-{
-    m_current_buffer = buffer;
-    m_current_buffer_size = count;
-    m_operation = operation;
-
-    adcStartConversion(m_driver, &m_group_config, buffer, count);
-    SClock::start();
-}
-
-void ADC::stop()
-{
-    SClock::stop();
-    adcStopConversion(m_driver);
-
-    m_current_buffer = nullptr;
-    m_current_buffer_size = 0;
-    m_operation = nullptr;
-}
-
-adcsample_t ADC::readAlt(unsigned int id)
-{
-    if (id > 1)
-        return 0;
-    static adcsample_t result[16] = {};
-    readAltDone = false;
-    adcStartConversion(m_driver2, &m_group_config2, result, 8);
-    while (!readAltDone)
-        __WFI();
-    adcStopConversion(m_driver2);
-    return result[id];
-}
-
-void ADC::setRate(SClock::Rate rate)
-{
-#if defined(TARGET_PLATFORM_H7)
-    std::array<std::array<uint32_t, 2>, 6> m_rate_presets = {{
-         // Rate   PLL N  PLL P
-        {/* 8k  */ 80,    20},
-        {/* 16k */ 80,    10},
-        {/* 20k */ 80,    8},
-        {/* 32k */ 80,    5},
-        {/* 48k */ 96,    4},
-        {/* 96k */ 288,   10}
-    }};
-
-    auto& preset = m_rate_presets[static_cast<unsigned int>(rate)];
-    auto pllbits = (preset[0] << RCC_PLL2DIVR_N2_Pos) |
-                   (preset[1] << RCC_PLL2DIVR_P2_Pos);
-
-    adcStop(m_driver);
-
-    // Adjust PLL2
-    RCC->CR &= ~(RCC_CR_PLL2ON);
-    while ((RCC->CR & RCC_CR_PLL2RDY) == RCC_CR_PLL2RDY);
-    auto pll2divr = RCC->PLL2DIVR &
-                    ~(RCC_PLL2DIVR_N2_Msk | RCC_PLL2DIVR_P2_Msk);
-    pll2divr |= pllbits;
-    RCC->PLL2DIVR = pll2divr;
-    RCC->CR |= RCC_CR_PLL2ON;
-    while ((RCC->CR & RCC_CR_PLL2RDY) != RCC_CR_PLL2RDY);
-
-    m_group_config.smpr[0] = rate != SClock::Rate::R96K ? ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5)
-                                                        : ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_2P5);
-
-    adcStart(m_driver, &m_config);
-#elif defined(TARGET_PLATFORM_L4)
-    std::array<std::array<uint32_t, 3>, 6> m_rate_presets = {{
-        //  Rate   PLLSAI2N  R  SMPR
-        {/* 8k  */ 8,        1, ADC_SMPR_SMP_12P5},
-        {/* 16k */ 16,       1, ADC_SMPR_SMP_12P5},
-        {/* 20k */ 20,       1, ADC_SMPR_SMP_12P5},
-        {/* 32k */ 32,       1, ADC_SMPR_SMP_12P5},
-        {/* 48k */ 24,       0, ADC_SMPR_SMP_12P5},
-        {/* 96k */ 73,       1, ADC_SMPR_SMP_6P5}   // Technically 96.05263kS/s
-    }};
-
-    auto& preset = m_rate_presets[static_cast<int>(rate)];
-    auto pllnr = (preset[0] << RCC_PLLSAI2CFGR_PLLSAI2N_Pos) |
-                 (preset[1] << RCC_PLLSAI2CFGR_PLLSAI2R_Pos);
-    auto smpr = preset[2];
-
-    // Adjust PLLSAI2
-    RCC->CR &= ~(RCC_CR_PLLSAI2ON);
-    while ((RCC->CR & RCC_CR_PLLSAI2RDY) == RCC_CR_PLLSAI2RDY);
-    RCC->PLLSAI2CFGR = (RCC->PLLSAI2CFGR & ~(RCC_PLLSAI2CFGR_PLLSAI2N_Msk | RCC_PLLSAI2CFGR_PLLSAI2R_Msk)) | pllnr;
-    RCC->CR |= RCC_CR_PLLSAI2ON;
-    while ((RCC->CR & RCC_CR_PLLSAI2RDY) != RCC_CR_PLLSAI2RDY);
-
-    m_group_config.smpr[0] = ADC_SMPR1_SMP_AN5(smpr);
-
-    // Set 2x oversampling
-    m_group_config.cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1;
-    m_group_config2.cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1;
-#endif
-}
-
-void ADC::setOperation(ADC::Operation operation)
-{
-    m_operation = operation;
-}
-
-void ADC::conversionCallback(ADCDriver *driver)
-{
-    if (m_operation != nullptr) {
-        auto half_size = m_current_buffer_size / 2;
-        if (adcIsBufferComplete(driver))
-            m_operation(m_current_buffer + half_size, half_size);
-        else
-            m_operation(m_current_buffer, half_size);
-    }
-}
-
diff --git a/source/adc.hpp b/source/adc.hpp
deleted file mode 100644 (file)
index 5f7fa08..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * @file adc.hpp
- * @brief Manages signal reading through the ADC.
- *
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-#ifndef STMDSP_ADC_HPP_
-#define STMDSP_ADC_HPP_
-
-#include "hal.h"
-#include "sclock.hpp"
-
-#include <array>
-
-class ADC
-{
-public:
-    using Operation = void (*)(adcsample_t *buffer, size_t count);
-
-    static void begin();
-
-    static void start(adcsample_t *buffer, size_t count, Operation operation);
-    static void stop();
-
-    static adcsample_t readAlt(unsigned int id);
-
-    static void setRate(SClock::Rate rate);
-    static void setOperation(Operation operation);
-
-private:
-    static ADCDriver *m_driver;
-    static ADCDriver *m_driver2;
-
-    static const ADCConfig m_config;
-    static const ADCConfig m_config2;
-    static ADCConversionGroup m_group_config;
-    static ADCConversionGroup m_group_config2;
-
-    static adcsample_t *m_current_buffer;
-    static size_t m_current_buffer_size;
-    static Operation m_operation;
-
-public:
-    static void conversionCallback(ADCDriver *);
-};
-
-#endif // STMDSP_ADC_HPP_
-
index 286872691c57005af155232e1b5cdc02d1f45d40..74285cf606822c46acfe3479d8e97856ced943bb 100644 (file)
@@ -262,5 +262,26 @@ bool mmc_lld_is_write_protected(MMCDriver *mmcp) {
  * @note    You can add your board-specific code here.\r
  */\r
 void boardInit(void) {\r
+    // Enable the FPU (floating-point unit)\r
+    SCB->CPACR |= 0xF << 20;\r
 \r
+    // Setup the MPU (memory protection unit):\r
+    //     Region 2: Data for algorithm thread\r
+    //     Region 3: Code for algorithm thread\r
+    //     Region 4: User algorithm code\r
+    mpuConfigureRegion(MPU_REGION_2,\r
+                       0x20000000,\r
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_64K |\r
+                       MPU_RASR_ENABLE);\r
+    mpuConfigureRegion(MPU_REGION_3,\r
+                       0x0807F800,\r
+                       MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_2K |\r
+                       MPU_RASR_ENABLE);\r
+    mpuConfigureRegion(MPU_REGION_4,\r
+                       0x00000000,\r
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_64K |\r
+                       MPU_RASR_ENABLE);\r
 }\r
index cd16e432e6f6589a9ac428c07d663b01a8622ef8..31d1d518a1d5a2874decc0f66e2ca82bd3e66611 100644 (file)
@@ -277,5 +277,28 @@ bool mmc_lld_is_write_protected(MMCDriver *mmcp) {
  * @note    You can add your board-specific code here.\r
  */\r
 void boardInit(void) {\r
+    palSetLineMode(LINE_LED_GREEN, PAL_MODE_OUTPUT_PUSHPULL);\r
+    palSetLineMode(LINE_LED_YELLOW, PAL_MODE_OUTPUT_PUSHPULL);\r
+    palSetLineMode(LINE_LED_RED, PAL_MODE_OUTPUT_PUSHPULL);\r
 \r
+    SCB->CPACR |= 0xF << 20; // Enable FPU\r
+\r
+    // Region 2: Data for algorithm thread and ADC/DAC buffers\r
+    // Region 3: Code for algorithm thread\r
+    // Region 4: User algorithm code\r
+    mpuConfigureRegion(MPU_REGION_2,\r
+                       0x20008000,\r
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_128K |\r
+                       MPU_RASR_ENABLE);\r
+    mpuConfigureRegion(MPU_REGION_3,\r
+                       0x0807F800,\r
+                       MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_2K |\r
+                       MPU_RASR_ENABLE);\r
+    mpuConfigureRegion(MPU_REGION_4,\r
+                       0x10000000,\r
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_32K |\r
+                       MPU_RASR_ENABLE);\r
 }\r
index 8291664639acdfbcd6c27159d4da25f256944776..e4dcf03cc393d8dd2862182f8a6c5d3d86e5b6db 100644 (file)
@@ -1502,4 +1502,8 @@ extern "C" {
 #endif\r
 #endif /* _FROM_ASM_ */\r
 \r
+#define LINE_LED_GREEN  PAL_LINE(GPIOC_BASE, 10U)\r
+#define LINE_LED_YELLOW PAL_LINE(GPIOC_BASE, 11U)\r
+#define LINE_LED_RED    PAL_LINE(GPIOC_BASE, 12U)\r
+\r
 #endif /* BOARD_H */\r
diff --git a/source/communication.cpp b/source/communication.cpp
new file mode 100644 (file)
index 0000000..5835aa5
--- /dev/null
@@ -0,0 +1,291 @@
+#include "communication.hpp"
+
+#include "ch.h"
+#include "hal.h"
+
+#include "periph/adc.hpp"
+#include "periph/dac.hpp"
+#include "periph/usbserial.hpp"
+#include "elfload.hpp"
+#include "error.hpp"
+#include "conversion.hpp"
+#include "runstatus.hpp"
+#include "samples.hpp"
+
+#include <algorithm>
+#include <tuple>
+
+__attribute__((section(".stacks")))
+std::array<char, 4096> CommunicationManager::m_thread_stack = {};
+
+void CommunicationManager::begin()
+{
+    chThdCreateStatic(m_thread_stack.data(),
+                      m_thread_stack.size(),
+                      NORMALPRIO,
+                      threadComm,
+                      nullptr);
+}
+
+static void writeADCBuffer(unsigned char *);
+static void setBufferSize(unsigned char *);
+static void updateGenerator(unsigned char *);
+static void loadAlgorithm(unsigned char *);
+static void readStatus(unsigned char *);
+static void startConversionMeasure(unsigned char *);
+static void startConversion(unsigned char *);
+static void stopConversion(unsigned char *);
+static void startGenerator(unsigned char *);
+static void readADCBuffer(unsigned char *);
+static void readDACBuffer(unsigned char *);
+static void unloadAlgorithm(unsigned char *);
+static void readIdentifier(unsigned char *);
+static void readExecTime(unsigned char *);
+static void sampleRate(unsigned char *);
+static void readConversionResults(unsigned char *);
+static void readConversionInput(unsigned char *);
+static void readMessage(unsigned char *);
+static void stopGenerator(unsigned char *);
+
+static const std::array<std::pair<char, void (*)(unsigned char *)>, 19> commandTable {{
+    {'A', writeADCBuffer},
+    {'B', setBufferSize},
+    {'D', updateGenerator},
+    {'E', loadAlgorithm},
+    {'I', readStatus},
+    {'M', startConversionMeasure},
+    {'R', startConversion},
+    {'S', stopConversion},
+    {'W', startGenerator},
+    {'a', readADCBuffer},
+    {'d', readDACBuffer},
+    {'e', unloadAlgorithm},
+    {'i', readIdentifier},
+    {'m', readExecTime},
+    {'r', sampleRate},
+    {'s', readConversionResults},
+    {'t', readConversionInput},
+    {'u', readMessage},
+    {'w', stopGenerator}
+}};
+
+void CommunicationManager::threadComm(void *)
+{
+       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
+                auto func = std::find_if(commandTable.cbegin(), commandTable.cend(),
+                                         [&cmd](const auto& f) { return f.first == cmd[0]; });
+                if (func != commandTable.cend())
+                    func->second(cmd);
+            }
+        }
+
+               chThdSleepMicroseconds(100);
+    }
+}
+
+void writeADCBuffer(unsigned char *)
+{
+    USBSerial::read(Samples::In.bytedata(), Samples::In.bytesize());
+}
+
+void setBufferSize(unsigned char *cmd)
+{
+    if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&
+        EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))
+    {
+        // count is multiplied by two since this command receives size of buffer
+        // for each algorithm application.
+        unsigned int count = (cmd[1] | (cmd[2] << 8)) * 2;
+        if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {
+            Samples::In.setSize(count);
+            Samples::Out.setSize(count);
+        }
+    }
+}
+
+void updateGenerator(unsigned char *cmd)
+{
+    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)) {
+            if (run_status == RunStatus::Idle) {
+                Samples::Generator.setSize(count);
+                USBSerial::read(
+                    reinterpret_cast<uint8_t *>(Samples::Generator.data()),
+                    Samples::Generator.bytesize());
+            } else if (run_status == RunStatus::Running) {
+                int more;
+                do {
+                    chThdSleepMicroseconds(10);
+                    more = DAC::sigGenWantsMore();
+                } while (more == -1);
+
+                USBSerial::read(reinterpret_cast<uint8_t *>(
+                    more == 0 ? Samples::Generator.data() : Samples::Generator.middata()),
+                    Samples::Generator.bytesize() / 2);
+            }
+        }
+    }
+}
+
+void loadAlgorithm(unsigned char *cmd)
+{
+    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 < MAX_ELF_FILE_SIZE, Error::BadUserCodeSize)) {
+            USBSerial::read(ELFManager::fileBuffer(), size);
+            auto entry = ELFManager::loadFromInternalBuffer();
+            EM.assert(entry != nullptr, Error::BadUserCodeLoad);
+        }
+    }
+}
+
+void readStatus(unsigned char *)
+{
+    unsigned char buf[2] = {
+        static_cast<unsigned char>(run_status),
+        static_cast<unsigned char>(EM.pop())
+    };
+
+    USBSerial::write(buf, sizeof(buf));
+}
+
+void startConversionMeasure(unsigned char *)
+{
+    if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
+        run_status = RunStatus::Running;
+        ConversionManager::startMeasured();
+    }
+}
+
+void startConversion(unsigned char *)
+{
+    if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
+        run_status = RunStatus::Running;
+        ConversionManager::start();
+    }
+}
+
+void stopConversion(unsigned char *)
+{
+    if (run_status == RunStatus::Running) {
+        ConversionManager::stop();
+        run_status = RunStatus::Idle;
+    }
+}
+
+void startGenerator(unsigned char *)
+{
+    DAC::start(1, Samples::Generator.data(), Samples::Generator.size());
+}
+
+void readADCBuffer(unsigned char *)
+{
+    USBSerial::write(Samples::In.bytedata(), Samples::In.bytesize());
+}
+
+void readDACBuffer(unsigned char *)
+{
+
+    USBSerial::write(Samples::Out.bytedata(), Samples::Out.bytesize());
+}
+
+void unloadAlgorithm(unsigned char *)
+{
+    ELFManager::unload();
+}
+
+void readIdentifier(unsigned char *)
+{
+#if defined(TARGET_PLATFORM_H7)
+    USBSerial::write(reinterpret_cast<const uint8_t *>("stmdsph"), 7);
+#else
+    USBSerial::write(reinterpret_cast<const uint8_t *>("stmdspl"), 7);
+#endif
+}
+
+void readExecTime(unsigned char *)
+{
+    USBSerial::write(reinterpret_cast<uint8_t *>(&conversion_time_measurement.last),
+                     sizeof(rtcnt_t));
+}
+
+void sampleRate(unsigned char *cmd)
+{
+    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<SClock::Rate>(cmd[1]);
+            SClock::setRate(r);
+            ADC::setRate(r);
+        }
+    }
+}
+
+void readConversionResults(unsigned char *)
+{
+    if (auto samps = Samples::Out.modified(); samps != nullptr) {
+        unsigned char buf[2] = {
+            static_cast<unsigned char>(Samples::Out.size() / 2 & 0xFF),
+            static_cast<unsigned char>(((Samples::Out.size() / 2) >> 8) & 0xFF)
+        };
+        USBSerial::write(buf, 2);
+        unsigned int total = Samples::Out.bytesize() / 2;
+        unsigned int offset = 0;
+        unsigned char unused;
+        while (total > 512) {
+            USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, 512);
+            while (USBSerial::read(&unused, 1) == 0);
+            offset += 512;
+            total -= 512;
+        }
+        USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, total);
+        while (USBSerial::read(&unused, 1) == 0);
+    } else {
+        USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);
+    }
+}
+
+void readConversionInput(unsigned char *)
+{
+    if (auto samps = Samples::In.modified(); samps != nullptr) {
+        unsigned char buf[2] = {
+            static_cast<unsigned char>(Samples::In.size() / 2 & 0xFF),
+            static_cast<unsigned char>(((Samples::In.size() / 2) >> 8) & 0xFF)
+        };
+        USBSerial::write(buf, 2);
+        unsigned int total = Samples::In.bytesize() / 2;
+        unsigned int offset = 0;
+        unsigned char unused;
+        while (total > 512) {
+            USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, 512);
+            while (USBSerial::read(&unused, 1) == 0);
+            offset += 512;
+            total -= 512;
+        }
+        USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, total);
+        while (USBSerial::read(&unused, 1) == 0);
+    } else {
+        USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);
+    }
+}
+
+void readMessage(unsigned char *)
+{
+    //USBSerial::write(reinterpret_cast<uint8_t *>(userMessageBuffer), userMessageSize);
+}
+
+void stopGenerator(unsigned char *)
+{
+    DAC::stop(1);
+}
+
diff --git a/source/communication.hpp b/source/communication.hpp
new file mode 100644 (file)
index 0000000..03220b8
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * @file communication.hpp
+ * @brief Manages communication with the host computer.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_COMMUNICATION_HPP
+#define STMDSP_COMMUNICATION_HPP
+
+#include <array>
+
+class CommunicationManager
+{
+public:
+    static void begin();
+
+private:
+    static void threadComm(void *);
+
+    static std::array<char, 4096> m_thread_stack;
+};
+
+#endif // STMDSP_COMMUNICATION_HPP
+
diff --git a/source/conversion.cpp b/source/conversion.cpp
new file mode 100644 (file)
index 0000000..27954be
--- /dev/null
@@ -0,0 +1,206 @@
+#include "conversion.hpp"
+
+#include "periph/adc.hpp"
+#include "periph/dac.hpp"
+#include "elfload.hpp"
+#include "error.hpp"
+#include "runstatus.hpp"
+#include "samples.hpp"
+
+constexpr msg_t MSG_CONVFIRST          = 1;
+constexpr msg_t MSG_CONVSECOND         = 2;
+constexpr msg_t MSG_CONVFIRST_MEASURE  = 3;
+constexpr msg_t MSG_CONVSECOND_MEASURE = 4;
+
+constexpr auto MSG_FOR_FIRST   = [](msg_t m) { return m & 1; };
+constexpr auto MSG_FOR_MEASURE = [](msg_t m) { return m > 2; };
+
+time_measurement_t conversion_time_measurement;
+
+__attribute__((section(".convdata")))
+thread_t *ConversionManager::m_thread_monitor = nullptr;
+thread_t *ConversionManager::m_thread_runner = nullptr;
+
+__attribute__((section(".stacks")))
+std::array<char, 1024> ConversionManager::m_thread_monitor_stack = {};
+__attribute__((section(".stacks")))
+std::array<char, 128> ConversionManager::m_thread_runner_entry_stack = {};
+__attribute__((section(".convdata")))
+std::array<char, CONVERSION_THREAD_STACK_SIZE> ConversionManager::m_thread_runner_stack = {};
+
+std::array<msg_t, 2> ConversionManager::m_mailbox_buffer;
+mailbox_t ConversionManager::m_mailbox = _MAILBOX_DATA(m_mailbox, m_mailbox_buffer.data(), m_mailbox_buffer.size());
+
+void ConversionManager::begin()
+{
+    m_thread_monitor = chThdCreateStatic(m_thread_monitor_stack.data(),
+                                         m_thread_monitor_stack.size(),
+                                         NORMALPRIO + 1,
+                                         threadMonitor,
+                                         nullptr);
+    auto runner_stack_end = &m_thread_runner_stack[CONVERSION_THREAD_STACK_SIZE];
+    m_thread_runner = chThdCreateStatic(m_thread_runner_entry_stack.data(),
+                                        m_thread_runner_entry_stack.size(),
+                                        HIGHPRIO,
+                                        threadRunnerEntry,
+                                        runner_stack_end);
+}
+
+void ConversionManager::start()
+{
+    Samples::Out.clear();
+    ADC::start(Samples::In.data(), Samples::In.size(), adcReadHandler);
+    DAC::start(0, Samples::Out.data(), Samples::Out.size());
+}
+
+void ConversionManager::startMeasured()
+{
+    Samples::Out.clear();
+    ADC::start(Samples::In.data(), Samples::In.size(), adcReadHandlerMeasure);
+    DAC::start(0, Samples::Out.data(), Samples::Out.size());
+}
+
+void ConversionManager::stop()
+{
+    DAC::stop(0);
+    ADC::stop();
+}
+
+thread_t *ConversionManager::getMonitorHandle()
+{
+    return m_thread_monitor;
+}
+
+void ConversionManager::abort()
+{
+    ELFManager::unload();
+    EM.add(Error::ConversionAborted);
+    run_status = RunStatus::Recovering;
+
+    // Confirm that the exception return thread is the algorithm...
+    uint32_t *psp;
+       asm("mrs %0, psp" : "=r" (psp));
+
+    bool isRunnerStack = 
+           (uint32_t)psp >= reinterpret_cast<uint32_t>(m_thread_runner_stack.data()) &&
+           (uint32_t)psp <= reinterpret_cast<uint32_t>(m_thread_runner_stack.data() +
+                                                       m_thread_runner_stack.size());
+
+    if (isRunnerStack)
+    {
+        // If it is, we can force the algorithm to exit by "resetting" its thread.
+        // We do this by rebuilding the thread's stacked exception return.
+        auto newpsp = reinterpret_cast<uint32_t *>(m_thread_runner_stack.data() + 
+                                                   m_thread_runner_stack.size() -
+                                                   8 * sizeof(uint32_t));
+        // Set the LR register to the thread's entry point.
+        newpsp[5] = reinterpret_cast<uint32_t>(threadRunner);
+        // Overwrite the instruction we'll return to with "bx lr" (jump to address in LR).
+        newpsp[6] = psp[6];
+        *reinterpret_cast<uint16_t *>(newpsp[6]) = 0x4770; // "bx lr"
+        // Keep PSR contents (bit set forces Thumb mode, just in case).
+        newpsp[7] = psp[7] | (1 << 24);
+        // Set the new stack pointer.
+           asm("msr psp, %0" :: "r" (newpsp));
+    }
+}
+
+void ConversionManager::threadMonitor(void *)
+{
+    while (1) {
+        msg_t message;
+        msg_t fetch = chMBFetchTimeout(&m_mailbox, &message, TIME_INFINITE);
+        if (fetch == MSG_OK)
+            chMsgSend(m_thread_runner, message);
+    }
+}
+
+void ConversionManager::threadRunnerEntry(void *stack)
+{
+    ELFManager::unload();
+    port_unprivileged_jump(reinterpret_cast<uint32_t>(threadRunner),
+                           reinterpret_cast<uint32_t>(stack));
+}
+
+__attribute__((section(".convcode")))
+void ConversionManager::threadRunner(void *)
+{
+    while (1) {
+        // Sleep until we receive a mailbox message.
+        msg_t message;
+        asm("svc 0; mov %0, r0" : "=r" (message));
+
+        if (message != 0) {
+            auto samples = MSG_FOR_FIRST(message) ? Samples::In.data()
+                                                  : Samples::In.middata();
+            auto size = Samples::In.size() / 2;
+
+            auto entry = ELFManager::loadedElf();
+            if (entry) {
+                // Below, we remember the stack pointer just in case the
+                // loaded algorithm messes things up.
+                uint32_t sp;
+
+                if (!MSG_FOR_MEASURE(message)) {
+                    asm("mov %0, sp" : "=r" (sp));
+                    samples = entry(samples, size);
+                    asm("mov sp, %0" :: "r" (sp));
+                } else {
+                    // Start execution timer:
+                    asm("mov %0, sp; eor r0, r0; svc 2" : "=r" (sp));
+                    samples = entry(samples, size);
+                    // Stop execution timer:
+                    asm("mov r0, #1; svc 2; mov sp, %0" :: "r" (sp));
+                } 
+            }
+
+            // Update the sample out buffer with the transformed samples.
+            if (samples != nullptr) {
+                if (MSG_FOR_FIRST(message))
+                    Samples::Out.modify(samples, size);
+                else
+                    Samples::Out.midmodify(samples, size);
+            }
+        }
+    }
+}
+
+void ConversionManager::adcReadHandler(adcsample_t *buffer, size_t)
+{
+    chSysLockFromISR();
+
+    // If previous request hasn't been handled, then we're going too slow.
+    // We'll need to abort.
+    if (chMBGetUsedCountI(&m_mailbox) > 1) {
+        chMBResetI(&m_mailbox);
+        chMBResumeX(&m_mailbox);
+        chSysUnlockFromISR();
+        abort();
+    } else {
+        // Mark the modified samples as 'fresh' or ready for manipulation.
+        if (buffer == Samples::In.data()) {
+            Samples::In.setModified();
+            chMBPostI(&m_mailbox, MSG_CONVFIRST);
+        } else {
+            Samples::In.setMidmodified();
+            chMBPostI(&m_mailbox, MSG_CONVSECOND);
+        }
+        chSysUnlockFromISR();
+    }
+}
+
+void ConversionManager::adcReadHandlerMeasure(adcsample_t *buffer, size_t)
+{
+    chSysLockFromISR();
+    if (buffer == Samples::In.data()) {
+        Samples::In.setModified();
+        chMBPostI(&m_mailbox, MSG_CONVFIRST_MEASURE);
+    } else {
+        Samples::In.setMidmodified();
+        chMBPostI(&m_mailbox, MSG_CONVSECOND_MEASURE);
+    }
+    chSysUnlockFromISR();
+
+    ADC::setOperation(adcReadHandler);
+}
+
diff --git a/source/conversion.hpp b/source/conversion.hpp
new file mode 100644 (file)
index 0000000..ad949dc
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * @file conversion.hpp
+ * @brief Manages algorithm application (converts input samples to output).
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_CONVERSION_HPP
+#define STMDSP_CONVERSION_HPP
+
+#include "ch.h"
+#include "hal.h"
+
+#include <array>
+
+constexpr unsigned int CONVERSION_THREAD_STACK_SIZE = 
+#if defined(TARGET_PLATFORM_H7)
+                                                  62 * 1024;
+#else
+                                                  15 * 1024;
+#endif
+
+class ConversionManager
+{
+public:
+    static void begin();
+
+    // Begins sample conversion.
+    static void start();
+    // Begins conversion with execution time measured.
+    static void startMeasured();
+    // Stops conversion.
+    static void stop();
+
+    static thread_t *getMonitorHandle();
+
+    // Internal only: Aborts a running conversion.
+    static void abort();
+
+private:
+    static void threadMonitor(void *);
+    static void threadRunnerEntry(void *stack);
+
+    static void threadRunner(void *);
+    static void adcReadHandler(adcsample_t *buffer, size_t);
+    static void adcReadHandlerMeasure(adcsample_t *buffer, size_t);
+
+    static thread_t *m_thread_monitor;
+    static thread_t *m_thread_runner;
+
+    static std::array<char, 1024> m_thread_monitor_stack;
+    static std::array<char, 128> m_thread_runner_entry_stack;
+    static std::array<char, CONVERSION_THREAD_STACK_SIZE> m_thread_runner_stack;
+
+    static std::array<msg_t, 2> m_mailbox_buffer;
+    static mailbox_t m_mailbox;
+};
+
+// Stores the measured execution time.
+extern time_measurement_t conversion_time_measurement;
+
+#endif // STMDSP_CONVERSION_HPP
+
diff --git a/source/cordic.cpp b/source/cordic.cpp
deleted file mode 100644 (file)
index 29ee068..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#include "cordic.hpp"
-#include "hal.h"
-
-#if !defined(TARGET_PLATFORM_L4)
-namespace cordic {
-
-void init()
-{
-    RCC->AHB2ENR |= RCC_AHB2ENR_CORDICEN;
-}
-
-static void prepare() {
-    while (CORDIC->CSR & CORDIC_CSR_RRDY)
-        asm("mov r0, %0" :: "r" (CORDIC->RDATA));
-}
-
-static uint32_t dtoq(double) {
-    uint32_t res;
-    asm("vcvt.s32.f64 d0, d0, #31;"
-        "vmov %0, r5, d0"
-    : "=r" (res));
-    return res;
-}
-__attribute__((naked))
-static double qtod(uint32_t) {
-    asm("eor r1, r1;"
-        "vmov d0, r0, r1;"
-        "vcvt.f64.s32 d0, d0, #31;"
-        "bx lr");
-    return 0;
-}
-__attribute__((naked))
-double mod(double, double) {
-    asm("vdiv.f64   d2, d0, d1;"
-        "vrintz.f64 d2;"
-        "vmul.f64   d1, d1, d2;"
-        "vsub.f64   d0, d0, d1;"
-        "bx lr");
-    return 0;
-}
-
-double cos(double x) {
-    x = mod(x, 2 * math::PI) / math::PI;
-    auto input = dtoq(x > 1. ? x - 2 : x);
-
-    prepare();
-    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
-                  (6 << CORDIC_CSR_PRECISION_Pos) |
-                  (0 << CORDIC_CSR_FUNC_Pos);
-
-    CORDIC->WDATA = input;
-    CORDIC->WDATA = input;
-    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
-
-    double cosx = qtod(CORDIC->RDATA) / x;
-    [[maybe_unused]] auto sinx = CORDIC->RDATA;
-    return cosx;
-}
-
-double sin(double x) {
-    x = mod(x, 2 * math::PI) / math::PI;
-    auto input = dtoq(x > 1. ? x - 2 : x);
-
-    prepare();
-    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
-                  (6 << CORDIC_CSR_PRECISION_Pos) |
-                  (1 << CORDIC_CSR_FUNC_Pos);
-
-    CORDIC->WDATA = input;
-    CORDIC->WDATA = input;
-    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
-
-    double sinx = qtod(CORDIC->RDATA) / x;
-    [[maybe_unused]] auto cosx = CORDIC->RDATA;
-    return sinx;
-}
-
-double tan(double x) {
-    x = mod(x, 2 * math::PI) / math::PI;
-    auto input = dtoq(x > 1. ? x - 2 : x);
-
-    prepare();
-    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
-                  (6 << CORDIC_CSR_PRECISION_Pos) |
-                  (1 << CORDIC_CSR_FUNC_Pos);
-
-    CORDIC->WDATA = input;
-    CORDIC->WDATA = input;
-    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
-
-    double sinx = qtod(CORDIC->RDATA) / x;
-    double tanx = sinx * x / qtod(CORDIC->RDATA);
-    return tanx;
-}
-
-}
-#else // L4
-#include <cmath>
-namespace cordic {
-
-void init() {}
-
-float mod(float a, float b) {
-    return a - (b * std::floor(a / b));
-}
-
-float cos(float x) { return std::cos(x); }
-float sin(float x) { return std::sin(x); }
-float tan(float x) { return std::tan(x); }
-
-}
-#endif
-
diff --git a/source/cordic.hpp b/source/cordic.hpp
deleted file mode 100644 (file)
index 5d640cc..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef CORDIC_HPP_
-#define CORDIC_HPP_
-
-namespace cordic {
-    constexpr double PI = 3.1415926535L;
-
-    void init();
-
-#if !defined(TARGET_PLATFORM_L4)
-    double mod(double n, double d);
-
-    double cos(double x);
-    double sin(double x);
-    double tan(double x);
-#else
-    float mod(float n, float d);
-
-    float cos(float x);
-    float sin(float x);
-    float tan(float x);
-#endif
-}
-
-#endif // CORDIC_HPP_
-
diff --git a/source/dac.cpp b/source/dac.cpp
deleted file mode 100644 (file)
index 1ff8867..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * @file dac.cpp
- * @brief Manages signal creation using the DAC.
- *
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-#include "dac.hpp"
-#include "sclock.hpp"
-
-DACDriver *DAC::m_driver[2] = {
-    &DACD1, &DACD2
-};
-
-const DACConfig DAC::m_config = {
-    .init = 2048,
-    .datamode = DAC_DHRM_12BIT_RIGHT,
-    .cr = 0
-};
-
-static int dacIsDone = -1;
-static void dacEndCallback(DACDriver *dacd)
-{
-    if (dacd == &DACD2)
-        dacIsDone = dacIsBufferComplete(dacd) ? 1 : 0;
-}
-
-const DACConversionGroup DAC::m_group_config = {
-    .num_channels = 1,
-    .end_cb = dacEndCallback,
-    .error_cb = nullptr,
-#if defined(TARGET_PLATFORM_H7)
-    .trigger = 5 // TIM6_TRGO
-#elif defined(TARGET_PLATFORM_L4)
-    .trigger = 0 // TIM6_TRGO
-#endif
-};
-
-void DAC::begin()
-{
-    palSetPadMode(GPIOA, 4, PAL_STM32_MODE_ANALOG);
-    palSetPadMode(GPIOA, 5, PAL_STM32_MODE_ANALOG);
-
-    dacStart(m_driver[0], &m_config);
-    dacStart(m_driver[1], &m_config);
-}
-
-void DAC::start(int channel, dacsample_t *buffer, size_t count)
-{
-    if (channel >= 0 && channel < 2) {
-        if (channel == 1)
-            dacIsDone = -1;
-        dacStartConversion(m_driver[channel], &m_group_config, buffer, count);
-        SClock::start();
-    }
-}
-
-int DAC::sigGenWantsMore()
-{
-    return dacIsDone;
-}
-
-void DAC::stop(int channel)
-{
-    if (channel >= 0 && channel < 2) {
-        dacStopConversion(m_driver[channel]);
-        SClock::stop();
-    }
-}
-
diff --git a/source/dac.hpp b/source/dac.hpp
deleted file mode 100644 (file)
index 4360c26..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * @file dac.hpp
- * @brief Manages signal creation using the DAC.
- *
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-#ifndef STMDSP_DAC_HPP_
-#define STMDSP_DAC_HPP_
-
-#include "hal.h"
-#undef DAC
-
-class DAC
-{
-public:
-    static void begin();
-
-    static void start(int channel, dacsample_t *buffer, size_t count);
-    static void stop(int channel);
-
-    static int sigGenWantsMore();
-
-private:
-    static DACDriver *m_driver[2];
-
-    static const DACConfig m_config;
-    static const DACConversionGroup m_group_config;
-};
-
-#endif // STMDSP_DAC_HPP_
-
diff --git a/source/elf.h b/source/elf.h
new file mode 100644 (file)
index 0000000..998356d
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * @file elf.h
+ * @brief Defines ELF binary format info.
+ *
+ * Free to use, written by Clyne Sullivan.
+ */
+
+#ifndef STMDSP_ELF_HPP
+#define STMDSP_ELF_HPP
+
+#include <cstdint>
+
+#define EI_NIDENT 16
+
+#define PT_NULL     0
+#define PT_LOAD     1
+#define PT_DYNAMIC  2
+#define PT_INTERP   3
+#define PT_NOTE     4
+#define PT_SHLIB    5
+#define PT_PHDR     6
+#define PT_RESERVED 0x70000000
+
+#define ELF32_ST_BIND(i)    ((i) >> 4)
+#define ELF32_ST_TYPE(i)    ((i) & 0xF)
+#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t) & 0xF))
+
+#define ELF32_R_SYM(i)     ((i) >> 8)
+#define ELF32_R_TYPE(i)    ((i) & 0xFF)
+#define ELF32_R_INFO(s, t) (((s) << 8) + ((t) & 0xFF))
+
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef uint32_t Elf32_Sword;
+typedef uint32_t Elf32_Word;
+
+typedef struct {
+       unsigned char e_ident[EI_NIDENT];
+       Elf32_Half    e_type;
+       Elf32_Half    e_machine;
+       Elf32_Word    e_version;
+       Elf32_Addr    e_entry;
+       Elf32_Off     e_phoff;
+       Elf32_Off     e_shoff;
+       Elf32_Word    e_flags;
+       Elf32_Half    e_ehsize;
+       Elf32_Half    e_phentsize;
+       Elf32_Half    e_phnum;
+       Elf32_Half    e_shentsize;
+       Elf32_Half    e_shnum;
+       Elf32_Half    e_shstrndx;
+} __attribute__((packed)) Elf32_Ehdr;
+
+typedef struct {
+       Elf32_Word sh_name;
+       Elf32_Word sh_type;
+       Elf32_Word sh_flags;
+       Elf32_Addr sh_addr;
+       Elf32_Off  sh_offset;
+       Elf32_Word sh_size;
+       Elf32_Word sh_link;
+       Elf32_Word sh_info;
+       Elf32_Word sh_addralign;
+       Elf32_Word sh_entsize;
+} __attribute__((packed)) Elf32_Shdr;
+
+typedef struct {
+       Elf32_Word    st_name;
+       Elf32_Addr    st_value;
+       Elf32_Word    st_size;
+       unsigned char st_info;
+       unsigned char st_other;
+       Elf32_Half    st_shndx;
+} __attribute__((packed)) Elf32_Sym;
+
+typedef struct {
+       Elf32_Addr r_offset;
+       Elf32_Word r_info;
+} __attribute__((packed)) Elf32_Rel;
+
+typedef struct {
+       Elf32_Addr  r_offset;
+       Elf32_Word  r_info;
+       Elf32_Sword r_addend;
+} __attribute__((packed)) Elf32_Rela;
+
+typedef struct {
+       Elf32_Word p_type;
+       Elf32_Off  p_offset;
+       Elf32_Addr p_vaddr;
+       Elf32_Addr p_paddr;
+       Elf32_Word p_filesz;
+       Elf32_Word p_memsz;
+       Elf32_Word p_flags;
+       Elf32_Word p_align;
+} __attribute__((packed)) Elf32_Phdr;
+
+#endif // STMDSP_ELF_HPP
+
diff --git a/source/elf_format.hpp b/source/elf_format.hpp
deleted file mode 100644 (file)
index d4feb99..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * @file elf_format.cpp
- * @brief Defines ELF binary format info.
- *
- * Free to use, written by Clyne Sullivan.
- */
-
-#ifndef STMDSP_ELF_FORMAT_HPP_
-#define STMDSP_ELF_FORMAT_HPP_
-
-#include <cstdint>
-
-#define EI_NIDENT 16
-
-#define PT_NULL     0
-#define PT_LOAD     1
-#define PT_DYNAMIC  2
-#define PT_INTERP   3
-#define PT_NOTE     4
-#define PT_SHLIB    5
-#define PT_PHDR     6
-#define PT_RESERVED 0x70000000
-
-#define ELF32_ST_BIND(i)    ((i) >> 4)
-#define ELF32_ST_TYPE(i)    ((i) & 0xF)
-#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t) & 0xF))
-
-#define ELF32_R_SYM(i)     ((i) >> 8)
-#define ELF32_R_TYPE(i)    ((i) & 0xFF)
-#define ELF32_R_INFO(s, t) (((s) << 8) + ((t) & 0xFF))
-
-typedef uint32_t Elf32_Addr;
-typedef uint16_t Elf32_Half;
-typedef uint32_t Elf32_Off;
-typedef uint32_t Elf32_Sword;
-typedef uint32_t Elf32_Word;
-
-typedef struct {
-       unsigned char e_ident[EI_NIDENT];
-       Elf32_Half    e_type;
-       Elf32_Half    e_machine;
-       Elf32_Word    e_version;
-       Elf32_Addr    e_entry;
-       Elf32_Off     e_phoff;
-       Elf32_Off     e_shoff;
-       Elf32_Word    e_flags;
-       Elf32_Half    e_ehsize;
-       Elf32_Half    e_phentsize;
-       Elf32_Half    e_phnum;
-       Elf32_Half    e_shentsize;
-       Elf32_Half    e_shnum;
-       Elf32_Half    e_shstrndx;
-} __attribute__((packed)) Elf32_Ehdr;
-
-typedef struct {
-       Elf32_Word sh_name;
-       Elf32_Word sh_type;
-       Elf32_Word sh_flags;
-       Elf32_Addr sh_addr;
-       Elf32_Off  sh_offset;
-       Elf32_Word sh_size;
-       Elf32_Word sh_link;
-       Elf32_Word sh_info;
-       Elf32_Word sh_addralign;
-       Elf32_Word sh_entsize;
-} __attribute__((packed)) Elf32_Shdr;
-
-typedef struct {
-       Elf32_Word    st_name;
-       Elf32_Addr    st_value;
-       Elf32_Word    st_size;
-       unsigned char st_info;
-       unsigned char st_other;
-       Elf32_Half    st_shndx;
-} __attribute__((packed)) Elf32_Sym;
-
-typedef struct {
-       Elf32_Addr r_offset;
-       Elf32_Word r_info;
-} __attribute__((packed)) Elf32_Rel;
-
-typedef struct {
-       Elf32_Addr  r_offset;
-       Elf32_Word  r_info;
-       Elf32_Sword r_addend;
-} __attribute__((packed)) Elf32_Rela;
-
-typedef struct {
-       Elf32_Word p_type;
-       Elf32_Off  p_offset;
-       Elf32_Addr p_vaddr;
-       Elf32_Addr p_paddr;
-       Elf32_Word p_filesz;
-       Elf32_Word p_memsz;
-       Elf32_Word p_flags;
-       Elf32_Word p_align;
-} __attribute__((packed)) Elf32_Phdr;
-
-#endif // STMDSP_ELF_FORMAT_HPP_
-
diff --git a/source/elf_load.cpp b/source/elf_load.cpp
deleted file mode 100644 (file)
index e161206..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * @file elf_load.cpp
- * @brief Loads ELF binary data into memory for execution.
- *
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-#include "elf_load.hpp"
-#include "elf_format.hpp"
-
-#include <algorithm>
-#include <cstring>
-
-static const unsigned char elf_header[] = { '\177', 'E', 'L', 'F' };
-
-template<typename T>
-constexpr static auto ptr_from_offset(void *base, uint32_t offset)
-{
-    return reinterpret_cast<T>(reinterpret_cast<uint8_t *>(base) + offset);
-}
-
-namespace ELF {
-
-Entry load(void *elf_data)
-{
-    // Check the ELF's header signature
-    auto ehdr = reinterpret_cast<Elf32_Ehdr *>(elf_data);
-    if (!std::equal(ehdr->e_ident, ehdr->e_ident + 4, elf_header))
-        return nullptr;
-
-    // Iterate through program header LOAD sections
-    bool loaded = false;
-    auto phdr = ptr_from_offset<Elf32_Phdr *>(elf_data, ehdr->e_phoff);
-    for (Elf32_Half i = 0; i < ehdr->e_phnum; i++) {
-        if (phdr->p_type == PT_LOAD) {
-            if (phdr->p_filesz == 0) {
-                std::memset(reinterpret_cast<void *>(phdr->p_vaddr),
-                            0,
-                            phdr->p_memsz);
-            } else {
-                std::memcpy(reinterpret_cast<void *>(phdr->p_vaddr),
-                            ptr_from_offset<void *>(elf_data, phdr->p_offset),
-                            phdr->p_filesz);
-                if (!loaded)
-                    loaded = true;
-            }
-        }
-
-        phdr = ptr_from_offset<Elf32_Phdr *>(phdr, ehdr->e_phentsize);
-    }
-
-
-    return loaded ? reinterpret_cast<Entry>(ehdr->e_entry) : nullptr;
-}
-
-} // namespace ELF
-
diff --git a/source/elf_load.hpp b/source/elf_load.hpp
deleted file mode 100644 (file)
index ae7265b..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * @file elf_load.hpp
- * @brief Loads ELF binary data into memory for execution.
- *
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-#ifndef ELF_LOAD_HPP_
-#define ELF_LOAD_HPP_
-
-#include <cstddef>
-#include <cstdint>
-
-namespace ELF
-{
-    using Entry = uint16_t *(*)(uint16_t *, size_t);
-
-    Entry load(void *elf_data);
-}
-
-#endif // ELF_LOAD_HPP_
-
diff --git a/source/elfload.cpp b/source/elfload.cpp
new file mode 100644 (file)
index 0000000..a430ad2
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * @file elfload.cpp
+ * @brief Loads ELF binary data into memory for execution.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "elfload.hpp"
+#include "elf.h"
+
+#include <algorithm>
+#include <cstring>
+
+__attribute__((section(".convdata")))
+ELFManager::EntryFunc ELFManager::m_entry = nullptr;
+std::array<unsigned char, MAX_ELF_FILE_SIZE> ELFManager::m_file_buffer = {};
+
+static const unsigned char elf_header[] = { '\177', 'E', 'L', 'F' };
+
+template<typename T>
+constexpr static auto ptr_from_offset(void *base, uint32_t offset)
+{
+    return reinterpret_cast<T>(reinterpret_cast<uint8_t *>(base) + offset);
+}
+
+ELFManager::EntryFunc ELFManager::loadFromInternalBuffer()
+{
+    auto elf_data = m_file_buffer.data();
+
+    // Check the ELF's header signature
+    auto ehdr = reinterpret_cast<Elf32_Ehdr *>(elf_data);
+    if (!std::equal(ehdr->e_ident, ehdr->e_ident + 4, elf_header))
+        return nullptr;
+
+    // Iterate through program header LOAD sections
+    bool loaded = false;
+    auto phdr = ptr_from_offset<Elf32_Phdr *>(elf_data, ehdr->e_phoff);
+    for (Elf32_Half i = 0; i < ehdr->e_phnum; i++) {
+        if (phdr->p_type == PT_LOAD) {
+            if (phdr->p_filesz == 0) {
+                std::memset(reinterpret_cast<void *>(phdr->p_vaddr),
+                            0,
+                            phdr->p_memsz);
+            } else {
+                std::memcpy(reinterpret_cast<void *>(phdr->p_vaddr),
+                            ptr_from_offset<void *>(elf_data, phdr->p_offset),
+                            phdr->p_filesz);
+                if (!loaded)
+                    loaded = true;
+            }
+        }
+
+        phdr = ptr_from_offset<Elf32_Phdr *>(phdr, ehdr->e_phentsize);
+    }
+
+
+    return loaded ? reinterpret_cast<ELFManager::EntryFunc>(ehdr->e_entry) : nullptr;
+}
+
diff --git a/source/elfload.hpp b/source/elfload.hpp
new file mode 100644 (file)
index 0000000..ada35e5
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * @file elfload.hpp
+ * @brief Loads ELF binary data into memory for execution.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ELF_LOAD_HPP_
+#define ELF_LOAD_HPP_
+
+#include "samplebuffer.hpp"
+
+#include <array>
+#include <cstddef>
+
+constexpr unsigned int MAX_ELF_FILE_SIZE = 16 * 1024;
+
+class ELFManager
+{
+public:
+    using EntryFunc = Sample *(*)(Sample *, size_t);
+    
+    static EntryFunc loadFromInternalBuffer();
+
+    static EntryFunc loadedElf() {
+        return m_entry;
+    }
+
+    static unsigned char *fileBuffer() {
+        return m_file_buffer.data();
+    }
+
+    static void unload() {
+        m_entry = nullptr;
+    }
+
+private:
+    static EntryFunc m_entry;
+
+    static std::array<unsigned char, MAX_ELF_FILE_SIZE> m_file_buffer;
+};
+
+#endif // ELF_LOAD_HPP_
+
diff --git a/source/error.cpp b/source/error.cpp
new file mode 100644 (file)
index 0000000..9f2e98f
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * @file error.cpp
+ * @brief Tracks and reports non-critical errors.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "error.hpp"
+
+ErrorManager EM;
+
+void ErrorManager::add(Error error)
+{ 
+    if (m_index < m_queue.size())
+        m_queue[m_index++] = error;
+}
+
+bool ErrorManager::assert(bool condition, Error error)
+{
+    if (!condition)
+        add(error);
+    return condition;
+}
+
+bool ErrorManager::hasError()
+{
+    return m_index > 0;
+}
+
+Error ErrorManager::pop()
+{
+    return m_index == 0 ? Error::None : m_queue[--m_index];
+}
+
index 6911792560711be2eee39f13b5dd0569c4e65bec..9bbbe2c02a4f2873f14b8b56e87965954ff09a32 100644 (file)
@@ -1,6 +1,18 @@
-#include <array>
+/**
+ * @file error.hpp
+ * @brief Tracks and reports non-critical errors.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_ERROR_HPP
+#define STMDSP_ERROR_HPP
 
-constexpr unsigned int MAX_ERROR_QUEUE_SIZE = 8;
+#include <array>
 
 enum class Error : char
 {
@@ -15,28 +27,20 @@ enum class Error : char
 
 class ErrorManager
 {
-public:
-    void add(Error error) {
-        if (m_index < m_queue.size())
-            m_queue[m_index++] = error;
-    }
-
-    bool assert(bool condition, Error error) {
-        if (!condition)
-            add(error);
-        return condition;
-    }
+    constexpr static unsigned int MAX_ERROR_QUEUE_SIZE = 8;
 
-    bool hasError() {
-        return m_index > 0;
-    }
-
-    Error pop() {
-        return m_index == 0 ? Error::None : m_queue[--m_index];
-    }
+public:
+    void add(Error error);
+    bool assert(bool condition, Error error);
+    bool hasError();
+    Error pop();
 
 private:
     std::array<Error, MAX_ERROR_QUEUE_SIZE> m_queue;
     unsigned int m_index = 0;
 };
 
+extern ErrorManager EM;
+
+#endif // STMDSP_ERROR_HPP
+
diff --git a/source/handlers.cpp b/source/handlers.cpp
new file mode 100644 (file)
index 0000000..4b0e3eb
--- /dev/null
@@ -0,0 +1,138 @@
+#include "handlers.hpp"
+
+#include "adc.hpp"
+#include "conversion.hpp"
+#include "cordic.hpp"
+#include "runstatus.hpp"
+
+extern "C" {
+
+__attribute__((naked))
+void port_syscall(struct port_extctx *ctxp, uint32_t n)
+{
+    switch (n) {
+
+    // Sleeps the current thread until a message is received.
+    // Used the algorithm runner to wait for new data.
+    case 0:
+        {
+            chSysLock();
+            chMsgWaitS();
+            auto monitor = ConversionManager::getMonitorHandle();
+            auto msg = chMsgGet(monitor);
+            chMsgReleaseS(monitor, MSG_OK);
+            chSysUnlock();
+            ctxp->r0 = msg;
+        }
+        break;
+
+    // Provides access to advanced math functions.
+    // A service call like this is required for some hardware targets that
+    // provide hardware-accelerated math computations (e.g. CORDIC).
+    case 1:
+        {
+            using mathcall = void (*)();
+            static mathcall funcs[3] = {
+                reinterpret_cast<mathcall>(cordic::sin),
+                reinterpret_cast<mathcall>(cordic::cos),
+                reinterpret_cast<mathcall>(cordic::tan),
+            };
+#if defined(PLATFORM_H7)
+            asm("vmov.f64 d0, %0, %1" :: "r" (ctxp->r1), "r" (ctxp->r2));
+            if (ctxp->r0 < 3) {
+                funcs[ctxp->r0]();
+                asm("vmov.f64 %0, %1, d0" : "=r" (ctxp->r1), "=r" (ctxp->r2));
+            } else {
+                asm("eor r0, r0; vmov.f64 d0, r0, r0");
+            }
+#else
+            asm("vmov.f32 s0, %0" :: "r" (ctxp->r1));
+            if (ctxp->r0 < 3) {
+                funcs[ctxp->r0]();
+                asm("vmov.f32 %0, s0" : "=r" (ctxp->r1));
+            } else {
+                asm("eor r0, r0; vmov.f32 s0, r0");
+            }
+#endif
+        }
+        break;
+
+    // Starts or stops precise cycle time measurement.
+    // Used to measure algorithm execution time.
+    case 2:
+        if (ctxp->r0 == 0) {
+            chTMStartMeasurementX(&conversion_time_measurement);
+        } else {
+            chTMStopMeasurementX(&conversion_time_measurement);
+            // Subtract measurement overhead from the result.
+            // Running an empty algorithm ("bx lr") takes 196 cycles as of 2/4/21.
+            // Only measures algorithm code time (loading args/storing result takes 9 cycles).
+            constexpr rtcnt_t measurement_overhead = 196 - 1;
+            if (conversion_time_measurement.last > measurement_overhead)
+                conversion_time_measurement.last -= measurement_overhead;
+        }
+        break;
+
+    // Reads one of the analog inputs made available for algorithm run-time input.
+    case 3:
+        ctxp->r0 = ADC::readAlt(ctxp->r0);
+        break;
+
+    //case 4:
+    //    {
+    //        const char *str = reinterpret_cast<const char *>(ctxp->r0);
+    //        auto src = str;
+    //        auto dst = userMessageBuffer;
+    //        while (*src)
+    //            *dst++ = *src++;
+    //        *dst = '\0';
+    //        userMessageSize = src - str;
+    //    }
+    //    break;
+    default:
+        while (1);
+        break;
+    }
+
+    asm("svc 0");
+    while (1);
+}
+
+__attribute__((naked))
+void MemManage_Handler()
+{
+    // 1. Get the stack pointer.
+    uint32_t lr;
+       asm("mov %0, lr" : "=r" (lr));
+
+    // 2. Recover from the fault.
+    ConversionManager::abort();
+
+    // 3. Return.
+    asm("mov lr, %0; bx lr" :: "r" (lr));
+}
+
+__attribute__((naked))
+void HardFault_Handler()
+{
+    // Get the stack pointer.
+    //uint32_t *stack;
+    uint32_t lr;
+       asm("mov %0, lr" : "=r" (lr));
+       /*asm("\
+               tst lr, #4; \
+               ite eq; \
+               mrseq %0, msp; \
+               mrsne %0, psp; \
+        mov %1, lr; \
+       " : "=r" (stack), "=r" (lr));*/
+
+    // If coming from the algorithm, attempt to recover; otherwise, give up.
+    if (run_status != RunStatus::Running && (lr & 4) != 0)
+        MemManage_Handler();
+
+    while (1);
+}
+
+} // extern "C"
+
diff --git a/source/handlers.hpp b/source/handlers.hpp
new file mode 100644 (file)
index 0000000..fd7e10c
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * @file handlers.hpp
+ * @brief Interrupt service routine handlers.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_HANDLERS_HPP
+#define STMDSP_HANDLERS_HPP
+
+#include "ch.h"
+
+extern "C" {
+
+__attribute__((naked))
+void port_syscall(struct port_extctx *ctxp, uint32_t n);
+
+__attribute__((naked))
+void MemManage_Handler();
+
+__attribute__((naked))
+void HardFault_Handler();
+
+}
+
+#endif // STMDSP_HANDLERS_HPP
+
index e9c33efba168f4b6fe52a0f07594f269b50580bc..9a22a73f27e9cc7ee2b94fb6ad577d2cecd0022f 100644 (file)
 #include "ch.h"
 #include "hal.h"
 
-static_assert(sizeof(adcsample_t) == sizeof(uint16_t));
-static_assert(sizeof(dacsample_t) == sizeof(uint16_t));
-
 #include "adc.hpp"
 #include "cordic.hpp"
 #include "dac.hpp"
-#include "elf_load.hpp"
 #include "error.hpp"
-#include "samplebuffer.hpp"
 #include "sclock.hpp"
 #include "usbserial.hpp"
 
-#include <array>
-
-// Pin definitions
-//
-#if defined(TARGET_PLATFORM_L4)
-constexpr auto LINE_LED_GREEN = PAL_LINE(GPIOC_BASE, 10U);
-constexpr auto LINE_LED_YELLOW = PAL_LINE(GPIOC_BASE, 11U);
-constexpr auto LINE_LED_RED = PAL_LINE(GPIOC_BASE, 12U);
-#endif
-
-// Run status
-//
-enum class RunStatus : char
-{
-    Idle = '1',
-    Running,
-    Recovering
-};
-static RunStatus run_status = RunStatus::Idle;
-
-// Conversion threads messaging
-//
-#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[2];
-static MAILBOX_DECL(conversionMB, conversionMBBuffer, 2);
-
-// Sample input and output buffers
-//
-#if defined(TARGET_PLATFORM_H7)
-__attribute__((section(".convdata")))
-static SampleBuffer samplesIn  (reinterpret_cast<Sample *>(0x38000000)); // 16k
-__attribute__((section(".convdata")))
-static SampleBuffer samplesOut (reinterpret_cast<Sample *>(0x30004000)); // 16k
-static SampleBuffer samplesSigGen (reinterpret_cast<Sample *>(0x30000000)); // 16k
-#else
-__attribute__((section(".convdata")))
-static SampleBuffer samplesIn  (reinterpret_cast<Sample *>(0x20008000)); // 16k
-__attribute__((section(".convdata")))
-static SampleBuffer samplesOut (reinterpret_cast<Sample *>(0x2000C000)); // 16k
-static SampleBuffer samplesSigGen (reinterpret_cast<Sample *>(0x20010000)); // 16k
-#endif
-
-// Algorithm binary storage
-//
-constexpr unsigned int MAX_ELF_FILE_SIZE = 16 * 1024;
-static unsigned char elf_file_store[MAX_ELF_FILE_SIZE];
-__attribute__((section(".convdata")))
-static ELF::Entry elf_entry = nullptr;
+#include "runstatus.hpp"
+RunStatus run_status = RunStatus::Idle;
 
 // Other variables
 //
-static ErrorManager EM;
-static time_measurement_t conversion_time_measurement;
-static char userMessageBuffer[128];
-static unsigned char userMessageSize = 0;
+//static char userMessageBuffer[128];
+//static unsigned char userMessageSize = 0;
 
-// Functions
-//
-__attribute__((section(".convcode")))
-static void conversion_unprivileged_main();
-static void startThreads();
-static void mpuSetup();
-static void abortAlgorithmFromISR();
-static void signalOperate(adcsample_t *buffer, size_t count);
-static void signalOperateMeasure(adcsample_t *buffer, size_t count);
+#include "conversion.hpp"
+#include "communication.hpp"
+#include "monitor.hpp"
 
 int main()
 {
@@ -103,15 +37,7 @@ int main()
     halInit();
     chSysInit();
 
-    SCB->CPACR |= 0xF << 20; // Enable FPU
-    mpuSetup();
-
-#if defined(TARGET_PLATFORM_L4)
-    palSetLineMode(LINE_LED_GREEN, PAL_MODE_OUTPUT_PUSHPULL);
-    palSetLineMode(LINE_LED_YELLOW, PAL_MODE_OUTPUT_PUSHPULL);
-    palSetLineMode(LINE_LED_RED, PAL_MODE_OUTPUT_PUSHPULL);
-#endif
-
+    // Init peripherials
     ADC::begin();
     DAC::begin();
     SClock::begin();
@@ -121,624 +47,12 @@ int main()
     SClock::setRate(SClock::Rate::R32K);
     ADC::setRate(SClock::Rate::R32K);
 
-    startThreads();
+    // Start our threads.
+    ConversionManager::begin();
+    CommunicationManager::begin();
+    Monitor::begin();
+
     chThdExit(0);
     return 0;
 }
 
-static THD_FUNCTION(monitorThread, arg);           // Runs status LEDs and allows debug halt.
-static THD_FUNCTION(conversionThreadMonitor, arg); // Monitors and manages algo. thread.
-static THD_FUNCTION(conversionThread, arg);        // Algorithm thread (unprivileged).
-static THD_FUNCTION(communicationThread, arg);     // Manages USB communications.
-
-// Need to hold some thread handles for mailbox usage.
-static thread_t *conversionThreadHandle = nullptr;
-__attribute__((section(".convdata")))
-static thread_t *conversionThreadMonitorHandle = nullptr;
-
-// The more stack for the algorithm, the merrier.
-constexpr unsigned int conversionThreadUPWASize =
-#if defined(TARGET_PLATFORM_H7)
-                                                  62 * 1024;
-#else
-                                                  15 * 1024;
-#endif
-
-__attribute__((section(".stacks")))
-static THD_WORKING_AREA(monitorThreadWA, 256);
-__attribute__((section(".stacks")))
-static THD_WORKING_AREA(conversionThreadMonitorWA, 1024);
-__attribute__((section(".stacks")))
-static THD_WORKING_AREA(conversionThreadWA, 128); // For entering unprivileged mode.
-__attribute__((section(".convdata")))
-static THD_WORKING_AREA(conversionThreadUPWA, conversionThreadUPWASize);
-__attribute__((section(".stacks")))
-static THD_WORKING_AREA(communicationThreadWA, 4096);
-
-void startThreads()
-{
-    chThdCreateStatic(
-        monitorThreadWA, sizeof(monitorThreadWA),
-        LOWPRIO,
-        monitorThread, nullptr);
-    conversionThreadMonitorHandle = chThdCreateStatic(
-        conversionThreadMonitorWA, sizeof(conversionThreadMonitorWA),
-        NORMALPRIO + 1,
-        conversionThreadMonitor, nullptr);
-    auto conversionThreadUPWAEnd =
-        reinterpret_cast<uint32_t>(conversionThreadUPWA) + conversionThreadUPWASize;
-    conversionThreadHandle = chThdCreateStatic(
-        conversionThreadWA, sizeof(conversionThreadWA),
-        HIGHPRIO,
-        conversionThread,
-        reinterpret_cast<void *>(conversionThreadUPWAEnd));
-    chThdCreateStatic(
-        communicationThreadWA, sizeof(communicationThreadWA),
-        NORMALPRIO,
-        communicationThread, nullptr);
-}
-
-THD_FUNCTION(communicationThread, arg)
-{
-    (void)arg;
-       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]) {
-
-                // 'a' - Read contents of ADC buffer.
-                // 'A' - Write contents of ADC buffer.
-                // 'B' - Set ADC/DAC buffer size.
-                // 'd' - Read contents of DAC buffer.
-                // 'D' - Set siggen size and write to its buffer.
-                // 'E' - Load algorithm binary.
-                // 'e' - Unload algorithm.
-                // 'i' - Read "stmdsp" identifier string.
-                // 'I' - Read status information.
-                // 'M' - Begin conversion, measure algorithm execution time.
-                // 'm' - Read last algorithm execution time.
-                // 'R' - Begin conversion.
-                // 'r' - Read or write sample rate.
-                // 'S' - Stop conversion.
-                // 's' - Get latest block of conversion results.
-                // 't' - Get latest block of conversion input.
-                // 'u' - Get user message.
-                // 'W' - Start signal generator (siggen).
-                // 'w' - Stop siggen.
-
-                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))
-                    {
-                        // count is multiplied by two since this command receives size of buffer
-                        // for each algorithm application.
-                        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)) {
-                            if (run_status == RunStatus::Idle) {
-                                samplesSigGen.setSize(count * 2);
-                                USBSerial::read(
-                                    reinterpret_cast<uint8_t *>(samplesSigGen.middata()),
-                                    samplesSigGen.bytesize() / 2);
-                            } else if (run_status == RunStatus::Running) {
-                                int more;
-                                do {
-                                    chThdSleepMicroseconds(10);
-                                    more = DAC::sigGenWantsMore();
-                                } while (more == -1);
-
-                                USBSerial::read(reinterpret_cast<uint8_t *>(
-                                    more == 0 ? samplesSigGen.data() : samplesSigGen.middata()),
-                                    samplesSigGen.bytesize() / 2);
-                            }
-                        }
-                    }
-                    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':
-#if defined(TARGET_PLATFORM_H7)
-                    USBSerial::write(reinterpret_cast<const uint8_t *>("stmdsph"), 7);
-#else
-                    USBSerial::write(reinterpret_cast<const uint8_t *>("stmdspl"), 7);
-#endif
-                    break;
-
-                // 'I' - Sends the current run status.
-                case 'I':
-                    {
-                        unsigned char buf[2] = {
-                            static_cast<unsigned char>(run_status),
-                            static_cast<unsigned char>(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(), signalOperateMeasure);
-                        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<uint8_t *>(&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(), signalOperate);
-                        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<SClock::Rate>(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<unsigned char>(samplesOut.size() / 2 & 0xFF),
-                            static_cast<unsigned char>(((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<uint8_t *>(samps) + offset, 512);
-                            while (USBSerial::read(&unused, 1) == 0);
-                            offset += 512;
-                            total -= 512;
-                        }
-                        USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, total);
-                        while (USBSerial::read(&unused, 1) == 0);
-                    } else {
-                        USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);
-                    }
-                    break;
-                case 't':
-                    if (auto samps = samplesIn.modified(); samps != nullptr) {
-                        unsigned char buf[2] = {
-                            static_cast<unsigned char>(samplesIn.size() / 2 & 0xFF),
-                            static_cast<unsigned char>(((samplesIn.size() / 2) >> 8) & 0xFF)
-                        };
-                        USBSerial::write(buf, 2);
-                        unsigned int total = samplesIn.bytesize() / 2;
-                        unsigned int offset = 0;
-                        unsigned char unused;
-                        while (total > 512) {
-                            USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, 512);
-                            while (USBSerial::read(&unused, 1) == 0);
-                            offset += 512;
-                            total -= 512;
-                        }
-                        USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, total);
-                        while (USBSerial::read(&unused, 1) == 0);
-                    } else {
-                        USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);
-                    }
-                    break;
-
-                case 'u':
-                    USBSerial::write(reinterpret_cast<uint8_t *>(userMessageBuffer), userMessageSize);
-                    break;
-
-                case 'W':
-                    DAC::start(1, samplesSigGen.data(), samplesSigGen.size());
-                    break;
-                case 'w':
-                    DAC::stop(1);
-                    break;
-
-                default:
-                    break;
-                }
-            }
-        }
-
-               chThdSleepMicroseconds(100);
-       }
-}
-
-THD_FUNCTION(conversionThreadMonitor, arg)
-{
-    (void)arg;
-    while (1) {
-        msg_t message;
-        if (chMBFetchTimeout(&conversionMB, &message, TIME_INFINITE) == MSG_OK)
-            chMsgSend(conversionThreadHandle, message);
-    }
-}
-
-THD_FUNCTION(conversionThread, stack)
-{
-    elf_entry = nullptr;
-    port_unprivileged_jump(reinterpret_cast<uint32_t>(conversion_unprivileged_main),
-                           reinterpret_cast<uint32_t>(stack));
-}
-
-THD_FUNCTION(monitorThread, arg)
-{
-    (void)arg;
-
-    palSetLineMode(LINE_BUTTON, PAL_MODE_INPUT_PULLUP);
-    auto readButton = [] {
-#if defined(TARGET_PLATFORM_L4)
-            return !palReadLine(LINE_BUTTON);
-#else
-            return palReadLine(LINE_BUTTON);
-#endif
-        };
-
-    palClearLine(LINE_LED_RED);
-    palClearLine(LINE_LED_YELLOW);
-
-    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 (run_status == RunStatus::Idle && readButton()) {
-            palSetLine(LINE_LED_RED);
-            palSetLine(LINE_LED_YELLOW);
-            chSysLock();
-            while (readButton())
-                asm("nop");
-            while (!readButton())
-                asm("nop");
-            chSysUnlock();
-            palClearLine(LINE_LED_RED);
-            palClearLine(LINE_LED_YELLOW);
-            chThdSleepMilliseconds(500);
-        }
-
-        static bool erroron = false;
-        if (auto err = EM.hasError(); err ^ erroron) {
-            erroron = err;
-            if (err)
-                palSetLine(LINE_LED_RED);
-            else
-                palClearLine(LINE_LED_RED);
-        }
-    }
-}
-
-void conversion_unprivileged_main()
-{
-    while (1) {
-        msg_t message;
-        asm("svc 0; mov %0, r0" : "=r" (message)); // sleep until next message
-        if (message != 0) {
-            auto samples = MSG_FOR_FIRST(message) ? samplesIn.data() : samplesIn.middata();
-            auto size = samplesIn.size() / 2;
-
-            if (elf_entry) {
-                if (!MSG_FOR_MEASURE(message)) {
-                    // Remember the stack pointer in case the algorithm messes things up.
-                    uint32_t sp;
-                    asm("mov %0, sp" : "=r" (sp));
-                    samples = elf_entry(samples, size);
-                    asm("mov sp, %0" :: "r" (sp));
-                } else {
-                    uint32_t sp;
-                    asm("mov %0, sp; eor r0, r0; svc 2" : "=r" (sp)); // start measurement
-                    samples = elf_entry(samples, size);
-                    asm("mov r0, #1; svc 2; mov sp, %0" :: "r" (sp)); // stop measurement
-                } 
-            }
-
-            if (samples != nullptr) {
-                if (MSG_FOR_FIRST(message))
-                    samplesOut.modify(samples, size);
-                else
-                    samplesOut.midmodify(samples, size);
-            }
-        }
-    }
-}
-
-void mpuSetup()
-{
-    // Set up MPU for user algorithm
-#if defined(TARGET_PLATFORM_H7)
-    // Region 2: Data for algorithm thread
-    // Region 3: Code for algorithm thread
-    // Region 4: User algorithm code
-    mpuConfigureRegion(MPU_REGION_2,
-                       0x20000000,
-                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_64K |
-                       MPU_RASR_ENABLE);
-    mpuConfigureRegion(MPU_REGION_3,
-                       0x0807F800,
-                       MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_2K |
-                       MPU_RASR_ENABLE);
-    mpuConfigureRegion(MPU_REGION_4,
-                       0x00000000,
-                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_64K |
-                       MPU_RASR_ENABLE);
-#else
-    // Region 2: Data for algorithm thread and ADC/DAC buffers
-    // Region 3: Code for algorithm thread
-    // Region 4: User algorithm code
-    mpuConfigureRegion(MPU_REGION_2,
-                       0x20008000,
-                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_128K|
-                       MPU_RASR_ENABLE);
-    mpuConfigureRegion(MPU_REGION_3,
-                       0x0807F800,
-                       MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_2K |
-                       MPU_RASR_ENABLE);
-    mpuConfigureRegion(MPU_REGION_4,
-                       0x10000000,
-                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_32K |
-                       MPU_RASR_ENABLE);
-#endif
-}
-
-void abortAlgorithmFromISR()
-{
-    elf_entry = nullptr;
-    EM.add(Error::ConversionAborted);
-    run_status = RunStatus::Recovering;
-
-    // Confirm that the exception return thread is the algorithm...
-    uint32_t *psp;
-       asm("mrs %0, psp" : "=r" (psp));
-    if ((uint32_t)psp >= (uint32_t)conversionThreadUPWA &&
-        (uint32_t)psp <= (uint32_t)conversionThreadUPWA + conversionThreadUPWASize)
-    {
-        // If it is, we can force the algorithm to exit by "resetting" its thread.
-        // We do this by rebuilding the thread's stacked exception return.
-        uint32_t *newpsp = reinterpret_cast<uint32_t *>(
-            (char *)conversionThreadUPWA +
-            conversionThreadUPWASize - 8 * sizeof(uint32_t));
-        // Set the LR register to the thread's entry point.
-        newpsp[5] = reinterpret_cast<uint32_t>(conversion_unprivileged_main);
-        // Overwrite the instruction we'll return to with "bx lr" (jump to address in LR).
-        newpsp[6] = psp[6];
-        *reinterpret_cast<uint16_t *>(newpsp[6]) = 0x4770; // "bx lr"
-        // Keep PSR contents (bit set forces Thumb mode, just in case).
-        newpsp[7] = psp[7] | (1 << 24);
-        // Set the new stack pointer.
-           asm("msr psp, %0" :: "r" (newpsp));
-    }
-}
-
-void signalOperate(adcsample_t *buffer, size_t)
-{
-    chSysLockFromISR();
-
-    if (chMBGetUsedCountI(&conversionMB) > 1) {
-        chMBResetI(&conversionMB);
-        chMBResumeX(&conversionMB);
-        chSysUnlockFromISR();
-        abortAlgorithmFromISR();
-    } else {
-        if (buffer == samplesIn.data()) {
-            samplesIn.setModified();
-            chMBPostI(&conversionMB, MSG_CONVFIRST);
-        } else {
-            samplesIn.setMidmodified();
-            chMBPostI(&conversionMB, MSG_CONVSECOND);
-        }
-        chSysUnlockFromISR();
-    }
-}
-
-void signalOperateMeasure(adcsample_t *buffer, [[maybe_unused]] size_t count)
-{
-    chSysLockFromISR();
-    if (buffer == samplesIn.data()) {
-        samplesIn.setModified();
-        chMBPostI(&conversionMB, MSG_CONVFIRST_MEASURE);
-    } else {
-        samplesIn.setMidmodified();
-        chMBPostI(&conversionMB, MSG_CONVSECOND_MEASURE);
-    }
-    chSysUnlockFromISR();
-
-    ADC::setOperation(signalOperate);
-}
-
-extern "C" {
-
-__attribute__((naked))
-void port_syscall(struct port_extctx *ctxp, uint32_t n)
-{
-    switch (n) {
-    case 0:
-        {
-            chSysLock();
-            chMsgWaitS();
-            auto msg = chMsgGet(conversionThreadMonitorHandle);
-            chMsgReleaseS(conversionThreadMonitorHandle, MSG_OK);
-            chSysUnlock();
-            ctxp->r0 = msg;
-        }
-        break;
-    case 1:
-        {
-            using mathcall = void (*)();
-            static mathcall funcs[3] = {
-                reinterpret_cast<mathcall>(cordic::sin),
-                reinterpret_cast<mathcall>(cordic::cos),
-                reinterpret_cast<mathcall>(cordic::tan),
-            };
-#if defined(PLATFORM_H7)
-            asm("vmov.f64 d0, %0, %1" :: "r" (ctxp->r1), "r" (ctxp->r2));
-            if (ctxp->r0 < 3) {
-                funcs[ctxp->r0]();
-                asm("vmov.f64 %0, %1, d0" : "=r" (ctxp->r1), "=r" (ctxp->r2));
-            } else {
-                asm("eor r0, r0; vmov.f64 d0, r0, r0");
-            }
-#else
-            asm("vmov.f32 s0, %0" :: "r" (ctxp->r1));
-            if (ctxp->r0 < 3) {
-                funcs[ctxp->r0]();
-                asm("vmov.f32 %0, s0" : "=r" (ctxp->r1));
-            } else {
-                asm("eor r0, r0; vmov.f32 s0, r0");
-            }
-#endif
-        }
-        break;
-    case 2:
-        if (ctxp->r0 == 0) {
-            chTMStartMeasurementX(&conversion_time_measurement);
-        } else {
-            chTMStopMeasurementX(&conversion_time_measurement);
-            // Subtract measurement overhead from the result.
-            // Running an empty algorithm ("bx lr") takes 196 cycles as of 2/4/21.
-            // Only measures algorithm code time (loading args/storing result takes 9 cycles).
-            constexpr rtcnt_t measurement_overhead = 196 - 1;
-            if (conversion_time_measurement.last > measurement_overhead)
-                conversion_time_measurement.last -= measurement_overhead;
-        }
-        break;
-    case 3:
-        ctxp->r0 = ADC::readAlt(ctxp->r0);
-        break;
-    case 4:
-        {
-            const char *str = reinterpret_cast<const char *>(ctxp->r0);
-            auto src = str;
-            auto dst = userMessageBuffer;
-            while (*src)
-                *dst++ = *src++;
-            *dst = '\0';
-            userMessageSize = src - str;
-        }
-        break;
-    default:
-        while (1);
-        break;
-    }
-
-    asm("svc 0");
-    while (1);
-}
-
-__attribute__((naked))
-void MemManage_Handler()
-{
-    // 1. Get the stack pointer.
-    uint32_t lr;
-       asm("mov %0, lr" : "=r" (lr));
-
-    // 2. Recover from the fault.
-    abortAlgorithmFromISR();
-
-    // 3. Return.
-    asm("mov lr, %0; bx lr" :: "r" (lr));
-}
-
-__attribute__((naked))
-void HardFault_Handler()
-{
-    // Get the stack pointer.
-    //uint32_t *stack;
-    uint32_t lr;
-       asm("mov %0, lr" : "=r" (lr));
-       /*asm("\
-               tst lr, #4; \
-               ite eq; \
-               mrseq %0, msp; \
-               mrsne %0, psp; \
-        mov %1, lr; \
-       " : "=r" (stack), "=r" (lr));*/
-
-    // If coming from the algorithm, attempt to recover; otherwise, give up.
-    if (run_status != RunStatus::Running && (lr & 4) != 0)
-        MemManage_Handler();
-
-    while (1);
-}
-
-} // extern "C"
-
diff --git a/source/monitor.cpp b/source/monitor.cpp
new file mode 100644 (file)
index 0000000..335a1eb
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * @file monitor.cpp
+ * @brief Manages the device monitoring thread (status LEDs, etc.).
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "monitor.hpp"
+
+#include "error.hpp"
+#include "runstatus.hpp"
+
+#include "ch.h"
+#include "hal.h"
+
+void Monitor::begin()
+{
+    chThdCreateStatic(m_thread_stack.data(),
+                      m_thread_stack.size(),
+                      LOWPRIO,
+                      threadMonitor,
+                      nullptr);
+}
+
+void Monitor::threadMonitor(void *)
+{
+    palSetLineMode(LINE_BUTTON, PAL_MODE_INPUT_PULLUP);
+    auto readButton = [] {
+#ifdef TARGET_PLATFORM_L4
+        return !palReadLine(LINE_BUTTON);
+#else
+        return palReadLine(LINE_BUTTON);
+#endif
+    };
+
+    palClearLine(LINE_LED_RED);
+    palClearLine(LINE_LED_YELLOW);
+
+    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 (run_status == RunStatus::Idle && readButton()) {
+            palSetLine(LINE_LED_RED);
+            palSetLine(LINE_LED_YELLOW);
+            chSysLock();
+            while (readButton())
+                asm("nop");
+            while (!readButton())
+                asm("nop");
+            chSysUnlock();
+            palClearLine(LINE_LED_RED);
+            palClearLine(LINE_LED_YELLOW);
+            chThdSleepMilliseconds(500);
+        }
+
+        static bool erroron = false;
+        if (auto err = EM.hasError(); err ^ erroron) {
+            erroron = err;
+            if (err)
+                palSetLine(LINE_LED_RED);
+            else
+                palClearLine(LINE_LED_RED);
+        }
+    }
+}
+
+__attribute__((section(".stacks")))
+std::array<char, 256> Monitor::m_thread_stack = {};
+
diff --git a/source/monitor.hpp b/source/monitor.hpp
new file mode 100644 (file)
index 0000000..44545c3
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * @file monitor.hpp
+ * @brief Manages the device monitoring thread (status LEDs, etc.).
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_MONITOR_HPP
+#define STMDSP_MONITOR_HPP
+
+#include <array>
+
+class Monitor
+{
+public:
+    static void begin();
+
+private:
+    static void threadMonitor(void *);
+
+    static std::array<char, 256> m_thread_stack;
+};
+
+#endif // STMDSP_MONITOR_HPP
+
diff --git a/source/periph/adc.cpp b/source/periph/adc.cpp
new file mode 100644 (file)
index 0000000..00438f2
--- /dev/null
@@ -0,0 +1,229 @@
+/**
+ * @file adc.cpp
+ * @brief Manages signal reading through the ADC.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "adc.hpp"
+
+#if defined(TARGET_PLATFORM_L4)
+ADCDriver *ADC::m_driver = &ADCD1;
+ADCDriver *ADC::m_driver2 = &ADCD3;
+#else
+ADCDriver *ADC::m_driver = &ADCD3;
+//ADCDriver *ADC::m_driver2 = &ADCD1; // TODO
+#endif
+
+const ADCConfig ADC::m_config = {
+    .difsel = 0,
+#if defined(TARGET_PLATFORM_H7)
+    .calibration = 0,
+#endif
+};
+
+const ADCConfig ADC::m_config2 = {
+    .difsel = 0,
+#if defined(TARGET_PLATFORM_H7)
+    .calibration = 0,
+#endif
+};
+
+ADCConversionGroup ADC::m_group_config = {
+    .circular = true,
+    .num_channels = 1,
+    .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_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
+#if defined(TARGET_PLATFORM_H7)
+    .ccr = 0,
+    .pcsel = 0,
+    .ltr1 = 0, .htr1 = 4095,
+    .ltr2 = 0, .htr2 = 4095,
+    .ltr3 = 0, .htr3 = 4095,
+#else
+    .tr1 = ADC_TR(0, 4095),
+    .tr2 = ADC_TR(0, 4095),
+    .tr3 = ADC_TR(0, 4095),
+    .awd2cr = 0,
+    .awd3cr = 0,
+#endif
+    .smpr = {
+        ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5), 0
+    },
+    .sqr = {
+        ADC_SQR1_SQ1_N(ADC_CHANNEL_IN5),
+        0, 0, 0
+    },
+};
+
+static bool readAltDone = false;
+static void readAltCallback(ADCDriver *)
+{
+    readAltDone = true;
+}
+ADCConversionGroup ADC::m_group_config2 = {
+    .circular = false,
+    .num_channels = 2,
+    .end_cb = readAltCallback,
+    .error_cb = nullptr,
+    .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13),  /* TIM6_TRGO */
+    .cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
+#if defined(TARGET_PLATFORM_H7)
+    .ccr = 0,
+    .pcsel = 0,
+    .ltr1 = 0, .htr1 = 4095,
+    .ltr2 = 0, .htr2 = 4095,
+    .ltr3 = 0, .htr3 = 4095,
+#else
+    .tr1 = ADC_TR(0, 4095),
+    .tr2 = ADC_TR(0, 4095),
+    .tr3 = ADC_TR(0, 4095),
+    .awd2cr = 0,
+    .awd3cr = 0,
+#endif
+    .smpr = {
+        ADC_SMPR1_SMP_AN1(ADC_SMPR_SMP_2P5) | ADC_SMPR1_SMP_AN2(ADC_SMPR_SMP_2P5), 0
+    },
+    .sqr = {
+        ADC_SQR1_SQ1_N(ADC_CHANNEL_IN1) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN2),
+        0, 0, 0
+    },
+};
+
+adcsample_t *ADC::m_current_buffer = nullptr;
+size_t ADC::m_current_buffer_size = 0;
+ADC::Operation ADC::m_operation = nullptr;
+
+void ADC::begin()
+{
+#if defined(TARGET_PLATFORM_H7)
+    palSetPadMode(GPIOF, 3, PAL_MODE_INPUT_ANALOG);
+#else
+    palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG); // Algorithm in
+    palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG); // Potentiometer 1
+    palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG); // Potentiometer 2
+#endif
+
+    adcStart(m_driver, &m_config);
+    adcStart(m_driver2, &m_config2);
+}
+
+void ADC::start(adcsample_t *buffer, size_t count, Operation operation)
+{
+    m_current_buffer = buffer;
+    m_current_buffer_size = count;
+    m_operation = operation;
+
+    adcStartConversion(m_driver, &m_group_config, buffer, count);
+    SClock::start();
+}
+
+void ADC::stop()
+{
+    SClock::stop();
+    adcStopConversion(m_driver);
+
+    m_current_buffer = nullptr;
+    m_current_buffer_size = 0;
+    m_operation = nullptr;
+}
+
+adcsample_t ADC::readAlt(unsigned int id)
+{
+    if (id > 1)
+        return 0;
+    static adcsample_t result[16] = {};
+    readAltDone = false;
+    adcStartConversion(m_driver2, &m_group_config2, result, 8);
+    while (!readAltDone);
+        //__WFI();
+    adcStopConversion(m_driver2);
+    return result[id];
+}
+
+void ADC::setRate(SClock::Rate rate)
+{
+#if defined(TARGET_PLATFORM_H7)
+    std::array<std::array<uint32_t, 2>, 6> m_rate_presets = {{
+         // Rate   PLL N  PLL P
+        {/* 8k  */ 80,    20},
+        {/* 16k */ 80,    10},
+        {/* 20k */ 80,    8},
+        {/* 32k */ 80,    5},
+        {/* 48k */ 96,    4},
+        {/* 96k */ 288,   10}
+    }};
+
+    auto& preset = m_rate_presets[static_cast<unsigned int>(rate)];
+    auto pllbits = (preset[0] << RCC_PLL2DIVR_N2_Pos) |
+                   (preset[1] << RCC_PLL2DIVR_P2_Pos);
+
+    adcStop(m_driver);
+
+    // Adjust PLL2
+    RCC->CR &= ~(RCC_CR_PLL2ON);
+    while ((RCC->CR & RCC_CR_PLL2RDY) == RCC_CR_PLL2RDY);
+    auto pll2divr = RCC->PLL2DIVR &
+                    ~(RCC_PLL2DIVR_N2_Msk | RCC_PLL2DIVR_P2_Msk);
+    pll2divr |= pllbits;
+    RCC->PLL2DIVR = pll2divr;
+    RCC->CR |= RCC_CR_PLL2ON;
+    while ((RCC->CR & RCC_CR_PLL2RDY) != RCC_CR_PLL2RDY);
+
+    m_group_config.smpr[0] = rate != SClock::Rate::R96K ? ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5)
+                                                        : ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_2P5);
+
+    adcStart(m_driver, &m_config);
+#elif defined(TARGET_PLATFORM_L4)
+    std::array<std::array<uint32_t, 3>, 6> m_rate_presets = {{
+        //  Rate   PLLSAI2N  R  SMPR
+        {/* 8k  */ 8,        1, ADC_SMPR_SMP_12P5},
+        {/* 16k */ 16,       1, ADC_SMPR_SMP_12P5},
+        {/* 20k */ 20,       1, ADC_SMPR_SMP_12P5},
+        {/* 32k */ 32,       1, ADC_SMPR_SMP_12P5},
+        {/* 48k */ 24,       0, ADC_SMPR_SMP_12P5},
+        {/* 96k */ 73,       1, ADC_SMPR_SMP_6P5}   // Technically 96.05263kS/s
+    }};
+
+    auto& preset = m_rate_presets[static_cast<int>(rate)];
+    auto pllnr = (preset[0] << RCC_PLLSAI2CFGR_PLLSAI2N_Pos) |
+                 (preset[1] << RCC_PLLSAI2CFGR_PLLSAI2R_Pos);
+    auto smpr = preset[2];
+
+    // Adjust PLLSAI2
+    RCC->CR &= ~(RCC_CR_PLLSAI2ON);
+    while ((RCC->CR & RCC_CR_PLLSAI2RDY) == RCC_CR_PLLSAI2RDY);
+    RCC->PLLSAI2CFGR = (RCC->PLLSAI2CFGR & ~(RCC_PLLSAI2CFGR_PLLSAI2N_Msk | RCC_PLLSAI2CFGR_PLLSAI2R_Msk)) | pllnr;
+    RCC->CR |= RCC_CR_PLLSAI2ON;
+    while ((RCC->CR & RCC_CR_PLLSAI2RDY) != RCC_CR_PLLSAI2RDY);
+
+    m_group_config.smpr[0] = ADC_SMPR1_SMP_AN5(smpr);
+
+    // Set 2x oversampling
+    m_group_config.cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1;
+    m_group_config2.cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1;
+#endif
+}
+
+void ADC::setOperation(ADC::Operation operation)
+{
+    m_operation = operation;
+}
+
+void ADC::conversionCallback(ADCDriver *driver)
+{
+    if (m_operation != nullptr) {
+        auto half_size = m_current_buffer_size / 2;
+        if (adcIsBufferComplete(driver))
+            m_operation(m_current_buffer + half_size, half_size);
+        else
+            m_operation(m_current_buffer, half_size);
+    }
+}
+
diff --git a/source/periph/adc.hpp b/source/periph/adc.hpp
new file mode 100644 (file)
index 0000000..5f7fa08
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * @file adc.hpp
+ * @brief Manages signal reading through the ADC.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_ADC_HPP_
+#define STMDSP_ADC_HPP_
+
+#include "hal.h"
+#include "sclock.hpp"
+
+#include <array>
+
+class ADC
+{
+public:
+    using Operation = void (*)(adcsample_t *buffer, size_t count);
+
+    static void begin();
+
+    static void start(adcsample_t *buffer, size_t count, Operation operation);
+    static void stop();
+
+    static adcsample_t readAlt(unsigned int id);
+
+    static void setRate(SClock::Rate rate);
+    static void setOperation(Operation operation);
+
+private:
+    static ADCDriver *m_driver;
+    static ADCDriver *m_driver2;
+
+    static const ADCConfig m_config;
+    static const ADCConfig m_config2;
+    static ADCConversionGroup m_group_config;
+    static ADCConversionGroup m_group_config2;
+
+    static adcsample_t *m_current_buffer;
+    static size_t m_current_buffer_size;
+    static Operation m_operation;
+
+public:
+    static void conversionCallback(ADCDriver *);
+};
+
+#endif // STMDSP_ADC_HPP_
+
diff --git a/source/periph/cordic.cpp b/source/periph/cordic.cpp
new file mode 100644 (file)
index 0000000..29ee068
--- /dev/null
@@ -0,0 +1,113 @@
+#include "cordic.hpp"
+#include "hal.h"
+
+#if !defined(TARGET_PLATFORM_L4)
+namespace cordic {
+
+void init()
+{
+    RCC->AHB2ENR |= RCC_AHB2ENR_CORDICEN;
+}
+
+static void prepare() {
+    while (CORDIC->CSR & CORDIC_CSR_RRDY)
+        asm("mov r0, %0" :: "r" (CORDIC->RDATA));
+}
+
+static uint32_t dtoq(double) {
+    uint32_t res;
+    asm("vcvt.s32.f64 d0, d0, #31;"
+        "vmov %0, r5, d0"
+    : "=r" (res));
+    return res;
+}
+__attribute__((naked))
+static double qtod(uint32_t) {
+    asm("eor r1, r1;"
+        "vmov d0, r0, r1;"
+        "vcvt.f64.s32 d0, d0, #31;"
+        "bx lr");
+    return 0;
+}
+__attribute__((naked))
+double mod(double, double) {
+    asm("vdiv.f64   d2, d0, d1;"
+        "vrintz.f64 d2;"
+        "vmul.f64   d1, d1, d2;"
+        "vsub.f64   d0, d0, d1;"
+        "bx lr");
+    return 0;
+}
+
+double cos(double x) {
+    x = mod(x, 2 * math::PI) / math::PI;
+    auto input = dtoq(x > 1. ? x - 2 : x);
+
+    prepare();
+    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+                  (6 << CORDIC_CSR_PRECISION_Pos) |
+                  (0 << CORDIC_CSR_FUNC_Pos);
+
+    CORDIC->WDATA = input;
+    CORDIC->WDATA = input;
+    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+    double cosx = qtod(CORDIC->RDATA) / x;
+    [[maybe_unused]] auto sinx = CORDIC->RDATA;
+    return cosx;
+}
+
+double sin(double x) {
+    x = mod(x, 2 * math::PI) / math::PI;
+    auto input = dtoq(x > 1. ? x - 2 : x);
+
+    prepare();
+    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+                  (6 << CORDIC_CSR_PRECISION_Pos) |
+                  (1 << CORDIC_CSR_FUNC_Pos);
+
+    CORDIC->WDATA = input;
+    CORDIC->WDATA = input;
+    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+    double sinx = qtod(CORDIC->RDATA) / x;
+    [[maybe_unused]] auto cosx = CORDIC->RDATA;
+    return sinx;
+}
+
+double tan(double x) {
+    x = mod(x, 2 * math::PI) / math::PI;
+    auto input = dtoq(x > 1. ? x - 2 : x);
+
+    prepare();
+    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+                  (6 << CORDIC_CSR_PRECISION_Pos) |
+                  (1 << CORDIC_CSR_FUNC_Pos);
+
+    CORDIC->WDATA = input;
+    CORDIC->WDATA = input;
+    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+    double sinx = qtod(CORDIC->RDATA) / x;
+    double tanx = sinx * x / qtod(CORDIC->RDATA);
+    return tanx;
+}
+
+}
+#else // L4
+#include <cmath>
+namespace cordic {
+
+void init() {}
+
+float mod(float a, float b) {
+    return a - (b * std::floor(a / b));
+}
+
+float cos(float x) { return std::cos(x); }
+float sin(float x) { return std::sin(x); }
+float tan(float x) { return std::tan(x); }
+
+}
+#endif
+
diff --git a/source/periph/cordic.hpp b/source/periph/cordic.hpp
new file mode 100644 (file)
index 0000000..5d640cc
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef CORDIC_HPP_
+#define CORDIC_HPP_
+
+namespace cordic {
+    constexpr double PI = 3.1415926535L;
+
+    void init();
+
+#if !defined(TARGET_PLATFORM_L4)
+    double mod(double n, double d);
+
+    double cos(double x);
+    double sin(double x);
+    double tan(double x);
+#else
+    float mod(float n, float d);
+
+    float cos(float x);
+    float sin(float x);
+    float tan(float x);
+#endif
+}
+
+#endif // CORDIC_HPP_
+
diff --git a/source/periph/dac.cpp b/source/periph/dac.cpp
new file mode 100644 (file)
index 0000000..1ff8867
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * @file dac.cpp
+ * @brief Manages signal creation using the DAC.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "dac.hpp"
+#include "sclock.hpp"
+
+DACDriver *DAC::m_driver[2] = {
+    &DACD1, &DACD2
+};
+
+const DACConfig DAC::m_config = {
+    .init = 2048,
+    .datamode = DAC_DHRM_12BIT_RIGHT,
+    .cr = 0
+};
+
+static int dacIsDone = -1;
+static void dacEndCallback(DACDriver *dacd)
+{
+    if (dacd == &DACD2)
+        dacIsDone = dacIsBufferComplete(dacd) ? 1 : 0;
+}
+
+const DACConversionGroup DAC::m_group_config = {
+    .num_channels = 1,
+    .end_cb = dacEndCallback,
+    .error_cb = nullptr,
+#if defined(TARGET_PLATFORM_H7)
+    .trigger = 5 // TIM6_TRGO
+#elif defined(TARGET_PLATFORM_L4)
+    .trigger = 0 // TIM6_TRGO
+#endif
+};
+
+void DAC::begin()
+{
+    palSetPadMode(GPIOA, 4, PAL_STM32_MODE_ANALOG);
+    palSetPadMode(GPIOA, 5, PAL_STM32_MODE_ANALOG);
+
+    dacStart(m_driver[0], &m_config);
+    dacStart(m_driver[1], &m_config);
+}
+
+void DAC::start(int channel, dacsample_t *buffer, size_t count)
+{
+    if (channel >= 0 && channel < 2) {
+        if (channel == 1)
+            dacIsDone = -1;
+        dacStartConversion(m_driver[channel], &m_group_config, buffer, count);
+        SClock::start();
+    }
+}
+
+int DAC::sigGenWantsMore()
+{
+    return dacIsDone;
+}
+
+void DAC::stop(int channel)
+{
+    if (channel >= 0 && channel < 2) {
+        dacStopConversion(m_driver[channel]);
+        SClock::stop();
+    }
+}
+
diff --git a/source/periph/dac.hpp b/source/periph/dac.hpp
new file mode 100644 (file)
index 0000000..4360c26
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * @file dac.hpp
+ * @brief Manages signal creation using the DAC.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_DAC_HPP_
+#define STMDSP_DAC_HPP_
+
+#include "hal.h"
+#undef DAC
+
+class DAC
+{
+public:
+    static void begin();
+
+    static void start(int channel, dacsample_t *buffer, size_t count);
+    static void stop(int channel);
+
+    static int sigGenWantsMore();
+
+private:
+    static DACDriver *m_driver[2];
+
+    static const DACConfig m_config;
+    static const DACConversionGroup m_group_config;
+};
+
+#endif // STMDSP_DAC_HPP_
+
diff --git a/source/periph/usbcfg.c b/source/periph/usbcfg.c
new file mode 100644 (file)
index 0000000..b726e23
--- /dev/null
@@ -0,0 +1,346 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+#include "hal.h"\r
+\r
+/* Virtual serial port over USB.*/\r
+SerialUSBDriver SDU1;\r
+\r
+/*\r
+ * Endpoints to be used for USBD1.\r
+ */\r
+#define USBD1_DATA_REQUEST_EP           1\r
+#define USBD1_DATA_AVAILABLE_EP         1\r
+#define USBD1_INTERRUPT_REQUEST_EP      2\r
+\r
+/*\r
+ * USB Device Descriptor.\r
+ */\r
+static const uint8_t vcom_device_descriptor_data[18] = {\r
+  USB_DESC_DEVICE       (0x0110,        /* bcdUSB (1.1).                    */\r
+                         0x02,          /* bDeviceClass (CDC).              */\r
+                         0x00,          /* bDeviceSubClass.                 */\r
+                         0x00,          /* bDeviceProtocol.                 */\r
+                         0x40,          /* bMaxPacketSize.                  */\r
+                         0x0483,        /* idVendor (ST).                   */\r
+                         0x5740,        /* idProduct.                       */\r
+                         0x0200,        /* bcdDevice.                       */\r
+                         1,             /* iManufacturer.                   */\r
+                         2,             /* iProduct.                        */\r
+                         3,             /* iSerialNumber.                   */\r
+                         1)             /* bNumConfigurations.              */\r
+};\r
+\r
+/*\r
+ * Device Descriptor wrapper.\r
+ */\r
+static const USBDescriptor vcom_device_descriptor = {\r
+  sizeof vcom_device_descriptor_data,\r
+  vcom_device_descriptor_data\r
+};\r
+\r
+/* Configuration Descriptor tree for a CDC.*/\r
+static const uint8_t vcom_configuration_descriptor_data[67] = {\r
+  /* Configuration Descriptor.*/\r
+  USB_DESC_CONFIGURATION(67,            /* wTotalLength.                    */\r
+                         0x02,          /* bNumInterfaces.                  */\r
+                         0x01,          /* bConfigurationValue.             */\r
+                         0,             /* iConfiguration.                  */\r
+                         0xC0,          /* bmAttributes (self powered).     */\r
+                         50),           /* bMaxPower (100mA).               */\r
+  /* Interface Descriptor.*/\r
+  USB_DESC_INTERFACE    (0x00,          /* bInterfaceNumber.                */\r
+                         0x00,          /* bAlternateSetting.               */\r
+                         0x01,          /* bNumEndpoints.                   */\r
+                         0x02,          /* bInterfaceClass (Communications\r
+                                           Interface Class, CDC section\r
+                                           4.2).                            */\r
+                         0x02,          /* bInterfaceSubClass (Abstract\r
+                                         Control Model, CDC section 4.3).   */\r
+                         0x01,          /* bInterfaceProtocol (AT commands,\r
+                                           CDC section 4.4).                */\r
+                         0),            /* iInterface.                      */\r
+  /* Header Functional Descriptor (CDC section 5.2.3).*/\r
+  USB_DESC_BYTE         (5),            /* bLength.                         */\r
+  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
+  USB_DESC_BYTE         (0x00),         /* bDescriptorSubtype (Header\r
+                                           Functional Descriptor.           */\r
+  USB_DESC_BCD          (0x0110),       /* bcdCDC.                          */\r
+  /* Call Management Functional Descriptor. */\r
+  USB_DESC_BYTE         (5),            /* bFunctionLength.                 */\r
+  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
+  USB_DESC_BYTE         (0x01),         /* bDescriptorSubtype (Call Management\r
+                                           Functional Descriptor).          */\r
+  USB_DESC_BYTE         (0x00),         /* bmCapabilities (D0+D1).          */\r
+  USB_DESC_BYTE         (0x01),         /* bDataInterface.                  */\r
+  /* ACM Functional Descriptor.*/\r
+  USB_DESC_BYTE         (4),            /* bFunctionLength.                 */\r
+  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
+  USB_DESC_BYTE         (0x02),         /* bDescriptorSubtype (Abstract\r
+                                           Control Management Descriptor).  */\r
+  USB_DESC_BYTE         (0x02),         /* bmCapabilities.                  */\r
+  /* Union Functional Descriptor.*/\r
+  USB_DESC_BYTE         (5),            /* bFunctionLength.                 */\r
+  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
+  USB_DESC_BYTE         (0x06),         /* bDescriptorSubtype (Union\r
+                                           Functional Descriptor).          */\r
+  USB_DESC_BYTE         (0x00),         /* bMasterInterface (Communication\r
+                                           Class Interface).                */\r
+  USB_DESC_BYTE         (0x01),         /* bSlaveInterface0 (Data Class\r
+                                           Interface).                      */\r
+  /* Endpoint 2 Descriptor.*/\r
+  USB_DESC_ENDPOINT     (USBD1_INTERRUPT_REQUEST_EP|0x80,\r
+                         0x03,          /* bmAttributes (Interrupt).        */\r
+                         0x0008,        /* wMaxPacketSize.                  */\r
+                         0xFF),         /* bInterval.                       */\r
+  /* Interface Descriptor.*/\r
+  USB_DESC_INTERFACE    (0x01,          /* bInterfaceNumber.                */\r
+                         0x00,          /* bAlternateSetting.               */\r
+                         0x02,          /* bNumEndpoints.                   */\r
+                         0x0A,          /* bInterfaceClass (Data Class\r
+                                           Interface, CDC section 4.5).     */\r
+                         0x00,          /* bInterfaceSubClass (CDC section\r
+                                           4.6).                            */\r
+                         0x00,          /* bInterfaceProtocol (CDC section\r
+                                           4.7).                            */\r
+                         0x00),         /* iInterface.                      */\r
+  /* Endpoint 3 Descriptor.*/\r
+  USB_DESC_ENDPOINT     (USBD1_DATA_AVAILABLE_EP,       /* bEndpointAddress.*/\r
+                         0x02,          /* bmAttributes (Bulk).             */\r
+                         0x0040,        /* wMaxPacketSize.                  */\r
+                         0x00),         /* bInterval.                       */\r
+  /* Endpoint 1 Descriptor.*/\r
+  USB_DESC_ENDPOINT     (USBD1_DATA_REQUEST_EP|0x80,    /* bEndpointAddress.*/\r
+                         0x02,          /* bmAttributes (Bulk).             */\r
+                         0x0040,        /* wMaxPacketSize.                  */\r
+                         0x00)          /* bInterval.                       */\r
+};\r
+\r
+/*\r
+ * Configuration Descriptor wrapper.\r
+ */\r
+static const USBDescriptor vcom_configuration_descriptor = {\r
+  sizeof vcom_configuration_descriptor_data,\r
+  vcom_configuration_descriptor_data\r
+};\r
+\r
+/*\r
+ * U.S. English language identifier.\r
+ */\r
+static const uint8_t vcom_string0[] = {\r
+  USB_DESC_BYTE(4),                     /* bLength.                         */\r
+  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
+  USB_DESC_WORD(0x0409)                 /* wLANGID (U.S. English).          */\r
+};\r
+\r
+/*\r
+ * Vendor string.\r
+ */\r
+static const uint8_t vcom_string1[] = {\r
+  USB_DESC_BYTE(38),                    /* bLength.                         */\r
+  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
+  'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,\r
+  'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,\r
+  'c', 0, 's', 0\r
+};\r
+\r
+/*\r
+ * Device Description string.\r
+ */\r
+static const uint8_t vcom_string2[] = {\r
+  USB_DESC_BYTE(56),                    /* bLength.                         */\r
+  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
+  'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,\r
+  'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0,\r
+  'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0,\r
+  'o', 0, 'r', 0, 't', 0\r
+};\r
+\r
+/*\r
+ * Serial Number string.\r
+ */\r
+static const uint8_t vcom_string3[] = {\r
+  USB_DESC_BYTE(8),                     /* bLength.                         */\r
+  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
+  '0' + CH_KERNEL_MAJOR, 0,\r
+  '0' + CH_KERNEL_MINOR, 0,\r
+  '0' + CH_KERNEL_PATCH, 0\r
+};\r
+\r
+/*\r
+ * Strings wrappers array.\r
+ */\r
+static const USBDescriptor vcom_strings[] = {\r
+  {sizeof vcom_string0, vcom_string0},\r
+  {sizeof vcom_string1, vcom_string1},\r
+  {sizeof vcom_string2, vcom_string2},\r
+  {sizeof vcom_string3, vcom_string3}\r
+};\r
+\r
+/*\r
+ * Handles the GET_DESCRIPTOR callback. All required descriptors must be\r
+ * handled here.\r
+ */\r
+static const USBDescriptor *get_descriptor(USBDriver *usbp,\r
+                                           uint8_t dtype,\r
+                                           uint8_t dindex,\r
+                                           uint16_t lang) {\r
+\r
+  (void)usbp;\r
+  (void)lang;\r
+  switch (dtype) {\r
+  case USB_DESCRIPTOR_DEVICE:\r
+    return &vcom_device_descriptor;\r
+  case USB_DESCRIPTOR_CONFIGURATION:\r
+    return &vcom_configuration_descriptor;\r
+  case USB_DESCRIPTOR_STRING:\r
+    if (dindex < 4)\r
+      return &vcom_strings[dindex];\r
+  }\r
+  return NULL;\r
+}\r
+\r
+/**\r
+ * @brief   IN EP1 state.\r
+ */\r
+static USBInEndpointState ep1instate;\r
+\r
+/**\r
+ * @brief   OUT EP1 state.\r
+ */\r
+static USBOutEndpointState ep1outstate;\r
+\r
+/**\r
+ * @brief   EP1 initialization structure (both IN and OUT).\r
+ */\r
+static const USBEndpointConfig ep1config = {\r
+  USB_EP_MODE_TYPE_BULK,\r
+  NULL,\r
+  sduDataTransmitted,\r
+  sduDataReceived,\r
+  0x0040,\r
+  0x0040,\r
+  &ep1instate,\r
+  &ep1outstate,\r
+  1,\r
+  NULL\r
+};\r
+\r
+/**\r
+ * @brief   IN EP2 state.\r
+ */\r
+static USBInEndpointState ep2instate;\r
+\r
+/**\r
+ * @brief   EP2 initialization structure (IN only).\r
+ */\r
+static const USBEndpointConfig ep2config = {\r
+  USB_EP_MODE_TYPE_INTR,\r
+  NULL,\r
+  sduInterruptTransmitted,\r
+  NULL,\r
+  0x0010,\r
+  0x0000,\r
+  &ep2instate,\r
+  NULL,\r
+  1,\r
+  NULL\r
+};\r
+\r
+/*\r
+ * Handles the USB driver global events.\r
+ */\r
+static void usb_event(USBDriver *usbp, usbevent_t event) {\r
+  extern SerialUSBDriver SDU1;\r
+\r
+  switch (event) {\r
+  case USB_EVENT_ADDRESS:\r
+    return;\r
+  case USB_EVENT_CONFIGURED:\r
+    chSysLockFromISR();\r
+\r
+    /* Enables the endpoints specified into the configuration.\r
+       Note, this callback is invoked from an ISR so I-Class functions\r
+       must be used.*/\r
+    usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config);\r
+    usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config);\r
+\r
+    /* Resetting the state of the CDC subsystem.*/\r
+    sduConfigureHookI(&SDU1);\r
+\r
+    chSysUnlockFromISR();\r
+    return;\r
+  case USB_EVENT_RESET:\r
+    /* Falls into.*/\r
+  case USB_EVENT_UNCONFIGURED:\r
+    /* Falls into.*/\r
+  case USB_EVENT_SUSPEND:\r
+    chSysLockFromISR();\r
+\r
+    /* Disconnection event on suspend.*/\r
+    sduSuspendHookI(&SDU1);\r
+\r
+    chSysUnlockFromISR();\r
+    return;\r
+  case USB_EVENT_WAKEUP:\r
+    chSysLockFromISR();\r
+\r
+    /* Connection event on wakeup.*/\r
+    sduWakeupHookI(&SDU1);\r
+\r
+    chSysUnlockFromISR();\r
+    return;\r
+  case USB_EVENT_STALLED:\r
+    return;\r
+  }\r
+  return;\r
+}\r
+\r
+/*\r
+ * Handles the USB driver global events.\r
+ */\r
+static void sof_handler(USBDriver *usbp) {\r
+\r
+  (void)usbp;\r
+\r
+  osalSysLockFromISR();\r
+  sduSOFHookI(&SDU1);\r
+  osalSysUnlockFromISR();\r
+}\r
+\r
+/*\r
+ * USB driver configuration.\r
+ */\r
+const USBConfig usbcfg = {\r
+  usb_event,\r
+  get_descriptor,\r
+  sduRequestsHook,\r
+  sof_handler\r
+};\r
+\r
+/*\r
+ * Serial over USB driver configuration.\r
+ */\r
+const SerialUSBConfig serusbcfg = {\r
+#if defined(TARGET_PLATFORM_H7)\r
+  &USBD2,\r
+#else\r
+  &USBD1,\r
+#endif\r
+  USBD1_DATA_REQUEST_EP,\r
+  USBD1_DATA_AVAILABLE_EP,\r
+  USBD1_INTERRUPT_REQUEST_EP\r
+};\r
diff --git a/source/periph/usbcfg.h b/source/periph/usbcfg.h
new file mode 100644 (file)
index 0000000..2fceccb
--- /dev/null
@@ -0,0 +1,28 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+#ifndef USBCFG_H\r
+#define USBCFG_H\r
+\r
+#include "hal.h"\r
+\r
+extern const USBConfig usbcfg;\r
+extern SerialUSBConfig serusbcfg;\r
+extern SerialUSBDriver SDU1;\r
+\r
+#endif  /* USBCFG_H */\r
+\r
+/** @} */\r
diff --git a/source/periph/usbserial.cpp b/source/periph/usbserial.cpp
new file mode 100644 (file)
index 0000000..775a911
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * @file usbserial.cpp
+ * @brief Wrapper for ChibiOS's SerialUSBDriver.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "usbserial.hpp"
+
+SerialUSBDriver *USBSerial::m_driver = &SDU1;
+
+void USBSerial::begin()
+{
+    palSetPadMode(GPIOA, 11, PAL_MODE_ALTERNATE(10));
+    palSetPadMode(GPIOA, 12, PAL_MODE_ALTERNATE(10));
+
+    sduObjectInit(m_driver);
+    sduStart(m_driver, &serusbcfg);
+
+    // Reconnect bus so device can re-enumerate on reset
+    usbDisconnectBus(serusbcfg.usbp);
+    chThdSleepMilliseconds(1500);
+    usbStart(serusbcfg.usbp, &usbcfg);
+    usbConnectBus(serusbcfg.usbp);
+}
+
+bool USBSerial::isActive()
+{
+    if (auto config = m_driver->config; config != nullptr) {
+        if (auto usbp = config->usbp; usbp != nullptr)
+            return usbp->state == USB_ACTIVE && !ibqIsEmptyI(&m_driver->ibqueue);
+    }
+
+    return false;
+}
+
+size_t USBSerial::read(unsigned char *buffer, size_t count)
+{
+    auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
+    return streamRead(bss, buffer, count);
+}
+
+size_t USBSerial::write(const unsigned char *buffer, size_t count)
+{
+    auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
+    return streamWrite(bss, buffer, count);
+}
+
diff --git a/source/periph/usbserial.hpp b/source/periph/usbserial.hpp
new file mode 100644 (file)
index 0000000..58113c9
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * @file usbserial.hpp
+ * @brief Wrapper for ChibiOS's SerialUSBDriver.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_USBSERIAL_HPP_
+#define STMDSP_USBSERIAL_HPP_
+
+#include "usbcfg.h"
+
+class USBSerial
+{
+public:
+    static void begin();
+
+    static bool isActive();
+
+    static size_t read(unsigned char *buffer, size_t count);
+    static size_t write(const unsigned char *buffer, size_t count);
+
+private:
+    static SerialUSBDriver *m_driver;
+};
+
+#endif // STMDSP_USBSERIAL_HPP_
+
diff --git a/source/runstatus.hpp b/source/runstatus.hpp
new file mode 100644 (file)
index 0000000..ab269b4
--- /dev/null
@@ -0,0 +1,11 @@
+// Run status
+//
+enum class RunStatus : char
+{
+    Idle = '1',
+    Running,
+    Recovering
+};
+
+extern RunStatus run_status;
+
diff --git a/source/samples.cpp b/source/samples.cpp
new file mode 100644 (file)
index 0000000..cfbf835
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * @file samples.cpp
+ * @brief Provides sample buffers for inputs and outputs.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "samples.hpp"
+
+#include "ch.h"
+#include "hal.h"
+
+#include <cstdint>
+
+static_assert(sizeof(adcsample_t) == sizeof(uint16_t));
+static_assert(sizeof(dacsample_t) == sizeof(uint16_t));
+
+#if defined(TARGET_PLATFORM_H7)
+__attribute__((section(".convdata")))
+SampleBuffer Samples::In (reinterpret_cast<Sample *>(0x38000000)); // 16k
+__attribute__((section(".convdata")))
+SampleBuffer Samples::Out (reinterpret_cast<Sample *>(0x30004000)); // 16k
+SampleBuffer Samples::SigGen (reinterpret_cast<Sample *>(0x30000000)); // 16k
+#else
+__attribute__((section(".convdata")))
+SampleBuffer Samples::In (reinterpret_cast<Sample *>(0x20008000)); // 16k
+__attribute__((section(".convdata")))
+SampleBuffer Samples::Out (reinterpret_cast<Sample *>(0x2000C000)); // 16k
+SampleBuffer Samples::Generator (reinterpret_cast<Sample *>(0x20010000)); // 16k
+#endif
+
diff --git a/source/samples.hpp b/source/samples.hpp
new file mode 100644 (file)
index 0000000..6551e25
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * @file samples.hpp
+ * @brief Provides sample buffers for inputs and outputs.
+ *
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_SAMPLES_HPP
+#define STMDSP_SAMPLES_HPP
+
+#include "samplebuffer.hpp"
+
+class Samples
+{
+public:
+    static SampleBuffer In;
+    static SampleBuffer Out;
+    static SampleBuffer Generator;
+};
+
+#endif // STMDSP_SAMPLES_HPP
+
diff --git a/source/usbcfg.c b/source/usbcfg.c
deleted file mode 100644 (file)
index b726e23..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-/*\r
-    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
-\r
-    Licensed under the Apache License, Version 2.0 (the "License");\r
-    you may not use this file except in compliance with the License.\r
-    You may obtain a copy of the License at\r
-\r
-        http://www.apache.org/licenses/LICENSE-2.0\r
-\r
-    Unless required by applicable law or agreed to in writing, software\r
-    distributed under the License is distributed on an "AS IS" BASIS,\r
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-    See the License for the specific language governing permissions and\r
-    limitations under the License.\r
-*/\r
-\r
-#include "hal.h"\r
-\r
-/* Virtual serial port over USB.*/\r
-SerialUSBDriver SDU1;\r
-\r
-/*\r
- * Endpoints to be used for USBD1.\r
- */\r
-#define USBD1_DATA_REQUEST_EP           1\r
-#define USBD1_DATA_AVAILABLE_EP         1\r
-#define USBD1_INTERRUPT_REQUEST_EP      2\r
-\r
-/*\r
- * USB Device Descriptor.\r
- */\r
-static const uint8_t vcom_device_descriptor_data[18] = {\r
-  USB_DESC_DEVICE       (0x0110,        /* bcdUSB (1.1).                    */\r
-                         0x02,          /* bDeviceClass (CDC).              */\r
-                         0x00,          /* bDeviceSubClass.                 */\r
-                         0x00,          /* bDeviceProtocol.                 */\r
-                         0x40,          /* bMaxPacketSize.                  */\r
-                         0x0483,        /* idVendor (ST).                   */\r
-                         0x5740,        /* idProduct.                       */\r
-                         0x0200,        /* bcdDevice.                       */\r
-                         1,             /* iManufacturer.                   */\r
-                         2,             /* iProduct.                        */\r
-                         3,             /* iSerialNumber.                   */\r
-                         1)             /* bNumConfigurations.              */\r
-};\r
-\r
-/*\r
- * Device Descriptor wrapper.\r
- */\r
-static const USBDescriptor vcom_device_descriptor = {\r
-  sizeof vcom_device_descriptor_data,\r
-  vcom_device_descriptor_data\r
-};\r
-\r
-/* Configuration Descriptor tree for a CDC.*/\r
-static const uint8_t vcom_configuration_descriptor_data[67] = {\r
-  /* Configuration Descriptor.*/\r
-  USB_DESC_CONFIGURATION(67,            /* wTotalLength.                    */\r
-                         0x02,          /* bNumInterfaces.                  */\r
-                         0x01,          /* bConfigurationValue.             */\r
-                         0,             /* iConfiguration.                  */\r
-                         0xC0,          /* bmAttributes (self powered).     */\r
-                         50),           /* bMaxPower (100mA).               */\r
-  /* Interface Descriptor.*/\r
-  USB_DESC_INTERFACE    (0x00,          /* bInterfaceNumber.                */\r
-                         0x00,          /* bAlternateSetting.               */\r
-                         0x01,          /* bNumEndpoints.                   */\r
-                         0x02,          /* bInterfaceClass (Communications\r
-                                           Interface Class, CDC section\r
-                                           4.2).                            */\r
-                         0x02,          /* bInterfaceSubClass (Abstract\r
-                                         Control Model, CDC section 4.3).   */\r
-                         0x01,          /* bInterfaceProtocol (AT commands,\r
-                                           CDC section 4.4).                */\r
-                         0),            /* iInterface.                      */\r
-  /* Header Functional Descriptor (CDC section 5.2.3).*/\r
-  USB_DESC_BYTE         (5),            /* bLength.                         */\r
-  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
-  USB_DESC_BYTE         (0x00),         /* bDescriptorSubtype (Header\r
-                                           Functional Descriptor.           */\r
-  USB_DESC_BCD          (0x0110),       /* bcdCDC.                          */\r
-  /* Call Management Functional Descriptor. */\r
-  USB_DESC_BYTE         (5),            /* bFunctionLength.                 */\r
-  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
-  USB_DESC_BYTE         (0x01),         /* bDescriptorSubtype (Call Management\r
-                                           Functional Descriptor).          */\r
-  USB_DESC_BYTE         (0x00),         /* bmCapabilities (D0+D1).          */\r
-  USB_DESC_BYTE         (0x01),         /* bDataInterface.                  */\r
-  /* ACM Functional Descriptor.*/\r
-  USB_DESC_BYTE         (4),            /* bFunctionLength.                 */\r
-  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
-  USB_DESC_BYTE         (0x02),         /* bDescriptorSubtype (Abstract\r
-                                           Control Management Descriptor).  */\r
-  USB_DESC_BYTE         (0x02),         /* bmCapabilities.                  */\r
-  /* Union Functional Descriptor.*/\r
-  USB_DESC_BYTE         (5),            /* bFunctionLength.                 */\r
-  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
-  USB_DESC_BYTE         (0x06),         /* bDescriptorSubtype (Union\r
-                                           Functional Descriptor).          */\r
-  USB_DESC_BYTE         (0x00),         /* bMasterInterface (Communication\r
-                                           Class Interface).                */\r
-  USB_DESC_BYTE         (0x01),         /* bSlaveInterface0 (Data Class\r
-                                           Interface).                      */\r
-  /* Endpoint 2 Descriptor.*/\r
-  USB_DESC_ENDPOINT     (USBD1_INTERRUPT_REQUEST_EP|0x80,\r
-                         0x03,          /* bmAttributes (Interrupt).        */\r
-                         0x0008,        /* wMaxPacketSize.                  */\r
-                         0xFF),         /* bInterval.                       */\r
-  /* Interface Descriptor.*/\r
-  USB_DESC_INTERFACE    (0x01,          /* bInterfaceNumber.                */\r
-                         0x00,          /* bAlternateSetting.               */\r
-                         0x02,          /* bNumEndpoints.                   */\r
-                         0x0A,          /* bInterfaceClass (Data Class\r
-                                           Interface, CDC section 4.5).     */\r
-                         0x00,          /* bInterfaceSubClass (CDC section\r
-                                           4.6).                            */\r
-                         0x00,          /* bInterfaceProtocol (CDC section\r
-                                           4.7).                            */\r
-                         0x00),         /* iInterface.                      */\r
-  /* Endpoint 3 Descriptor.*/\r
-  USB_DESC_ENDPOINT     (USBD1_DATA_AVAILABLE_EP,       /* bEndpointAddress.*/\r
-                         0x02,          /* bmAttributes (Bulk).             */\r
-                         0x0040,        /* wMaxPacketSize.                  */\r
-                         0x00),         /* bInterval.                       */\r
-  /* Endpoint 1 Descriptor.*/\r
-  USB_DESC_ENDPOINT     (USBD1_DATA_REQUEST_EP|0x80,    /* bEndpointAddress.*/\r
-                         0x02,          /* bmAttributes (Bulk).             */\r
-                         0x0040,        /* wMaxPacketSize.                  */\r
-                         0x00)          /* bInterval.                       */\r
-};\r
-\r
-/*\r
- * Configuration Descriptor wrapper.\r
- */\r
-static const USBDescriptor vcom_configuration_descriptor = {\r
-  sizeof vcom_configuration_descriptor_data,\r
-  vcom_configuration_descriptor_data\r
-};\r
-\r
-/*\r
- * U.S. English language identifier.\r
- */\r
-static const uint8_t vcom_string0[] = {\r
-  USB_DESC_BYTE(4),                     /* bLength.                         */\r
-  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
-  USB_DESC_WORD(0x0409)                 /* wLANGID (U.S. English).          */\r
-};\r
-\r
-/*\r
- * Vendor string.\r
- */\r
-static const uint8_t vcom_string1[] = {\r
-  USB_DESC_BYTE(38),                    /* bLength.                         */\r
-  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
-  'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,\r
-  'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,\r
-  'c', 0, 's', 0\r
-};\r
-\r
-/*\r
- * Device Description string.\r
- */\r
-static const uint8_t vcom_string2[] = {\r
-  USB_DESC_BYTE(56),                    /* bLength.                         */\r
-  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
-  'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,\r
-  'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0,\r
-  'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0,\r
-  'o', 0, 'r', 0, 't', 0\r
-};\r
-\r
-/*\r
- * Serial Number string.\r
- */\r
-static const uint8_t vcom_string3[] = {\r
-  USB_DESC_BYTE(8),                     /* bLength.                         */\r
-  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
-  '0' + CH_KERNEL_MAJOR, 0,\r
-  '0' + CH_KERNEL_MINOR, 0,\r
-  '0' + CH_KERNEL_PATCH, 0\r
-};\r
-\r
-/*\r
- * Strings wrappers array.\r
- */\r
-static const USBDescriptor vcom_strings[] = {\r
-  {sizeof vcom_string0, vcom_string0},\r
-  {sizeof vcom_string1, vcom_string1},\r
-  {sizeof vcom_string2, vcom_string2},\r
-  {sizeof vcom_string3, vcom_string3}\r
-};\r
-\r
-/*\r
- * Handles the GET_DESCRIPTOR callback. All required descriptors must be\r
- * handled here.\r
- */\r
-static const USBDescriptor *get_descriptor(USBDriver *usbp,\r
-                                           uint8_t dtype,\r
-                                           uint8_t dindex,\r
-                                           uint16_t lang) {\r
-\r
-  (void)usbp;\r
-  (void)lang;\r
-  switch (dtype) {\r
-  case USB_DESCRIPTOR_DEVICE:\r
-    return &vcom_device_descriptor;\r
-  case USB_DESCRIPTOR_CONFIGURATION:\r
-    return &vcom_configuration_descriptor;\r
-  case USB_DESCRIPTOR_STRING:\r
-    if (dindex < 4)\r
-      return &vcom_strings[dindex];\r
-  }\r
-  return NULL;\r
-}\r
-\r
-/**\r
- * @brief   IN EP1 state.\r
- */\r
-static USBInEndpointState ep1instate;\r
-\r
-/**\r
- * @brief   OUT EP1 state.\r
- */\r
-static USBOutEndpointState ep1outstate;\r
-\r
-/**\r
- * @brief   EP1 initialization structure (both IN and OUT).\r
- */\r
-static const USBEndpointConfig ep1config = {\r
-  USB_EP_MODE_TYPE_BULK,\r
-  NULL,\r
-  sduDataTransmitted,\r
-  sduDataReceived,\r
-  0x0040,\r
-  0x0040,\r
-  &ep1instate,\r
-  &ep1outstate,\r
-  1,\r
-  NULL\r
-};\r
-\r
-/**\r
- * @brief   IN EP2 state.\r
- */\r
-static USBInEndpointState ep2instate;\r
-\r
-/**\r
- * @brief   EP2 initialization structure (IN only).\r
- */\r
-static const USBEndpointConfig ep2config = {\r
-  USB_EP_MODE_TYPE_INTR,\r
-  NULL,\r
-  sduInterruptTransmitted,\r
-  NULL,\r
-  0x0010,\r
-  0x0000,\r
-  &ep2instate,\r
-  NULL,\r
-  1,\r
-  NULL\r
-};\r
-\r
-/*\r
- * Handles the USB driver global events.\r
- */\r
-static void usb_event(USBDriver *usbp, usbevent_t event) {\r
-  extern SerialUSBDriver SDU1;\r
-\r
-  switch (event) {\r
-  case USB_EVENT_ADDRESS:\r
-    return;\r
-  case USB_EVENT_CONFIGURED:\r
-    chSysLockFromISR();\r
-\r
-    /* Enables the endpoints specified into the configuration.\r
-       Note, this callback is invoked from an ISR so I-Class functions\r
-       must be used.*/\r
-    usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config);\r
-    usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config);\r
-\r
-    /* Resetting the state of the CDC subsystem.*/\r
-    sduConfigureHookI(&SDU1);\r
-\r
-    chSysUnlockFromISR();\r
-    return;\r
-  case USB_EVENT_RESET:\r
-    /* Falls into.*/\r
-  case USB_EVENT_UNCONFIGURED:\r
-    /* Falls into.*/\r
-  case USB_EVENT_SUSPEND:\r
-    chSysLockFromISR();\r
-\r
-    /* Disconnection event on suspend.*/\r
-    sduSuspendHookI(&SDU1);\r
-\r
-    chSysUnlockFromISR();\r
-    return;\r
-  case USB_EVENT_WAKEUP:\r
-    chSysLockFromISR();\r
-\r
-    /* Connection event on wakeup.*/\r
-    sduWakeupHookI(&SDU1);\r
-\r
-    chSysUnlockFromISR();\r
-    return;\r
-  case USB_EVENT_STALLED:\r
-    return;\r
-  }\r
-  return;\r
-}\r
-\r
-/*\r
- * Handles the USB driver global events.\r
- */\r
-static void sof_handler(USBDriver *usbp) {\r
-\r
-  (void)usbp;\r
-\r
-  osalSysLockFromISR();\r
-  sduSOFHookI(&SDU1);\r
-  osalSysUnlockFromISR();\r
-}\r
-\r
-/*\r
- * USB driver configuration.\r
- */\r
-const USBConfig usbcfg = {\r
-  usb_event,\r
-  get_descriptor,\r
-  sduRequestsHook,\r
-  sof_handler\r
-};\r
-\r
-/*\r
- * Serial over USB driver configuration.\r
- */\r
-const SerialUSBConfig serusbcfg = {\r
-#if defined(TARGET_PLATFORM_H7)\r
-  &USBD2,\r
-#else\r
-  &USBD1,\r
-#endif\r
-  USBD1_DATA_REQUEST_EP,\r
-  USBD1_DATA_AVAILABLE_EP,\r
-  USBD1_INTERRUPT_REQUEST_EP\r
-};\r
diff --git a/source/usbcfg.h b/source/usbcfg.h
deleted file mode 100644 (file)
index 2fceccb..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*\r
-    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
-\r
-    Licensed under the Apache License, Version 2.0 (the "License");\r
-    you may not use this file except in compliance with the License.\r
-    You may obtain a copy of the License at\r
-\r
-        http://www.apache.org/licenses/LICENSE-2.0\r
-\r
-    Unless required by applicable law or agreed to in writing, software\r
-    distributed under the License is distributed on an "AS IS" BASIS,\r
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-    See the License for the specific language governing permissions and\r
-    limitations under the License.\r
-*/\r
-\r
-#ifndef USBCFG_H\r
-#define USBCFG_H\r
-\r
-#include "hal.h"\r
-\r
-extern const USBConfig usbcfg;\r
-extern SerialUSBConfig serusbcfg;\r
-extern SerialUSBDriver SDU1;\r
-\r
-#endif  /* USBCFG_H */\r
-\r
-/** @} */\r
diff --git a/source/usbserial.cpp b/source/usbserial.cpp
deleted file mode 100644 (file)
index 775a911..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * @file usbserial.cpp
- * @brief Wrapper for ChibiOS's SerialUSBDriver.
- *
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-#include "usbserial.hpp"
-
-SerialUSBDriver *USBSerial::m_driver = &SDU1;
-
-void USBSerial::begin()
-{
-    palSetPadMode(GPIOA, 11, PAL_MODE_ALTERNATE(10));
-    palSetPadMode(GPIOA, 12, PAL_MODE_ALTERNATE(10));
-
-    sduObjectInit(m_driver);
-    sduStart(m_driver, &serusbcfg);
-
-    // Reconnect bus so device can re-enumerate on reset
-    usbDisconnectBus(serusbcfg.usbp);
-    chThdSleepMilliseconds(1500);
-    usbStart(serusbcfg.usbp, &usbcfg);
-    usbConnectBus(serusbcfg.usbp);
-}
-
-bool USBSerial::isActive()
-{
-    if (auto config = m_driver->config; config != nullptr) {
-        if (auto usbp = config->usbp; usbp != nullptr)
-            return usbp->state == USB_ACTIVE && !ibqIsEmptyI(&m_driver->ibqueue);
-    }
-
-    return false;
-}
-
-size_t USBSerial::read(unsigned char *buffer, size_t count)
-{
-    auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
-    return streamRead(bss, buffer, count);
-}
-
-size_t USBSerial::write(const unsigned char *buffer, size_t count)
-{
-    auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
-    return streamWrite(bss, buffer, count);
-}
-
diff --git a/source/usbserial.hpp b/source/usbserial.hpp
deleted file mode 100644 (file)
index 58113c9..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * @file usbserial.hpp
- * @brief Wrapper for ChibiOS's SerialUSBDriver.
- *
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-#ifndef STMDSP_USBSERIAL_HPP_
-#define STMDSP_USBSERIAL_HPP_
-
-#include "usbcfg.h"
-
-class USBSerial
-{
-public:
-    static void begin();
-
-    static bool isActive();
-
-    static size_t read(unsigned char *buffer, size_t count);
-    static size_t write(const unsigned char *buffer, size_t count);
-
-private:
-    static SerialUSBDriver *m_driver;
-};
-
-#endif // STMDSP_USBSERIAL_HPP_
-