mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[usb] Separate bin file for DFU code, copied in RAM for execution.
Change-Id: I1b2147ecd2f3d4a5c3d7db5e7a07b66258666fca
This commit is contained in:
5
Makefile
5
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 $@
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
45
ion/src/device/boot/dfu.ld
Normal file
45
ion/src/device/boot/dfu.ld
Normal file
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
#include "display.h"
|
||||
#include "regs/regs.h"
|
||||
#include <stdlib.h>
|
||||
#include <new>
|
||||
|
||||
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<char *>(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<char *>(&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<PollFunctionPointer>(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();
|
||||
|
||||
@@ -27,9 +27,6 @@ void shutdownGPIO();
|
||||
void initOTG();
|
||||
void shutdownOTG();
|
||||
|
||||
// Enters DFU mode, returns when detached.
|
||||
void poll(Calculator * calculator);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
ion/src/device/usb/Makefile
Normal file
48
ion/src/device/usb/Makefile
Normal file
@@ -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
|
||||
2
ion/src/device/usb/boot.cpp
Normal file
2
ion/src/device/usb/boot.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
extern "C" void abort() {
|
||||
}
|
||||
@@ -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<sizeof(m_descriptors)/sizeof(m_descriptors[0]); i++) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "stack/dfu_functional_descriptor.h"
|
||||
#include "stack/interface_descriptor.h"
|
||||
#include "stack/language_id_string_descriptor.h"
|
||||
#include "stack/relocatable_string_descriptor.h"
|
||||
#include "stack/string_descriptor.h"
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "dfu_interface.h"
|
||||
#include "../regs/flash.h"
|
||||
#include <ion.h> //TODO REMOVE
|
||||
#include <kandinsky.h>//TODO REMOVE
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
@@ -11,26 +10,6 @@ static inline uint32_t min(uint32_t x, uint32_t y) { return (x<y ? x : y); }
|
||||
|
||||
//TODO vérifier qu'on ne change pas d'état si on est dans dfuError, sauf en cas de clear status
|
||||
|
||||
// DEBUG functions
|
||||
void whiteScreen() {
|
||||
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorWhite);
|
||||
}
|
||||
void redScreen() {
|
||||
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorRed);
|
||||
}
|
||||
void blueScreen() {
|
||||
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorBlue);
|
||||
}
|
||||
void greenScreen() {
|
||||
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorGreen);
|
||||
}
|
||||
void yellowScreen() {
|
||||
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorYellow);
|
||||
}
|
||||
void blackScreen() {
|
||||
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorWhite);
|
||||
}
|
||||
|
||||
void DFUInterface::StatusData::push(Channel * c) const {
|
||||
c->push(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;
|
||||
|
||||
@@ -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<uint8_t>(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() {
|
||||
|
||||
@@ -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 <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
template <int T>
|
||||
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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
43
ion/src/device/usb_dfu_relocated_ram.cpp
Normal file
43
ion/src/device/usb_dfu_relocated_ram.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <ion/usb.h>
|
||||
#include "usb.h"
|
||||
#include "device.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
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<char *>(0x20000000 + 256*1024 - 32*1024);
|
||||
|
||||
char * ram_backup_buffer = static_cast<char *>(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<FunctionPointer>(dfu_bootloader_ram_start+1);
|
||||
|
||||
dfu_bootloader_entry();
|
||||
|
||||
memcpy(dfu_bootloader_ram_start, ram_backup_buffer, dfu_bootloader_size);
|
||||
|
||||
free(ram_backup_buffer);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
ion/src/device/usb_dfu_xip_flash.cpp
Normal file
11
ion/src/device/usb_dfu_xip_flash.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
|
||||
void DFU() {
|
||||
Ion::USB::Device::Calculator::Poll();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user