summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rwxr-xr-xcommandline/builds/Windows/micronucleus.exebin26126 -> 0 bytes
-rw-r--r--commandline/library/micronucleus_lib.c69
-rw-r--r--commandline/library/micronucleus_lib.h9
-rw-r--r--commandline/micronucleus.c33
-rw-r--r--firmware/Makefile24
-rw-r--r--firmware/bootloaderconfig.h10
-rw-r--r--firmware/crt1.S17
-rw-r--r--firmware/main.c355
-rw-r--r--firmware/osccalASM.S82
-rw-r--r--firmware/usbconfig.h12
-rw-r--r--firmware/usbdrv/asmcommon.inc55
-rw-r--r--firmware/usbdrv/usbdrv.c68
-rw-r--r--firmware/usbdrv/usbdrvasm165.inc32
14 files changed, 470 insertions, 298 deletions
diff --git a/.gitignore b/.gitignore
index 933e699..ec9d3aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
deleted file mode 100755
index bfdb56b..0000000
--- a/commandline/builds/Windows/micronucleus.exe
+++ /dev/null
Binary files differ
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(&current_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:
;----------------------------------------------------------------------------