From: travisutk Date: Thu, 18 Jun 2009 18:33:07 +0000 (+0000) Subject: MSP430 JTAG works. X-Git-Url: http://git.rot13.org/?p=goodfet;a=commitdiff_plain;h=a653ba1562dc444e4be791e22264a89a3c6b4a19;hp=8b09ba82bd3b82cfbe869408fc36e558550b9b69 MSP430 JTAG works. git-svn-id: https://svn.code.sf.net/p/goodfet/code/trunk@38 12e2690d-a6be-4b82-a7b7-67c4a43b65c8 --- diff --git a/client/GoodFET.py b/client/GoodFET.py new file mode 100755 index 0000000..387eeab --- /dev/null +++ b/client/GoodFET.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# GoodFET Client Library +# +# (C) 2009 Travis Goodspeed +# +# This code is ugly as sin, for bootstrapping the firmware only. +# Rewrite cleanly as soon as is convenient. + +import sys, time, string, cStringIO, struct +sys.path.append("/usr/lib/tinyos") +import serial + + +class GoodFET: + def __init__(self, *args, **kargs): + print "inited\n"; + self.data=[0]; + def timeout(self): + print "timout\n"; + def serInit(self, port): + """Open the serial port""" + self.serialport = serial.Serial( + port, + 9600, + parity = serial.PARITY_NONE + ) + #Drop DTR, which is !RST, low to begin the app. + self.serialport.setDTR(0); + self.serialport.flushInput() + self.serialport.flushOutput() + + #Read and handle the initial command. + time.sleep(1); + self.readcmd(); #Read the first command. + if(self.verb!=0x7F): + print "Verb is wrong. Incorrect firmware?"; + + + def writecmd(self, app, verb, count, data): + """Write a command and some data to the GoodFET.""" + self.serialport.write(chr(app)); + self.serialport.write(chr(verb)); + self.serialport.write(chr(count)); + #print "count=%02x, len(data)=%04x" % (count,len(data)); + if count!=0: + for d in data: + self.serialport.write(chr(d)); + self.readcmd(); #Uncomment this later, to ensure a response. + def readcmd(self): + """Read a reply from the GoodFET.""" + self.app=ord(self.serialport.read(1)); + self.verb=ord(self.serialport.read(1)); + self.count=ord(self.serialport.read(1)); + if self.count>0: + self.data=self.serialport.read(self.count); + #print "READ %02x %02x %02x " % (self.app, self.verb, self.count); + + #Monitor stuff + def peekbyte(self,address): + """Read a byte of memory from the monitor.""" + self.data=[address&0xff,address>>8]; + self.writecmd(0,0x02,2,self.data); + #self.readcmd(); + return ord(self.data[0]); + def peekword(self,address): + """Read a word of memory from the monitor.""" + return self.peekbyte(address)+(self.peekbyte(address+1)<<8); + def pokebyte(self,address,value): + """Set a byte of memory by the monitor.""" + self.data=[address&0xff,address>>8,value]; + self.writecmd(0,0x03,3,self.data); + return ord(self.data[0]); + + def monitortest(self): + """Self-test several functions through the monitor.""" + print "Performing monitor self-test."; + + if self.peekword(0x0c00)!=0x0c04: + print "ERROR Fetched wrong value from 0x0c04."; + self.pokebyte(0x0021,0); #Drop LED + if self.peekbyte(0x0021)!=0: + print "ERROR, P1OUT not cleared."; + self.pokebyte(0x0021,1); #Light LED + + print "Self-test complete."; + + def spisetup(self): + """Moved the FET into the SPI application.""" + print "Initializing SPI."; + self.writecmd(1,0x10,0,self.data); #SPI/SETUP + #self.readcmd(); + def spitrans8(self,byte): + """Read and write 8 bits by SPI.""" + self.data=[byte]; + self.writecmd(1,0,1,self.data); #SPI exchange + #self.readcmd(); + + if self.app!=1 or self.verb!=0: + print "Error in SPI transaction; app=%02x, verb=%02x" % (self.app, self.verb); + return ord(self.data[0]); + def MSP430setup(self): + """Move the FET into the MSP430 JTAG application.""" + print "Initializing MSP430."; + self.writecmd(0x11,0x10,0,self.data); + def MSP430peek(self,adr): + """Read the contents of memory at an address.""" + self.data=[adr&0xff, (adr&0xff00)>>8]; + self.writecmd(0x11,0x02,2,self.data); + return ord(self.data[0])+(ord(self.data[1])<<8); + def MSP430poke(self,adr,val): + """Read the contents of memory at an address.""" + self.data=[adr&0xff, (adr&0xff00)>>8, val&0xff, (val&0xff00)>>8]; + self.writecmd(0x11,0x03,4,self.data); + return;# ord(self.data[0])+(ord(self.data[1])<<8); + + def MSP430start(self): + """Start debugging.""" + self.writecmd(0x11,0x20,0,self.data); + def MSP430haltcpu(self): + """Halt the CPU.""" + self.writecmd(0x11,0xA0,0,self.data); + def MSP430releasecpu(self): + """Resume the CPU.""" + self.writecmd(0x11,0xA1,0,self.data); + + def MSP430shiftir8(self,ins): + """Shift the 8-bit Instruction Register.""" + data=[ins]; + self.writecmd(0x11,0x80,1,data); + return ord(self.data[0]); + def MSP430shiftdr16(self,dat): + """Shift the 16-bit Data Register.""" + data=[dat&0xFF,(dat&0xFF00)>>8]; + self.writecmd(0x11,0x81,2,data); + return ord(self.data[0])#+(ord(self.data[1])<<8); + def MSP430setinstrfetch(self): + """Set the instruction fetch mode.""" + self.writecmd(0x11,0xC1,0,self.data); + return self.data[0]; + def MSP430test(self): + """Test MSP430 JTAG. Requires that a chip be attached.""" + self.MSP430setup(); + self.MSP430start(); + self.MSP430haltcpu(); + + ident=self.MSP430peek(0x0ff0); + print "Target identifies as %04x." % ident; + if ident==0xffff: + print "Is anything connected?"; + print "Testing RAM."; + temp=self.MSP430peek(0x0200); + self.MSP430poke(0x0200,0xdead); + if(self.MSP430peek(0x0200)!=0xdead): + print "Poke of 0x0200 did not set to 0xDEAD properly."; + exit; + self.MSP430poke(0x0200,temp); #restore old value. + self.MSP430releasecpu(); + + def MSP430dumpbsl(self): + i=0xC00; + while i<0x1000: + print "%04x %04x" % (i, self.MSP430peek(i)); + i+=2; + diff --git a/client/goodfet b/client/goodfet deleted file mode 100755 index 78171b7..0000000 --- a/client/goodfet +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python -# GoodFET Client Application -# -# (C) 2009 Travis Goodspeed -# -# This code is ugly as sin, for bootstrapping the firmware only. -# Rewrite cleanly as soon as is convenient. - -import sys, time, string, cStringIO, struct -sys.path.append("/usr/lib/tinyos") -import serial - - -class Client: - def __init__(self, *args, **kargs): - print "inited\n"; - def timeout(self): - print "timout\n"; - def serInit(self, port): - """Open the serial port""" - self.serialport = serial.Serial( - port, - 9600, - parity = serial.PARITY_NONE - ) - #Drop DTR, which is !RST, low to begin the app. - self.serialport.setDTR(0); - self.serialport.flushInput() - self.serialport.flushOutput() - - #Read and handle the initial command. - time.sleep(1); - client.readcmd(); #Read the first command. - if(self.verb!=0x7F): - print "Verb is wrong. Incorrect firmware?"; - - - def writecmd(self, app, verb, count, data): - """Write a command and some data to the GoodFET.""" - self.serialport.write(chr(app)); - self.serialport.write(chr(verb)); - self.serialport.write(chr(count)); - #print "count=%02x, len(data)=%04x" % (count,len(data)); - if count!=0: - for d in data: - self.serialport.write(chr(d)); - self.readcmd(); #Uncomment this later, to ensure a response. - def readcmd(self): - """Read a reply from the GoodFET.""" - self.app=ord(self.serialport.read(1)); - self.verb=ord(self.serialport.read(1)); - self.count=ord(self.serialport.read(1)); - if self.count>0: - self.data=self.serialport.read(self.count); - #print "%02x %02x %02x" % (self.app, self.verb, self.count); - - #Monitor stuff - def peekbyte(self,address): - """Read a byte of memory from the monitor.""" - self.data=[address&0xff,address>>8]; - self.writecmd(0,0x02,2,self.data); - #self.readcmd(); - return ord(self.data[0]); - def peekword(self,address): - """Read a word of memory from the monitor.""" - return self.peekbyte(address)+(self.peekbyte(address+1)<<8); - def pokebyte(self,address,value): - """Set a byte of memory by the monitor.""" - self.data=[address&0xff,address>>8,value]; - self.writecmd(0,0x03,3,self.data); - return ord(self.data[0]); - - def monitortest(self): - """Self-test several functions through the monitor.""" - print "Performing self-test."; - - if self.peekword(0x0c00)!=0x0c04: - print "ERROR Fetched wrong value from 0x0c04."; - self.pokebyte(0x0021,0); #Drop LED - if self.peekbyte(0x0021)!=0: - print "ERROR, P1OUT not cleared."; - self.pokebyte(0x0021,1); #Light LED - - print "Self-test complete."; - - def spisetup(self): - """Moved the FET into the SPI application.""" - print "Initializing SPI."; - self.writecmd(1,0x10,0,self.data); #SPI/SETUP - #self.readcmd(); - def spitrans8(self,byte): - """Read and write 8 bits by SPI.""" - self.data=[byte]; - self.writecmd(1,0,1,self.data); #SPI exchange - #self.readcmd(); - - if self.app!=1 or self.verb!=0: - print "Error in SPI transaction; app=%02x, verb=%02x" % (self.app, self.verb); - return ord(self.data[0]); - -client=Client(); -client.serInit("/dev/ttyUSB0") - -client.monitortest(); - -client.spisetup(); -while 1: - print "%02x" % client.spitrans8(5); - time.sleep(0.1); diff --git a/client/goodfet.msp430 b/client/goodfet.msp430 new file mode 100755 index 0000000..8a17a26 --- /dev/null +++ b/client/goodfet.msp430 @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +from GoodFET import GoodFET; + + +client=GoodFET(); +client.serInit("/dev/ttyUSB0") + +client.MSP430test(); +#client.MSP430dumpbsl(); + +# client.MSP430setup(); +# client.MSP430start(); +# client.MSP430haltcpu(); +# ident=client.MSP430peek(0x0ff0); +# print "Target identifies as %04x." % ident; + diff --git a/firmware/apps/Makefile b/firmware/apps/Makefile index 1ffeb5a..40114b3 100644 --- a/firmware/apps/Makefile +++ b/firmware/apps/Makefile @@ -1,6 +1,6 @@ PORT=/dev/ttyUSB0 -BSL=tos-bsl --invert-reset --invert-test -c $(PORT) +BSL=tos-bsl --invert-reset --invert-test -c $(PORT) --speed=38400 #mcu=msp430x1611 mcu=msp430x1612 @@ -10,7 +10,7 @@ GCCINC=-T ../ldscripts/161x.x CC=msp430-gcc -g -mmcu=$(mcu) -DGCC $(GCCINC) -I ../include -apps= monitor/monitor.c spi/spi.c i2c/i2c.c chipcon/chipcon.c jtag/jtag.c +apps= monitor/monitor.c spi/spi.c i2c/i2c.c chipcon/chipcon.c jtag/jtag.c jtag/jtag430.c libs= ../lib/msp430f1612.c ../lib/command.c app=goodfet @@ -18,10 +18,12 @@ all: $(app) goodfet.hex: goodfet +run: + ../../client/goodfet.msp430 install: $(app) - $(BSL) -e -p $(app) - $(BSL) -P $(app) -r + $(BSL) -e -p $(app).hex + $(BSL) -P $(app).hex -r $(app): $(app).c $(libs) $(apps) $(app).hex: $(app) msp430-objcopy goodfet -O ihex goodfet.hex diff --git a/firmware/apps/goodfet.c b/firmware/apps/goodfet.c index 19feb63..a8d5b7a 100644 --- a/firmware/apps/goodfet.c +++ b/firmware/apps/goodfet.c @@ -48,6 +48,9 @@ void handle(unsigned char app, case JTAG: jtaghandle(app,verb,len); break; + case JTAG430: + jtag430handle(app,verb,len); + break; default: txdata(app,NOK,0); break; diff --git a/firmware/apps/jtag/Readme.txt b/firmware/apps/jtag/Readme.txt new file mode 100644 index 0000000..862cb48 --- /dev/null +++ b/firmware/apps/jtag/Readme.txt @@ -0,0 +1,5 @@ +GoodFET JTAG Applications +by Travis Goodspeed + +Most MSP430 macros come from definitions in SLAU265A. + diff --git a/firmware/apps/jtag/jtag430.c b/firmware/apps/jtag/jtag430.c new file mode 100644 index 0000000..03541c1 --- /dev/null +++ b/firmware/apps/jtag/jtag430.c @@ -0,0 +1,209 @@ + +#include "platform.h" +#include "command.h" +#include "jtag.h" + + +//! Set the program counter. +void jtag430_setpc(unsigned int adr){ + jtag_ir_shift8(IR_CNTRL_SIG_16BIT); + jtag_dr_shift16(0x3401);// release low byte + jtag_ir_shift8(IR_DATA_16BIT); + jtag_dr_shift16(0x4030);//Instruction to load PC + CLRTCLK; + SETTCLK; + jtag_dr_shift16(adr);// Value for PC + CLRTCLK; + jtag_ir_shift8(IR_ADDR_CAPTURE); + SETTCLK; + CLRTCLK ;// Now PC is set to "PC_Value" + jtag_ir_shift8(IR_CNTRL_SIG_16BIT); + jtag_dr_shift16(0x2401);// low byte controlled by JTAG +} + +//! Halt the CPU +void jtag430_haltcpu(){ + //jtag430_setinstrfetch(); + + jtag_ir_shift8(IR_DATA_16BIT); + jtag_dr_shift16(0x3FFF);//JMP $+0 + + CLRTCLK; + jtag_ir_shift8(IR_CNTRL_SIG_16BIT); + jtag_dr_shift16(0x2409);//set JTAG_HALT bit + SETTCLK; +} + +//! Release the CPU +void jtag430_releasecpu(){ + CLRTCLK; + jtag_ir_shift8(IR_CNTRL_SIG_16BIT); + jtag_dr_shift16(0x2401); + jtag_ir_shift8(IR_ADDR_CAPTURE); + SETTCLK; +} + +//! Read data from address +unsigned int jtag430_readmem(unsigned int adr){ + unsigned int toret; + + CLRTCLK; + jtag_ir_shift8(IR_CNTRL_SIG_16BIT); + if(adr>0xFF) + jtag_dr_shift16(0x2409);//word read + else + jtag_dr_shift16(0x2419);//byte read + jtag_ir_shift8(IR_ADDR_16BIT); + jtag_dr_shift16(adr);//address + jtag_ir_shift8(IR_DATA_TO_ADDR); + SETTCLK; + + CLRTCLK; + toret=jtag_dr_shift16(0x0000);//16 bit return + + return toret; +} + +//! Write data to address. +void jtag430_writemem(unsigned int adr, unsigned int data){ + CLRTCLK; + jtag_ir_shift8(IR_CNTRL_SIG_16BIT); + if(adr>0xFF) + jtag_dr_shift16(0x2408);//word write + else + jtag_dr_shift16(0x2418);//byte write + jtag_ir_shift8(IR_ADDR_16BIT); + jtag_dr_shift16(adr); + jtag_ir_shift8(IR_DATA_TO_ADDR); + jtag_dr_shift16(data); + SETTCLK; + +} + +//! Write data to address. +void jtag430_writeflash(unsigned int adr, unsigned int data){ + //TODO; this is complicated. +} + + +//! Reset the TAP state machine. +void jtag430_resettap(){ + int i; + // Settle output + SETTMS; + SETTDI; + SETTCK; + + // Navigate to reset state. + // Should be at least six. + for(i=0;i<4;i++){ + CLRTCK; + SETTCK; + } + + // test-logic-reset + CLRTCK; + CLRTMS; + SETTCK; + SETTMS; + // idle + + + /* sacred, by spec. + Sometimes this isn't necessary. */ + // fuse check + CLRTMS; + delay(50); + SETTMS; + CLRTMS; + delay(50); + SETTMS; + /**/ + +} + +//! Start JTAG, take pins +void jtag430_start(){ + jtagsetup(); + + //Known-good starting position. + //Might be unnecessary. + SETTST; + SETRST; + delay(0xFFFF); + + //Entry sequence from Page 67 of SLAU265A for 4-wire MSP430 JTAG + CLRRST; + delay(100); + CLRTST; + delay(50); + SETTST; + delay(50); + SETRST; + P5DIR&=~RST; + delay(0xFFFF); +} + +//! Set CPU to Instruction Fetch +void jtag430_setinstrfetch(){ + jtag_ir_shift8(IR_CNTRL_SIG_CAPTURE); + + // Wait until instruction fetch state. + while(1){ + if (jtag_dr_shift16(0x0000) & 0x0080) + return; + CLRTCLK; + SETTCLK; + } +} + +//! Handles unique MSP430 JTAG commands. Forwards others to JTAG. +void jtag430handle(unsigned char app, + unsigned char verb, + unsigned char len){ + unsigned char i; + switch(verb){ + case START: + //Enter JTAG mode. + jtag430_start(); + //TAP setup, fuse check + jtag430_resettap(); + txdata(app,verb,0); + break; + case JTAG430_HALTCPU: + jtag430_haltcpu(); + txdata(app,verb,0); + break; + case JTAG430_RELEASECPU: + jtag430_releasecpu(); + txdata(app,verb,0); + break; + case JTAG430_SETINSTRFETCH: + jtag430_setinstrfetch(); + txdata(app,verb,0); + break; + + + case JTAG430_READMEM: + case PEEK: + cmddataword[0]=jtag430_readmem(cmddataword[0]); + txdata(app,verb,2); + break; + case JTAG430_WRITEMEM: + case POKE: + jtag430_writemem(cmddataword[0],cmddataword[1]); + txdata(app,verb,0); + break; + case JTAG430_WRITEFLASH: + jtag430_writeflash(cmddataword[0],cmddataword[1]); + txdata(app,verb,0); + break; + case JTAG430_SETPC: + jtag430_setpc(cmddataword[0]); + txdata(app,verb,0); + break; + default: + jtaghandle(app,verb,len); + } +} + diff --git a/firmware/include/command.h b/firmware/include/command.h index 2c722c3..0c65462 100644 --- a/firmware/include/command.h +++ b/firmware/include/command.h @@ -40,6 +40,21 @@ extern unsigned char cmddata[256]; #define CC_MASS_ERASE_FLASH 0x97 #define CC_PROGRAM_FLASH 0x98 +//JTAG commands +#define JTAG_IR_SHIFT 0x80 +#define JTAG_DR_SHIFT 0x81 +#define JTAG_DR_SHIFT20 0x91 + +//JTAG430 commands +#define JTAG430_HALTCPU 0xA0 +#define JTAG430_RELEASECPU 0xA1 +#define JTAG430_SETINSTRFETCH 0xC1 +#define JTAG430_SETPC 0xC2 +#define JTAG430_WRITEMEM 0xE0 +#define JTAG430_WRITEFLASH 0xE1 +#define JTAG430_READMEM 0xE2 + + //! Handle a command. Defined in goodfet.c void handle(unsigned char app, unsigned char verb, diff --git a/firmware/include/jtag.h b/firmware/include/jtag.h index 6f9b6dc..17833d6 100644 --- a/firmware/include/jtag.h +++ b/firmware/include/jtag.h @@ -1,6 +1,105 @@ +#include +#include +#include + + +// Generic Commands //! Shift 8 bits of the IR. unsigned char jtag_ir_shift8(unsigned char); //! Shift 16 bits of the DR. -unsigned int jtag_dr_shift16(unsigned int in); +unsigned int jtag_dr_shift16(unsigned int); +//! Stop JTAG, release pins +void jtag_stop(); + + +// JTAG430 Commands + +//! Start JTAG, unique to the '430. +void jtag430_start(); +//! Reset the TAP state machine, check the fuse. +void jtag430_resettap(); + +//High-level Macros follow +//! Write data to address. +void jtag430_writemem(unsigned int adr, unsigned int data); +//! Read data from address +unsigned int jtag430_readmem(unsigned int adr); +//! Halt the CPU +void jtag430_haltcpu(); +//! Release the CPU +void jtag430_releasecpu(); +//! Set CPU to Instruction Fetch +void jtag430_setinstrfetch(); +//! Set the program counter. +void jtag430_setpc(unsigned int adr); +//! Write data to address. +void jtag430_writeflash(unsigned int adr, unsigned int data); + +//Pins. Both SPI and JTAG names are acceptable. +//#define SS BIT0 +#define MOSI BIT1 +#define MISO BIT2 +#define SCK BIT3 + +#define TMS BIT0 +#define TDI BIT1 +#define TDO BIT2 +#define TCK BIT3 + +#define TCLK TDI + +//These are not on P5 +#define RST BIT6 +#define TST BIT0 + +//This could be more accurate. +//Does it ever need to be? +#define JTAGSPEED 20 +#define JTAGDELAY(x) delay(x) + +#define SETMOSI P5OUT|=MOSI +#define CLRMOSI P5OUT&=~MOSI +#define SETCLK P5OUT|=SCK +#define CLRCLK P5OUT&=~SCK +#define READMISO (P5IN&MISO?1:0) +#define SETTMS P5OUT|=TMS +#define CLRTMS P5OUT&=~TMS +#define SETTCK P5OUT|=TCK +#define CLRTCK P5OUT&=~TCK +#define SETTDI P5OUT|=TDI +#define CLRTDI P5OUT&=~TDI + +#define SETTST P4OUT|=TST +#define CLRTST P4OUT&=~TST +#define SETRST P2OUT|=RST +#define CLRRST P2OUT&=~RST + +#define SETTCLK SETTDI +#define CLRTCLK CLRTDI + +extern int savedtclk; +#define SAVETCLK savedtclk=P5OUT&TCLK; +#define RESTORETCLK if(savedtclk) P5OUT|=TCLK; else P5OUT&=~TCLK + +//JTAG commands, bit-swapped +#define IR_CNTRL_SIG_16BIT 0xC8 // 0x13 +#define IR_CNTRL_SIG_CAPTURE 0x28 // 0x14 +#define IR_CNTRL_SIG_RELEASE 0xA8 // 0x15 +// Instructions for the JTAG Fuse +#define IR_PREPARE_BLOW 0x44 // 0x22 +#define IR_EX_BLOW 0x24 // 0x24 +// Instructions for the JTAG data register +#define IR_DATA_16BIT 0x82 // 0x41 +#define IR_DATA_QUICK 0xC2 // 0x43 +// Instructions for the JTAG PSA mode +#define IR_DATA_PSA 0x22 // 0x44 +#define IR_SHIFT_OUT_PSA 0x62 // 0x46 +// Instructions for the JTAG address register +#define IR_ADDR_16BIT 0xC1 // 0x83 +#define IR_ADDR_CAPTURE 0x21 // 0x84 +#define IR_DATA_TO_ADDR 0xA1 // 0x85 +// Bypass instruction +#define IR_BYPASS 0xFF // 0xFF + diff --git a/firmware/lib/command.c b/firmware/lib/command.c index 44a853d..9a9421d 100644 --- a/firmware/lib/command.c +++ b/firmware/lib/command.c @@ -19,5 +19,5 @@ void txdata(unsigned char app, //! Delay for a count. void delay(unsigned int count){ volatile unsigned int i=count; - while(i--); + while(i--) asm("nop"); }