---------------------------------------------------------------------- Patch name: patch.uisp-mword-write Author: Bryce Denney Date: Fri Mar 1 18:35:46 EST 2002 Detailed description: Add ability to send multiword write commands 'M', if they are supported by the programmer or boot loader. This command is equivalent to doing a series of 'c' and 'C' commands. Because the speed of code download is almost certainly baudrate limited, it pays to reduce the overhead of sending 'c' or 'C' and waiting for a response. Programmers that understand multiword write also have a command 'n' that simply returns 'Y'. This is used to detect whether multiword write should be used or not. To combine a series of write commands into a multiword write, I create a queue called mword_buf. As bytes are parsed in the input file, WriteByte is called with one byte at a time. Inside WriteByte, if the byte is written to the next address in sequence, it is placed in the mword_buf instead of being sent directly to hardware. If the address is not sequential, crosses a page boundary, etc., then the buffer contents are flushed to the programmer immediately, and the new byte is placed in at the start of a clean buffer. Also, I added a Synchronize() function which is executed before any commands are sent to the programmer. Because the programmer code contains multi-byte commands, it is possible for the programmer to be left in a state where it is expecting bytes from a previous command. If this happens, uisp will fail. This problem becomes more likely when using multiword writes because it expects so many bytes at a time. The Synchronize function takes care of this problem in nearly every case. If the first programmer command, 'V', returns something unusual or nothing, it will now send a large number of escape characters to finish off any pending command in the programmer, and then throw away any bytes sitting in the receive queue. This is repeated until a reasonable response to the 'V' is found, or else it gives up after a few retries. This will clears up a an interrupted multiword write without any trouble. The only programmer that currently supports multiword write is AVR109 with my patch.mword-write patch applied. It will continue to work with standard programmers as well. Patch was created with: diff -u Instructions: To patch, go to the src directory of uisp. Type "patch < THIS_PATCH_FILE". ---------------------------------------------------------------------- Index: AvrAtmel.C =================================================================== RCS file: /usr/src/CVSROOT/uisp/src/AvrAtmel.C,v retrieving revision 1.1 diff -u -r1.1 AvrAtmel.C --- AvrAtmel.C 2002/01/17 03:00:02 1.1 +++ AvrAtmel.C 2002/03/01 22:35:19 @@ -192,6 +192,67 @@ else return 0; } +void TAvrAtmel::FlushMwordBuffer () +{ + if (mword_buf_index==0) return; + TByte buf[2 + 2*MWORD_BUFFER_MAX_WORDS]; + // currently this function cannot handle an odd number of bytes in the + // buffer. I could change the boot loader code, or send an individual + // 'C' command for the odd numbered one. + assert ((mword_buf_index & 1) == 0); +#if 1 + buf[0] = 'M'; + buf[1] = mword_buf_index/2; + for (int i=0; i= '0' && (c) <= '9')) +void TAvrAtmel::Synchronize () +{ + bool in_sync = false; + int sync_retries = 5; + TByte sw_version [3]; + Info(3, "Begin synchronize\n"); + do { + try { + FlushRx (200); + sw_version[0] = 0x1b; + sw_version[1] = 0x1b; + sw_version[2] = 'V'; + Send(sw_version, 3, 2); + // if it returned a number 0-9, then assume we're in sync. + // accept either characters '0'-'9' or raw 0-9 + if (IS_DIGIT_OR_0_9(sw_version[0]) && IS_DIGIT_OR_0_9(sw_version[1])) + in_sync = true; + } catch (Error_Device ed) { + // assume that it failed because the programmer is waiting for bytes + // to complete some old command. To fix this problem, just send + // a bunch of escapes (0x1b) and retry. + TByte junk[256]; + for (int i=0; i0); + if (!in_sync) throw new Error_Device ("TAvrAtmel::Synchronize failed"); + Info(3, "Synchronize ok\n"); +} + void TAvrAtmel::WriteByte(TAddr addr, TByte byte, bool flush_buffer){ CheckMemoryRange(addr); @@ -208,11 +269,31 @@ When current address is out of the page address flush page buffer and continue programming. */ + + // Figure out if this byte can be added to the multiword buffer, or if + // it must sent out as a single write. It can be added to the mword + // buffer with the following restrictions: + // 1. the first byte into the mword buffer must be a low byte, the + // second must be a high byte, etc. + // 2. all bytes sent to the mword buffer will be written consecutively. + // If there are any non-consecutive addresses, the buffer must be + // flushed before the next word can be added. + // 3. all bytes sent to the mword buffer will be written to the same + // memory page. If a page boundary is reached, the buffer must be + // flushed. + // 4. the mword buffer has a capacity of MWORD_BUFFER_MAX_WORDS words + if (page_size){ Info(4, "Loading data to address: %d (page_addr_fetched=%s)\n", addr, page_addr_fetched?"Yes":"No"); - if (page_addr_fetched && page_addr != (addr & ~(page_size - 1))){ + if (page_addr_fetched && page_addr != (addr & ~(page_size - 1))) { + // next byte is in a different page from the previous. + // flush the multiword buffer (condition #3). + if (enable_mword_write) { + Info (4, "Reached page boundary, flushing mword buffer\n"); + FlushMwordBuffer (); + } WriteProgramMemoryPage(); page_addr_fetched = false; } @@ -223,13 +304,46 @@ if (flush_buffer){WriteProgramMemoryPage();} } - TByte wrF [2] = { (addr&1)?'C':'c', byte }; + if (mword_buf_index == 2*MWORD_BUFFER_MAX_WORDS) { + if (enable_mword_write) { + Info (4, "Mword buffer full, flushing mword buffer\n"); + FlushMwordBuffer (); // condition #4 + } + } + + bool force_set_address = false; + if (addr != (mword_buf_target0 + mword_buf_index)) { + // this address is not the previous address + 1. Flush the + // mword buffer. (condition #2) + if (enable_mword_write) { + Info (4, "Address discontinuity, flushing mword buffer\n"); + FlushMwordBuffer (); + force_set_address = true; + } + } + if (force_set_address || apc_address!=(addr>>1) || apc_autoinc==false) { + Info (4, "Set address = %04x\n", addr); + SetAddress (addr>>1); + } - if (apc_address!=(addr>>1) || apc_autoinc==false) SetAddress (addr>>1); - if (wrF[0]=='C') apc_address++; + bool add_to_mword_buffer = enable_mword_write; + if (mword_buf_index == 0 && (addr&1)) + add_to_mword_buffer = false; // condition 1 + + if (add_to_mword_buffer) { + if (mword_buf_index==0) { + Info (4, "Setting mword_buf_target0 = %04x\n", addr); + mword_buf_target0 = addr; + } + mword_buf[mword_buf_index++] = byte; + Info (4, "Adding byte %02x to mword buffer, now index=%x\n", byte, mword_buf_index); + } else { + TByte wrF [2] = { (addr&1)?'C':'c', byte }; Send(wrF, 2, 1); CheckResponse(wrF[0]); } + if (addr&1) apc_address++; + } else if (segment==SEG_EEPROM){ SetAddress(addr); TByte writeEE [2] = { 'D', byte }; @@ -239,6 +353,7 @@ } void TAvrAtmel::FlushWriteBuffer(){ + FlushMwordBuffer (); if (page_addr_fetched){ WriteProgramMemoryPage(); } @@ -286,6 +401,9 @@ if (prg_part[j].name[0]==0){throw Error_Device("-dpart: Invalid name.");} } } + + /* check that we are in sync with the programmer */ + Synchronize (); /* check: software version and supported part codes */ TByte sw_version [2] = {'V', 0}; @@ -302,6 +420,19 @@ apc_autoinc=true; Info(2, "Address Auto Increment Optimization Enabled\n"); } + + // decide whether to use multiword write. + TByte query_mword [1] = {'n'}; + Send (query_mword, 1, 1); + if (query_mword[0] == 'Y') { + enable_mword_write = true; + Info(2, "Multiword Write Optimization Enabled\n"); + } else { + enable_mword_write = false; + } + + // initialize multiword write buffer to empty + mword_buf_index = 0; /* Retrieve supported codes */ TByte sup_codes[1] = {'t'}; Index: AvrAtmel.h =================================================================== RCS file: /usr/src/CVSROOT/uisp/src/AvrAtmel.h,v retrieving revision 1.1 diff -u -r1.1 AvrAtmel.h --- AvrAtmel.h 2002/01/17 03:00:02 1.1 +++ AvrAtmel.h 2002/03/01 22:30:20 @@ -29,9 +29,17 @@ bool apc_autoinc; /* Auto Increment Supported by AVR ISP SoftVer 2 */ private: + void Synchronize (); void EnterProgrammingMode(); void LeaveProgrammingMode(); void CheckResponse(TByte x); + + /* Multiword write buffer, only implemented on Bryce's AVR109 bootloader */ +#define MWORD_BUFFER_MAX_WORDS 64 + bool enable_mword_write; + TByte mword_buf[2 * MWORD_BUFFER_MAX_WORDS]; + TAddr mword_buf_index; // points to next word to be written in mword_buf. + TAddr mword_buf_target0; // points to address where the first word will go void EnableAvr(); void SetAddress(TAddr addr); void WriteProgramMemoryPage(); @@ -40,6 +48,7 @@ /* Read byte from active segment at address addr. */ TByte ReadByte(TAddr addr); + void FlushMwordBuffer (); /* Write byte to active segment at address addr */ void WriteByte(TAddr addr, TByte byte, bool flush_buffer=true); void FlushWriteBuffer(); Index: Serial.C =================================================================== RCS file: /usr/src/CVSROOT/uisp/src/Serial.C,v retrieving revision 1.3 diff -u -r1.3 Serial.C --- Serial.C 2002/01/18 21:37:41 1.3 +++ Serial.C 2002/03/01 22:37:10 @@ -30,6 +30,30 @@ return size; } +void TSerial::FlushRx (int millisec) +{ + // call Rx with a variable timeout, and keep calling it until it really times + // out. The idea is to clear out any junk produced by any previous command. + unsigned char buf[256]; + struct timeval time_out; + while (1) { + try { + // with each iter, keep resetting the timeout to the threshold. + // select() will reduce the timeout. + time_out.tv_sec = millisec/1000; + time_out.tv_usec = millisec%1000; + Rx (buf, 256, &time_out); + // if it returned with 256 chars instead of timing out, just keep + // reading. If the device is spewing constant serial data, this + // function will keep reading forever. + } catch (Error_Device ed) { + // this is actually the exit condition. No characters were read for + // MILLISEC milliseconds, so the rx queue has been flushed. + return; + } + } +} + int TSerial::Send(unsigned char* queue, int queue_size, int rec_queue_size){ Tx(queue, queue_size); struct timeval time_out; Index: Serial.h =================================================================== RCS file: /usr/src/CVSROOT/uisp/src/Serial.h,v retrieving revision 1.1 diff -u -r1.1 Serial.h --- Serial.h 2002/01/17 03:00:03 1.1 +++ Serial.h 2002/03/01 22:37:10 @@ -26,6 +26,7 @@ protected: int Tx(unsigned char* queue, int queue_size); int Rx(unsigned char* queue, int queue_size, timeval* timeout); + void FlushRx(int millisec); public: int Send(unsigned char* queue, int queue_size, int rec_queue_size=-1);