diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rwxr-xr-x | commandline/builds/Windows/micronucleus.exe | bin | 26126 -> 0 bytes | |||
-rw-r--r-- | commandline/library/micronucleus_lib.c | 69 | ||||
-rw-r--r-- | commandline/library/micronucleus_lib.h | 9 | ||||
-rw-r--r-- | commandline/micronucleus.c | 33 | ||||
-rw-r--r-- | firmware/Makefile | 24 | ||||
-rw-r--r-- | firmware/bootloaderconfig.h | 10 | ||||
-rw-r--r-- | firmware/crt1.S | 17 | ||||
-rw-r--r-- | firmware/main.c | 355 | ||||
-rw-r--r-- | firmware/osccalASM.S | 82 | ||||
-rw-r--r-- | firmware/usbconfig.h | 12 | ||||
-rw-r--r-- | firmware/usbdrv/asmcommon.inc | 55 | ||||
-rw-r--r-- | firmware/usbdrv/usbdrv.c | 68 | ||||
-rw-r--r-- | firmware/usbdrv/usbdrvasm165.inc | 32 |
14 files changed, 470 insertions, 298 deletions
@@ -1,3 +1,5 @@ +*.o +*.tmp firmware/*.bin commandline/littleWire_util.o commandline/micronucleus diff --git a/commandline/builds/Windows/micronucleus.exe b/commandline/builds/Windows/micronucleus.exe Binary files differdeleted file mode 100755 index bfdb56b..0000000 --- a/commandline/builds/Windows/micronucleus.exe +++ /dev/null diff --git a/commandline/library/micronucleus_lib.c b/commandline/library/micronucleus_lib.c index 6e574f5..76594d8 100644 --- a/commandline/library/micronucleus_lib.c +++ b/commandline/library/micronucleus_lib.c @@ -28,7 +28,7 @@ #include "micronucleus_lib.h" #include "littleWire_util.h" -micronucleus* micronucleus_connect() { +micronucleus* micronucleus_connect(int fast_mode) { micronucleus *nucleus = NULL; struct usb_bus *busses; @@ -62,15 +62,22 @@ micronucleus* micronucleus_connect() { // get nucleus info unsigned char buffer[4]; - int res = usb_control_msg(nucleus->device, 0xC0, 0, 0, 0, (char *)buffer, 4, MICRONUCLEUS_USB_TIMEOUT); + int res = usb_control_msg(nucleus->device, USB_ENDPOINT_IN| USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, 0, (char *)buffer, 4, MICRONUCLEUS_USB_TIMEOUT); assert(res >= 4); nucleus->flash_size = (buffer[0]<<8) + buffer[1]; nucleus->page_size = buffer[2]; nucleus->pages = (nucleus->flash_size / nucleus->page_size); if (nucleus->pages * nucleus->page_size < nucleus->flash_size) nucleus->pages += 1; - nucleus->write_sleep = buffer[3]; - nucleus->erase_sleep = nucleus->write_sleep * nucleus->pages; + + if ((nucleus->version.major>=2)&&(!fast_mode)) { + // firmware v2 reports more aggressive write times. Add 2ms if fast mode is not used. + nucleus->write_sleep = buffer[3]+2; + nucleus->erase_sleep = nucleus->write_sleep * nucleus->pages; + } else { + nucleus->write_sleep = buffer[3]; + nucleus->erase_sleep = nucleus->write_sleep * nucleus->pages; + } } } } @@ -80,7 +87,7 @@ micronucleus* micronucleus_connect() { int micronucleus_eraseFlash(micronucleus* deviceHandle, micronucleus_callback progress) { int res; - res = usb_control_msg(deviceHandle->device, 0xC0, 2, 0, 0, NULL, 0, MICRONUCLEUS_USB_TIMEOUT); + res = usb_control_msg(deviceHandle->device, USB_ENDPOINT_OUT| USB_TYPE_VENDOR | USB_RECIP_DEVICE, 2, 0, 0, NULL, 0, MICRONUCLEUS_USB_TIMEOUT); // give microcontroller enough time to erase all writable pages and come back online float i = 0; @@ -122,6 +129,7 @@ int micronucleus_writeFlash(micronucleus* deviceHandle, unsigned int program_siz unsigned int page_address; // address within this page when copying buffer unsigned int res; unsigned int pagecontainsdata; + unsigned int userReset; for (address = 0; address < deviceHandle->flash_size; address += deviceHandle->page_size) { // work around a bug in older bootloader versions @@ -142,12 +150,59 @@ int micronucleus_writeFlash(micronucleus* deviceHandle, unsigned int program_siz } } + // Reset vector patching is done in the host tool in micronucleus >=2 + if (deviceHandle->version.major >=2) + { + if ( address == 0 ) + // save user reset vector (bootloader will patch with its vector) + userReset = page_buffer [1] * 0x100 + page_buffer [0]; + + if ( address >= deviceHandle->flash_size - deviceHandle->page_size ) + { + // move user reset vector to end of last page + // The reset vector is always the last vector in the tinyvectortable + unsigned user_reset_addr = (deviceHandle->pages*deviceHandle->page_size)-2; + unsigned data = (userReset + 0x1000 - user_reset_addr/2) & ~0x1000; + + page_buffer [user_reset_addr - address + 0] = data >> 0 & 0xff; + page_buffer [user_reset_addr - address + 1] = data >> 8 & 0xff; + } + } + + // always write last page so bootloader can insert the tiny vector table if ( address >= deviceHandle->flash_size - deviceHandle->page_size ) pagecontainsdata = 1; // ask microcontroller to write this page's data if (pagecontainsdata) { + + if (deviceHandle->version.major == 1) { + // Firmware rev.1 transfers a page as a single block + // ask microcontroller to write this page's data + res = usb_control_msg(deviceHandle->device, + USB_ENDPOINT_OUT| USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, + page_length, address, + page_buffer, page_length, + MICRONUCLEUS_USB_TIMEOUT); + } else if (deviceHandle->version.major >= 2) { + // Firmware rev.2 uses individual set up packets to transfer data + res = usb_control_msg(deviceHandle->device, USB_ENDPOINT_OUT| USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1, page_length, address, NULL, 0, MICRONUCLEUS_USB_TIMEOUT); + if (res) return -1; + int i; + + for (i=0; i< page_length; i+=4) + { + int w1,w2; + w1=(page_buffer[i+1]<<8)+(page_buffer[i+0]<<0); + w2=(page_buffer[i+3]<<8)+(page_buffer[i+2]<<0); + + res = usb_control_msg(deviceHandle->device, USB_ENDPOINT_OUT| USB_TYPE_VENDOR | USB_RECIP_DEVICE, 3, w1, w2, NULL, 0, MICRONUCLEUS_USB_TIMEOUT); + if (res) return -1; + } + } + /* res = usb_control_msg(deviceHandle->device, USB_ENDPOINT_OUT| USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1, @@ -156,7 +211,7 @@ int micronucleus_writeFlash(micronucleus* deviceHandle, unsigned int program_siz MICRONUCLEUS_USB_TIMEOUT); if (res != page_length) return -1; - + */ // give microcontroller enough time to write this page and come back online delay(deviceHandle->write_sleep); } @@ -174,7 +229,7 @@ int micronucleus_writeFlash(micronucleus* deviceHandle, unsigned int program_siz int micronucleus_startApp(micronucleus* deviceHandle) { int res; - res = usb_control_msg(deviceHandle->device, 0xC0, 4, 0, 0, NULL, 0, MICRONUCLEUS_USB_TIMEOUT); + res = usb_control_msg(deviceHandle->device, USB_ENDPOINT_OUT| USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4, 0, 0, NULL, 0, MICRONUCLEUS_USB_TIMEOUT); if(res!=0) return -1; diff --git a/commandline/library/micronucleus_lib.h b/commandline/library/micronucleus_lib.h index 9118ace..d9f4999 100644 --- a/commandline/library/micronucleus_lib.h +++ b/commandline/library/micronucleus_lib.h @@ -44,11 +44,12 @@ #define MICRONUCLEUS_VENDOR_ID 0x16D0 #define MICRONUCLEUS_PRODUCT_ID 0x0753 #define MICRONUCLEUS_USB_TIMEOUT 0xFFFF -#define MICRONUCLEUS_MAX_MAJOR_VERSION 1 +#define MICRONUCLEUS_MAX_MAJOR_VERSION 2 + /*******************************************************************************/ /******************************************************************************** -* Declearations +* Declarations ********************************************************************************/ //typedef usb_dev_handle micronucleus; // representing version number of micronucleus device @@ -57,6 +58,8 @@ typedef struct _micronucleus_version { unsigned char minor; } micronucleus_version; +#define MICRONUCLEUS_COMMANDLINE_VERSION "Commandline tool version: 2.0a2" + // handle representing one micronucleus device typedef struct _micronucleus { usb_dev_handle *device; @@ -77,7 +80,7 @@ typedef void (*micronucleus_callback)(float progress); * Try to connect to the device * Returns: device handle for success, NULL for fail ********************************************************************************/ -micronucleus* micronucleus_connect(); +micronucleus* micronucleus_connect(int fast_mode); /*******************************************************************************/ /******************************************************************************** diff --git a/commandline/micronucleus.c b/commandline/micronucleus.c index b7d49ab..4ef829a 100644 --- a/commandline/micronucleus.c +++ b/commandline/micronucleus.c @@ -54,6 +54,7 @@ static char* progress_friendly_name; // name of progress section static int dump_progress = 0; // output computer friendly progress info static int use_ansi = 0; // output ansi control character stuff static int erase_only = 0; // only erase, dont't write file +static int fast_mode = 0; // normal mode adds 2ms to page writing times and waits longer for connect. static int timeout = 0; /*****************************************************************************/ @@ -69,11 +70,12 @@ int main(int argc, char **argv) { int run = 0; int file_type = FILE_TYPE_INTEL_HEX; int arg_pointer = 1; - char* usage = "usage: micronucleus [--run] [--dump-progress] [--type intel-hex|raw] [--no-ansi] [--timeout integer] [--erase-only] filename"; + char* usage = "usage: micronucleus [--run] [--dump-progress] [--fast-mode] [--type intel-hex|raw] [--no-ansi] [--timeout integer] [--erase-only] filename"; progress_step = 0; progress_total_steps = 5; // steps: waiting, connecting, parsing, erasing, writing, (running)? dump_progress = 0; erase_only = 0; + fast_mode=0; timeout = 0; // no timeout by default //#if defined(WIN) // use_ansi = 0; @@ -104,6 +106,8 @@ int main(int argc, char **argv) { puts(" for driving GUIs"); puts(" --erase-only: Erase the device without programming. Fills the"); puts(" program memory with 0xFFFF. Any files are ignored."); + puts(" --fast-mode: Speed up the timing of micronucleus. Do not use if"); + puts(" you encounter USB errors. "); puts(" --run: Ask bootloader to run the program when finished"); puts(" uploading provided program"); //#ifndef WIN @@ -112,11 +116,15 @@ int main(int argc, char **argv) { puts(" --timeout [integer]: Timeout after waiting specified number of seconds"); puts(" filename: Path to intel hex or raw data file to upload,"); puts(" or \"-\" to read from stdin"); + puts(""); + puts(MICRONUCLEUS_COMMANDLINE_VERSION); return EXIT_SUCCESS; } else if (strcmp(argv[arg_pointer], "--dump-progress") == 0) { dump_progress = 1; } else if (strcmp(argv[arg_pointer], "--no-ansi") == 0) { use_ansi = 0; + } else if (strcmp(argv[arg_pointer], "--fast-mode") == 0) { + fast_mode = 1; } else if (strcmp(argv[arg_pointer], "--erase-only") == 0) { erase_only = 1; progress_total_steps -= 1; @@ -149,7 +157,7 @@ int main(int argc, char **argv) { while (my_device == NULL) { delay(100); - my_device = micronucleus_connect(); + my_device = micronucleus_connect(fast_mode); time(¤t_time); if (timeout && start_time + timeout < current_time) { @@ -164,17 +172,18 @@ int main(int argc, char **argv) { printf("> Device is found!\n"); - // wait for CONNECT_WAIT milliseconds with progress output - float wait = 0.0f; - setProgressData("connecting", 2); - while (wait < CONNECT_WAIT) { - printProgress((wait / ((float) CONNECT_WAIT)) * 0.9f); - wait += 50.0f; - delay(50); + if (!fast_mode) { + // wait for CONNECT_WAIT milliseconds with progress output + float wait = 0.0f; + setProgressData("connecting", 2); + while (wait < CONNECT_WAIT) { + printProgress((wait / ((float) CONNECT_WAIT)) * 0.9f); + wait += 50.0f; + delay(50); + } } - - //my_device = micronucleus_connect(); printProgress(1.0); + //my_device = micronucleus_connect(); // if (my_device->page_size == 64) { // printf("> Device looks like ATtiny85!\n"); @@ -239,7 +248,7 @@ int main(int argc, char **argv) { int deciseconds_till_reconnect_notice = 50; // notice after 5 seconds while (my_device == NULL) { delay(100); - my_device = micronucleus_connect(); + my_device = micronucleus_connect(fast_mode); deciseconds_till_reconnect_notice -= 1; if (deciseconds_till_reconnect_notice == 0) { diff --git a/firmware/Makefile b/firmware/Makefile index 941310c..996b08c 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -24,7 +24,7 @@ LOCKOPT = -U lock:w:0x2f:m # - for the size of your device (8kb = 1024 * 8 = 8192) subtract above value 2124... = 6068 # - How many pages in is that? 6068 / 64 (tiny85 page size in bytes) = 94.8125 # - round that down to 94 - our new bootloader address is 94 * 64 = 6016, in hex = 1780 -BOOTLOADER_ADDRESS = 18C0 +BOOTLOADER_ADDRESS = 17C0 PROGRAMMER = -c USBasp # PROGRAMMER contains AVRDUDE options to address your programmer @@ -150,7 +150,7 @@ DEFINES = -DBOOTLOADER_ADDRESS=0x$(BOOTLOADER_ADDRESS) #-DDEBUG_LEVEL=2 # Remove the -fno-* options when you use gcc 3, it does not understand them # CFLAGS = -g2 -nostartfiles -ffunction-sections -fdata-sections -fpack-struct -Wall -Os -fno-inline-small-functions -fno-move-loop-invariants -fno-tree-scev-cprop -I. -mmcu=$(DEVICE) -DF_CPU=$(F_CPU) $(DEFINES) -LDFLAGS = -Wl,--relax,--section-start=.text=$(BOOTLOADER_ADDRESS),-Map=main.map,--section-start=.zerotable=0 +LDFLAGS = -Wl,--relax,--section-start=.text=$(BOOTLOADER_ADDRESS),-Map=main.map OBJECTS = crt1.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o @@ -159,17 +159,17 @@ OBJECTS += osccalASM.o all: main.hex .c.o: - $(CC) $(CFLAGS) -c $< -o $@ -Wa,-ahls=$<.lst + @$(CC) $(CFLAGS) -c $< -o $@ -Wa,-ahls=$<.lst .S.o: - $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ + @$(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ # "-x assembler-with-cpp" should not be necessary since this is the default # file type for the .S (with capital S) extension. However, upper case # characters are not always preserved on Windows. To ensure WinAVR # compatibility define the file type manually. .c.s: - $(CC) $(CFLAGS) -S $< -o $@ + @$(CC) $(CFLAGS) -S $< -o $@ flash: all $(AVRDUDE) -U flash:w:main.hex:i -B 10 @@ -190,22 +190,22 @@ read_fuses: $(UISP) --rd_fuses clean: - rm -f main.hex main.bin main.c.lst main.map *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s *.lss + @rm -f main.hex main.bin main.c.lst main.map *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s main.lss # file targets: main.bin: $(OBJECTS) - $(CC) $(CFLAGS) -o main.bin $(OBJECTS) $(LDFLAGS) + @$(CC) $(CFLAGS) -o main.bin $(OBJECTS) $(LDFLAGS) main.hex: main.bin - rm -f main.hex main.eep.hex - avr-objcopy -j .text -j .zerotable -j .data -O ihex main.bin main.hex - avr-size main.hex + @rm -f main.hex main.eep.hex + @avr-objcopy -j .text -j .data -O ihex main.bin main.hex + @avr-size main.bin disasm: main.bin - avr-objdump -d -S main.bin >main.lss + @avr-objdump -d -S main.bin >main.lss cpp: - $(CC) $(CFLAGS) -E main.c + @$(CC) $(CFLAGS) -E main.c # Special rules for generating hex files for various devices and clock speeds ALLHEXFILES = hexfiles/mega8_12mhz.hex hexfiles/mega8_15mhz.hex hexfiles/mega8_16mhz.hex \ diff --git a/firmware/bootloaderconfig.h b/firmware/bootloaderconfig.h index a93e6ff..80922ca 100644 --- a/firmware/bootloaderconfig.h +++ b/firmware/bootloaderconfig.h @@ -140,16 +140,14 @@ these macros are defined, the boot loader uses them. // Microcontroller vectortable entries in the flash #define RESET_VECTOR_OFFSET 0 -#define USBPLUS_VECTOR_OFFSET 2 // number of bytes before the boot loader vectors to store the tiny application vector table -#define TINYVECTOR_RESET_OFFSET 4 -#define TINYVECTOR_USBPLUS_OFFSET 2 -#define TINYVECTOR_OSCCAL_OFFSET 6 +#define TINYVECTOR_RESET_OFFSET 2 +#define TINYVECTOR_OSCCAL_OFFSET 4 /* ------------------------------------------------------------------------ */ // postscript are the few bytes at the end of programmable memory which store tinyVectors -#define POSTSCRIPT_SIZE 6 +#define POSTSCRIPT_SIZE 4 #define PROGMEM_SIZE (BOOTLOADER_ADDRESS - POSTSCRIPT_SIZE) /* max size of user program */ /* ------------------------------------------------------------------------- */ @@ -257,7 +255,7 @@ these macros are defined, the boot loader uses them. * comes with its own OSCCAL calibration or an external clock source is used. */ - #define OSCCAL_RESTORE 0 + #define OSCCAL_RESTORE 1 #define OSCCAL_16_5MHz 1 /* diff --git a/firmware/crt1.S b/firmware/crt1.S index 29693ac..be5aef4 100644 --- a/firmware/crt1.S +++ b/firmware/crt1.S @@ -56,12 +56,13 @@ .global __vectors .global __bad_interrupt .func __vectors + __bad_interrupt: __vectors: XJMP __init - vector __vector_1 - vector __vector_2 - vector __vector_3 +; vector __vector_1 +; vector __vector_2 +; vector __vector_3 .endfunc /* Handle unexpected interrupts (enabled and no handler), which @@ -95,13 +96,9 @@ __init: out 0x3d, r28 out 0x3e, r29 .section .init9,"ax",@progbits +; rcall main +; XJMP __vectors-2 XJMP main + ; .endfunc - - .section .zerotable,"ax",@progbits -zerovectors: - XJMP __init - XJMP __vector_1 - XJMP __vector_2 - XJMP __vector_3 diff --git a/firmware/main.c b/firmware/main.c index 16cca65..be31176 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -1,10 +1,9 @@ /* - * Project: Micronucleus - v1.11 - * - * Original author (c) 2012 Jenna Fox + * Project: Micronucleus - v2.0 * - * Optimizations v1.10/v1.11 (c) 2013 Tim Bo"scke - cpldcpu@gmail.com - * v1.11 (c) 2013 Shay Green + * Micronucleus V2.0 (c) 2014 Tim Bo"scke - cpldcpu@gmail.com + * (c) 2014 Shay Green + * Original Micronucleus (c) 2012 Jenna Fox * * Based on USBaspLoader-tiny85 (c) 2012 Louis Beaudoin * Based on USBaspLoader (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH @@ -12,13 +11,8 @@ * License: GNU GPL v2 (see License.txt) */ -#define MICRONUCLEUS_VERSION_MAJOR 1 -#define MICRONUCLEUS_VERSION_MINOR 11 -// how many milliseconds should host wait till it sends another erase or write? -// needs to be above 4.5 (and a whole integer) as avr freezes for 4.5ms -#define MICRONUCLEUS_WRITE_SLEEP 8 -// Use the old delay routines without NOP padding. This saves memory. -#define __DELAY_BACKWARD_COMPATIBLE__ +#define MICRONUCLEUS_VERSION_MAJOR 2 +#define MICRONUCLEUS_VERSION_MINOR 0 #include <avr/io.h> #include <avr/pgmspace.h> @@ -27,11 +21,12 @@ #include <util/delay.h> #include "bootloaderconfig.h" - - - #include "usbdrv/usbdrv.c" +// how many milliseconds should host wait till it sends another erase or write? +// needs to be above 4.5 (and a whole integer) as avr freezes for 4.5ms +#define MICRONUCLEUS_WRITE_SLEEP 5 + // verify the bootloader address aligns with page size #if BOOTLOADER_ADDRESS % SPM_PAGESIZE != 0 #error "BOOTLOADER_ADDRESS in makefile must be a multiple of chip's pagesize" @@ -41,39 +36,72 @@ #error "Micronucleus only supports pagesizes up to 256 bytes" #endif -// command system schedules functions to run in the main loop -register uint8_t command asm("r3"); // bind command to r3 -register uint16_union_t currentAddress asm("r4"); // r4/r5 current progmem address, used for erasing and writing -register uint16_union_t idlePolls asm("r6"); // r6/r7 idlecounter +// Device configuration reply +// Length: 4 bytes +// Byte 0: User program memory size, high byte +// Byte 1: User program memory size, low byte +// Byte 2: Flash Pagesize in bytes +// Byte 3: Page write timing in ms + +PROGMEM const uint8_t configurationReply[4] = { + (((uint16_t)PROGMEM_SIZE) >> 8) & 0xff, + ((uint16_t)PROGMEM_SIZE) & 0xff, + SPM_PAGESIZE, + MICRONUCLEUS_WRITE_SLEEP +}; #if OSCCAL_RESTORE register uint8_t osccal_default asm("r2"); #endif -static uint16_t vectorTemp[2]; // remember data to create tinyVector table before BOOTLOADER_ADDRESS +register uint16_union_t currentAddress asm("r4"); // r4/r5 current progmem address, used for erasing and writing +register uint16_union_t idlePolls asm("r6"); // r6/r7 idlecounter +// command system schedules functions to run in the main loop enum { cmd_local_nop=0, // also: get device info cmd_device_info=0, cmd_transfer_page=1, cmd_erase_application=2, + cmd_write_data=3, cmd_exit=4, - cmd_write_page=5, + cmd_write_page=64, // internal commands start at 64 }; +register uint8_t command asm("r3"); // bind command to r3 // Definition of sei and cli without memory barrier keyword to prevent reloading of memory variables #define sei() asm volatile("sei") #define cli() asm volatile("cli") #define nop() asm volatile("nop") +// Use the old delay routines without NOP padding. This saves memory. +#define __DELAY_BACKWARD_COMPATIBLE__ + /* ------------------------------------------------------------------------ */ static inline void eraseApplication(void); static void writeFlashPage(void); static void writeWordToPageBuffer(uint16_t data); static uint8_t usbFunctionSetup(uint8_t data[8]); -static uint8_t usbFunctionWrite(uint8_t *data, uint8_t length); static inline void leaveBootloader(void); +// This function is never called, it is just here to suppress a compiler warning. +USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) { return 0; } + +// clear memory which stores data to be written by next writeFlashPage call +#define __boot_page_fill_clear() \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "out %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "r" ((uint8_t)(__BOOT_PAGE_FILL | (1 << CTPB))) \ + ); \ +})) +// : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + + // erase any existing application and write in jumps for usb interrupt and reset to bootloader // - Because flash can be erased once and programmed several times, we can write the bootloader // - vectors in now, and write in the application stuff around them later. @@ -83,139 +111,73 @@ static inline void eraseApplication(void) { // while the vectors don't matter for usb comms as interrupts are disabled during erase, it's important // to minimise the chance of leaving the device in a state where the bootloader wont run, if there's power failure // during upload - - uint8_t i; + uint16_t ptr = BOOTLOADER_ADDRESS; - cli(); while (ptr) { ptr -= SPM_PAGESIZE; boot_page_erase(ptr); } - - currentAddress.w = 0; - for (i=0; i<8; i++) writeWordToPageBuffer(0xFFFF); // Write first 8 words to fill in vectors. - writeFlashPage(); // enables interrupts + + // clear page buffer as a precaution before filling the buffer in case + // a previous write operation failed and there is still something in the buffer. + __boot_page_fill_clear(); + + // Write reset vector into first page. + currentAddress.w = 0; + writeWordToPageBuffer(0xffff); + command=cmd_write_page; } // simply write currently stored page in to already erased flash memory -static void writeFlashPage(void) { - cli(); - boot_page_write(currentAddress.w - 2); // will halt CPU, no waiting required - sei(); +static inline void writeFlashPage(void) { + if (currentAddress.w - 2 <BOOTLOADER_ADDRESS) + boot_page_write(currentAddress.w - 2); // will halt CPU, no waiting required } -// clear memory which stores data to be written by next writeFlashPage call -#define __boot_page_fill_clear() \ -(__extension__({ \ - __asm__ __volatile__ \ - ( \ - "sts %0, %1\n\t" \ - "spm\n\t" \ - : \ - : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ - "r" ((uint8_t)(__BOOT_PAGE_FILL | (1 << CTPB))) \ - ); \ -})) - -// write a word in to the page buffer, doing interrupt table modifications where they're required +// write a word into the page buffer, doing interrupt table modifications where they're required static void writeWordToPageBuffer(uint16_t data) { - uint8_t previous_sreg; - // first two interrupt vectors get replaced with a jump to the bootloader's vector table - // remember vectors or the tinyvector table - if (currentAddress.w == RESET_VECTOR_OFFSET * 2) { - vectorTemp[0] = data; - data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1; - } - - if (currentAddress.w == USBPLUS_VECTOR_OFFSET * 2) { - vectorTemp[1] = data; - data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1; - } - - // at end of page just before bootloader, write in tinyVector table - // see http://embedded-creations.com/projects/attiny85-usb-bootloader-overview/avr-jtag-programmer/ - // for info on how the tiny vector table works - if (currentAddress.w == BOOTLOADER_ADDRESS - TINYVECTOR_RESET_OFFSET) { - data = vectorTemp[0] + ((FLASHEND + 1) - BOOTLOADER_ADDRESS)/2 + 2 + RESET_VECTOR_OFFSET; - } else if (currentAddress.w == BOOTLOADER_ADDRESS - TINYVECTOR_USBPLUS_OFFSET) { - data = vectorTemp[1] + ((FLASHEND + 1) - BOOTLOADER_ADDRESS)/2 + 1 + USBPLUS_VECTOR_OFFSET; -#if (!OSCCAL_RESTORE) && OSCCAL_16_5MHz - } else if (currentAddress.w == BOOTLOADER_ADDRESS - TINYVECTOR_OSCCAL_OFFSET) { - data = OSCCAL; -#endif + // Patch the bootloader reset vector into the main vectortable to ensure + // the device can not be bricked. + // Saving user-reset-vector is done in the host tool, starting with + // firmware V2 + + if (currentAddress.w == RESET_VECTOR_OFFSET * 2) { + data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1; } - previous_sreg=SREG; - cli(); // ensure interrupts are disabled +#if (!OSCCAL_RESTORE) && OSCCAL_16_5MHz + if (currentAddress.w == BOOTLOADER_ADDRESS - TINYVECTOR_OSCCAL_OFFSET) { + data = OSCCAL; + } +#endif boot_page_fill(currentAddress.w, data); - - // increment progmem address by one word currentAddress.w += 2; - SREG=previous_sreg; } -// This function is never called, it is just here to suppress a compiler warning. -USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) { return 0; } - /* ------------------------------------------------------------------------ */ static uint8_t usbFunctionSetup(uint8_t data[8]) { usbRequest_t *rq = (void *)data; - - static uint8_t replyBuffer[4] = { - (((uint16_t)PROGMEM_SIZE) >> 8) & 0xff, - ((uint16_t)PROGMEM_SIZE) & 0xff, - SPM_PAGESIZE, - MICRONUCLEUS_WRITE_SLEEP - }; - + idlePolls.b[1]=0; // reset idle polls when we get usb traffic if (rq->bRequest == cmd_device_info) { // get device info - usbMsgPtr = replyBuffer; - return 4; - } else if (rq->bRequest == cmd_transfer_page) { // transfer page - // clear page buffer as a precaution before filling the buffer in case - // a previous write operation failed and there is still something in the buffer. - __boot_page_fill_clear(); - currentAddress.w = rq->wIndex.word; - return USB_NO_MSG; // hands off work to usbFunctionWrite + usbMsgPtr = (usbMsgPtr_t)configurationReply; + return sizeof(configurationReply); + } else if (rq->bRequest == cmd_transfer_page) { // initialize write page + currentAddress.w = rq->wIndex.word; + } else if (rq->bRequest == cmd_write_data) { // Write data + writeWordToPageBuffer(rq->wValue.word); + writeWordToPageBuffer(rq->wIndex.word); + if ((currentAddress.b[0] % SPM_PAGESIZE) == 0) + command=cmd_write_page; // ask runloop to write our page } else { // Handle cmd_erase_application and cmd_exit command=rq->bRequest; - return 0; } -} - -// read in a page over usb, and write it in to the flash write buffer -static uint8_t usbFunctionWrite(uint8_t *data, uint8_t length) { - do { - // make sure we don't write over the bootloader! - if (currentAddress.w >= BOOTLOADER_ADDRESS) break; - - writeWordToPageBuffer(*(uint16_t *) data); - data += 2; // advance data pointer - length -= 2; - } while(length); - - // if we have now reached another page boundary, we're done - uint8_t isLast = ((currentAddress.b[0] % SPM_PAGESIZE) == 0); - if (isLast) command=cmd_write_page; // ask runloop to write our page - - return isLast; // let V-USB know we're done with this request -} - -/* ------------------------------------------------------------------------ */ -void PushMagicWord (void) __attribute__ ((naked)) __attribute__ ((section (".init3"))); - -// put the word "B007" at the bottom of the stack (RAMEND - RAMEND-1) -void PushMagicWord (void) { - asm volatile("ldi r16, 0xB0"::); - asm volatile("push r16"::); - asm volatile("ldi r16, 0x07"::); - asm volatile("push r16"::); + return 0; } static void initHardware (void) @@ -231,31 +193,28 @@ static void initHardware (void) #endif usbDeviceDisconnect(); /* do this while interrupts are disabled */ - _delay_ms(500); + _delay_ms(300); usbDeviceConnect(); - usbInit(); // Initialize INT settings after reconnect - sei(); + usbInit(); // Initialize INT settings after reconnect } /* ------------------------------------------------------------------------ */ // reset system to a normal state and launch user program static void leaveBootloader(void) __attribute__((__noreturn__)); static inline void leaveBootloader(void) { - + bootLoaderExit(); - cli(); + + _delay_ms(10); // Bus needs to see a few more SOFs before it can be disconnected usbDeviceDisconnect(); /* Disconnect micronucleus */ USB_INTR_ENABLE = 0; USB_INTR_CFG = 0; /* also reset config bits */ - // clear magic word from bottom of stack before jumping to the app - *(uint8_t*)(RAMEND) = 0x00; // A single write is sufficient to invalidate magic word - #if OSCCAL_RESTORE OSCCAL=osccal_default; - nop(); // NOP to avoid CPU hickup during oscillator stabilization + nop(); // NOP to avoid CPU hickup during oscillator stabilization #elif OSCCAL_16_5MHz // adjust clock to previous calibration value, so user program always starts with same calibration // as when it was uploaded originally @@ -266,15 +225,18 @@ static inline void leaveBootloader(void) { } #endif - asm volatile ("rjmp __vectors - 4"); // jump to application reset vector at end of flash + asm volatile ("rjmp __vectors - 2"); // jump to application reset vector at end of flash - for (;;); // Make sure function does not return to help compiler optimize + for (;;); // Make sure function does not return to help compiler optimize } +void USB_INTR_VECTOR(void); int main(void) { - + uint8_t ackSent=0; bootLoaderInit(); + DDRB|=3; + if (bootLoaderStartCondition()||(pgm_read_byte(BOOTLOADER_ADDRESS - TINYVECTOR_RESET_OFFSET + 1)==0xff)) { initHardware(); @@ -287,34 +249,111 @@ int main(void) { } do { - _delay_us(100); - wdt_reset(); // Only necessary if WDT is fused on - - command=cmd_local_nop; - usbPoll(); - - idlePolls.w++; + // 15 clockcycles per loop. + // adjust fastctr for 1ms timeout - // Try to execute program if bootloader exit condition is met - if (AUTO_EXIT_MS&&(idlePolls.w==AUTO_EXIT_MS*10L)) command=cmd_exit; + uint16_t fastctr=(uint16_t)(F_CPU/(1000.0f*15.0f)); + uint8_t resetctr=20; + + do { + if ((USBIN & USBMASK) !=0) resetctr=20; + + if (!--resetctr) { // reset encountered + usbNewDeviceAddr = 0; // bits from the reset handling of usbpoll() + usbDeviceAddr = 0; + calibrateOscillatorASM(); + } + + if (USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) { + USB_INTR_VECTOR(); // clears INT_PENDING (See se0: in asmcommon.inc) + break; + } + + } while(--fastctr); + + PORTB|=_BV(PB1); + command=cmd_local_nop; - LED_MACRO( idlePolls.b[1] ); + { + // This is usbpoll() minus reset logic and double buffering + int8_t len; + len = usbRxLen - 3; + if(len >= 0){ + usbProcessRx(usbRxBuf + 1, len); // only single buffer due to in-order processing + usbRxLen = 0; /* mark rx buffer as available */ + } + if(usbTxLen & 0x10){ /* transmit system idle */ + if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ + usbBuildTxBlock(); + } + } + } + + idlePolls.w++; - // Wait for USB traffic to finish before a blocking event is executed - // All events will render the MCU unresponsive to USB traffic for a while. - if (command!=cmd_local_nop) _delay_ms(2); - - if (command==cmd_erase_application) - eraseApplication(); - else if (command==cmd_write_page) - writeFlashPage(); + // Try to execute program when bootloader times out + if (AUTO_EXIT_MS&&(idlePolls.w==AUTO_EXIT_MS)) { + if (pgm_read_byte(BOOTLOADER_ADDRESS - TINYVECTOR_RESET_OFFSET + 1)!=0xff) break; + } + + LED_MACRO( idlePolls.b[1] ); + + // Test whether another interrupt occured during the processing of USBpoll and commands. + // If yes, we missed a data packet on the bus. This is not a big issue, since + // USB seems to allow timeout of up the two packets. (On my machine an USB + // error is triggered after the third missed packet.) + // The most critical situation occurs when a PID IN packet is missed due to + // it's short length. Each packet + timeout takes around 45µs, meaning that + // usbpoll must take less than 90µs or resyncing is not possible. + // To avoid synchronizing of the interrupt routine, we must not call it while + // a packet is transmitted. Therefore we have to wait until the bus is idle again. + // + // Just waiting for EOP (SE0) or no activity for 6 bus cycles is not enough, + // as the host may have been sending a multi-packet transmission (eg. OUT or SETUP) + // In that case we may resynch within a transmission, causing errors. + // + // A safer way is to wait until the bus was idle for the time it takes to send + // an ACK packet by the client (10.5µs on D+) but not as long as bus + // time out (12µs) + // - /* main event loop runs as long as no problem is uploaded or existing program is not executed */ - } while((command!=cmd_exit)||(pgm_read_byte(BOOTLOADER_ADDRESS - TINYVECTOR_RESET_OFFSET + 1)==0xff)); + if (USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) // Usbpoll() collided with data packet + { + PORTB|=_BV(PB0); + uint8_t ctr; + + // loop takes 5 cycles + asm volatile( + " ldi %0,%1 \n\t" + "loop%=: sbic %2,%3 \n\t" + " ldi %0,%1 \n\t" + " subi %0,1 \n\t" + " brne loop%= \n\t" + : "=&d" (ctr) + : "M" ((uint8_t)(10.0f*(F_CPU/1.0e6f)/5.0f+0.5)), "I" (_SFR_IO_ADDR(USBIN)), "M" (USB_CFG_DPLUS_BIT) + ); + USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT; + PORTB&=~_BV(PB0); + } + PORTB&=~_BV(PB1); + + if (command == cmd_local_nop) continue; + + USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT; + while ( !(USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) ); + USB_INTR_VECTOR(); + + if (command==cmd_erase_application) + eraseApplication(); + // Attention: eraseApplication will set command=cmd_write_page! + if (command==cmd_write_page) + writeFlashPage(); + + } while(command!=cmd_exit); LED_EXIT(); } - + leaveBootloader(); } /* ------------------------------------------------------------------------ */ diff --git a/firmware/osccalASM.S b/firmware/osccalASM.S index 9a317f1..9c3b45a 100644 --- a/firmware/osccalASM.S +++ b/firmware/osccalASM.S @@ -1,6 +1,8 @@ -/* Name: osccalASM.S +/* Name: osccalASM.S v1.1 * Author: cpldcpu@gmail.com * Creation Date: 2013-11-3 + * Update : 2014-01-4 + * * Tabsize: 4 * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) */ @@ -11,28 +13,34 @@ * * * Benefits: - * - Codesize reduced by 90 bytes. + * - Codesize reduced by 90 bytes. * - Improved robustness due to removing timeout from frame length measurement and - * inserted NOP after OSCCAL writes. + * inserted NOP after OSCCAL writes. * * Changes: * - The new routine performs a combined binary and neighborhood search * in a single loop. * Note that the neighborhood search is necessary due to the quasi-monotonic * nature of OSCCAL. (See Atmel application note AVR054). - * - Inserted NOP after writes to OSCCAL to avoid CPU errors during oscillator + * - Inserted NOP after writes to OSCCAL to avoid CPU errors during oscillator * stabilization. * - Implemented new routine to measure frame time "usbMeasureFrameLengthDecreasing". - * This routine takes the target time as a parameter and returns the deviation. - * - usbMeasureFrameLengthDecreasing measures in multiples of 5 cycles and is thus - * slighly more accurate. - * - usbMeasureFrameLengthDecreasing does not support time out anymore. The original - * implementation returned zero in case of time out, which would have caused the old + * This routine takes the target time as a parameter and returns the deviation. + * - usbMeasureFrameLengthDecreasing measures in multiples of 5 cycles and is thus + * slighly more accurate. + * - usbMeasureFrameLengthDecreasing does not support time out anymore. The original + * implementation returned zero in case of time out, which would have caused the old * calibrateOscillator() implementation to increase OSSCAL to 255, effictively * overclocking and most likely crashing the CPU. The new implementation will enter - * an infinite loop when no USB activity is encountered. The user program should + * an infinite loop when no USB activity is encountered. The user program should * use the watchdog to escape from situations like this. - * + * Update 2014-01-4 + * - Added an initial sync-state, which will discard the first delay measurement. + * This allows to call this routine before or during the SE0 bus reset without + * corrupting OSCCAL. + * - Removed CLI/SEI to allow more flexibility. + * + * * This routine will work both on controllers with and without split OSCCAL range. * The first trial value is 128 which is the lowest value of the upper OSCCAL range * on Attiny85 and will effectively limit the search to the upper range, unless the @@ -90,26 +98,31 @@ .global calibrateOscillatorASM calibrateOscillatorASM: - cli - ldi opD, 255 + ldi opD, 255 - ldi try, 128 ; calibration start value - ldi stp, 64 ; initial step width - ldi i, 10 ; 10 iterations + ldi try, 128 ; calibration start value + ldi stp, 0 ; initial step width=0 for sync phase (first delay is discarded) + ldi i, 11 ; 11 iterations (1x sync, 7x binary search, 3x neighbourhood) usbCOloop: - out OSCCAL, try - nop + out OSCCAL, try + nop - ; Delay values = F_CPU * 999e-6 / 5 + 0.5 - + ; Delay values = F_CPU * 999e-6 / 5 + 0.5 + #if (F_CPU == 16500000) - ldi cnt16L, lo8(3297) - ldi cnt16H, hi8(3297) + ldi cnt16L, lo8(3297) + ldi cnt16H, hi8(3297) #elif (F_CPU == 12800000) - ldi cnt16L, lo8(2557) - ldi cnt16H, hi8(2557) + ldi cnt16L, lo8(2557) + ldi cnt16H, hi8(2557) +#elif (F_CPU == 12000000) + ldi cnt16L, lo8(2398) + ldi cnt16H, hi8(2398) +#elif (F_CPU == 16000000) + ldi cnt16L, lo8(3197) + ldi cnt16H, hi8(3197) #else #error "calibrateOscillatorASM: no delayvalues defined for this F_CPU setting" #endif @@ -133,22 +146,27 @@ usbCOWaitLoop: usbCOclocktoolow: add try, stp usbCOclocktoohigh: - lsr stp - brne usbCOnoneighborhoodsearch + lsr stp ; stp = 0 in first iteration (sync) + ; stp = 2^x (x=0..6) during binary search, + ; stp = 1 during neighbourhood search + + brne usbCObinarysearch + ldi stp, 64 ; stp=64 to initiate binary search. + ; If we are in neighbourhood search (c=1), it is changed to 1 below + brcc usbCObinarysearch cp opD, cnt16L brcs usbCOnoimprovement in opV, OSCCAL mov opD, cnt16L usbCOnoimprovement: - ldi stp, 1 -usbCOnoneighborhoodsearch: + ldi stp, 1 ; stp=1 to continue with neighbourhood search +usbCObinarysearch: subi i, 1 brne usbCOloop out OSCCAL, opV nop - sei - ret + ret #undef i #undef opV @@ -221,8 +239,6 @@ void calibrateOscillator(void) } OSCCAL = optimumValue; - asm volatile(" NOP"); - - sei(); // enable interrupts + asm volatile(" NOP"); } #endif
\ No newline at end of file diff --git a/firmware/usbconfig.h b/firmware/usbconfig.h index dc5cafc..65864df 100644 --- a/firmware/usbconfig.h +++ b/firmware/usbconfig.h @@ -77,7 +77,7 @@ * The value is in milliamperes. [It will be divided by two since USB * communicates power requirements in units of 2 mA.] */ -#define USB_CFG_IMPLEMENT_FN_WRITE 1 +#define USB_CFG_IMPLEMENT_FN_WRITE 0 /* Set this to 1 if you want usbFunctionWrite() to be called for control-out * transfers. Set it to 0 if you don't need it and want to save a couple of * bytes. @@ -112,10 +112,12 @@ /* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */ // Check CRC of all received data +/* #define USB_RX_USER_HOOK( data, len ) { \ if ( usbCrc16( data, len + 2 ) != 0x4FFE )\ return;\ } +*/ /* This macro is a hook if you want to do unconventional things. If it is * defined, it's inserted at the beginning of received message processing. * If you eat the received message and don't want default processing to @@ -163,20 +165,18 @@ return;\ * usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable * for each control- and out-endpoint to check for duplicate packets. */ -//#if USB_CFG_CLOCK_KHZ==16500 - -//#include "osccal.h" + #ifndef __ASSEMBLER__ void calibrateOscillatorASM(void); - +/* #if AUTO_EXIT_NO_USB_MS>0 extern uint16_union_t idlePolls; #define USB_RESET_HOOK(resetStarts) if(!resetStarts){ idlePolls.b[1]=0; calibrateOscillatorASM();} #else #define USB_RESET_HOOK(resetStarts) if(!resetStarts){ calibrateOscillatorASM();} #endif - +*/ #define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0 #endif diff --git a/firmware/usbdrv/asmcommon.inc b/firmware/usbdrv/asmcommon.inc index d2a4f7c..19fb3d3 100644 --- a/firmware/usbdrv/asmcommon.inc +++ b/firmware/usbdrv/asmcommon.inc @@ -82,9 +82,41 @@ se0: ; rjmp handleSetupOrOut ; fallthrough ;Setup and Out are followed by a data packet two bit times (16 cycles) after -;the end of SE0. The sync code allows up to 40 cycles delay from the start of -;the sync pattern until the first bit is sampled. That's a total of 56 cycles. +;the end of SE0. The sync code allows up to 40 cycles delay (5 bit times) from +;the start of the sync pattern until the first bit is sampled. That's a total of 56 cycles. + +; TB 2014-01-04 +; USB1.1 spec defines a minimum of two bit times and a maximum of 6.5 bit times +; between Setup/Out and Data. We have to make sure we also cover the upper end +; of the spec on a 16Mhz device. +; +; Bit times µs cycles @12Mhz cycles @16 Mhz +; minimum 2 1.33 16 21 +; maximum 6.5 4.33 52 69 +; meas. Win7 3 2.04 24 32 +; meas. Linux 5 3.5 40 53 +; +; Currently it is only checked at cycle 46..49 if another interrupt occured. This is +; too early for 16 Mhz and the interrupt will not catch the data packet if it is later +; than 4 bit times. +; +; fix: Introduce additional delay with timeout for the 16 und 16.5 Mhz version. +; The 12 and 12.8 Mhz versions are still fine without as the maximum delay is less than 46 cycles. +; +; The total time until the next packet may not exceed 2 (min) +5 (sync tolerance) bit times +; = 75 cycles @16Mhz to +; The minimum time to cover max. timeout is 6.5 bit times = 70 cycles @16 Mhz +; +; Additional delay = 70-46=24 cycles. -> going to 21 cycles to be safe. + handleSetupOrOut: ;[32] +; Delay, see above +#if (F_CPU >= 16000000) + ldi YL, 7 ; +USBdelay: + subi YL, 1 + brne USBdelay +#endif #if USB_CFG_IMPLEMENT_FN_WRITEOUT /* if we have data for endpoint != 0, set usbCurrentTok to address */ andi x3, 0xf ;[32] breq storeTokenAndReturn ;[33] @@ -94,13 +126,17 @@ storeTokenAndReturn: sts usbCurrentTok, token;[35] doReturn: POP_STANDARD ;[37] 12...16 cycles + USB_LOAD_PENDING(YL) ;[49] sbrc YL, USB_INTR_PENDING_BIT;[50] check whether data is already arriving rjmp waitForJ ;[51] save the pops and pushes -- a new interrupt is already pending sofError: POP_RETI ;macro call - reti - + + CBI PORTB,0 +; reti + ret + handleData: #if USB_CFG_CHECK_CRC CRC_CLEANUP_AND_CHECK ; jumps to ignorePacket if CRC error @@ -120,10 +156,13 @@ handleData: #endif sts usbRxLen, cnt ;[28] store received data, swap buffers sts usbRxToken, shift ;[30] - lds x2, usbInputBufOffset;[32] swap buffers - ldi cnt, USB_BUFSIZE ;[34] - sub cnt, x2 ;[35] - sts usbInputBufOffset, cnt;[36] buffers now swapped + +; Micronculeus v2 needs no double buffer due to in-order processing +; TB 2014-01-04 +; lds x2, usbInputBufOffset;[32] swap buffers +; ldi cnt, USB_BUFSIZE ;[34] +; sub cnt, x2 ;[35] +; sts usbInputBufOffset, cnt;[36] buffers now swapped rjmp sendAckAndReti ;[38] 40 + 17 = 57 until SOP handleIn: diff --git a/firmware/usbdrv/usbdrv.c b/firmware/usbdrv/usbdrv.c index d838935..50adf5a 100644 --- a/firmware/usbdrv/usbdrv.c +++ b/firmware/usbdrv/usbdrv.c @@ -3,10 +3,25 @@ * Author: Christian Starkjohann * Creation Date: 2004-12-29 * Tabsize: 4 + * + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) */ +/* This copy of usbdrv.c was optimized to reduce the memory footprint with micronucleus V2 + * + * Changes: + * a) Replies to USB SETUP IN Packets are now only possible from Flash + * * Commented out routines to copy from SRAM + * * remove msgflag variable and all handling involving it + */ +#define MNHACK_ONLY_FLASH_MSGPTR +/* b) Do not use preinitialized global variables to avoid having to initialize + * the data section. + */ +#define MNHACK_NO_DATASECTION + #include "usbdrv.h" #include "oddebug.h" @@ -27,7 +42,11 @@ uchar usbConfiguration; /* currently selected configuration. Administere volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */ uchar usbCurrentTok; /* last token received or endpoint number for last OUT token if != 0 */ uchar usbRxToken; /* token for data we received; or endpont number for last OUT */ -volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ +#ifdef MNHACK_NO_DATASECTION + volatile uchar usbTxLen; /* number of bytes to transmit with next IN token or handshake token */ +#else + volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ +#endif uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ #if USB_COUNT_SOF volatile uchar usbSofCount; /* incremented by assembler module every SOF */ @@ -44,8 +63,15 @@ uchar usbCurrentDataToken;/* when we check data toggling to ignore duplica /* USB status registers / not shared with asm code */ usbMsgPtr_t usbMsgPtr; /* data to transmit next -- ROM or RAM address */ -static usbMsgLen_t usbMsgLen = USB_NO_MSG; /* remaining number of bytes */ +#ifdef MNHACK_NO_DATASECTION + static usbMsgLen_t usbMsgLen; /* remaining number of bytes */ +#else + static usbMsgLen_t usbMsgLen = USB_NO_MSG; /* remaining number of bytes */ +#endif + +#ifndef MNHACK_ONLY_FLASH_MSGPTR static uchar usbMsgFlags; /* flag values see below */ +#endif #define USB_FLG_MSGPTR_IS_ROM (1<<6) #define USB_FLG_USE_USER_RW (1<<7) @@ -291,17 +317,18 @@ USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) * This may cause problems with undefined symbols if compiled without * optimizing! */ -#define GET_DESCRIPTOR(cfgProp, staticName) \ - if(cfgProp){ \ - if((cfgProp) & USB_PROP_IS_RAM) \ - flags = 0; \ - if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ - len = usbFunctionDescriptor(rq); \ - }else{ \ - len = USB_PROP_LENGTH(cfgProp); \ - usbMsgPtr = (usbMsgPtr_t)(staticName); \ - } \ - } + + #define GET_DESCRIPTOR(cfgProp, staticName) \ + if(cfgProp){ \ + if((cfgProp) & USB_PROP_IS_RAM) \ + flags = 0; \ + if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ + len = usbFunctionDescriptor(rq); \ + }else{ \ + len = USB_PROP_LENGTH(cfgProp); \ + usbMsgPtr = (usbMsgPtr_t)(staticName); \ + } \ + } /* usbDriverDescriptor() is similar to usbFunctionDescriptor(), but used * internally for all types of descriptors. @@ -348,7 +375,9 @@ uchar flags = USB_FLG_MSGPTR_IS_ROM; len = usbFunctionDescriptor(rq); } SWITCH_END +#ifndef MNHACK_ONLY_FLASH_MSGPTR usbMsgFlags = flags; +#endif return len; } @@ -441,7 +470,9 @@ usbRequest_t *rq = (void *)data; usbMsgLen_t replyLen; usbTxBuf[0] = USBPID_DATA0; /* initialize data toggling */ usbTxLen = USBPID_NAK; /* abort pending transmit */ +#ifndef MNHACK_ONLY_FLASH_MSGPTR usbMsgFlags = 0; +#endif uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; if(type != USBRQ_TYPE_STANDARD){ /* standard requests are handled by driver */ replyLen = usbFunctionSetup(data); @@ -499,18 +530,22 @@ static uchar usbDeviceRead(uchar *data, uchar len) { uchar i = len; usbMsgPtr_t r = usbMsgPtr; +#ifndef MNHACK_ONLY_FLASH_MSGPTR if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ +#endif do{ uchar c = USB_READ_FLASH(r); /* assign to char size variable to enforce byte ops */ *data++ = c; r++; }while(--i); - }else{ /* RAM data */ +#ifndef MNHACK_ONLY_FLASH_MSGPTR + }else{ // RAM data do{ *data++ = *((uchar *)r); r++; }while(--i); } +#endif usbMsgPtr = r; } } @@ -609,6 +644,11 @@ isNotReset: USB_PUBLIC void usbInit(void) { +#ifdef MNHACK_NO_DATASECTION + usbTxLen = USBPID_NAK; + usbMsgLen = USB_NO_MSG; +#endif + #if USB_INTR_CFG_SET != 0 USB_INTR_CFG |= USB_INTR_CFG_SET; #endif diff --git a/firmware/usbdrv/usbdrvasm165.inc b/firmware/usbdrv/usbdrvasm165.inc index 450d2fd..1433701 100644 --- a/firmware/usbdrv/usbdrvasm165.inc +++ b/firmware/usbdrv/usbdrvasm165.inc @@ -36,41 +36,15 @@ of CPU cycles, but even an exact number of cycles! ; Numbers in brackets are clocks counted from center of last sync bit ; when instruction starts -; the code enabled by TINY85MODE is inteded only for use with the tiny85-compatible USBaspLoader -; project. -; with TINY85MODE set, the beginning of the ISR checks for a magic word "B007" at the very bottom of the stack -; and jumps to the application's ISR if the magic word isn't present -; the max allowable interrupt latency is lower given this additional code before getting to the -; USB-specific portion of the ISR. USB_INTR_VECTOR: ;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt + SBI PORTB,0 + push YL ; push only what is necessary to sync with edge ASAP in YL, SREG ; push YL ; - -#ifdef TINY85MODE -; look for magic word "B007" at the bottom of the stack - lds YL, RAMEND - cpi YL, 0xB0 - brne cleanupAndJumpToApp - - lds YL, RAMEND-1 - cpi YL, 0x07 - breq cleanupBootloaderIntStack - -cleanupAndJumpToApp: -; magic word was not found, put registers back to where they were before this ISR ran, and jump to application ISR - pop YL - out SREG, YL - pop YL - - rjmp __vectors - TINYVECTOR_USBPLUS_OFFSET - -cleanupBootloaderIntStack: -; magic word was found, put registers - CLR YL ; [-19] ensure we meet below requirements for YL < 0x80 -#endif + ;---------------------------------------------------------------------------- ; Synchronize with sync pattern: ;---------------------------------------------------------------------------- |