diff --git a/Makefile b/Makefile index 50e7905..7cfc8fe 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # Compiler options here. ifeq ($(USE_OPT),) - USE_OPT = -Og -ggdb -fomit-frame-pointer -falign-functions=16 + USE_OPT = -Og -ggdb -fomit-frame-pointer -falign-functions=16 -mtune=cortex-m4 endif # C specific options here (added to USE_OPT). @@ -15,7 +15,7 @@ endif # C++ specific options here (added to USE_OPT). ifeq ($(USE_CPPOPT),) - USE_CPPOPT = -std=c++2a -fno-rtti -fno-exceptions + USE_CPPOPT = -std=c++2a -fno-rtti -fno-exceptions endif # Enable this if you want the linker to remove unused code and data. diff --git a/gui/wxmain.cpp b/gui/wxmain.cpp index 0c6c9b3..277f369 100644 --- a/gui/wxmain.cpp +++ b/gui/wxmain.cpp @@ -176,16 +176,24 @@ void MainFrame::prepareEditor() wxT("void char short int long auto float double unsigned signed " "volatile static const constexpr constinit consteval " "virtual final noexcept public private protected")); - m_text_editor->SetText("void process_data(adcsample_t *samples, unsigned int size)\n{\n\t\n}\n"); + m_text_editor->SetText( +R"cpp(adcsample_t *process_data(adcsample_t *samples, unsigned int size) +{ + return samples; +} +)cpp"); } static const char *makefile_text = R"make( all: - @arm-none-eabi-g++ -x c++ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Os --specs=nosys.specs -nostartfiles -fPIE $0 -o $0.o -Wl,-Ttext-segment=0 -Wl,-eprocess_data_entry -Wl,-zmax-page-size=512 + @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 \ + -nostartfiles \ + -Wl,-Ttext-segment=0x10000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry \ + $0 -o $0.o @cp $0.o $0.orig.o @arm-none-eabi-strip -s -S --strip-unneeded $0.o - @arm-none-eabi-objcopy --remove-section .ARM.exidx \ - --remove-section .ARM.attributes \ + @arm-none-eabi-objcopy --remove-section .ARM.attributes \ --remove-section .comment \ --remove-section .noinit \ $0.o @@ -196,14 +204,15 @@ static const char *file_header = R"cpp( using adcsample_t = uint16_t; -void process_data(adcsample_t *samples, unsigned int size); +adcsample_t *process_data(adcsample_t *samples, unsigned int size); -extern "C" void process_data_entry() { - auto func = (void (*)())process_data; - func(); +extern "C" void process_data_entry() +{ + ((void (*)())process_data)(); } // End stmdspgui header code + )cpp"; wxString MainFrame::compileEditorCode() diff --git a/openocd.sh b/openocd.sh new file mode 100755 index 0000000..4543fef --- /dev/null +++ b/openocd.sh @@ -0,0 +1 @@ +sudo openocd -f /usr/local/share/openocd/scripts/interface/stlink.cfg -f /usr/local/share/openocd/scripts/target/stm32l4x.cfg diff --git a/source/adc.cpp b/source/adc.cpp index f63c386..eca0d16 100644 --- a/source/adc.cpp +++ b/source/adc.cpp @@ -29,7 +29,7 @@ static ADCConversionGroup adc_group_config = { .cfgr2 = 0, .tr1 = ADC_TR(0, 4095), .smpr = { - ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_24P5), 0 + ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_2P5), 0 }, .sqr = { ADC_SQR1_SQ1_N(ADC_CHANNEL_IN5), @@ -38,7 +38,7 @@ static ADCConversionGroup adc_group_config = { }; constexpr static const GPTConfig gpt_config = { - .frequency = 4000000, + .frequency = 600000, .callback = nullptr, .cr2 = TIM_CR2_MMS_1, /* TRGO */ .dier = 0 @@ -65,7 +65,7 @@ namespace adc adc_is_read_finished = false; adc_group_config.circular = false; adcStartConversion(adcd, &adc_group_config, buffer, count); - gptStartContinuous(gptd, 8); + gptStartContinuous(gptd, 5); while (!adc_is_read_finished); return buffer; } @@ -77,7 +77,7 @@ namespace adc adc_operation_func = operation_func; adc_group_config.circular = true; adcStartConversion(adcd, &adc_group_config, buffer, count); - gptStartContinuous(gptd, 8); + gptStartContinuous(gptd, 5); } void read_stop() diff --git a/source/dac.cpp b/source/dac.cpp index 8fae1e5..9f5e457 100644 --- a/source/dac.cpp +++ b/source/dac.cpp @@ -28,7 +28,7 @@ constexpr static const DACConversionGroup dac_group_config = { }; constexpr static const GPTConfig gpt_config = { - .frequency = 4000000, + .frequency = 600000, .callback = nullptr, .cr2 = TIM_CR2_MMS_1, /* TRGO */ .dier = 0 @@ -48,7 +48,7 @@ namespace dac void write_start(dacsample_t *buffer, size_t count) { dacStartConversion(dacd, &dac_group_config, buffer, count); - gptStartContinuous(gptd, 8); + gptStartContinuous(gptd, 5); } void write_stop() diff --git a/source/elf_load.cpp b/source/elf_load.cpp index 161bd7e..3fd8a0f 100644 --- a/source/elf_load.cpp +++ b/source/elf_load.cpp @@ -4,6 +4,8 @@ #include #include +//constexpr unsigned int ELF_LOAD_ADDR = 0x10000000; + static const unsigned char elf_header[] = { '\177', 'E', 'L', 'F' }; template @@ -12,67 +14,58 @@ constexpr static auto ptr_from_offset(void *base, uint32_t offset) return reinterpret_cast(reinterpret_cast(base) + offset); } -static Elf32_Shdr *find_section(Elf32_Ehdr *ehdr, const char *name); +//static Elf32_Shdr *find_section(Elf32_Ehdr *ehdr, const char *name); namespace elf { -entry_t load(void *elf_data, void *elf_load_offset) +entry_t load(void *elf_data) { + // Check the ELF's header signature auto ehdr = reinterpret_cast(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(elf_data, ehdr->e_phoff); for (Elf32_Half i = 0; i < ehdr->e_phnum; i++) { if (phdr->p_type == PT_LOAD) { - std::memcpy(ptr_from_offset(elf_load_offset, phdr->p_vaddr), - ptr_from_offset(elf_data, phdr->p_offset), - phdr->p_filesz); - //break; + if (phdr->p_filesz == 0) { + std::memset(reinterpret_cast(phdr->p_vaddr), + 0, + phdr->p_memsz); + } else { + std::memcpy(reinterpret_cast(phdr->p_vaddr), + ptr_from_offset(elf_data, phdr->p_offset), + phdr->p_filesz); + if (!loaded) + loaded = true; + } } phdr = ptr_from_offset(phdr, ehdr->e_phentsize); } - // Zero .bss section - if (auto bss_section = find_section(ehdr, ".bss"); bss_section) { - auto bss = ptr_from_offset(elf_load_offset, bss_section->sh_addr); - std::fill(bss, bss + bss_section->sh_size / sizeof(uint32_t), 0); - } - - // Fix global offset table (GOT) entries - if (auto got_section = find_section(ehdr, ".got"); got_section) { - auto got = ptr_from_offset(elf_load_offset, got_section->sh_addr); - for (size_t i = 0; i < got_section->sh_size / sizeof(void *); i++) - got[i] = ptr_from_offset(got[i], reinterpret_cast(elf_load_offset)); - } - - //// Run any initial constructors - //if (auto init_section = find_section(ehdr, ".init_array"); init_section) { - // auto init_array = reinterpret_cast(elf_load_offset + init_section->sh_addr); - // std::for_each(init_array, init_array + init_section->sh_size / sizeof(void (*)()), - // [elf_load_offset](auto func) { (func + elf_load_offset)(); }); - //} - return ptr_from_offset(elf_load_offset, ehdr->e_entry); + return loaded ? reinterpret_cast(ehdr->e_entry) : nullptr; } } // namespace elf -Elf32_Shdr *find_section(Elf32_Ehdr *ehdr, const char *name) -{ - auto shdr = ptr_from_offset(ehdr, ehdr->e_shoff); - auto shdr_str = ptr_from_offset(ehdr, - ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize); - - for (Elf32_Half i = 0; i < ehdr->e_shnum; i++) { - char *section = ptr_from_offset(ehdr, shdr_str->sh_offset) + shdr->sh_name; - if (!strcmp(section, name)) - return shdr; - - shdr = ptr_from_offset(shdr, ehdr->e_shentsize); - } - - return 0; -} +//Elf32_Shdr *find_section(Elf32_Ehdr *ehdr, const char *name) +//{ +// auto shdr = ptr_from_offset(ehdr, ehdr->e_shoff); +// auto shdr_str = ptr_from_offset(ehdr, +// ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize); +// +// for (Elf32_Half i = 0; i < ehdr->e_shnum; i++) { +// char *section = ptr_from_offset(ehdr, shdr_str->sh_offset) + shdr->sh_name; +// if (!strcmp(section, name)) +// return shdr; +// +// shdr = ptr_from_offset(shdr, ehdr->e_shentsize); +// } +// +// return 0; +//} diff --git a/source/elf_load.hpp b/source/elf_load.hpp index 3e03f1d..4fda526 100644 --- a/source/elf_load.hpp +++ b/source/elf_load.hpp @@ -6,9 +6,9 @@ namespace elf { - using entry_t = void (*)(uint16_t *, size_t); + using entry_t = uint16_t *(*)(uint16_t *, size_t); - entry_t load(void *elf_data, void *elf_load_offset); + entry_t load(void *elf_data); } #endif // ELF_LOAD_HPP_ diff --git a/source/main.cpp b/source/main.cpp index 1e2772a..043353d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -19,6 +19,8 @@ #include +constexpr unsigned int MAX_SAMPLE_BUFFER_SIZE = 2048; + enum class RunStatus : char { Idle = '1', @@ -42,14 +44,13 @@ static_assert(sizeof(dacsample_t) == sizeof(uint16_t)); #if CACHE_LINE_SIZE > 0 CC_ALIGN(CACHE_LINE_SIZE) #endif -static std::array adc_samples; +static std::array adc_samples; #if CACHE_LINE_SIZE > 0 CC_ALIGN(CACHE_LINE_SIZE) #endif -static std::array dac_samples; +static std::array dac_samples; -static uint8_t elf_file_store[2048]; -static uint8_t elf_exec_store[4096]; +static uint8_t elf_file_store[8192]; static elf::entry_t elf_entry = nullptr; static void signal_operate(adcsample_t *buffer, size_t count); @@ -57,81 +58,125 @@ static void main_loop(); int main() { + // Initialize the RTOS halInit(); chSysInit(); // Enable FPU SCB->CPACR |= 0xF << 20; - palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL); // LED + // Prepare LED + palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL); + palClearPad(GPIOA, 5); adc::init(); dac::init(); usbserial::init(); + // Start the conversion manager thread chThdCreateStatic(conversionThreadWA, sizeof(conversionThreadWA), NORMALPRIO, conversionThread, nullptr); + main_loop(); } void main_loop() { - static unsigned int dac_sample_count = 2048; - while (true) { + static unsigned int dac_sample_count = MAX_SAMPLE_BUFFER_SIZE; + + while (1) { if (usbserial::is_active()) { - // Expect to receive a byte command 'packet'. + // Attempt to receive a command packet if (char cmd[3]; usbserial::read(&cmd, 1) > 0) { + // Packet received, first byte represents the desired command/action switch (cmd[0]) { - case 'r': // Read in analog signal - if (usbserial::read(&cmd[1], 2) < 2) + + // 'r' - Conduct a single sample of the ADC, and send the results back over USB. + case 'r': + // Get the next two bytes of the packet to determine the desired sample size + if (run_status != RunStatus::Idle || usbserial::read(&cmd[1], 2) < 2) break; - if (auto count = std::min(static_cast(cmd[1] | (cmd[2] << 8)), adc_samples.size()); count > 0) { - adc::read(&adc_samples[0], count); - usbserial::write(adc_samples.data(), count * sizeof(adcsample_t)); + if (unsigned int desiredSize = cmd[1] | (cmd[2] << 8); desiredSize <= adc_samples.size()) { + adc::read(&adc_samples[0], desiredSize); + usbserial::write(adc_samples.data(), desiredSize * sizeof(adcsample_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 (run_status != RunStatus::Idle) + // break; + run_status = RunStatus::Converting; dac_samples.fill(0); adc::read_start(signal_operate, &adc_samples[0], adc_samples.size()); dac::write_start(&dac_samples[0], dac_samples.size()); break; + + // 's' - Sends the current contents of the DAC buffer back over USB. case 's': - usbserial::write(dac_samples.data(), dac_samples.size() * sizeof(adcsample_t)); + usbserial::write(dac_samples.data(), 1/*dac_samples.size()*/ * sizeof(dacsample_t)); break; + + // 'S' - Stops the continuous sampling/conversion. case 'S': + //if (run_status != RunStatus::Converting) + // break; + dac::write_stop(); adc::read_stop(); run_status = RunStatus::Idle; break; + + // 'e' - Reads in and loads the compiled conversion code binary from USB. case 'e': + // Get the binary's size if (usbserial::read(&cmd[1], 2) < 2) break; - if (unsigned int count = cmd[1] | (cmd[2] << 8); count < sizeof(elf_file_store)) { - usbserial::read(elf_file_store, count); - elf_entry = elf::load(elf_file_store, elf_exec_store); + + // Only load the binary if it can fit in the memory reserved for it. + if (unsigned int binarySize = cmd[1] | (cmd[2] << 8); binarySize < sizeof(elf_file_store)) { + usbserial::read(elf_file_store, binarySize); + elf_entry = elf::load(elf_file_store); } break; + + // 'W' - Sets the number of samples for DAC writing with command 'w'. + // If the provided count is zero, DAC writing is stopped. case 'W': if (usbserial::read(&cmd[1], 2) < 2) break; - if (auto count = std::min(static_cast(cmd[1] | (cmd[2] << 8)), dac_samples.size()); count > 0) - dac_sample_count = count; - else - dac::write_stop(); + if (unsigned int sampleCount = cmd[1] | (cmd[2] << 8); sampleCount <= dac_samples.size()) { + if (sampleCount > 0) + dac_sample_count = sampleCount; + else + dac::write_stop(); + } break; + + // 'w' - Starts the DAC, looping over the given data (data size set by command 'W'). case 'w': - if (usbserial::read(&dac_samples[0], 2 * dac_sample_count) != 2 * dac_sample_count) + if (usbserial::read(&dac_samples[0], dac_sample_count * sizeof(dacsample_t) != + dac_sample_count * sizeof(dacsample_t))) + { break; - dac::write_start(&dac_samples[0], dac_sample_count); + } else { + dac::write_start(&dac_samples[0], dac_sample_count); + } break; - case 'i': // Identify ourself as an stmdsp device + + // 'i' - Sends an identifying string to confirm that this is the stmdsp device. + case 'i': usbserial::write("stmdsp", 6); break; - case 'I': // Info (i.e. run status) - usbserial::write(&run_status, 1); + + // 'I' - Sends the current run status. + case 'I': + usbserial::write(&run_status, sizeof(run_status)); break; + default: break; } @@ -157,22 +202,26 @@ THD_FUNCTION(conversionThread, arg) while (1) { msg_t message; if (chMBFetchTimeout(&conversionMB, &message, TIME_INFINITE) == MSG_OK) { - auto samples = &adc_samples[0]; + adcsample_t *samples = nullptr; auto halfsize = adc_samples.size() / 2; if (message == MSG_CONVFIRST) { if (elf_entry) - elf_entry(samples, halfsize); + samples = elf_entry(&adc_samples[0], halfsize); + if (!samples) + samples = &adc_samples[0]; std::copy(samples, samples + halfsize, &dac_samples[0]); } else if (message == MSG_CONVSECOND) { if (elf_entry) - elf_entry(samples + halfsize, halfsize); - std::copy(samples + halfsize, samples + halfsize * 2, &dac_samples[1024]); + samples = elf_entry(&adc_samples[adc_samples.size() / 2], halfsize); + if (!samples) + samples = &adc_samples[adc_samples.size() / 2]; + std::copy(samples, samples + halfsize, &dac_samples[dac_samples.size() / 2]); } } } } -void signal_operate(adcsample_t *buffer, size_t count) +void signal_operate(adcsample_t *buffer, [[maybe_unused]] size_t count) { if (chMBGetUsedCountI(&conversionMB) > 1) conversion_abort(); @@ -185,22 +234,32 @@ extern "C" { __attribute__((naked)) void HardFault_Handler() { - asm("push {lr}"); + //asm("push {lr}"); uint32_t *stack; - asm("mrs %0, psp" : "=r" (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 + 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 + // stack[6] = (uint32_t)main_loop & ~1; // Return to safety //} - asm("pop {lr}; bx lr"); + //asm("pop {lr}; bx lr"); + asm("bx lr"); } } // extern "C"