From 3668d168288ef085724f0c48bd7a4123599f2719 Mon Sep 17 00:00:00 2001
From: Clyne Sullivan <clyne@bitgloo.com>
Date: Thu, 4 Feb 2021 21:14:19 -0500
Subject: firmware cleanup, thread optimizing; make buffer size refs consistent

---
 gui/wxmain.cpp          |  11 +-
 source/main.cpp         | 261 +++++++++++++++++++++++++++++-------------------
 source/samplebuffer.hpp |   2 +-
 3 files changed, 166 insertions(+), 108 deletions(-)

diff --git a/gui/wxmain.cpp b/gui/wxmain.cpp
index 10e5d7a..c7acddf 100644
--- a/gui/wxmain.cpp
+++ b/gui/wxmain.cpp
@@ -581,7 +581,7 @@ void MainFrame::onRunLogResults(wxCommandEvent& ce)
 
 void MainFrame::onRunEditBSize(wxCommandEvent&)
 {
-    wxTextEntryDialog dialog (this, "Enter new buffer size (100-3000)", "Set Buffer Size");
+    wxTextEntryDialog dialog (this, "Enter new buffer size (100-4000)", "Set Buffer Size");
     if (dialog.ShowModal() == wxID_OK) {
         if (wxString value = dialog.GetValue(); !value.IsEmpty()) {
             if (unsigned long n; value.ToULong(&n)) {
@@ -610,8 +610,9 @@ void MainFrame::onToolbarSampleRate(wxCommandEvent& ce)
 
 void MainFrame::onRunGenUpload(wxCommandEvent&)
 {
-    wxTextEntryDialog dialog (this, "Enter generator values below. Values must be whole numbers "
-                                    "between zero and 4095.", "Enter Generator Values");
+    wxTextEntryDialog dialog (this, "Enter up to 8000 generator values below. "
+                                    "Values must be whole numbers between zero and 4095.",
+                                    "Enter Generator Values");
     if (dialog.ShowModal() == wxID_OK) {
         if (wxString values = dialog.GetValue(); !values.IsEmpty()) {
             if (values[0] == '/') {
@@ -625,7 +626,7 @@ void MainFrame::onRunGenUpload(wxCommandEvent&)
                 }
             } else {
                 std::vector<stmdsp::dacsample_t> samples;
-                while (!values.IsEmpty()) {
+                while (!values.IsEmpty() && samples.size() <= stmdsp::SAMPLES_MAX * 2) {
                     if (auto number_end = values.find_first_not_of("0123456789");
                         number_end != wxString::npos && number_end > 0)
                     {
@@ -649,7 +650,7 @@ void MainFrame::onRunGenUpload(wxCommandEvent&)
                     m_device->siggen_upload(&samples[0], samples.size());
                     m_status_bar->SetStatusText("Generator ready.");
                 } else {
-                    m_status_bar->SetStatusText("Error: Too many samples.");
+                    m_status_bar->SetStatusText("Error: Too many samples (max is 8000).");
                 }
             }
         } else {
diff --git a/source/main.cpp b/source/main.cpp
index d94f694..0dfc0e7 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -51,24 +51,29 @@ static MAILBOX_DECL(conversionMB, conversionMBBuffer, 2);
 
 // Thread for LED status and wakeup hold
 __attribute__((section(".stacks")))
-static THD_WORKING_AREA(monitorThreadWA, 1024);
+static THD_WORKING_AREA(monitorThreadWA, 4096);
 static THD_FUNCTION(monitorThread, arg);
+
 // Thread for managing the conversion task
 __attribute__((section(".stacks")))
-static THD_WORKING_AREA(conversionThreadMonitorWA, 1024);
+static THD_WORKING_AREA(conversionThreadMonitorWA, 4096);
 static THD_FUNCTION(conversionThreadMonitor, arg);
+static thread_t *conversionThreadHandle = nullptr;
+
 // Thread for unprivileged algorithm execution
 __attribute__((section(".stacks")))
-static THD_WORKING_AREA(conversionThreadWA, 1024);
+static THD_WORKING_AREA(conversionThreadWA, 128); // All we do is enter unprivileged mode.
 static THD_FUNCTION(conversionThread, arg);
 __attribute__((section(".convdata")))
 static THD_WORKING_AREA(conversionThreadUPWA, 62 * 1024);
-
-static thread_t *conversionThreadHandle = nullptr;
 __attribute__((section(".convdata")))
 static thread_t *conversionThreadMonitorHandle = nullptr;
 
-__attribute__((section(".convdata")))
+// Thread for USB monitoring
+__attribute__((section(".stacks")))
+static THD_WORKING_AREA(communicationThreadWA, 4096);
+static THD_FUNCTION(communicationThread, arg);
+
 static time_measurement_t conversion_time_measurement;
 __attribute__((section(".convdata")))
 static SampleBuffer samplesIn  (reinterpret_cast<Sample *>(0x38000000)); // 16k
@@ -81,9 +86,13 @@ static unsigned char elf_file_store[MAX_ELF_FILE_SIZE];
 __attribute__((section(".convdata")))
 static ELF::Entry elf_entry = nullptr;
 
+__attribute__((section(".convcode")))
+static void conversion_unprivileged_main();
+
+static void mpu_setup();
+static void conversion_abort();
 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();
 
 int main()
 {
@@ -91,31 +100,10 @@ int main()
     halInit();
     chSysInit();
 
-    palSetLineMode(LINE_BUTTON, PAL_MODE_INPUT);
-
-    // Enable FPU
-    SCB->CPACR |= 0xF << 20;
-
-    // Set up MPU for user algorithm
-    // Region 2: Data for algorithm manager thread
-    mpuConfigureRegion(MPU_REGION_2,
-                       0x20000000,
-                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_64K |
-                       MPU_RASR_ENABLE);
-    // Region 3: Code for algorithm manager thread
-    mpuConfigureRegion(MPU_REGION_3,
-                       0x0807F800,
-                       MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_2K |
-                       MPU_RASR_ENABLE);
-    // Region 4: Algorithm code
-    mpuConfigureRegion(MPU_REGION_4,
-                       0x00000000,
-                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
-                       MPU_RASR_SIZE_64K |
-                       MPU_RASR_ENABLE);
+    SCB->CPACR |= 0xF << 20; // Enable FPU
+    mpu_setup();
 
+    palSetLineMode(LINE_BUTTON, PAL_MODE_INPUT);
     ADC::begin();
     DAC::begin();
     SClock::begin();
@@ -126,21 +114,31 @@ int main()
     ADC::setRate(SClock::Rate::R32K);
 
     chTMObjectInit(&conversion_time_measurement);
-    chThdCreateStatic(monitorThreadWA, sizeof(monitorThreadWA),
-                      NORMALPRIO, monitorThread, nullptr);
+    chThdCreateStatic(
+        monitorThreadWA, sizeof(monitorThreadWA),
+        LOWPRIO,
+        monitorThread, nullptr);
     conversionThreadMonitorHandle = chThdCreateStatic(
-                      conversionThreadMonitorWA, sizeof(conversionThreadMonitorWA),
-                      NORMALPRIO, conversionThreadMonitor, nullptr);
+        conversionThreadMonitorWA, sizeof(conversionThreadMonitorWA),
+        NORMALPRIO + 1,
+        conversionThreadMonitor, nullptr);
     conversionThreadHandle = chThdCreateStatic(
-                      conversionThreadWA, sizeof(conversionThreadWA),
-                      NORMALPRIO + 1, conversionThread, nullptr);
-
-    main_loop();
+        conversionThreadWA, sizeof(conversionThreadWA),
+        HIGHPRIO,
+        conversionThread,
+        reinterpret_cast<void *>(reinterpret_cast<uint32_t>(conversionThreadUPWA) + 62 * 1024));
+    chThdCreateStatic(
+        communicationThreadWA, sizeof(communicationThreadWA),
+        NORMALPRIO,
+        communicationThread, nullptr);
+
+    chThdExit(0);
+    return 0;
 }
 
-void main_loop()
+THD_FUNCTION(communicationThread, arg)
 {
-
+    (void)arg;
 	while (1) {
         if (USBSerial::isActive()) {
             // Attempt to receive a command packet
@@ -148,6 +146,24 @@ void main_loop()
                 // 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.
+                // 'W' - Start signal generator (siggen).
+                // 'w' - Stop siggen.
+
                 case 'a':
                     USBSerial::write(samplesIn.bytedata(), samplesIn.bytesize());
                     break;
@@ -159,6 +175,8 @@ void main_loop()
                     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);
@@ -308,17 +326,6 @@ void main_loop()
 	}
 }
 
-void conversion_abort()
-{
-    elf_entry = nullptr;
-    DAC::stop(0);
-    ADC::stop();
-    EM.add(Error::ConversionAborted);
-
-    chMBReset(&conversionMB);
-    run_status = RunStatus::Idle;
-}
-
 THD_FUNCTION(conversionThreadMonitor, arg)
 {
     (void)arg;
@@ -333,22 +340,57 @@ THD_FUNCTION(conversionThreadMonitor, arg)
     }
 }
 
-__attribute__((section(".convcode")))
-static void convThdMain();
+THD_FUNCTION(conversionThread, stack)
+{
+    elf_entry = nullptr;
+    port_unprivileged_jump(reinterpret_cast<uint32_t>(conversion_unprivileged_main),
+                           reinterpret_cast<uint32_t>(stack));
+}
 
-THD_FUNCTION(conversionThread, arg)
+THD_FUNCTION(monitorThread, arg)
 {
     (void)arg;
-    elf_entry = nullptr;
-    port_unprivileged_jump(reinterpret_cast<uint32_t>(convThdMain),
-                           reinterpret_cast<uint32_t>(conversionThreadUPWA) + 256);
+
+    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 (run_status == RunStatus::Idle && palReadLine(LINE_BUTTON)) {
+            palSetLine(LINE_LED_RED);
+            palSetLine(LINE_LED_YELLOW);
+            chSysLock();
+            while (palReadLine(LINE_BUTTON))
+                asm("nop");
+            while (!palReadLine(LINE_BUTTON))
+                asm("nop");
+            chSysUnlock();
+            palClearLine(LINE_LED_RED);
+            palClearLine(LINE_LED_YELLOW);
+            chThdSleepMilliseconds(500);
+        }
+
+        if (auto err = EM.hasError(); err ^ erroron) {
+            erroron = err;
+            if (err)
+                palSetLine(LINE_LED_RED);
+            else
+                palClearLine(LINE_LED_RED);
+        }
+    }
 }
 
-void convThdMain()
+void conversion_unprivileged_main()
 {
     while (1) {
         msg_t message;
-        asm("svc 0; mov %0, r0" : "=r" (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;
@@ -357,9 +399,9 @@ void convThdMain()
                 if (!MSG_FOR_MEASURE(message)) {
                     samples = elf_entry(samples, size);
                 } else {
-                    //chTMStartMeasurementX(&conversion_time_measurement);
+                    asm("eor r0, r0; svc 2"); // start measurement
                     samples = elf_entry(samples, size);
-                    //chTMStopMeasurementX(&conversion_time_measurement);
+                    asm("mov r0, #1; svc 2"); // stop measurement
                 } 
             }
 
@@ -371,6 +413,40 @@ void convThdMain()
     }
 }
 
+void mpu_setup()
+{
+    // Set up MPU for user algorithm
+    // Region 2: Data for algorithm manager thread
+    mpuConfigureRegion(MPU_REGION_2,
+                       0x20000000,
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
+                       MPU_RASR_SIZE_64K |
+                       MPU_RASR_ENABLE);
+    // Region 3: Code for algorithm manager thread
+    mpuConfigureRegion(MPU_REGION_3,
+                       0x0807F800,
+                       MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |
+                       MPU_RASR_SIZE_2K |
+                       MPU_RASR_ENABLE);
+    // Region 4: Algorithm code
+    mpuConfigureRegion(MPU_REGION_4,
+                       0x00000000,
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
+                       MPU_RASR_SIZE_64K |
+                       MPU_RASR_ENABLE);
+}
+
+void conversion_abort()
+{
+    elf_entry = nullptr;
+    DAC::stop(0);
+    ADC::stop();
+    EM.add(Error::ConversionAborted);
+
+    chMBReset(&conversionMB);
+    run_status = RunStatus::Idle;
+}
+
 void signal_operate(adcsample_t *buffer, size_t)
 {
     chSysLockFromISR();
@@ -387,51 +463,13 @@ void signal_operate(adcsample_t *buffer, size_t)
 void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count)
 {
     chSysLockFromISR();
-    chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST_MEASURE : MSG_CONVSECOND_MEASURE);
+    chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST_MEASURE
+                                                        : MSG_CONVSECOND_MEASURE);
     chSysUnlockFromISR();
 
     ADC::setOperation(signal_operate);
 }
 
-THD_FUNCTION(monitorThread, 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 (run_status == RunStatus::Idle && palReadLine(LINE_BUTTON)) {
-            palSetLine(LINE_LED_RED);
-            palSetLine(LINE_LED_YELLOW);
-            chSysLock();
-            while (palReadLine(LINE_BUTTON))
-                asm("nop");
-            while (!palReadLine(LINE_BUTTON))
-                asm("nop");
-            chSysUnlock();
-            palClearLine(LINE_LED_RED);
-            palClearLine(LINE_LED_YELLOW);
-            chThdSleepMilliseconds(500);
-        }
-
-        if (auto err = EM.hasError(); err ^ erroron) {
-            erroron = err;
-            if (err)
-                palSetLine(LINE_LED_RED);
-            else
-                palClearLine(LINE_LED_RED);
-        }
-    }
-}
-
 extern "C" {
 
 __attribute__((naked))
@@ -466,6 +504,19 @@ void port_syscall(struct port_extctx *ctxp, uint32_t n)
             }
         }
         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;
     default:
         while (1);
         break;
@@ -475,6 +526,12 @@ void port_syscall(struct port_extctx *ctxp, uint32_t n)
     while (1);
 }
 
+__attribute__((naked))
+void MemManage_Handler()
+{
+    while (1);
+}
+
 __attribute__((naked))
 void HardFault_Handler()
 {
diff --git a/source/samplebuffer.hpp b/source/samplebuffer.hpp
index 9aabfdd..334c048 100644
--- a/source/samplebuffer.hpp
+++ b/source/samplebuffer.hpp
@@ -6,7 +6,7 @@
 
 using Sample = uint16_t;
 
-// gives 8000
+// gives 8000 (8192)
 constexpr unsigned int MAX_SAMPLE_BUFFER_BYTESIZE = 16384;
 constexpr unsigned int MAX_SAMPLE_BUFFER_SIZE = MAX_SAMPLE_BUFFER_BYTESIZE / sizeof(Sample);
 
-- 
cgit v1.2.3