diff options
| author | Vegard Storheil Eriksen <zyp@jvnv.net> | 2013-10-11 21:19:08 +0200 | 
|---|---|---|
| committer | Vegard Storheil Eriksen <zyp@jvnv.net> | 2013-10-11 21:19:08 +0200 | 
| commit | 9a09653330f2af11149b83e1df42a05a6cb018dc (patch) | |
| tree | ba7a72f9500039f923ebc1f021d742bed2c6c063 | |
| parent | 99246622402221c3a368cd1433c8f744cd9d41ea (diff) | |
Added bootloader.
| -rw-r--r-- | .gdbinit | 5 | ||||
| -rw-r--r-- | SConstruct | 4 | ||||
| -rw-r--r-- | arcin.ld | 7 | ||||
| -rw-r--r-- | bootloader.cpp | 305 | ||||
| -rw-r--r-- | bootloader.ld | 7 | ||||
| -rwxr-xr-x | dfugen.py | 47 | ||||
| m--------- | laks | 0 | ||||
| -rw-r--r-- | main.cpp | 61 | 
8 files changed, 430 insertions, 6 deletions
| @@ -3,6 +3,11 @@ file arcin.elf  load  end +define flash_bootloader +file bootloader.elf +load +end +  define restart  run  end @@ -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 @@ -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));  		} | 
