summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVegard Storheil Eriksen <zyp@jvnv.net>2013-10-11 21:19:08 +0200
committerVegard Storheil Eriksen <zyp@jvnv.net>2013-10-11 21:19:08 +0200
commit9a09653330f2af11149b83e1df42a05a6cb018dc (patch)
treeba7a72f9500039f923ebc1f021d742bed2c6c063
parent99246622402221c3a368cd1433c8f744cd9d41ea (diff)
Added bootloader.
-rw-r--r--.gdbinit5
-rw-r--r--SConstruct4
-rw-r--r--arcin.ld7
-rw-r--r--bootloader.cpp305
-rw-r--r--bootloader.ld7
-rwxr-xr-xdfugen.py47
m---------laks0
-rw-r--r--main.cpp61
8 files changed, 430 insertions, 6 deletions
diff --git a/.gdbinit b/.gdbinit
index 197f2ca..5c25ccd 100644
--- a/.gdbinit
+++ b/.gdbinit
@@ -3,6 +3,11 @@ file arcin.elf
load
end
+define flash_bootloader
+file bootloader.elf
+load
+end
+
define restart
run
end
diff --git a/SConstruct b/SConstruct
index 62b30cb..00e8e8f 100644
--- a/SConstruct
+++ b/SConstruct
@@ -8,4 +8,6 @@ SConscript('laks/build_rules')
env.SelectMCU('stm32f303rc')
-env.Firmware('arcin.elf', Glob('*.cpp'))
+env.Firmware('arcin.elf', ['main.cpp'], LINK_SCRIPT = 'arcin.ld')
+
+env.Firmware('bootloader.elf', ['bootloader.cpp'], LINK_SCRIPT = 'bootloader.ld')
diff --git a/arcin.ld b/arcin.ld
new file mode 100644
index 0000000..43f0e44
--- /dev/null
+++ b/arcin.ld
@@ -0,0 +1,7 @@
+MEMORY {
+ flash (rx) : org = 0x08002000, len = 120k
+ ram (rwx) : org = 0x20000000, len = 32k
+ ccm (rwx) : org = 0x10000000, len = 8k
+}
+
+INCLUDE "arm_flash_ram.ld"
diff --git a/bootloader.cpp b/bootloader.cpp
new file mode 100644
index 0000000..fa79d9b
--- /dev/null
+++ b/bootloader.cpp
@@ -0,0 +1,305 @@
+#include <rcc/rcc.h>
+#include <rcc/flash.h>
+#include <gpio/gpio.h>
+#include <interrupt/interrupt.h>
+#include <os/time.h>
+#include <usb/usb.h>
+#include <usb/descriptor.h>
+#include <usb/hid.h>
+#include <usb/dfu.h>
+
+void reset() {
+ SCB.AIRCR = (0x5fa << 16) | (1 << 2); // SYSRESETREQ
+}
+
+auto dev_desc = device_desc(0x200, 0, 0, 0, 64, 0x1d50, 0x6084, 0, 0, 0, 0, 1);
+auto conf_desc = configuration_desc(1, 1, 0, 0xc0, 0,
+ interface_desc(0, 0, 0, 0xfe, 0x01, 0x02, 0,
+ dfu_functional_desc(0x0d, 0, 64, 0x110)
+ )
+ // HID interface.
+ //interface_desc(1, 0, 1, 0x03, 0x00, 0x00, 0,
+ // hid_desc(0x111, 0, 1, 0x22, sizeof(report_desc)),
+ // endpoint_desc(0x81, 0x03, 16, 1)
+ //)
+);
+
+desc_t dev_desc_p = {sizeof(dev_desc), (void*)&dev_desc};
+desc_t conf_desc_p = {sizeof(conf_desc), (void*)&conf_desc};
+//desc_t report_desc_p = {sizeof(report_desc), (void*)&report_desc};
+
+static Pin usb_dm = GPIOA[11];
+static Pin usb_dp = GPIOA[12];
+static Pin usb_pu = GPIOA[15];
+
+static PinArray button_inputs = GPIOB.array(0, 10);
+static PinArray button_leds = GPIOC.array(0, 10);
+
+static Pin led1 = GPIOB[14];
+
+USB_f1 usb(USB, dev_desc_p, conf_desc_p);
+
+class Flashloader {
+ private:
+ bool state;
+ uint32_t addr;
+
+ public:
+ Flashloader() : state(false) {}
+
+ bool prepare() {
+ addr = 0x8002000;
+ state = true;
+
+ // Unlock flash.
+ FLASH.KEYR = 0x45670123;
+ FLASH.KEYR = 0xCDEF89AB;
+
+ return true;
+ }
+
+ bool write_block(uint32_t size, void* data) {
+ if(!state) {
+ return false;
+ }
+
+ if(size & 1) {
+ return false;
+ }
+
+ if(addr + size > 0x8020000) {
+ return false;
+ }
+
+ if(!(addr & (2048 - 1))) {
+ // Erase page.
+
+ FLASH.CR = 1 << 1; // PER
+ FLASH.AR = addr;
+ FLASH.CR = (1 << 6) | (1 << 1); // STRT, PER
+
+ while(FLASH.SR & (1 << 0)); // BSY
+
+ FLASH.SR &= ~(1 << 5); // EOP
+ FLASH.CR = 0;
+ }
+
+ uint16_t* src = (uint16_t*)data;
+ uint16_t* dest = (uint16_t*)addr;
+
+ for(uint32_t n = 0; n < size; n += 2) {
+ FLASH.CR = 1 << 0; // PG
+
+ *dest++ = *src++;
+
+ while(FLASH.SR & (1 << 0)); // BSY
+ }
+
+
+ addr += size;
+ return true;
+ }
+
+ bool finish() {
+ state = false;
+
+ FLASH.CR = 1 << 7; // LOCK
+
+ return true;
+ }
+};
+
+Flashloader flashloader;
+
+class USB_DFU : public USB_class_driver {
+ private:
+ USB_generic& usb;
+
+ uint8_t state;
+
+ bool get_status(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
+ if(wLength > 6) {
+ wLength = 6;
+ }
+
+ uint8_t buf[] = {0, 0, 0, 0, state, 0};
+
+ usb.write(0, (uint32_t*)buf, wLength);
+
+ return true;
+ }
+
+ bool download(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
+ if(!wLength) {
+ state = 2;
+
+ if(!flashloader.finish()) {
+ return false;
+ }
+
+ usb.write(0, nullptr, 0);
+
+ return true;
+ }
+
+ if(state == 2) {
+ state = 5;
+
+ return flashloader.prepare();
+ }
+
+ return true;
+ }
+
+ public:
+ USB_DFU(USB_generic& usbd) : usb(usbd), state(2) {
+ usb.register_driver(this);
+ }
+
+ protected:
+ virtual SetupStatus handle_setup(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
+ // DFU_GETSTATUS
+ if(bmRequestType == 0xa1 && bRequest == 0x03) {
+ return get_status(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall;
+ }
+
+ // DFU_CLRSTATUS
+ // DFU_GETSTATE
+ // DFU_ABORT
+
+ // DFU_DNLOAD
+ if(bmRequestType == 0x21 && bRequest == 0x01) {
+ return download(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall;
+ }
+
+ return SetupStatus::Unhandled;
+ }
+
+ virtual void handle_out(uint8_t ep, uint32_t len) {
+ if(ep != 0 || len == 0) {
+ return;
+ }
+
+ uint32_t buf[16];
+ usb.read(ep, buf, len);
+
+ if(state == 5) {
+ flashloader.write_block(len, buf);
+ }
+
+ usb.write(0, nullptr, 0);
+ }
+};
+
+USB_DFU usb_dfu(usb);
+
+/*
+uint32_t last_led_time;
+
+class HID_arcin : public USB_HID {
+ public:
+ HID_arcin(USB_generic& usbd, desc_t rdesc) : USB_HID(usbd, rdesc, 1, 1, 64) {}
+
+ protected:
+ virtual bool set_output_report(uint32_t* buf, uint32_t len) {
+ last_led_time = Time::time();
+ button_leds.set(*buf);
+ return true;
+ }
+};
+
+HID_arcin usb_hid(usb, report_desc_p);
+*/
+
+void chainload(uint32_t offset) {
+ SCB.VTOR = offset;
+
+ asm volatile("ldr sp, [%0]; ldr %0, [%0, #4]; bx %0" :: "r" (offset));
+}
+
+uint32_t& reset_reason = *(uint32_t*)0x10000000;
+uint32_t* firmware_vtors = (uint32_t*)0x8002000;
+
+bool normal_boot() {
+ // Check if this was a reset-to-bootloader.
+ if(reset_reason == 0xb007) {
+ reset_reason = 0;
+ return false;
+ }
+
+ // Check buttons.
+ if((button_inputs.get() ^ 0x7ff) == ((1 << 1) | (1 << 0))) {
+ return false;
+ }
+
+ // Check that reset vector is a valid flash address.
+ uint32_t reset_vector = firmware_vtors[1];
+ if(reset_vector < 0x8002000 || reset_vector >= 0x8020000) {
+ return false;
+ }
+
+ // No reason to enter bootloader.
+ return true;
+}
+
+int main() {
+ RCC.enable(RCC.GPIOA);
+ RCC.enable(RCC.GPIOB);
+ RCC.enable(RCC.GPIOC);
+
+ button_inputs.set_mode(Pin::Input);
+ button_inputs.set_pull(Pin::PullUp);
+
+ button_leds.set_mode(Pin::Output);
+
+ led1.set_mode(Pin::Output);
+
+ if(normal_boot()) {
+ chainload(0x8002000);
+ }
+
+ rcc_init();
+
+ // Initialize system timer.
+ STK.LOAD = 72000000 / 8 / 1000; // 1000 Hz.
+ STK.CTRL = 0x03;
+
+ usb_dm.set_mode(Pin::AF);
+ usb_dm.set_af(14);
+ usb_dp.set_mode(Pin::AF);
+ usb_dp.set_af(14);
+
+ RCC.enable(RCC.USB);
+
+ usb.init();
+
+ usb_pu.set_mode(Pin::Output);
+ usb_pu.on();
+
+
+ while(1) {
+ usb.process();
+
+ if(~button_inputs.get() & (1 << 5)) {
+ reset();
+ }
+
+ GPIOC[0].set(Time::time() & 512);
+
+ /*
+ usb.process();
+
+ uint16_t buttons = button_inputs.get() ^ 0x7ff;
+
+ if(Time::time() - last_led_time > 1000) {
+ button_leds.set(buttons);
+ }
+
+ if(usb.ep_ready(1)) {
+ report_t report = {buttons, uint8_t(TIM2.CNT), uint8_t(TIM3.CNT)};
+
+ usb.write(1, (uint32_t*)&report, sizeof(report));
+ }
+ */
+ }
+}
diff --git a/bootloader.ld b/bootloader.ld
new file mode 100644
index 0000000..9776f7f
--- /dev/null
+++ b/bootloader.ld
@@ -0,0 +1,7 @@
+MEMORY {
+ flash (rx) : org = 0x08000000, len = 8k
+ ram (rwx) : org = 0x20000000, len = 32k
+ ccm (rwx) : org = 0x10000000, len = 8k
+}
+
+INCLUDE "arm_flash_ram.ld"
diff --git a/dfugen.py b/dfugen.py
new file mode 100755
index 0000000..dc93d1d
--- /dev/null
+++ b/dfugen.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+import sys, struct, zlib
+from elftools.elf.elffile import ELFFile
+
+infile = sys.argv[1]
+outfile = sys.argv[2]
+
+e = ELFFile(open(infile))
+
+buf = ''
+
+for segment in sorted(e.iter_segments(), key = lambda x: x.header.p_paddr):
+ if segment.header.p_type != 'PT_LOAD':
+ continue
+
+ data = segment.data()
+ lma = segment.header.p_paddr
+
+ # Workaround for LD aligning segments to a larger boundary than 8k.
+ if lma == 0x8000000:
+ lma += 0x2000
+ data = data[0x2000:]
+
+ # Add padding if necessary.
+ buf += '\0' * (lma - 0x8002000 - len(buf))
+
+ buf += data
+
+# Align to 64B
+if len(buf) & (64 - 1):
+ buf += '\0' * (64 - (len(buf) & (64 - 1)))
+
+# Add DFU suffix
+buf += struct.pack('<HHHH3sB',
+ 0xffff,
+ 0x5679,
+ 0x1234,
+ 0x0100,
+ 'UFD',
+ 16,
+)
+
+# Calculate CRC.
+buf += struct.pack('<I', ~zlib.crc32(buf) & 0xffffffff)
+
+open(outfile, 'w').write(buf)
diff --git a/laks b/laks
-Subproject 49b9c31a097b99945a5bb406db403655467fe21
+Subproject b4a27cc8e6a5eb91266cc515cef0b63e7e20db2
diff --git a/main.cpp b/main.cpp
index 7b9ccf1..45b58a6 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,10 +1,19 @@
#include <rcc/rcc.h>
#include <gpio/gpio.h>
+#include <interrupt/interrupt.h>
#include <timer/timer.h>
#include <os/time.h>
#include <usb/usb.h>
#include <usb/descriptor.h>
#include <usb/hid.h>
+#include <usb/dfu.h>
+
+uint32_t& reset_reason = *(uint32_t*)0x10000000;
+
+void reset_bootloader() {
+ reset_reason = 0xb007;
+ SCB.AIRCR = (0x5fa << 16) | (1 << 2); // SYSRESETREQ
+}
auto report_desc = gamepad(
// Inputs.
@@ -141,12 +150,15 @@ auto report_desc = gamepad(
padding_out(5)
);
-auto dev_desc = device_desc(0x200, 0, 0, 0, 64, 0x1234, 0x5678, 0, 0, 0, 0, 1);
-auto conf_desc = configuration_desc(1, 1, 0, 0xc0, 0,
+auto dev_desc = device_desc(0x200, 0, 0, 0, 64, 0x1d50, 0x6080, 0, 0, 0, 0, 1);
+auto conf_desc = configuration_desc(2, 1, 0, 0xc0, 0,
// HID interface.
- interface_desc(1, 0, 1, 0x03, 0x00, 0x00, 0,
+ interface_desc(0, 0, 1, 0x03, 0x00, 0x00, 0,
hid_desc(0x111, 0, 1, 0x22, sizeof(report_desc)),
endpoint_desc(0x81, 0x03, 16, 1)
+ ),
+ interface_desc(1, 0, 0, 0xfe, 0x01, 0x01, 0,
+ dfu_functional_desc(0x0d, 0, 64, 0x110)
)
);
@@ -174,7 +186,7 @@ uint32_t last_led_time;
class HID_arcin : public USB_HID {
public:
- HID_arcin(USB_generic& usbd, desc_t rdesc) : USB_HID(usbd, rdesc, 1, 1, 64) {}
+ HID_arcin(USB_generic& usbd, desc_t rdesc) : USB_HID(usbd, rdesc, 0, 1, 64) {}
protected:
virtual bool set_output_report(uint32_t* buf, uint32_t len) {
@@ -186,6 +198,39 @@ class HID_arcin : public USB_HID {
HID_arcin usb_hid(usb, report_desc_p);
+class USB_DFU_runtime : public USB_class_driver {
+ private:
+ USB_generic& usb;
+
+ public:
+ USB_DFU_runtime(USB_generic& usbd) : usb(usbd) {
+ usb.register_driver(this);
+ }
+
+ protected:
+ virtual SetupStatus handle_setup(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
+ // DFU_DETACH
+ if(bmRequestType == 0x21 && bRequest == 0x00 && wIndex == 0x01) {
+ detach();
+ return SetupStatus::Ok;
+ }
+
+ return SetupStatus::Unhandled;
+ }
+
+ bool detach() {
+ usb.write(0, nullptr, 0);
+
+ Time::sleep(10);
+
+ reset_bootloader();
+
+ return true;
+ }
+};
+
+USB_DFU_runtime usb_dfu_runtime(usb);
+
struct report_t {
uint16_t buttons;
uint8_t axis_x;
@@ -193,6 +238,8 @@ struct report_t {
} __attribute__((packed));
int main() {
+ rcc_init();
+
// Initialize system timer.
STK.LOAD = 72000000 / 8 / 1000; // 1000 Hz.
STK.CTRL = 0x03;
@@ -249,12 +296,16 @@ int main() {
uint16_t buttons = button_inputs.get() ^ 0x7ff;
+ //if(buttons & (1 << 4)) {
+ // reset_bootloader();
+ //}
+
if(Time::time() - last_led_time > 1000) {
button_leds.set(buttons);
}
if(usb.ep_ready(1)) {
- report_t report = {buttons, uint8_t(TIM2.CNT), uint8_t(TIM3.CNT)};
+ report_t report = {buttons, uint8_t(TIM2.CNT >> 2), uint8_t(TIM3.CNT >> 2)};
usb.write(1, (uint32_t*)&report, sizeof(report));
}