// $Id$ /* * $Id$ * **************************************************************************** * * uisp - The Micro In-System Programmer for Atmel AVR microcontrollers. * Copyright (C) 1999, 2000, 2001, 2002, 2003 Uros Platise * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************** */ /* MotIntl.C Motorola and Intel Uploading/Downloading Routines Uros Platise (c) 1999 */ #include "config.h" #include #include #include #include #include "timeradd.h" #include "Global.h" #include "Error.h" #include "MotIntl.h" TByte TMotIntl::Htoi(const char* p){ unsigned char val = 0; if (*p>='0' && *p<='9') val += *p-'0'; else if (*p>='A' && *p<='F') val += *p-'A'+10; else if (*p>='a' && *p<='f') val += *p-'a'+10; else throw Error_Device("Bad file format."); val <<= 4; p++; if (*p>='0' && *p<='9') val += *p-'0'; else if (*p>='A' && *p<='F') val += *p-'A'+10; else if (*p>='a' && *p<='f') val += *p-'a'+10; else throw Error_Device("Bad file format."); cc_sum += val; return val; } void TMotIntl::InfoOperation(const char* prefix, const char* seg_name){ Info(1, "%s", prefix); if (!upload && !verify) { Info(1, "Downloading"); } if (upload){Info(1, "Uploading");} if (upload && verify){Info(1, "/");} if (verify){Info(1, "Verifying");} Info(1, ": %s\n", seg_name); } void TMotIntl::ReportStats(float elapsed, TAddr total_bytes) { float rate = (float)total_bytes / elapsed; Info(2, "\n(total %u bytes transferred in %.2f s (%.0f bytes/s)\n", total_bytes, elapsed, rate); if (upload) { unsigned int total_polled = device->GetPollCount(); if (total_polled) { float min_poll_time = device->GetMinPollTime() * 1000.0; float max_poll_time = device->GetMaxPollTime() * 1000.0; float avg_poll_time = device->GetTotPollTime() * (1000.0 / total_polled); Info(2, "Polling: count = %u, min/avg/max = %.2f/%.2f/%.2f ms\n", total_polled, min_poll_time, avg_poll_time, max_poll_time); } } } void TMotIntl::UploadMotorola(){ unsigned char srec_len, buf_len, srec_cc_sum; char seg_name[256]; /* data field length is a byte, so this is safe */ char* p; /* line buffer pointer */ TAddr addr; TAddr total_bytes_uploaded=0; TAddr hash_cnt=0; TByte byte; struct timeval t1, t2; device->ResetMinMax(); do{ /* convert to upper case */ buf_len = strlen(line_buf); for (int i=0;i MI_LINEBUF_SIZE) throw ("Bad Motorola file format."); /* Load address */ addr = Htoi(p); p+=2; addr <<= 8; addr += Htoi(p); p+=2; switch(line_buf[1]){ case '0':{ /* Load segment name */ int i; for (i=0;iSetSegment(seg_name)){InfoOperation("Auto-", seg_name);} else{InfoOperation("", device->TellActiveSegment());} /* Print first hash */ if (device->GetSegmentSize() >= 16) Info(2, "#"); hash_cnt=0; /* Set statistic variables */ total_bytes_uploaded = 0; gettimeofday(&t1, NULL); } break; /* 4 byte address */ case '3': addr <<= 8; addr += Htoi(p); p+=2; srec_len--; /* Note that we fall through. */ /* 3 byte address */ case '2': addr <<= 8; addr += Htoi(p); p+=2; srec_len--; /* Note that we fall through. */ /* 2 byte address */ case '1':{ /* Upload/Verify bytes */ total_bytes_uploaded += srec_len; while(srec_len-->0){ byte = Htoi(p); if (upload){device->WriteByte(addr, byte, false);} if (verify){ TByte rbyte = device->ReadByte(addr); if (rbyte != byte){ Info(0, "%s error at address 0x%x: file=0x%02x, mem=0x%02x\n", device->TellActiveSegment(), addr, (int) byte, (int) rbyte); } } p+=2; addr++; if (total_bytes_uploaded >= hash_cnt+hash_marker){ Info(2, "#"); hash_cnt+=hash_marker; } } } break; case '7': case '8': case '9':{ if (upload){device->FlushWriteBuffer();} gettimeofday(&t2, NULL); timersub(&t2, &t1, &t2); float elapsed = t2.tv_sec + t2.tv_usec*1e-6; ReportStats(elapsed, total_bytes_uploaded); /* verify check sum */ for (int i=0;iTellActiveSegment()); /* Print first hash */ if (device->GetSegmentSize() >= 16) Info(2, "#"); /* Set statistic variables */ total_bytes_uploaded = 0; gettimeofday(&t1, NULL); device->ResetMinMax(); do{ /* convert to upper case */ buf_len = strlen(line_buf); for (int i=0;i MI_LINEBUF_SIZE) throw ("Bad Intel file format."); /* Load address */ addr = Htoi(p); p+=2; addr <<= 8; addr += Htoi(p); p+=2; rec_type = Htoi(p); p+=2; /* read control byte: 00-data, 01-end, 02-seg_offset */ addr += seg_offset; /* this allows access to more than 64K */ switch(rec_type) { case 0x00:{ /* Upload/Verify bytes */ total_bytes_uploaded += ihex_len; while(ihex_len-->0){ byte = Htoi(p); if (upload){device->WriteByte(addr, byte, false);} if (verify){ TByte rbyte = device->ReadByte(addr); if (rbyte != byte){ Info(0, "%s error at address 0x%x: file=0x%02x, mem=0x%02x\n", device->TellActiveSegment(), addr, (int) byte, (int) rbyte); } } p+=2; addr++; if (total_bytes_uploaded >= hash_cnt+hash_marker){ Info(2, "#"); hash_cnt+=hash_marker; } } } break; case 0x01: /* end */ case 0x03: /* start address record */ case 0x05: /* start linear address record */ /* don't need to do anything, except calculate the checksum */ while (ihex_len-- > 0) { byte = Htoi(p); p += 2; } break; case 0x02:{ seg_offset = Htoi(p); p+=2; seg_offset <<=8; seg_offset += Htoi(p); p+=2; /* seg_offset is bits 4-19 of addr, so shift to that. */ seg_offset <<=4; } break; case 0x04: /* extended linear address record */ seg_offset = Htoi(p); p += 2; seg_offset <<= 8; seg_offset += Htoi(p); p += 2; seg_offset <<= 16; break; default: throw Error_Device("Bad Intel Hex record.\n"); } /* Read Check Sum and give a report */ ihex_cc_sum = Htoi(p); if (cc_sum != 0x0){ Info(2, "Intel check sum: %d uisp check sum: %d\n", (unsigned)ihex_cc_sum, (unsigned)cc_sum); throw Error_Device("Check sum error.\n"); } } while(fgets(line_buf, MI_LINEBUF_SIZE, fd)!=NULL); if (upload){device->FlushWriteBuffer();} /* Print transfer statistics */ gettimeofday(&t2, NULL); timersub(&t2, &t1, &t2); float elapsed = t2.tv_sec + t2.tv_usec*1e-6; ReportStats(elapsed, total_bytes_uploaded); } void TMotIntl::Read(const char* filename, bool _upload, bool _verify){ upload = _upload; verify = _verify; if ((fd=fopen(filename,"rt"))==NULL){ throw Error_C(filename); } /* Set-up Hash Marker */ const char* val = GetCmdParam("--hash"); if (val!=NULL){hash_marker = atoi(val);} /* auto-detect Motorola or Intel file format */ fgets(line_buf, MI_LINEBUF_SIZE, fd); if (strncasecmp(line_buf, "S0", 2)==0){UploadMotorola();} else if (line_buf[0]==':'){UploadIntel();} else {throw Error_Device("Unknown file format.");} fclose(fd); } void TMotIntl::SrecWrite(unsigned int type, const unsigned char *buf, unsigned int len) { unsigned i, sum; fprintf(fd, "S%01X%02X", type, len + 1); sum = len + 1; for (i = 0; i < len; i++) { sum += buf[i]; fprintf(fd, "%02X", (unsigned int) buf[i]); } fprintf(fd, "%02X\r\n", (unsigned int)(~sum & 0xFF)); } void TMotIntl::DownloadMotorola() { /* A short description of S-Records may be found at http://www.ndsu.nodak.edu/instruct/tareski/373f98/notes/srecord.htm */ TAddr addr, size; TAddr total_bytes_uploaded=0; TAddr hash_cnt=0; struct timeval t1, t2; unsigned char buf[40]; const char *seg; int s5count=0; seg = device->TellActiveSegment(); InfoOperation("", seg); /* Set statistic variables */ total_bytes_uploaded = 0; gettimeofday(&t1, NULL); device->ResetMinMax(); size = device->GetSegmentSize(); /* Print first hash (except for fuse bits) */ if (size >= 16) Info(2, "#"); buf[0] = 0; buf[1] = 0; strncpy((char *) buf+2, seg, 16); buf[18] = 0; SrecWrite(0, buf, 2 + strlen((const char *) buf + 2)); for (addr = 0; addr < size; addr += 16) { int i, len; len = size - addr; if (len > 16) len = 16; buf[0] = (addr >> 24) & 0xFF; buf[1] = (addr >> 16) & 0xFF; buf[2] = (addr >> 8) & 0xFF; buf[3] = addr & 0xFF; for (i = 0; i < len; i++) { TByte rbyte = device->ReadByte(addr + i); buf[4 + i] = rbyte; total_bytes_uploaded++; if (total_bytes_uploaded >= hash_cnt + hash_marker) { Info(2, "#"); hash_cnt += hash_marker; } } if (addr < 0x10000) SrecWrite(1, buf + 2, 2 + len); else if (addr < 0x1000000) SrecWrite(2, buf + 1, 3 + len); else SrecWrite(3, buf , 4 + len); s5count++; } /* number of S1/2/3 records written */ buf[0] = (s5count >> 8) & 0xFF; buf[1] = s5count & 0xFF; SrecWrite(5, buf, 2 ); /* starting address is 0 */ buf[0] = 0; buf[1] = 0; buf[2] = 0; buf[3] = 0; if (addr < 0x10000) SrecWrite(9, buf+2, 2 ); else if (addr < 0x1000000) SrecWrite(8, buf+1, 3); else SrecWrite(7, buf, 4); /* Print transfer statistics */ gettimeofday(&t2, NULL); timersub(&t2, &t1, &t2); float elapsed = t2.tv_sec + t2.tv_usec*1e-6; ReportStats(elapsed, total_bytes_uploaded); } void TMotIntl::Write(const char *filename) { if (filename) { fd = fopen(filename, "wb"); if (!fd) { throw Error_C(filename); } DownloadMotorola(); fclose(fd); } else { fd = stdout; DownloadMotorola(); } } TMotIntl::TMotIntl(): hash_marker(32){ }