]> code.bitgloo.com Git - clyne/stmdsp.git/commitdiff
increase signal buffers; fix oversample
authorClyne Sullivan <clyne@bitgloo.com>
Sat, 23 Jan 2021 19:25:04 +0000 (14:25 -0500)
committerClyne Sullivan <clyne@bitgloo.com>
Sat, 23 Jan 2021 19:25:04 +0000 (14:25 -0500)
ChibiOS_20.3.2/os/hal/ports/STM32/STM32H7xx/hal_lld.c
Makefile
STM32H723xG.ld
cfg/halconf.h
cfg/mcuconf.h
gui/stmdsp.hpp
gui/wxmain.cpp
source/adc.cpp
source/common.hpp
source/error.hpp
source/main.cpp

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