X-Git-Url: http://git.rot13.org//?p=goodfet;a=blobdiff_plain;f=firmware%2Fapps%2Fjtag%2Fjtag.c;fp=firmware%2Fapps%2Fjtag%2Fjtag.c;h=b813d5d0ea7c990014723c42ed2ab16cbfe8ff9f;hp=bda0fac41a8756a4fe2eb614ba67a9be2aea8b4b;hb=cfbda5b069e5c792b4b3a91911c3fc9844b68fbc;hpb=27b44f95b9b34cfd2e8e99106bc55428630e844a diff --git a/firmware/apps/jtag/jtag.c b/firmware/apps/jtag/jtag.c index bda0fac..b813d5d 100644 --- a/firmware/apps/jtag/jtag.c +++ b/firmware/apps/jtag/jtag.c @@ -38,292 +38,511 @@ app_t const jtag_app = { }; +//! Remembers what the current JTAG state is +enum eTAPState jtag_state = UNKNOWN; + +//! Returns true if we're in any of the data register states +int in_dr() +{ + return (int)(jtag_state & (SELECT_DR_SCAN | CAPTURE_DR | + SHIFT_DR | EXIT1_DR | PAUSE_DR | + EXIT2_DR | UPDATE_DR)); +} + +//! Returns true if we're in any of the instruction register states +int in_ir() +{ + return (int)(jtag_state & (SELECT_IR_SCAN | CAPTURE_IR | + SHIFT_IR | EXIT1_IR | PAUSE_IR | + EXIT2_IR | UPDATE_IR)); +} + +//! Returns true if we're in run-test-idle state +int in_run_test_idle() +{ + return (int)(jtag_state & RUN_TEST_IDLE); +} + +//! Check the state +int in_state(enum eTAPState state) +{ + return (int)(jtag_state & state); +} + + +//! Reset the target device +void jtag_reset_target() +{ + SETRST; + PLEDOUT^=PLEDPIN; + CLRRST; + PLEDOUT^=PLEDPIN; +} + +//! Clock the JTAG clock line +void jtag_tcktock() +{ + CLRTCK; + PLEDOUT^=PLEDPIN; + SETTCK; + PLEDOUT^=PLEDPIN; +} + +//! Goes through test-logic-reset and ends in run-test-idle +void jtag_reset_tap() +{ + CLRMOSI; + SETTMS; + jtag_tcktock(); + jtag_tcktock(); + jtag_tcktock(); + jtag_tcktock(); + jtag_tcktock(); // now in Reset state + CLRTMS; + jtag_tcktock(); // now in Run-Test/Idle state + jtag_state = RUN_TEST_IDLE; +} + //! Set up the pins for JTAG mode. -void jtagsetup(){ - P5DIR|=MOSI+SCK+TMS; - P5DIR&=~MISO; - /* - P5OUT|=0xFFFF; - P5OUT=0; - */ - P4DIR|=TST; - P2DIR|=RST; - msdelay(100); +void jtag_setup() +{ + P5DIR|=MOSI+SCK+TMS; + P5DIR&=~MISO; + P4DIR|=TST; + P2DIR|=RST; + msdelay(100); + jtag_state = UNKNOWN; } -/************************** JTAG Primitives ****************************/ -// these have been turned into functions to save flash space -void jtag_tcktock() { - CLRTCK; - PLEDOUT^=PLEDPIN; - SETTCK; - PLEDOUT^=PLEDPIN; +//! Stop JTAG, release pins +void jtag_stop() +{ + P5OUT=0; + P4OUT=0; } -void jtag_goto_shift_ir() { - SETTMS; - jtag_tcktock(); - jtag_tcktock(); - CLRTMS; - jtag_tcktock(); - jtag_tcktock(); +//! Get into Shift-IR or Shift-DR state +void jtag_shift_register() +{ + // assumes we're in any state that can transition to Shift-IR or Shift-DR + if (!in_state(CAPTURE_DR | CAPTURE_IR | SHIFT_DR | + SHIFT_IR | EXIT2_DR | EXIT2_IR )) + { + debugstr("Invalid JTAG state"); + return; + } + + CLRMOSI; + CLRTMS; + jtag_tcktock(); + if (in_dr()) + jtag_state = SHIFT_DR; + else + jtag_state = SHIFT_IR; } -void jtag_goto_shift_dr() { - SETTMS; - jtag_tcktock(); - CLRTMS; - jtag_tcktock(); - jtag_tcktock(); + +//! Get into Capture-IR state +void jtag_capture_ir() +{ + // assumes you're in Run-Test/Idle, Update-DR or Update-IR + if (!in_state(RUN_TEST_IDLE | UPDATE_DR | UPDATE_IR)) + { + debugstr("Invalid JTAG state"); + return; + } + + CLRMOSI; + SETTMS; + jtag_tcktock(); // Select-DR-Scan + jtag_tcktock(); // Select-IR-Scan + CLRTMS; + jtag_tcktock(); // Capture-IR + + jtag_state = CAPTURE_IR; } -void jtag_resettap(){ - SETTMS; - jtag_tcktock(); - jtag_tcktock(); - jtag_tcktock(); - jtag_tcktock(); - jtag_tcktock(); // now in Reset state - CLRTMS; - jtag_tcktock(); // now in Run-Test/Idle state +//! Get into Capture-DR state +void jtag_capture_dr() +{ + // assumes you're in Run-Test/Idle, Update-DR or Update-IR + if (!in_state(RUN_TEST_IDLE | UPDATE_DR | UPDATE_IR)) + { + debugstr("Invalid JTAG state"); + return; + } + + CLRMOSI; + SETTMS; + jtag_tcktock(); // Select-DR-Scan + CLRTMS; + jtag_tcktock(); // Capture-IR + + jtag_state = CAPTURE_DR; } +//! Gets back to run-test-idle without going through the test-logic-reset +void jtag_run_test_idle() +{ + CLRMOSI; + + if (in_state(SELECT_DR_SCAN | SELECT_IR_SCAN)) + { + CLRTMS; + jtag_tcktock(); + jtag_state <<= 1; //CAPTURE_DR or CAPTURE_IR + } + + if (in_state(CAPTURE_DR | CAPTURE_IR)) + { + SETTMS; + jtag_tcktock(); + jtag_state <<= 2; //EXIT1_DR or EXIT1_IR + } + + if (in_state(SHIFT_DR | SHIFT_IR)) + { + SETTMS; + jtag_tcktock(); + jtag_state <<= 1; //EXIT1_DR or EXIT1_IR + } + + if (in_state(EXIT1_DR | EXIT1_IR)) + { + SETTMS; + jtag_tcktock(); + jtag_state <<=3; //UPDATE_DR or UPDATE_IR + } + + if (in_state(PAUSE_DR | PAUSE_IR)) + { + SETTMS; + jtag_tcktock(); + jtag_state <<= 1; // EXIT2_DR or EXIT2_IR + } + + if (in_state(EXIT2_DR | EXIT2_IR)) + { + SETTMS; + jtag_tcktock(); + jtag_state <<= 1; // UPDATE_DR or UPDATE_IR + } + + if (in_state(UPDATE_DR | UPDATE_IR | TEST_LOGIC_RESET)) + { + CLRTMS; + jtag_tcktock(); + jtag_state = RUN_TEST_IDLE; + } +} -int savedtclk=0; -// NOTE: important: THIS MODULE REVOLVES AROUND RETURNING TO RUNTEST/IDLE, OR -// THE FUNCTIONAL EQUIVALENT -//! Shift N bits over TDI/TDO. May choose LSB or MSB, and select whether to -// terminate (TMS-high on last bit) and whether to return to RUNTEST/IDLE -// flags should be 0 for most uses. -// for the extreme case, flags should be (NOEND|NORETDLE|LSB) -// other edge cases can involve a combination of those three flags +int savedtclk; +// NOTE: important: THIS MODULE REVOLVES AROUND RETURNING TO RUNTEST/IDLE, OR +// THE FUNCTIONAL EQUIVALENT +//! Shift N bits over TDI/TDO. May choose LSB or MSB, and select whether to +// terminate (TMS-high on last bit) and whether to return to RUNTEST/IDLE +// flags should be 0 for most uses. +// for the extreme case, flags should be (NOEND|NORETDLE|LSB) +// other edge cases can involve a combination of those three flags // -// the max bit-size that can be be shifted is 32-bits. -// for longer shifts, use the NOEND flag (which infers NORETIDLE so the -// additional flag is unnecessary) +// the max bit-size that can be be shifted is 32-bits. +// for longer shifts, use the NOEND flag (which infers NORETIDLE so the +// additional flag is unnecessary) // -// NORETIDLE is used for special cases where (as with arm) the debug -// subsystem does not want to return to the RUN-TEST/IDLE state between -// setting IR and DR -unsigned long jtagtransn(unsigned long word, - unsigned char bitcount, - unsigned char flags) { - unsigned char bit; - unsigned long high = 1L; - unsigned long mask; - - //for (bit=(bitcount-1)/8; bit>0; bit--) - // high <<= 8; - //high <<= ((bitcount-1)%8); - high <<= (bitcount-1); - - mask = high-1; - - SAVETCLK; - if (flags & LSB) { - for (bit = bitcount; bit > 0; bit--) { - /* write MOSI on trailing edge of previous clock */ - if (word & 1) - {SETMOSI;} - else - {CLRMOSI;} - word >>= 1; - - if (bit==1 && !(flags & NOEND)) - SETTMS;//TMS high on last bit to exit. - - jtag_tcktock(); - - /* read MISO on trailing edge */ - if (READMISO){ - word += (high); - } - } - } else { - for (bit = bitcount; bit > 0; bit--) { - /* write MOSI on trailing edge of previous clock */ - if (word & high) - {SETMOSI;} - else - {CLRMOSI;} - word = (word & mask) << 1; - - if (bit==1 && !(flags & NOEND)) - SETTMS;//TMS high on last bit to exit. - - jtag_tcktock(); - - /* read MISO on trailing edge */ - word |= (READMISO); - } - } - - - RESTORETCLK; - //SETMOSI; - - if (!(flags & NOEND)){ - // exit state - jtag_tcktock(); - // update state - if (!(flags & NORETIDLE)){ - CLRTMS; - jtag_tcktock(); - } - } - return word; -} +// NORETIDLE is used for special cases where (as with arm) the debug +// subsystem does not want to return to the RUN-TEST/IDLE state between +// setting IR and DR +uint32_t jtag_trans_n(uint32_t word, + uint8_t bitcount, + enum eTransFlags flags) +{ + uint8_t bit; + uint32_t high = (1L << (bitcount - 1)); + uint32_t mask = high - 1; -//! Shift 8 bits in and out. -unsigned char jtagtrans8(unsigned char byte){ - unsigned int bit; - SAVETCLK; - for (bit = 0; bit < 8; bit++) { - /* write MOSI on trailing edge of previous clock */ - if (byte & 0x80) - {SETMOSI;} - else - {CLRMOSI;} - byte <<= 1; - - if(bit==7) - SETTMS;//TMS high on last bit to exit. - - TCKTOCK; - /* read MISO on trailing edge */ - byte |= READMISO; - } - RESTORETCLK; - - // exit state - TCKTOCK; - // update state - CLRTMS; - TCKTOCK; - - return byte; -} + if (!in_state(SHIFT_IR | SHIFT_DR)) + { + debugstr("jtag_trans_n from invalid TAP state"); + return 0; + } + + SAVETCLK; + + if (flags & LSB) + { + for (bit = bitcount; bit > 0; bit--) + { + /* write MOSI on trailing edge of previous clock */ + if (word & 1) + { + SETMOSI; + } + else + { + CLRMOSI; + } + word >>= 1; + + if ((bit == 1) && !(flags & NOEND)) + SETTMS; //TMS high on last bit to exit. + + jtag_tcktock(); + + if ((bit == 1) && !(flags & NOEND)) + jtag_state <<= 1; // Exit1-DR or Exit1-IR + + /* read MISO on trailing edge */ + if (READMISO) + { + word += (high); + } + } + } + else + { + for (bit = bitcount; bit > 0; bit--) + { + /* write MOSI on trailing edge of previous clock */ + if (word & high) + { + SETMOSI; + } + else + { + CLRMOSI; + } + word = (word & mask) << 1; + + if ((bit==1) && !(flags & NOEND)) + SETTMS; //TMS high on last bit to exit. + + jtag_tcktock(); + + if ((bit == 1) && !(flags & NOEND)) + jtag_state <<= 1; // Exit1-DR or Exit1-IR + + /* read MISO on trailing edge */ + word |= (READMISO); + } + } + + RESTORETCLK; + + if (!(flags & NOEND)) + { + // exit state + jtag_tcktock(); -//! Shift n bits in and out. -/*unsigned long jtagtransn(unsigned long word, - unsigned int bitcount){ - unsigned int bit; - //0x8000 - unsigned long high=0x8000; - - if(bitcount==20) - high=0x80000; - if(bitcount==16) - high= 0x8000; - - SAVETCLK; - - for (bit = 0; bit < bitcount; bit++) { - // write MOSI on trailing edge of previous clock * - if (word & high) - {SETMOSI;} - else - {CLRMOSI;} - word <<= 1; - - if(bit==bitcount-1) - SETTMS;//TMS high on last bit to exit. - - TCKTOCK; - // read MISO on trailing edge * - word |= READMISO; - } - - if(bitcount==20){ - word = ((word << 16) | (word >> 4)) & 0x000FFFFF; - } - - RESTORETCLK; - - // exit state - TCKTOCK; - // update state - CLRTMS; - TCKTOCK; - - return word; + jtag_state <<= 3; // Update-DR or Update-IR + + // update state + if (!(flags & NORETIDLE)) + { + CLRTMS; + jtag_tcktock(); + + jtag_state = RUN_TEST_IDLE; + } + } + + return word; } -*/ -//! Stop JTAG, release pins -void jtag_stop(){ - P5OUT=0; - P4OUT=0; +//! Detects the width of the IR register +uint16_t jtag_detect_ir_width() +{ + int i; + uint16_t width = 0; + + if (!in_run_test_idle()) + { + debugstr("Not in run-test-idle state"); + return 0; + } + + // get to shift-ir state + jtag_capture_ir(); + jtag_shift_register(); + + // first we shift in 1024 zeros + CLRMOSI; + for(i = 0; i < 1024; i++) + { + jtag_tcktock(); + } + + // now we'll clock in ones until we see one + SETMOSI; + for(i = 0; i < 1024; i++) + { + jtag_tcktock(); + if(READMISO) + break; + width++; + } + + // now get back to run-test-idle + jtag_run_test_idle(); + + return width; } -unsigned int drwidth=16; -//! Shift all bits of the DR. -unsigned long jtag_dr_shift20(unsigned long in){ - // idle - SETTMS; - TCKTOCK; - // select DR - CLRTMS; - TCKTOCK; - // capture IR - TCKTOCK; - - // shift DR, then idle - return(jtagtransn(in,20,0)); +//! Detects how many TAPs are in the JTAG chain +uint16_t jtag_detect_chain_length() +{ + int i; + int bit; + uint16_t length = 0; + + if (!in_run_test_idle()) + { + debugstr("detect chain length must start from run-test-idle"); + return 0; + } + + // The standard JTAG instruction for "bypass" mode is to set all 1's in the + // instruction register. When in "bypass" mode, the DR acts like a 1-bit + // shift regiser. So we can use that to detect how many TAPs are in the + // chain. + + // first get into shift IR mode + jtag_capture_ir(); + jtag_shift_register(); + + // then we flood the IR chain with all 1's to put all device's DRs + // into bypass mode + CLRTMS; + SETMOSI; + for (i = 0; i < 1024; i++) + { + if (i == 1023) + SETTMS; // exit on last bit + jtag_tcktock(); + } + jtag_state = EXIT1_IR; + + // go to Update-IR + CLRMOSI; + jtag_tcktock(); + jtag_state = UPDATE_IR; + + // go to Shift-DR state + jtag_capture_dr(); + jtag_shift_register(); + + // flush the DR's with zeros + CLRTMS; + CLRMOSI; + for (i = 0; i < 1024; i++) + { + jtag_tcktock(); + } + + // send 1's into the DRs until we see one come out the other side + SETMOSI; + for (i = 0; i < 1024; i++) + { + jtag_tcktock(); + bit = READMISO; + if (bit) + break; + length++; + } + + // now get back to run-test-idle + jtag_run_test_idle(); + + return length; } +//! Gets device ID for specified chip in the chain +uint32_t jtag_get_device_id(int chip) +{ + int i; + uint16_t chain_length; + uint32_t id = 0; + + // reset everything + jtag_reset_tap(); -//! Shift 16 bits of the DR. -unsigned int jtag_dr_shift16(unsigned int in){ - // idle - SETTMS; - TCKTOCK; - // select DR - CLRTMS; - TCKTOCK; - // capture IR - TCKTOCK; - - // shift DR, then idle - return(jtagtransn(in,16,0)); + // figure out how many devices are in the chain + chain_length = jtag_detect_chain_length(); + + if (chip >= chain_length) + { + debugstr("invalid part index"); + return 0; + } + + // reset everything again because going through test-logic-reset forces + // all IR's to have their manufacturer specific instruction for IDCODE + // and loads the DR's with the chip's ID code. + jtag_reset_tap(); + + // get into shift DR state + jtag_capture_dr(); + jtag_shift_register(); + + // read out the 32-bit ID codes for each device + CLRTMS; + CLRMOSI; + for (i = 0; i < (chip + 1); i++) + { + id = jtag_trans_n(0xFFFFFFFF, 32, LSB | NOEND | NORETIDLE); + } + + jtag_run_test_idle(); + + return id; } -//! Shift native width of the DR -unsigned long jtag_dr_shiftadr(unsigned long in){ - unsigned long out=0; - - // idle - SETTMS; - TCKTOCK; - // select DR - CLRTMS; - TCKTOCK; - // capture IR - TCKTOCK; - - - out=jtagtransn(in,drwidth,0); - - // shift DR, then idle - return(out); +//! Shift 8 bits in/out of selected register +uint8_t jtag_trans_8(uint8_t in) +{ + uint32_t out = jtag_trans_n((uint32_t)in, 8, MSB); + return (uint8_t)(0x000000FF & out); } +//! Shift 16 bits in/out of selected register +uint16_t jtag_trans_16(uint16_t in) +{ + uint32_t out = jtag_trans_n((uint32_t)in, 16, MSB); + return (uint16_t)(0x0000FFFF & out); +} //! Shift 8 bits of the IR. -unsigned char jtag_ir_shift8(unsigned char in){ - // idle - SETTMS; - TCKTOCK; - // select DR - TCKTOCK; - // select IR - CLRTMS; - TCKTOCK; - // capture IR - TCKTOCK; - - // shift IR, then idle. - return(jtagtrans8(in)); +uint8_t jtag_ir_shift_8(uint8_t in) +{ + if (!in_run_test_idle()) + { + debugstr("Not in run-test-idle state"); + return 0; + } + + // get intot the right state + jtag_capture_ir(); + jtag_shift_register(); + + // shift IR bits + return jtag_trans_8(in); +} + +//! Shift 16 bits of the DR. +uint16_t jtag_dr_shift_16(uint16_t in) +{ + + if (!in_run_test_idle()) + { + debugstr("Not in run-test-idle state"); + return 0; + } + + // get intot the right state + jtag_capture_dr(); + jtag_shift_register(); + + // shift DR, then idle + return jtag_trans_16(in); } //! Handles a monitor command. @@ -333,32 +552,56 @@ void jtag_handle_fn(uint8_t const app, { switch(verb) { - //START handled by specific JTAG + // START handled by specific JTAG case STOP: jtag_stop(); txdata(app,verb,0); break; case SETUP: - jtagsetup(); + jtag_setup(); txdata(app,verb,0); break; case JTAG_IR_SHIFT: - cmddata[0]=jtag_ir_shift8(cmddata[0]); + cmddata[0] = jtag_ir_shift_8(cmddata[0]); txdata(app,verb,1); break; case JTAG_DR_SHIFT: - cmddataword[0]=jtag_dr_shift16(cmddataword[0]); + cmddataword[0] = htons(jtag_dr_shift_16(ntohs(cmddataword[0]))); txdata(app,verb,2); break; - case JTAG_RESETTAP: - jtag_resettap(); + case JTAG_RESET_TAP: + jtag_reset_tap(); txdata(app,verb,0); break; + case JTAG_RESET_TARGET: + jtag_reset_tap(); + jtag_reset_target(); + txdata(app,verb,0); + break; + + case JTAG_DETECT_IR_WIDTH: + jtag_reset_tap(); + cmddataword[0] = htons(jtag_detect_ir_width()); + txdata(app,verb,2); + break; + + case JTAG_DETECT_CHAIN_LENGTH: + jtag_reset_tap(); + cmddataword[0] = htons(jtag_detect_chain_length()); + txdata(app,verb,2); + break; + + case JTAG_GET_DEVICE_ID: + jtag_reset_tap(); + cmddatalong[0] = htonl(jtag_get_device_id(ntohs(cmddataword[0]))); + txdata(app,verb,4); + break; + default: txdata(app,NOK,0); }