diff --git a/Makefile b/Makefile index 021051ad8..a55c41842 100644 --- a/Makefile +++ b/Makefile @@ -46,8 +46,11 @@ products += $(dependencies) $(all_objs): $(generated_headers) +epsilon.$(EXE): $(objs) +test.$(EXE): $(objs) + .SECONDARY: $(objs) -%.$(EXE): $(objs) +%.$(EXE): @echo "LD $@" $(Q) $(LD) $^ $(LDFLAGS) -o $@ diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index 3597947ec..9bba6d7fd 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -1,5 +1,6 @@ include ion/src/device/boot/Makefile include ion/src/device/bench/Makefile +include ion/src/device/usb/Makefile ion/src/shared/platform_info.o: SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" -DFORCE_LINK="__attribute__((used))" @@ -29,31 +30,6 @@ objs += $(addprefix ion/src/device/, \ wakeup.o \ ) -usb_objs += $(addprefix ion/src/device/usb/, \ - calculator.o \ - device.o\ - dfu_interface.o\ - endpoint0.o \ - interface.o\ - request_recipient.o\ - setup_packet.o\ -) - -$(usb_objs): SFLAGS += -fPIC - -objs += $(usb_objs) - -objs += $(addprefix ion/src/device/usb/stack/, \ - configuration_descriptor.o \ - descriptor.o\ - device_descriptor.o\ - dfu_functional_descriptor.o\ - interface_descriptor.o\ - language_id_string_descriptor.o \ - streamable.o\ - string_descriptor.o\ -) - # When using the register.h C++ file in production mode, we expect the compiler # to completely inline all bit manipulations. For some reason, if we build using # the -Os optimization flag, GCC doesn't inline everything and and ends up diff --git a/ion/src/device/boot/dfu.ld b/ion/src/device/boot/dfu.ld new file mode 100644 index 000000000..9432e2425 --- /dev/null +++ b/ion/src/device/boot/dfu.ld @@ -0,0 +1,45 @@ +/* DFU transfers can serve two purposes: + * - Transfering RAM data between the machine and the host, e.g. Python scripts + * - Upgrading the flash memory to perform a software update + * + * The second case raises a huge issue: code cannot be executed from memory that + * is being modified. We're solving this issue by copying the DFU code in RAM. + * + * This linker script will generate some code that expects to be executed from a + * fixed address in RAM. The corresponding instructions will be embedded in the + * main Epsilon ELF file, and copied to that address before execution. + * + * This address needs to live in RAM, and needs to be temporarily overwriteable + * when the program is being run. Epsilon has a large stack to allow deeply + * recursive code to run. But when doing DFU transfers it is safe to assume we + * will need very little stack space. We're therefore using the topmost 8K of + * the stack reserved by Epsilon. + * + * Last but not least, we'll want to jump to a known entry point when running + * the DFU code (namely, Ion::USB::Device::Calculator::Poll). We're simply + * making sure this is the first symbol output. */ + +EPSILON_STACK_END = 0x20000000 + 256K - 32K; + +MEMORY { + RAM_BUFFER (rw) : ORIGIN = EPSILON_STACK_END, LENGTH = 8K +} + +SECTIONS { + .text : { + . = ALIGN(4); + KEEP(*(.text._ZN3Ion3USB6Device10Calculator4PollEv)) + *(.text) + *(.text.*) + } >RAM_BUFFER + + .rodata : { + *(.rodata) + *(.rodata.*) + } >RAM_BUFFER + + .data : { + *(.data) + *(.data.*) + } >RAM_BUFFER +} diff --git a/ion/src/device/boot/flash.ld b/ion/src/device/boot/flash.ld index 4aa70d303..e5ddcc9e4 100644 --- a/ion/src/device/boot/flash.ld +++ b/ion/src/device/boot/flash.ld @@ -38,16 +38,6 @@ SECTIONS { KEEP(*(.header)) } >FLASH - .usb : { - . = ALIGN(4); - _flash_usb_stack_start = .; - *(.text.*3Ion3USB6Device*) - *(.text.memcpy) - *(.text.strlen) - *(.text.*Register*) - _flash_usb_stack_end = .; - } >FLASH - .text : { . = ALIGN(4); *(.text) diff --git a/ion/src/device/usb.cpp b/ion/src/device/usb.cpp index bd5877e64..8ed5a9f08 100644 --- a/ion/src/device/usb.cpp +++ b/ion/src/device/usb.cpp @@ -5,10 +5,6 @@ #include "display.h" #include "regs/regs.h" #include -#include - -extern char _flash_usb_stack_start; -extern char _flash_usb_stack_end; namespace Ion { namespace USB { @@ -17,43 +13,6 @@ bool isPlugged() { return Device::VbusPin.group().IDR()->get(Device::VbusPin.pin()); } -typedef void (*PollFunctionPointer)(Device::Calculator *); - -void DFU() { - size_t usb_stack_size = &_flash_usb_stack_end - &_flash_usb_stack_start; - - /* 1 - Allocate a buffer in RAM that's large enough to contain all the code - * and objects of our USB stack. */ - - size_t ram_buffer_size = usb_stack_size + sizeof(Device::Calculator); - char * ram_buffer = static_cast(malloc(ram_buffer_size)); - if (ram_buffer == nullptr) { - // Allocation failure - return; - } - - // 2 - Copy the USB stack code from Flash to RAM - char * ram_usb_stack_start = ram_buffer; - memcpy(ram_usb_stack_start, &_flash_usb_stack_start, usb_stack_size); - - // 3 - Initialize data in RAM buffer - Device::Calculator * calculator = new (ram_buffer + usb_stack_size) Device::Calculator(); - - // 4 - Figure out the address of Ion::USB::Device::poll() in RAM - char * flash_poll_function_address = reinterpret_cast(&Ion::USB::Device::poll); - assert(flash_poll_function_address >= &_flash_usb_stack_start); - assert(flash_poll_function_address < &_flash_usb_stack_end); - char * ram_poll_function_address = flash_poll_function_address - &_flash_usb_stack_start + ram_usb_stack_start; - PollFunctionPointer ram_poll_function = reinterpret_cast(ram_poll_function_address); - - // 5 - Execute Ion::USB::Device::poll() from RAM - ram_poll_function(calculator); - - // 6 - Upon return, delete data and free the buffer in RAM - calculator->~Calculator(); - free(ram_buffer); -} - } } @@ -61,16 +20,6 @@ namespace Ion { namespace USB { namespace Device { -void poll(Calculator * calculator) { - // Wait for speed enumeration done - while (!OTG.GINTSTS()->getENUMDNE()) { - } - - while (true) { - calculator->poll(); - } -} - void init() { initGPIO(); initOTG(); diff --git a/ion/src/device/usb.h b/ion/src/device/usb.h index 1f404c6d8..7c462cd47 100644 --- a/ion/src/device/usb.h +++ b/ion/src/device/usb.h @@ -27,9 +27,6 @@ void shutdownGPIO(); void initOTG(); void shutdownOTG(); -// Enters DFU mode, returns when detached. -void poll(Calculator * calculator); - } } } diff --git a/ion/src/device/usb/Makefile b/ion/src/device/usb/Makefile new file mode 100644 index 000000000..fafc1a44a --- /dev/null +++ b/ion/src/device/usb/Makefile @@ -0,0 +1,48 @@ +usb_objs += $(addprefix ion/src/device/usb/, \ + calculator.o \ + device.o\ + dfu_interface.o\ + endpoint0.o \ + interface.o\ + request_recipient.o\ + setup_packet.o\ +) + +usb_objs += $(addprefix ion/src/device/usb/stack/, \ + configuration_descriptor.o \ + descriptor.o\ + device_descriptor.o\ + dfu_functional_descriptor.o\ + interface_descriptor.o\ + language_id_string_descriptor.o \ + streamable.o\ + string_descriptor.o\ +) + +ifeq ($(USB_DFU_XIP_FLASH),1) + +objs += ion/src/device/usb_dfu_xip_flash.o +objs += $(usb_objs) + +else + +usb_objs += liba/src/assert.o +usb_objs += liba/src/strlen.o +usb_objs += liba/src/strlcpy.o +usb_objs += liba/src/memset.o +usb_objs += liba/src/memcpy.o +usb_objs += libaxx/src/cxxabi/pure_virtual.o +usb_objs += ion/src/device/usb/boot.o + +ion/src/device/usb/dfu.elf: LDFLAGS = --gc-sections -T ion/src/device/boot/dfu.ld +ion/src/device/usb/dfu.elf: $(usb_objs) + +ion/src/device/usb/dfu.o: ion/src/device/usb/dfu.bin + $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata --redefine-sym _binary_ion_src_device_usb_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_ion_src_device_usb_dfu_bin_end=_dfu_bootloader_flash_end $< $@ + +objs += ion/src/device/usb/dfu.o +objs += ion/src/device/usb_dfu_relocated_ram.o + +products += $(usb_objs) $(addprefix ion/src/device/usb/dfu, .elf .bin) + +endif diff --git a/ion/src/device/usb/boot.cpp b/ion/src/device/usb/boot.cpp new file mode 100644 index 000000000..0eb8d71d6 --- /dev/null +++ b/ion/src/device/usb/boot.cpp @@ -0,0 +1,2 @@ +extern "C" void abort() { +} diff --git a/ion/src/device/usb/calculator.cpp b/ion/src/device/usb/calculator.cpp index bb689aac4..a5449ac0f 100644 --- a/ion/src/device/usb/calculator.cpp +++ b/ion/src/device/usb/calculator.cpp @@ -1,9 +1,21 @@ #include "calculator.h" +#include "../regs/regs.h" namespace Ion { namespace USB { namespace Device { +void Calculator::Poll() { + // Wait for speed enumeration done + while (!OTG.GINTSTS()->getENUMDNE()) { + } + + Calculator c; + while (true) { + c.poll(); + } +} + Descriptor * Calculator::descriptor(uint8_t type, uint8_t index) { int typeCount = 0; for (size_t i=0; i #include @@ -19,6 +19,7 @@ namespace Device { class Calculator : public Device { public: + static void Poll(); Calculator() : Device(&m_dfuInterface), m_deviceDescriptor( @@ -90,10 +91,10 @@ private: InterfaceDescriptor m_interfaceDescriptor; ConfigurationDescriptor m_configurationDescriptor; LanguageIDStringDescriptor m_languageStringDescriptor; - RelocatableStringDescriptor<9> m_manufacturerStringDescriptor; - RelocatableStringDescriptor<11> m_productStringDescriptor; - RelocatableStringDescriptor<6> m_serialNumberStringDescriptor; - RelocatableStringDescriptor<47> m_interfaceStringDescriptor; + StringDescriptor m_manufacturerStringDescriptor; + StringDescriptor m_productStringDescriptor; + StringDescriptor m_serialNumberStringDescriptor; + StringDescriptor m_interfaceStringDescriptor; Descriptor * m_descriptors[7]; // We do not need to include m_interfaceDescriptor nor m_dfuFunctionalDescriptor, because they are inluded in other descriptors. diff --git a/ion/src/device/usb/dfu_interface.cpp b/ion/src/device/usb/dfu_interface.cpp index 6e325cbf0..a0e3602ca 100644 --- a/ion/src/device/usb/dfu_interface.cpp +++ b/ion/src/device/usb/dfu_interface.cpp @@ -1,7 +1,6 @@ #include "dfu_interface.h" #include "../regs/flash.h" -#include //TODO REMOVE -#include //TODO REMOVE +#include namespace Ion { namespace USB { @@ -11,26 +10,6 @@ static inline uint32_t min(uint32_t x, uint32_t y) { return (xpush(m_bStatus); c->push(m_bwPollTimeout[2]); @@ -50,22 +29,16 @@ bool DFUInterface::processSetupInRequest(SetupPacket * request, uint8_t * transf } switch (request->bRequest()) { case (uint8_t) DFURequest::GetStatus: - whiteScreen(); return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); case (uint8_t) DFURequest::ClearStatus: - greenScreen(); return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); case (uint8_t) DFURequest::Abort: - redScreen(); return dfuAbort(transferBufferLength); case (uint8_t) DFURequest::GetState: - blueScreen(); return getState(transferBuffer, transferBufferLength, request->wValue()); case (uint8_t) DFURequest::Download: - yellowScreen(); return processDownloadRequest(request->wLength(), transferBufferLength); case (uint8_t) DFURequest::Upload: - blackScreen(); return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength); } return false; diff --git a/ion/src/device/usb/endpoint0.cpp b/ion/src/device/usb/endpoint0.cpp index a4a921377..711c42167 100644 --- a/ion/src/device/usb/endpoint0.cpp +++ b/ion/src/device/usb/endpoint0.cpp @@ -86,9 +86,21 @@ void Endpoint0::readAndDispatchSetupPacket() { m_request = SetupPacket(m_largeBuffer); uint16_t maxBufferLength = MIN(m_request.wLength(), k_largeBufferLength); +#if 0 // Requests are only sent to the device or the interface for now. assert(((uint8_t)m_request.recipientType() == 0) || ((uint8_t)m_request.recipientType() == 1)); m_requestRecipients[(uint8_t)(m_request.recipientType())]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength); +#else + uint8_t type = static_cast(m_request.recipientType()); + if (type == 0) { + m_requestRecipients[0]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength); + } else { + for (volatile int i=0;i<10; i++) { + i = i+1; + } + m_requestRecipients[1]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength); + } +#endif } void Endpoint0::processINpacket() { diff --git a/ion/src/device/usb/stack/relocatable_string_descriptor.h b/ion/src/device/usb/stack/relocatable_string_descriptor.h deleted file mode 100644 index 56e9d19f9..000000000 --- a/ion/src/device/usb/stack/relocatable_string_descriptor.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef ION_DEVICE_USB_STACK_RELOCATABLE_STRING_DESCRIPTOR_H -#define ION_DEVICE_USB_STACK_RELOCATABLE_STRING_DESCRIPTOR_H - -#include "string_descriptor.h" -#include - -namespace Ion { -namespace USB { -namespace Device { - -template -class RelocatableStringDescriptor : public StringDescriptor { -public: - RelocatableStringDescriptor(const char * string) : - StringDescriptor() - { - strlcpy(m_string, string, T); - } -protected: - const char * string() const override { - return m_string; - } -private: - char m_string[T]; -}; - -} -} -} - -#endif diff --git a/ion/src/device/usb/stack/string_descriptor.cpp b/ion/src/device/usb/stack/string_descriptor.cpp index 9ba7ef663..c4ee5068e 100644 --- a/ion/src/device/usb/stack/string_descriptor.cpp +++ b/ion/src/device/usb/stack/string_descriptor.cpp @@ -7,7 +7,7 @@ namespace Device { void StringDescriptor::push(Channel * c) const { Descriptor::push(c); - const char * stringPointer = string(); + const char * stringPointer = m_string; while (*stringPointer != 0) { uint16_t stringAsUTF16CodePoint = *stringPointer; c->push(stringAsUTF16CodePoint); @@ -17,7 +17,7 @@ void StringDescriptor::push(Channel * c) const { uint8_t StringDescriptor::bLength() const { // The script is returned in UTF-16, hence the multiplication. - return Descriptor::bLength() + 2*strlen(string()); + return Descriptor::bLength() + 2*strlen(m_string); } } diff --git a/ion/src/device/usb/stack/string_descriptor.h b/ion/src/device/usb/stack/string_descriptor.h index 1c9b5e6d9..296ac79ca 100644 --- a/ion/src/device/usb/stack/string_descriptor.h +++ b/ion/src/device/usb/stack/string_descriptor.h @@ -9,11 +9,16 @@ namespace Device { class StringDescriptor : public Descriptor { public: - StringDescriptor() : Descriptor(0x03) { } + constexpr StringDescriptor(const char * string) : + Descriptor(0x03), + m_string(string) + { + } protected: void push(Channel * c) const override; virtual uint8_t bLength() const override; - virtual const char * string() const = 0; +private: + const char * m_string; }; } diff --git a/ion/src/device/usb_dfu_relocated_ram.cpp b/ion/src/device/usb_dfu_relocated_ram.cpp new file mode 100644 index 000000000..ad58ca45b --- /dev/null +++ b/ion/src/device/usb_dfu_relocated_ram.cpp @@ -0,0 +1,43 @@ +#include +#include "usb.h" +#include "device.h" +#include + +extern char _dfu_bootloader_flash_start; +extern char _dfu_bootloader_flash_end; + +namespace Ion { +namespace USB { + +typedef void (*FunctionPointer)(); + +void DFU() { + //TODO: Explain, in steps + + size_t dfu_bootloader_size = &_dfu_bootloader_flash_end - &_dfu_bootloader_flash_start; + char * dfu_bootloader_ram_start = reinterpret_cast(0x20000000 + 256*1024 - 32*1024); + + char * ram_backup_buffer = static_cast(malloc(dfu_bootloader_size)); + + if (ram_backup_buffer == nullptr) { + return; + } + + memcpy(ram_backup_buffer, dfu_bootloader_ram_start, dfu_bootloader_size); + + memcpy(dfu_bootloader_ram_start, &_dfu_bootloader_flash_start, dfu_bootloader_size); + + // Cortex-M expects jumps to be made to odd addresses when jumping to Thumb code + // In general this is handled by the compiler, but here we're jumping to an arbitrary address + // TODO: Check this is needed, maybe the compiler is super smart :) + FunctionPointer dfu_bootloader_entry = reinterpret_cast(dfu_bootloader_ram_start+1); + + dfu_bootloader_entry(); + + memcpy(dfu_bootloader_ram_start, ram_backup_buffer, dfu_bootloader_size); + + free(ram_backup_buffer); +} + +} +} diff --git a/ion/src/device/usb_dfu_xip_flash.cpp b/ion/src/device/usb_dfu_xip_flash.cpp new file mode 100644 index 000000000..f2985f394 --- /dev/null +++ b/ion/src/device/usb_dfu_xip_flash.cpp @@ -0,0 +1,11 @@ +#include + +namespace Ion { +namespace USB { + +void DFU() { + Ion::USB::Device::Calculator::Poll(); +} + +} +}