From 055ccef33ce2e8acd598321d3e98e1fc8ff534cd Mon Sep 17 00:00:00 2001 From: Vegard Storheil Eriksen Date: Mon, 25 Nov 2013 18:56:44 +0100 Subject: Added automated test code. --- SConstruct | 2 + autotest.py | 206 ++++++++++++++++++++++++++++++++++++++++++++++ laks | 2 +- test.cpp | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 477 insertions(+), 1 deletion(-) create mode 100755 autotest.py create mode 100644 test.cpp diff --git a/SConstruct b/SConstruct index 00e8e8f..0c60496 100644 --- a/SConstruct +++ b/SConstruct @@ -11,3 +11,5 @@ env.SelectMCU('stm32f303rc') env.Firmware('arcin.elf', ['main.cpp'], LINK_SCRIPT = 'arcin.ld') env.Firmware('bootloader.elf', ['bootloader.cpp'], LINK_SCRIPT = 'bootloader.ld') + +env.Firmware('test.elf', ['test.cpp']) diff --git a/autotest.py b/autotest.py new file mode 100755 index 0000000..4490c01 --- /dev/null +++ b/autotest.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python + +import usb.core +import usb.util +import time, struct + +from hidapi import hidapi +import ctypes + +from elftools.elf.elffile import ELFFile + +pid_runtime = 0x6080 +pid_bootloader = 0x6084 + +e = ELFFile(open('arcin.elf')) + +firmware = '' + +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. + firmware += '\0' * (lma - 0x8002000 - len(firmware)) + + firmware += data + +# Align to 64B +if len(firmware) & (64 - 1): + firmware += '\0' * (64 - (len(firmware) & (64 - 1))) + +# Find test board. +dev = usb.core.find(idVendor = 0x1234, idProduct = 0x5678) + +if not dev: + print 'Test board not found.' + exit(1) + +def set_buttons(value): + dev.ctrl_transfer(0xc0, 0xf0, value, 0, 0) + +def get_leds(): + return struct.unpack(' %d, %d -> %d' % (value, a, va, b, vb) + + if (a + value) & 0xff != va: + raise TestFail('qe1') + + if (b + value) & 0xff != vb: + raise TestFail('qe2') + +def test_all(): + print 'Testing leds.' + + test_leds(0) + test_leds(0x7ff) + + for i in range(11): + test_leds(1 << i) + + print 'Testing buttons.' + + test_buttons(0) + test_buttons(0x7ff) + + for i in range(11): + test_buttons(1 << i) + + print 'Testing encoders.' + + test_qe(5) + test_qe(5) + test_qe(-5) + test_qe(-5) + + print 'All passed.' + +def process(): + try: + open_hiddev(pid_runtime) + + print 'Found runtime device, resetting to bootloader.' + + # Reset bootloader + if hidapi.hid_send_feature_report(hiddev, ctypes.c_char_p('\x00\x10'), 2) != 2: + raise RuntimeError('Reset failed.') + + time.sleep(1) + except: + pass + + try: + open_hiddev(pid_bootloader) + + flash_board() + + time.sleep(1) + + open_hiddev(pid_runtime) + + test_all() + + except TestFail, e: + print 'Test failed:', e + + except RuntimeError, e: + print 'Error:', e + +while 1: + raw_input('Press enter to start\n') + + process() + print diff --git a/laks b/laks index b4a27cc..043e8eb 160000 --- a/laks +++ b/laks @@ -1 +1 @@ -Subproject commit b4a27cc8e6a5eb91266cc515cef0b63e7e20db2b +Subproject commit 043e8eb4929ca2078bba9aab3ca763beb429f237 diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..9475507 --- /dev/null +++ b/test.cpp @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include +#include +#include + +auto dev_desc = device_desc(0x200, 0, 0, 0, 64, 0x1234, 0x5678, 0x110, 1, 2, 3, 1); +auto conf_desc = configuration_desc(0, 1, 0, 0xc0, 0); + +desc_t dev_desc_p = {sizeof(dev_desc), (void*)&dev_desc}; +desc_t conf_desc_p = {sizeof(conf_desc), (void*)&conf_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 qe1a = GPIOA[0]; +static Pin qe1b = GPIOA[1]; +static Pin qe2a = GPIOA[6]; +static Pin qe2b = GPIOA[7]; + +static Pin led1 = GPIOA[8]; +static Pin led2 = GPIOA[9]; + +USB_f1 usb(USB, dev_desc_p, conf_desc_p); + +class USB_arcin_test : public USB_class_driver { + private: + USB_generic& usb; + + uint8_t qe_state; + + bool set_buttons(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + button_inputs.set(~wValue); + + usb.write(0, nullptr, 0); + return true; + } + + bool get_leds(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + if(wLength != 4) { + return false; + } + + uint32_t buf = (button_leds.get() ^ 0x7ff) | (led1.get() ? 0 : 0x10000) | (led2.get() ? 0 : 0x20000); + + usb.write(0, &buf, 4); + return true; + } + + void inc_qe() { + switch(qe_state++ & 3) { + case 0: + qe1b.on(); + qe2b.on(); + break; + + case 1: + qe1a.on(); + qe2a.on(); + break; + + case 2: + qe1b.off(); + qe2b.off(); + break; + + case 3: + qe1a.off(); + qe2a.off(); + break; + } + } + + void dec_qe() { + switch(qe_state-- & 3) { + case 0: + qe1a.on(); + qe2a.on(); + break; + + case 1: + qe1b.off(); + qe2b.off(); + break; + + case 2: + qe1a.off(); + qe2a.off(); + break; + + case 3: + qe1b.on(); + qe2b.on(); + break; + } + } + + bool count_qe(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + int8_t n = wValue & 0xff; + + while(n != 0) { + if(n > 0) { + inc_qe(); + n--; + } else { + dec_qe(); + n++; + } + } + + usb.write(0, nullptr, 0); + return true; + } + + public: + USB_arcin_test(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) { + if(bmRequestType == 0xc0 && bRequest == 0xf0) { + return set_buttons(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall; + } + + if(bmRequestType == 0xc0 && bRequest == 0xf1) { + return get_leds(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall; + } + + if(bmRequestType == 0xc0 && bRequest == 0xf2) { + return count_qe(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall; + } + + return SetupStatus::Unhandled; + } +}; + +USB_arcin_test usb_arcin_test(usb); + +uint32_t serial_num() { + uint32_t* uid = (uint32_t*)0x1ffff7ac; + + return uid[0] * uid[1] * uid[2]; +} + +class USB_strings : public USB_class_driver { + private: + USB_generic& usb; + + public: + USB_strings(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) { + // Get string descriptor. + if(bmRequestType == 0x80 && bRequest == 0x06 && (wValue & 0xff00) == 0x0300) { + const void* desc = nullptr; + uint16_t buf[9]; + + switch(wValue & 0xff) { + case 0: + desc = u"\u0304\u0409"; + break; + + case 1: + desc = u"\u0308zyp"; + break; + + case 2: + desc = u"\u0316arcin test"; + break; + + case 3: + { + buf[0] = 0x0312; + uint32_t id = serial_num(); + for(int i = 8; i > 0; i--) { + buf[i] = (id & 0xf) > 9 ? 'A' + (id & 0xf) - 0xa : '0' + (id & 0xf); + id >>= 4; + } + desc = buf; + } + break; + } + + if(!desc) { + return SetupStatus::Unhandled; + } + + uint8_t len = *(uint8_t*)desc; + + if(len > wLength) { + len = wLength; + } + + usb.write(0, (uint32_t*)desc, len); + + return SetupStatus::Ok; + } + + return SetupStatus::Unhandled; + } +}; + +USB_strings usb_strings(usb); + +int main() { + rcc_init(); + + // Initialize system timer. + STK.LOAD = 72000000 / 8 / 1000; // 1000 Hz. + STK.CTRL = 0x03; + + RCC.enable(RCC.GPIOA); + RCC.enable(RCC.GPIOB); + RCC.enable(RCC.GPIOC); + + 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(); + + button_inputs.set(0x7ff); + button_inputs.set_type(Pin::OpenDrain); + button_inputs.set_mode(Pin::Output); + + button_leds.set_mode(Pin::Input); + button_leds.set_pull(Pin::PullUp); + + led1.set_mode(Pin::Input); + led1.set_pull(Pin::PullUp); + + led2.set_mode(Pin::Input); + led2.set_pull(Pin::PullUp); + + qe1a.set(1); + qe1a.set_type(Pin::OpenDrain); + qe1a.set_mode(Pin::Output); + qe1b.set(1); + qe1b.set_type(Pin::OpenDrain); + qe1b.set_mode(Pin::Output); + + qe2a.set(1); + qe2a.set_type(Pin::OpenDrain); + qe2a.set_mode(Pin::Output); + qe2b.set(1); + qe2b.set_type(Pin::OpenDrain); + qe2b.set_mode(Pin::Output); + + while(1) { + usb.process(); + } +} -- cgit v1.2.3