diff --git a/.gitignore b/.gitignore index f3424d231e2..82b94b3d253 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,9 @@ tools/llgo tools/polly # Sphinx build tree, if building in-source dir. docs/_build +# AVR on-target test suite library +utils/AVR/avrlit/libavrlit/lufa +utils/AVR/avrlit/libavrlit/*/_build #==============================================================================# # Files created in tree by the Go bindings. diff --git a/CMakeLists.txt b/CMakeLists.txt index ee2609a60f1..01ddc7b85b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -586,6 +586,7 @@ if( LLVM_INCLUDE_UTILS ) add_subdirectory(utils/not) add_subdirectory(utils/llvm-lit) add_subdirectory(utils/yaml-bench) + add_subdirectory(utils/AVR/avrlit) else() if ( LLVM_INCLUDE_TESTS ) message(FATAL_ERROR "Including tests when not building utils will not work. diff --git a/test/CodeGen/AVR/execute_on_target_test.cpp b/test/CodeGen/AVR/execute_on_target_test.cpp new file mode 100644 index 00000000000..d46c9724191 --- /dev/null +++ b/test/CodeGen/AVR/execute_on_target_test.cpp @@ -0,0 +1,114 @@ +// RUN: llvm-avrlit %s %p/add.ll %p/and.ll %p/xor.ll + +#include + +using namespace avrlit; + +//=== add.ll ================================================================== +extern "C" { + i8 add8_reg_reg(i8, i8); + i8 add8_reg_imm(i8); + i8 add8_reg_increment(i8); + i16 add16_reg_reg(i16, i16); + i16 add16_reg_imm(i16); + i16 add16_reg_imm_subi(i16); + i32 add32_reg_reg(i32, i32); + i32 add32_reg_imm(i32); + i64 add64_reg_reg(i64, i64); + i64 add64_reg_imm(i64); +} + +void test_add(test & t) { + t.plan(20, PSTR("ADD")); + + t.ok(_(add8_reg_reg( 23, 42) == 23 + 42)); + t.ok(_(add8_reg_reg(-23, 42) == -23 + 42)); + + t.ok(_(add8_reg_imm(42) == 42 + 5)); + t.ok(_(add8_reg_imm(23) == 23 + 5)); + + t.ok(_(add8_reg_increment(42) == 42 + 1)); + t.ok(_(add8_reg_increment(23) == 23 + 1)); + + t.ok(_(add16_reg_reg(1234, 4321) == 1234 + 4321)); + t.ok(_(add16_reg_reg(5678, 8765) == 5678 + 8765)); + + t.ok(_(add16_reg_imm(1234) == 1234 + 63)); + t.ok(_(add16_reg_imm(8765) == 8765 + 63)); + + t.ok(_(add16_reg_imm_subi(1234) == 1234 + 123)); + t.ok(_(add16_reg_imm_subi(8765) == 8765 + 123)); + + t.ok(_(add32_reg_reg(123456, 987654) == 123456 + 987654)); + t.ok(_(add32_reg_reg(3, 5) == 3 + 5)); + + t.ok(_(add32_reg_imm(987654) == 987654 + 5)); + t.ok(_(add32_reg_imm(5) == 5 + 5)); + + t.ok(_(add64_reg_reg(9876543210, 9988776655) == + 9876543210 + 9988776655)); + t.ok(_(add64_reg_reg(198, 126) == 198 +126)); + + t.ok(_(add64_reg_imm(9876543210) == 9876543210 + 5)); + t.ok(_(add64_reg_imm(198) == 198 + 5)); + +} + +//=== and.ll ================================================================== +extern "C" { + i8 and8_reg_reg(i8, i8); + i8 and8_reg_imm(i8); + i16 and16_reg_reg(i16, i16); + i16 and16_reg_imm(i16); +} + +void test_and(test & t) { + t.plan(8, PSTR("AND")); + + t.ok(_(and8_reg_reg(23, 42) == (23 & 42))); + t.ok(_(and8_reg_reg(0xef, 0x2a) == (0xef & 0x2a))); + + t.ok(_(and8_reg_imm(23) == (23 & 5))); + t.ok(_(and8_reg_imm(0xef) == (0xef & 5))); + + t.ok(_(and16_reg_reg(2323, 4242) == (2323 & 4242))); + t.ok(_(and16_reg_reg(0xefff, 0x2aaa) == (0xefff & 0x2aaa))); + + t.ok(_(and16_reg_imm(2342) == (2342 & 1234))); + t.ok(_(and16_reg_imm(0xefff) == (0xefff & 1234))); +} + +//=== xor.ll ================================================================== +extern "C" { + i8 xor8_reg_reg(i8, i8); + i16 xor16_reg_reg(i16, i16); + i32 xor32_reg_reg(i32, i32); + i64 xor64_reg_reg(i64, i64); +} + +void test_xor(test & t) { + t.plan(8, PSTR("XOR")); + + t.ok(_(xor8_reg_reg(23, 42) == 23 ^ 42)); + t.ok(_(xor8_reg_reg(0xff, 0xaa) == 0xff ^ 0xaa)); + + t.ok(_(xor16_reg_reg(0xffff, 0xaaaa) == 0xffff ^ 0xaaaa)); + t.ok(_(xor16_reg_reg(0x5555, 0xaaaa) == 0x5555 ^ 0xaaaa)); + + t.ok(_(xor32_reg_reg(0xffffffff, 0xaaaaaaaa) == + 0xffffffff ^ 0xaaaaaaaa)); + t.ok(_(xor32_reg_reg(0x55555555, 0xaaaaaaaa) == + 0x55555555 ^ 0xaaaaaaaa)); + + t.ok(_(xor64_reg_reg(0xffffffffffffffff, 0xaaaaaaaaaaaaaaaa) == + 0xffffffffffffffff ^ 0xaaaaaaaaaaaaaaaa)); + t.ok(_(xor64_reg_reg(0x5555555555555555, 0xaaaaaaaaaaaaaaaa) == + 0x5555555555555555 ^ 0xaaaaaaaaaaaaaaaa)); +} + +//=== Test Suite ============================================================== + +AVRLIT_TEST_SUITE() { + run(test_add, test_and, test_xor); +} + diff --git a/test/CodeGen/AVR/lit.local.cfg b/test/CodeGen/AVR/lit.local.cfg index a825b111de8..7779fe2e9fa 100644 --- a/test/CodeGen/AVR/lit.local.cfg +++ b/test/CodeGen/AVR/lit.local.cfg @@ -1,6 +1,26 @@ -config.suffixes = ['.ll'] +config.suffixes = ['.ll', '.cpp'] + +import os, lit.TestRunner +from lit.formats import ShTest targets = set(config.root.targets_to_build.split()) if not 'AVR' in targets: - config.unsupported = True + config.unsupported = True + +if 'AVRLIT_PORT' in os.environ: + config.environment['AVRLIT_PORT'] = os.environ['AVRLIT_PORT'] + +class AVRCodeGenTest(ShTest): + def __init__(self): + ShTest.__init__(self) + + def execute(self, test, litConfig): + if test.getSourcePath().endswith('.cpp') and not 'AVRLIT_PORT' in os.environ: + return (lit.Test.UNSUPPORTED, 'AVRLIT_PORT environment variable is not set') + + return ShTest.execute(self, test, litConfig) + + +config.test_format = AVRCodeGenTest() +# vim: filetype=python diff --git a/utils/AVR/avrlit/CMakeLists.txt b/utils/AVR/avrlit/CMakeLists.txt new file mode 100644 index 00000000000..e10919d2792 --- /dev/null +++ b/utils/AVR/avrlit/CMakeLists.txt @@ -0,0 +1,22 @@ +if (WIN32 AND NOT CYGWIN) + # The script needs suffix.py for multiprocess to find a main module. + set(suffix .py) +endif () + +set(llvm_avrlit_path ${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-avrlit${suffix}) + +if(NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".") + foreach(BUILD_MODE ${CMAKE_CONFIGURATION_TYPES}) + string(REPLACE ${CMAKE_CFG_INTDIR} ${BUILD_MODE} bi ${llvm_avrlit_path}) + configure_file( + llvm-avrlit.in + ${bi} + ) + endforeach() +else() + set(BUILD_MODE .) + configure_file( + llvm-avrlit.in + ${llvm_avrlit_path} + ) +endif() diff --git a/utils/AVR/avrlit/README.md b/utils/AVR/avrlit/README.md new file mode 100644 index 00000000000..d460dfec3d4 --- /dev/null +++ b/utils/AVR/avrlit/README.md @@ -0,0 +1,61 @@ +# AVR LLVM Integrated Tester + +This tool builds an AVR executable from test code lowered by our backend and the +`libavrlit` test suite library using a known good toolchain (avr-gcc). The +resulting binary is uploaded to a development board. Test results are collected +using a virtual tty. + +### Setup + +Things you will need: + + * ATmega32U4 board with USB and AVR-109 type bootloader. (Arduino Leonardo, + RedBear Blend (Micro), &c.) + * [`pySerial`](http://pyserial.sourceforge.net) python module + * avr-gcc + * avrdude + * GNU Make + * Fire extinguisher + +Set the `AVRLIT_PORT` environment variable to the tty path of your board. + +```` bash +> export AVRLIT_PORT=/dev/tty.usbmodemfd141 +```` + +If your board currently runs an Arduino sketch that uses the serial port, you are +all set. Otherwise, you need to reset the board manually for the first run. + +```` bash +> bin/llvm-lit -v ../llvm/test/CodeGen/AVR/ +``` + +### Writing Tests + +The on-target execution tests reside in `llvm/test/CodeGen/AVR`. Like other lit +tests they contain a `RUN: ` line calling `llvm-avrlit`: + +```` C++ +// RUN: llvm-avrlit %s %p/add.ll + +#include + +using namespace avrlit; + +extern "C" { // actually this is extern "IR" but Bjarne forgot. + i8 add8_reg_reg(i8, i8); +} + +AVRLIT_TEST(llvm_avr) { + reenter (this) { // don't worry about the coroutine + plan(3); + ok(_(add8_reg_reg( 23, 42) == 23 + 42)); yield; + ok(_(add8_reg_reg(-23, 42) == -23 + 42)); yield; + ok(_(add8_reg_reg(-23, 42) == 0)); yield; + } +} +```` + +All of this is still in flux. I'll explain it if I decide to keep it. ;) + + diff --git a/utils/AVR/avrlit/libavrlit/Makefile b/utils/AVR/avrlit/libavrlit/Makefile new file mode 100644 index 00000000000..1cddd64a170 --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/Makefile @@ -0,0 +1,7 @@ +all: + $(MAKE) -C leonardo + + +clean: + $(MAKE) -C leonardo clean + diff --git a/utils/AVR/avrlit/libavrlit/README.md b/utils/AVR/avrlit/libavrlit/README.md new file mode 100644 index 00000000000..bc486719330 --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/README.md @@ -0,0 +1,22 @@ +# AVR LLVM Integrated Tester + +This directory contains the sources of `libavrlit`, a test suite library to +test code generated by our backend on the target. It uses the +[LUFA USB library](http://www.fourwalledcubicle.com/LUFA.php) +([GitHub](https://github.com/abcminiuser/lufa)) and its makefiles. This library +and the test executbale are compiled using avr-gcc. + +The test executables built with this framework connect to the host as a USB CDC +device. They wait for the terminal to become ready before executing the tests. Results +are printed in a format similar to `llvm-lit`. + +### Installation + +To avoid clutter the current library binary is checked in. To build the library +the LUFA library is required: + +```` +> git clone https://github.com/abcminiuser/lufa.git +> make +```` + diff --git a/utils/AVR/avrlit/libavrlit/avrlit.h b/utils/AVR/avrlit/libavrlit/avrlit.h new file mode 100644 index 00000000000..f80343003ef --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/avrlit.h @@ -0,0 +1,145 @@ +//===-- avrlit - AVR LLVM Integrated Tester - AVR Side --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef AVRLIT_H +# define AVRLIT_H + +# include + +# include + +namespace avrlit { + +typedef bool i1; +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +struct pstr { + explicit pstr(char const* p) : str_(p) {} + char const* str_; +}; + +class ostream { + public: + struct USB_ClassInfo_CDC_Device_t; + explicit ostream(USB_ClassInfo_CDC_Device_t * cdc); + + void print(char const* str); + void print(pstr const& str); + + private: + USB_ClassInfo_CDC_Device_t * cdc_; +}; + +} // end of namespace avrlit + +inline +avrlit::ostream & +operator << (avrlit::ostream & os, char const* str) { + os.print(str); + return os; +} + +inline +avrlit::ostream & +operator << (avrlit::ostream & os, avrlit::pstr const& str) { + os.print(str); + return os; +} + +#define P(str) pstr(PSTR(str)) + +namespace avrlit { + +class test { + public: + explicit test(ostream const& os) : + os_(os), planned_tests_(), passed_tests_(), failed_tests_() {} + ~test() { summarize(); } + void ok(bool result, char const* expression, char const* file, unsigned line); + void plan(unsigned count, char const * name = nullptr); + private: + void summarize(); + ostream os_; + unsigned planned_tests_; + unsigned passed_tests_; + unsigned failed_tests_; +}; + +void init(); + +enum test_suite_state { + waiting, + delaying, + running, + done +}; + +extern bool is_terminal_ready; + +void do_tasks(bool performRead); + +ostream get_cdc_stream(); + +int16_t cdc_receive_byte(); + +template +inline +int +run(F f, ostream const& os) { + test t(os); + f(t); + return 0; +} + +template +void +run(F... tests) { + init(); + ostream os(get_cdc_stream()); + test_suite_state state = waiting; + int16_t delay = 10; + for (;;) { + char c = cdc_receive_byte(); + switch (state) { + case waiting: + if (is_terminal_ready) { + delay = 10; + state = delaying; + } + break; + case delaying: + if (--delay == 0) { + state = running; + } + break; + case running: + [](...){ }(run(tests, os)...); + os << P("--\n"); + state = done; + break; + case done: + if (not is_terminal_ready or c == ' ') state = waiting; + break; + } + do_tasks(false); + } +} + +} // end of namespace avrlit + +# define AVRLIT_TEST_SUITE() int main() + +# define AVRLIT_TEST_EXPRESSION(x) x, PSTR(#x), PSTR(__FILE__), __LINE__ + +# define _(x) AVRLIT_TEST_EXPRESSION(x) + +#endif // AVRLIT_H diff --git a/utils/AVR/avrlit/libavrlit/avrlit.mk b/utils/AVR/avrlit/libavrlit/avrlit.mk new file mode 100644 index 00000000000..38149ab91c5 --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/avrlit.mk @@ -0,0 +1,23 @@ +# This file is included by makefiles in board specific subdirectories +F_USB = $(F_CPU) +ARCH = AVR8 +OPTIMIZATION = s +TARGET = avrlit +LUFA_PATH = ../lufa/LUFA +SRC = ../src/avrlit.cpp ../src/usb.c $(LUFA_SRC_USB_DEVICE) \ + $(LUFA_PATH)/Drivers/USB/Class/Device/CDCClassDevice.c +CC_FLAGS = -I../src -DUSE_LUFA_CONFIG_HEADER +CPP_FLAGS = +CPP_STANDARD = c++11 +LD_FLAGS = +OBJDIR = _build + +# Default target + +lib: + +include $(LUFA_PATH)/Build/lufa_core.mk +include $(LUFA_PATH)/Build/lufa_sources.mk +include $(LUFA_PATH)/Build/lufa_build.mk +include $(LUFA_PATH)/Build/lufa_cppcheck.mk + diff --git a/utils/AVR/avrlit/libavrlit/leonardo/Makefile b/utils/AVR/avrlit/libavrlit/leonardo/Makefile new file mode 100644 index 00000000000..72778c29a08 --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/leonardo/Makefile @@ -0,0 +1,3 @@ +include board.mk +include ../avrlit.mk + diff --git a/utils/AVR/avrlit/libavrlit/leonardo/board.mk b/utils/AVR/avrlit/libavrlit/leonardo/board.mk new file mode 100644 index 00000000000..4f272258247 --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/leonardo/board.mk @@ -0,0 +1,4 @@ +BOARD = leonardo +MCU = atmega32u4 +F_CPU = 16000000 + diff --git a/utils/AVR/avrlit/libavrlit/leonardo/libavrlit.a b/utils/AVR/avrlit/libavrlit/leonardo/libavrlit.a new file mode 100644 index 00000000000..0a7ecd462e6 Binary files /dev/null and b/utils/AVR/avrlit/libavrlit/leonardo/libavrlit.a differ diff --git a/utils/AVR/avrlit/libavrlit/src/LUFAConfig.h b/utils/AVR/avrlit/libavrlit/src/LUFAConfig.h new file mode 100644 index 00000000000..7f95a1fc278 --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/src/LUFAConfig.h @@ -0,0 +1,93 @@ +//===-- LUFAConfig.h - LUFA Compile-time Configuration --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef _LUFA_CONFIG_H_ +#define _LUFA_CONFIG_H_ + + #if (ARCH == ARCH_AVR8) + + /* Non-USB Related Configuration Tokens: */ +// #define DISABLE_TERMINAL_CODES + + /* USB Class Driver Related Tokens: */ +// #define HID_HOST_BOOT_PROTOCOL_ONLY +// #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} +// #define HID_USAGE_STACK_DEPTH {Insert Value Here} +// #define HID_MAX_COLLECTIONS {Insert Value Here} +// #define HID_MAX_REPORTITEMS {Insert Value Here} +// #define HID_MAX_REPORT_IDS {Insert Value Here} +// #define NO_CLASS_DRIVER_AUTOFLUSH + + /* General USB Driver Related Tokens: */ +// #define ORDERED_EP_CONFIG + #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL) + #define USB_DEVICE_ONLY +// #define USB_HOST_ONLY +// #define USB_STREAM_TIMEOUT_MS {Insert Value Here} +// #define NO_LIMITED_CONTROLLER_CONNECT +// #define NO_SOF_EVENTS + + /* USB Device Mode Driver Related Tokens: */ +// #define USE_RAM_DESCRIPTORS + #define USE_FLASH_DESCRIPTORS +// #define USE_EEPROM_DESCRIPTORS +// #define NO_INTERNAL_SERIAL + #define FIXED_CONTROL_ENDPOINT_SIZE 8 +// #define DEVICE_STATE_AS_GPIOR {Insert Value Here} + #define FIXED_NUM_CONFIGURATIONS 1 +// #define CONTROL_ONLY_DEVICE + #define INTERRUPT_CONTROL_ENDPOINT +// #define NO_DEVICE_REMOTE_WAKEUP +// #define NO_DEVICE_SELF_POWER + + /* USB Host Mode Driver Related Tokens: */ +// #define HOST_STATE_AS_GPIOR {Insert Value Here} +// #define USB_HOST_TIMEOUT_MS {Insert Value Here} +// #define HOST_DEVICE_SETTLE_DELAY_MS {Insert Value Here} +// #define NO_AUTO_VBUS_MANAGEMENT +// #define INVERTED_VBUS_ENABLE_LINE + + #elif (ARCH == ARCH_XMEGA) + + /* Non-USB Related Configuration Tokens: */ +// #define DISABLE_TERMINAL_CODES + + /* USB Class Driver Related Tokens: */ +// #define HID_HOST_BOOT_PROTOCOL_ONLY +// #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} +// #define HID_USAGE_STACK_DEPTH {Insert Value Here} +// #define HID_MAX_COLLECTIONS {Insert Value Here} +// #define HID_MAX_REPORTITEMS {Insert Value Here} +// #define HID_MAX_REPORT_IDS {Insert Value Here} +// #define NO_CLASS_DRIVER_AUTOFLUSH + + /* General USB Driver Related Tokens: */ + #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_RC32MCLKSRC | USB_OPT_BUSEVENT_PRIHIGH) +// #define USB_STREAM_TIMEOUT_MS {Insert Value Here} +// #define NO_LIMITED_CONTROLLER_CONNECT +// #define NO_SOF_EVENTS + + /* USB Device Mode Driver Related Tokens: */ +// #define USE_RAM_DESCRIPTORS + #define USE_FLASH_DESCRIPTORS +// #define USE_EEPROM_DESCRIPTORS +// #define NO_INTERNAL_SERIAL + #define FIXED_CONTROL_ENDPOINT_SIZE 8 +// #define DEVICE_STATE_AS_GPIOR {Insert Value Here} + #define FIXED_NUM_CONFIGURATIONS 1 +// #define CONTROL_ONLY_DEVICE + #define MAX_ENDPOINT_INDEX 6 +// #define NO_DEVICE_REMOTE_WAKEUP +// #define NO_DEVICE_SELF_POWER + + #else + + #error Unsupported architecture for this LUFA configuration file. + + #endif +#endif diff --git a/utils/AVR/avrlit/libavrlit/src/avrlit.cpp b/utils/AVR/avrlit/libavrlit/src/avrlit.cpp new file mode 100644 index 00000000000..db13b13c2f4 --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/src/avrlit.cpp @@ -0,0 +1,150 @@ +//===-- avrlit - AVR LLVM Integrated Tester - AVR Side --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../avrlit.h" + +#include +#include +#include +#include + +#include + +#include "usb.h" + +namespace avrlit { + +struct ostream::USB_ClassInfo_CDC_Device_t : ::USB_ClassInfo_CDC_Device_t {}; + +ostream::ostream(ostream::USB_ClassInfo_CDC_Device_t * cdc) : cdc_(cdc) {}; + +void +ostream::print(char const* str) { + for (char c = *str++; c != '\0'; c = *str++) CDC_Device_SendByte(cdc_, c); +} + +void +ostream::print(pstr const& str) { + char const* s = str.str_; + for (char c = pgm_read_byte(s++); c != '\0'; c = pgm_read_byte(s++)) { + CDC_Device_SendByte(cdc_, c); + } +} + +ostream get_cdc_stream() { return ostream((ostream::USB_ClassInfo_CDC_Device_t*)&cdc); } + +struct dec { + explicit dec(int n) { itoa(n, buf_, 10); } + operator char const*() { return buf_; } +private: + char buf_[6]; +}; + +void +test::ok(bool is_ok, char const* expr, char const* file, unsigned line) { + ++(is_ok ? passed_tests_ : failed_tests_); + int count = passed_tests_ + failed_tests_; + + os_ << (is_ok ? P("PASS") : P("FAIL")) << P(": ") << pstr(expr) << P(" (") + << dec(count) << P(" of ") << dec(planned_tests_) << P(")\n"); + if (not is_ok) { + pstr delineator(PSTR("********************")); + os_ << delineator << P(" TEST FAILED ") << delineator << P("\n") + << pstr(file) << P(":") << dec(line) << P(": ") << pstr(expr) << P("\n") + << delineator << P("\n"); + } + + do_tasks(true); +} + +void +test::plan(unsigned count, char const* name) { + planned_tests_ = count; + os_ << P("-- Testing ") << pstr(name) << P(": ") << dec(planned_tests_) + << P(" tests --\n"); +} + +bool is_terminal_ready = false; + +void +init() { + MCUSR &= ~(1 << WDRF); + wdt_disable(); + + clock_prescale_set(clock_div_1); + + USB_Init(); + + sei(); +} + +void +do_tasks(bool performRead) { + if (performRead) CDC_Device_ReceiveByte(&cdc); + CDC_Device_USBTask(&cdc); + USB_USBTask(); +} + +int16_t +cdc_receive_byte() { + return CDC_Device_ReceiveByte(&cdc); +} + +void +test::summarize() { + uint16_t total = passed_tests_ + failed_tests_; + if (planned_tests_ != total) { + os_ << P("FAIL: ") << dec(planned_tests_) << P(" tests planned but ") + << dec(total) << P(" tests executed\n"); + } + os_ << P("Testing done\n" + " Expected Passes : ") << dec(passed_tests_) << P("\n") + << P(" Unexpected Failures: ") << dec(failed_tests_) << P("\n"); +} + +inline +void +process_control_line_change(USB_ClassInfo_CDC_Device_t *const device) { + // this implements the same reset behaviour as the Arduino Leonardo + uint16_t control_lines = device->State.ControlLineStates.HostToDevice; + is_terminal_ready = control_lines & CDC_CONTROL_LINE_OUT_DTR; + if (not is_terminal_ready and + device->State.LineEncoding.BaudRateBPS == 1200) + { + USB_Disable(); + cli(); + + *(uint16_t *)0x0800 = 0x7777; + wdt_enable(WDTO_120MS); + } else { + wdt_disable(); + wdt_reset(); + *(uint16_t *)0x0800 = 0x0; + } +} + +} // end of namespace avrlit + +//=== USB Event Hooks ========================================================= + +void +EVENT_USB_Device_ConfigurationChanged() { + CDC_Device_ConfigureEndpoints(&cdc); +} + +void +EVENT_USB_Device_ControlRequest() { + CDC_Device_ProcessControlRequest(&cdc); +} + +void +EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t *const d) { + avrlit::process_control_line_change(d); +} + diff --git a/utils/AVR/avrlit/libavrlit/src/usb.c b/utils/AVR/avrlit/libavrlit/src/usb.c new file mode 100644 index 00000000000..a3fd8db7b6d --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/src/usb.c @@ -0,0 +1,230 @@ +//===-- usb.c - avrlit USB CDC Device Implementation ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "usb.h" + +const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = { + .Header = { + .Size = sizeof(USB_Descriptor_Device_t), + .Type = DTYPE_Device + }, + .USBSpecification = VERSION_BCD(1,1,0), + .Class = USB_CSCP_IADDeviceClass, + .SubClass = USB_CSCP_IADDeviceSubclass, + .Protocol = USB_CSCP_IADDeviceProtocol, + + .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, + + .VendorID = 0x03EB, + .ProductID = 0x204E, + .ReleaseNumber = VERSION_BCD(0,0,1), + + .ManufacturerStrIndex = STRING_ID_Manufacturer, + .ProductStrIndex = STRING_ID_Product, + .SerialNumStrIndex = USE_INTERNAL_SERIAL, + + .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS +}; + +const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { + .Config = { + .Header = { + .Size = sizeof(USB_Descriptor_Configuration_Header_t), + .Type = DTYPE_Configuration + }, + .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), + .TotalInterfaces = 4, + + .ConfigurationNumber = 1, + .ConfigurationStrIndex = NO_DESCRIPTOR, + + .ConfigAttributes = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_SELFPOWERED), + + .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) + }, + + .CDC_IAD = { + .Header = { + .Size = sizeof(USB_Descriptor_Interface_Association_t), + .Type = DTYPE_InterfaceAssociation + }, + .FirstInterfaceIndex = INTERFACE_ID_CDC_CCI, + .TotalInterfaces = 2, + + .Class = CDC_CSCP_CDCClass, + .SubClass = CDC_CSCP_ACMSubclass, + .Protocol = CDC_CSCP_ATCommandProtocol, + + .IADStrIndex = NO_DESCRIPTOR + }, + + .CDC_CCI_Interface = { + .Header = { + .Size = sizeof(USB_Descriptor_Interface_t), + .Type = DTYPE_Interface + }, + .InterfaceNumber = INTERFACE_ID_CDC_CCI, + .AlternateSetting = 0, + + .TotalEndpoints = 1, + + .Class = CDC_CSCP_CDCClass, + .SubClass = CDC_CSCP_ACMSubclass, + .Protocol = CDC_CSCP_ATCommandProtocol, + + .InterfaceStrIndex = NO_DESCRIPTOR + }, + + .CDC_Functional_Header = { + .Header = { + .Size = sizeof(USB_CDC_Descriptor_FunctionalHeader_t), + .Type = DTYPE_CSInterface + }, + .Subtype = CDC_DSUBTYPE_CSInterface_Header, + + .CDCSpecification = VERSION_BCD(1,1,0), + }, + + .CDC_Functional_ACM = { + .Header = { + .Size = sizeof(USB_CDC_Descriptor_FunctionalACM_t), + .Type = DTYPE_CSInterface + }, + .Subtype = CDC_DSUBTYPE_CSInterface_ACM, + + .Capabilities = 0x06, + }, + + .CDC_Functional_Union = { + .Header = { + .Size = sizeof(USB_CDC_Descriptor_FunctionalUnion_t), + .Type = DTYPE_CSInterface + }, + .Subtype = CDC_DSUBTYPE_CSInterface_Union, + + .MasterInterfaceNumber = INTERFACE_ID_CDC_CCI, + .SlaveInterfaceNumber = INTERFACE_ID_CDC_DCI, + }, + + .CDC_ManagementEndpoint = { + .Header = { + .Size = sizeof(USB_Descriptor_Endpoint_t), + .Type = DTYPE_Endpoint + }, + .EndpointAddress = CDC_NOTIFICATION_EPADDR, + .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = CDC_NOTIFICATION_EPSIZE, + .PollingIntervalMS = 0xFF + }, + + .CDC_DCI_Interface = { + .Header = { + .Size = sizeof(USB_Descriptor_Interface_t), + .Type = DTYPE_Interface + }, + .InterfaceNumber = INTERFACE_ID_CDC_DCI, + .AlternateSetting = 0, + + .TotalEndpoints = 2, + + .Class = CDC_CSCP_CDCDataClass, + .SubClass = CDC_CSCP_NoDataSubclass, + .Protocol = CDC_CSCP_NoDataProtocol, + + .InterfaceStrIndex = NO_DESCRIPTOR + }, + + .CDC_DataOutEndpoint = { + .Header = { + .Size = sizeof(USB_Descriptor_Endpoint_t), + .Type = DTYPE_Endpoint + }, + .EndpointAddress = CDC_RX_EPADDR, + .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = CDC_TXRX_EPSIZE, + .PollingIntervalMS = 0x05 + }, + + .CDC_DataInEndpoint = { + .Header = { + .Size = sizeof(USB_Descriptor_Endpoint_t), + .Type = DTYPE_Endpoint + }, + .EndpointAddress = CDC_TX_EPADDR, + .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = CDC_TXRX_EPSIZE, + .PollingIntervalMS = 0x05 + }, +}; + +const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG); +const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR(L"Dean Camera"); +const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"LUFA Dual CDC Demo"); + +USB_ClassInfo_CDC_Device_t cdc = { + .Config = { + .ControlInterfaceNumber = INTERFACE_ID_CDC_CCI, + .DataINEndpoint = { + .Address = CDC_TX_EPADDR, + .Size = CDC_TXRX_EPSIZE, + .Banks = 1, + }, + .DataOUTEndpoint = { + .Address = CDC_RX_EPADDR, + .Size = CDC_TXRX_EPSIZE, + .Banks = 1, + }, + .NotificationEndpoint = { + .Address = CDC_NOTIFICATION_EPADDR, + .Size = CDC_NOTIFICATION_EPSIZE, + .Banks = 1, + } + } +}; + +uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, + const uint8_t wIndex, + const void** const DescriptorAddress) +{ + const uint8_t DescriptorType = (wValue >> 8); + const uint8_t DescriptorNumber = (wValue & 0xFF); + + const void* Address = NULL; + uint16_t Size = NO_DESCRIPTOR; + + switch (DescriptorType) { + case DTYPE_Device: + Address = &DeviceDescriptor; + Size = sizeof(USB_Descriptor_Device_t); + break; + case DTYPE_Configuration: + Address = &ConfigurationDescriptor; + Size = sizeof(USB_Descriptor_Configuration_t); + break; + case DTYPE_String: + switch (DescriptorNumber) { + case STRING_ID_Language: + Address = &LanguageString; + Size = pgm_read_byte(&LanguageString.Header.Size); + break; + case STRING_ID_Manufacturer: + Address = &ManufacturerString; + Size = pgm_read_byte(&ManufacturerString.Header.Size); + break; + case STRING_ID_Product: + Address = &ProductString; + Size = pgm_read_byte(&ProductString.Header.Size); + break; + } + break; + } + + *DescriptorAddress = Address; + return Size; +} + diff --git a/utils/AVR/avrlit/libavrlit/src/usb.h b/utils/AVR/avrlit/libavrlit/src/usb.h new file mode 100644 index 00000000000..f8301ad8482 --- /dev/null +++ b/utils/AVR/avrlit/libavrlit/src/usb.h @@ -0,0 +1,66 @@ +//===-- usb.h - avrlit USB CDC Device Header ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_AVR_LIT_USB_DESCRIPTORS_H +# define LLVM_AVR_LIT_USB_DESCRIPTORS_H + +# include +# include + +# define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 1) +# define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 2) +# define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 3) +# define CDC_NOTIFICATION_EPSIZE 8 +# define CDC_TXRX_EPSIZE 16 + +# if defined(__cplusplus) +extern "C" { +# endif + +extern USB_ClassInfo_CDC_Device_t cdc; + +typedef struct { + USB_Descriptor_Configuration_Header_t Config; + + // CDC Control Interface + USB_Descriptor_Interface_Association_t CDC_IAD; + USB_Descriptor_Interface_t CDC_CCI_Interface; + USB_CDC_Descriptor_FunctionalHeader_t CDC_Functional_Header; + USB_CDC_Descriptor_FunctionalACM_t CDC_Functional_ACM; + USB_CDC_Descriptor_FunctionalUnion_t CDC_Functional_Union; + USB_Descriptor_Endpoint_t CDC_ManagementEndpoint; + + // CDC Data Interface + USB_Descriptor_Interface_t CDC_DCI_Interface; + USB_Descriptor_Endpoint_t CDC_DataOutEndpoint; + USB_Descriptor_Endpoint_t CDC_DataInEndpoint; + +} USB_Descriptor_Configuration_t; + +enum InterfaceDescriptors_t { + INTERFACE_ID_CDC_CCI = 0, + INTERFACE_ID_CDC_DCI = 1, +}; + +enum StringDescriptors_t { + STRING_ID_Language = 0, + STRING_ID_Manufacturer = 1, + STRING_ID_Product = 2, +}; + +uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, + const uint8_t wIndex, + const void** const DescriptorAddress) + ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); + +# if defined(__cplusplus) +} // end of extern "C" +# endif + +#endif // LLVM_AVR_LIT_USB_DESCRIPTORS_H + diff --git a/utils/AVR/avrlit/llvm-avrlit.in b/utils/AVR/avrlit/llvm-avrlit.in new file mode 100755 index 00000000000..17ea295b667 --- /dev/null +++ b/utils/AVR/avrlit/llvm-avrlit.in @@ -0,0 +1,146 @@ +#!/usr/bin/env python +#===-- llvm-avrlit - AVR LLVM Integrated Tester - Host Side ---------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===-----------------------------------------------------------------------===# + +import sys, os, textwrap, subprocess, time, serial +from optparse import OptionParser, OptionGroup + +# Variables configured at build time. +llvm_root = "@LLVM_SOURCE_DIR@" +avrlit_dir = os.path.join(llvm_root, "utils", "AVR", "avrlit", "libavrlit") + +parser = OptionParser("usage: %prog [options] {ll-and-cpp-files}...") + +def writeMakefile(name, cpp, ll): + fields = { + 'avrlit_dir': avrlit_dir, + 'testname' : name, + 'prog' : os.path.basename(sys.argv[0]), + 'cflags' : '-std=c++11', + } + with open(os.path.join(name, "Makefile"), "w") as makefile: + makefile.write(textwrap.dedent(''' + # Generated by {prog}. Do not edit. + AVRLIT_BOARD ?= leonardo + AVRLITD = {avrlit_dir} + + include $(AVRLITD)/$(AVRLIT_BOARD)/board.mk + + {testname}.$(AVRLIT_BOARD).hex: + + clean: + \trm -f *.hex *.elf *.o + + .PHONY: clean + + {testname}.$(AVRLIT_BOARD).hex : {testname}.$(AVRLIT_BOARD).elf + \tavr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $< $@ + + ''')[1:].format(**fields)) + objects = [] + sources = cpp + ll + compile_cpp = ('avr-g++ -Os -mmcu=$(MCU) {cflags} -I$(AVRLITD) -c $< ' + '-ffunction-sections -o $@').format(**fields) + compile_ll = 'llc -mtriple=avr-atmel-none $< -filetype=obj -o $@' + for src in sources: + (file, ext) = os.path.splitext(os.path.basename(src)) + obj = file + '.$(AVRLIT_BOARD).o' + command = compile_ll if ext == '.ll' else compile_cpp + makefile.write('{} : {}\n' + '\t{}\n\n'.format(obj, src, command)) + objects.append(obj) + fields['objects'] = ' '.join(objects) + fields['link'] = ('avr-g++ -Os -mmcu=$(MCU) {cflags} $^ -L$(AVRLITD)/$(AVRLIT_BOARD) ' + '-lavrlit -Wl,--gc-sections -Wl,--relax ' + '-o $@').format(**fields) + makefile.write('{testname}.$(AVRLIT_BOARD).elf: {objects}\n' + '\t{link}\n'.format(**fields)) + + +def buildTestExecutable(sources, board): + (name, ext) = os.path.splitext(os.path.basename(sources[0])) + ll_files = [] + cpp_files = [] + for src in sources: + if src.endswith('.ll'): + ll_files.append(src) + elif src.endswith('.cpp'): + cpp_files.append(src) + else: + print("Unhandled file ", src) + exit(1) + + if not os.path.exists(name): + os.mkdir(name) + + writeMakefile(name, cpp_files, ll_files) + subprocess.check_call(['make', '-C', name]) + + return os.path.join(name, '{}.{}.hex'.format(name, board)) + +#=============================================================================== + +def resetLeonardo(port): + serial.Serial(port, 1200).close() + while os.path.exists(port): + time.sleep(0.1) + while not os.path.exists(port): + time.sleep(0.1) + +def uploadLeonardo(executable, port): + resetLeonardo(port) + subprocess.check_call(['avrdude', '-patmega32u4', '-cavr109', + '-P', port, '-b57600', '-q', '-q', + '-D', '-Uflash:w:{}:i'.format(executable)], + stderr=subprocess.STDOUT) # doesn't work :-/ + +def runExecutable(executable, port, board): + uploadLeonardo(executable, port) + while os.path.exists(port): + time.sleep(0.1) + retries = 0 + while True: + try: + test = serial.Serial(port, 57600, timeout=1) + break + except OSError as e: + print(e) + time.sleep(0.1) + if ++retries == 50: + raise + + done = False + passed = True; + while not done: + line = test.readline().strip() + print(line) + if line.startswith(b'FAIL:'): + passed = False; + if line == '--': + done = True + test.close() + return passed; + +#=============================================================================== + +(opts, files) = parser.parse_args() +if not 'AVRLIT_PORT' in os.environ: + print('AVRLIT_PORT environment variable is not set') + exit(1) + +port = os.environ['AVRLIT_PORT'] +if not os.path.exists(port): + print('AVRLIT_PORT \'{}\' does not exist'.format(port)) + exit(1) + +board = os.environ['AVRLIT_BOARD'] if 'AVRLIT_BOARD' in os.environ else 'leonardo' +executable = buildTestExecutable(files, board) +exit(0 if runExecutable(executable, port, board) else 1) + +# vim: filetype=python