/*
 JTAGenum
 Given a Arduino compatible microcontroller JTAGenum scans
 pins[] for basic JTAG functionality. After programming 
 your microcontroller open a serial terminal with 115200 
 baud and send 'h' to see usage information.
 
 SETUP:
 Define the pins[] and pinnames[] map of pin names to pins 
 you want to scan with. If you are using a 3.3v board 
 uncomment the CPU_PRESCALE defintions at the top and in 
 the setup() function.
 
 If you plan to use IDCODE, Boundary or IR scan routines
 define the IR_IDCODE, IR_SAMPLE+SCAN_LEN and 
 IR_LEN+CHAIN_LEN values according to suspected or 
 documented values.
 
 This code is an extensive modification and port to Arduino 
 of Lekernel's ArduiNull [1] which was itself inspired by
 Hunz's JTAG Finder (aka jtagscanner) [2]. The advantage
 of using Arduino is that the code can be quickly programmed
 to any microcontroller supported by the platform (including
 PIC[3], AT90USB[4], others) with little to no modification
 required. While The Law Of Leaky Abstractions [5] still 
 applies using Arduino might be helpful for engineers with 
 tight deadlines.
 
 [1]http://lekernel.net/blog/?p=319
 [2]http://www.c3a.de/wiki/index.php/JTAG_Finder
 [3]http://www.create.ucsb.edu/~dano/CUI/
 [4]http://www.pjrc.com/teensy/  
 [5]http://joelonsoftware.com/articles/LeakyAbstractions.html
 TODO: add support for longer chains when using TAP_SHIFIR
 Copyright 2009 Nathan Fain
 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 3 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, see .
 */
/*
 * BEGIN USER DEFINITIONS
 */
//#define DEBUGTAP
//#define DEBUGIR
// For 3.3v AVR boards. Cuts clock in half. Also see cmd in setup()
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
// Setup the pins to be checked
// The first (currently commented out) is an example broad scan
// used to determine which pins from a set are meant for JTAG.
// The second (uncommented) is used when you know the JTAG pins
// already.
//byte pins[] = { 
//        PIN_B7,  PIN_D0,  PIN_D1,  PIN_D2,  PIN_D3,  PIN_D4,/*PIN_D5*/ PIN_D6, /*PIN_D7*/
//        PIN_B6,  PIN_B5,  PIN_B4,  PIN_B3,  PIN_B2,  PIN_B1,  PIN_B0 /*PIN_E7*//*PIN_E6*/
//};
//char * pinnames[] = {
//          " 3",    " 6",    "10",    "17",    "19",    "21",  /*"24"*/   "26", /*"PIN_D7"*/
//          " 2",    " 5",    " 9",    "13",    "18",    "20",    "22"   /*"25"*//*"PIN_E6"*/
//};
byte       pins[] = {   PIN_B7,   PIN_D0,   PIN_D1,   PIN_D2 };
char * pinnames[] = { "TCK_9 ", "TMS_22", "TDO   ", "TDI   " };
// Pattern used for scan() and loopback() tests
#define PATTERN_LEN 64
// Use something random when trying find JTAG lines:
static char pattern[PATTERN_LEN] = "0110011101001101101000010111001001";
// Use something more determinate when trying to find
// length of the DR register:
//static char pattern[PATTERN_LEN] = "1000000000000000000000000000000000";
// Number of JTAG enabled chips (CHAIN_LEN) and length
// of the DR register together define the number of
// iterations to run for scan_idcode():
#define CHAIN_LEN                 2 
#define DR_LEN                    32  
#define IR_IDCODE_ITERATIONS      CHAIN_LEN*DR_LEN
// Target specific, check your documentation or guess 
#define SCAN_LEN                  1890 // used for IR enum. bigger the better
#define IR_LEN                    10  
// IR registers must be IR_LEN wide:
#define IR_IDCODE                 "0110000000" // always 011
#define IR_SAMPLE                 "1010000000" // always 101
#define IR_PRELOAD                IR_SAMPLE
/*
 * END USER DEFINITIONS
 */
// TAP TMS states we care to use. NOTE: MSB sent first
// Meaning ALL TAP and IR codes have their leftmost
// bit sent first. This might be the reverse of what
// documentation for your target(s) show.
#define TAP_RESET        "11111"      // looping 1 will return 
                                      // IDCODE if reg available
#define TAP_SHIFTDR      "111110100"
#define TAP_SHIFTIR      "1111101100"
// how many bits must change in scan_idcode() in order to print?
// in some cases pulling a bit high or low might change the state
// of other pins, having nothing to do with JTAG. So 2 is likely
// a good number. Note: these first two bit changes, will not be
// printed to the console.
int IDCODETHRESHOLD = 2; 
// Ignore TCK, TMS use in loopback check:
#define IGNOREPIN 0xFFFF 
// Flags configured by UI:
boolean VERBOSE = 0; // 255 = true
boolean DELAY = 0;
long DELAYUS = 5000; // 5 Milliseconds
boolean PULLUP = 255; 
byte pinslen = sizeof(pins);   
void setup(void)
{
        // Uncomment for 3.3v boards. Cuts clock in half
        CPU_PRESCALE(0x01); 
        Serial.begin(115200);
}
/*
 * Set the JTAG TAP state machine
 */
void tap_state(char tap_state[], int tck, int tms) 
{
#ifdef DEBUGTAP
        Serial.print("tms set to: ");
#endif
        while (*tap_state) { // exit when string \0 terminator encountered
                if (DELAY) delayMicroseconds(50);
                digitalWrite(tck, LOW);                
                digitalWrite(tms, *tap_state-'0'); // conv from ascii pattern
#ifdef DEBUGTAP
                Serial.print(*tap_state-'0',DEC);
#endif
                digitalWrite(tck, HIGH); // rising edge shifts in TMS
                *tap_state++;
        }                
#ifdef DEBUGTAP
        Serial.println();
#endif
}
static void pulse_tms(int tck, int tms, int s_tms)
{
        if (tck == IGNOREPIN) return;
        digitalWrite(tck, LOW);
        digitalWrite(tms, s_tms); 
        digitalWrite(tck, HIGH);
}
static void pulse_tdi(int tck, int tdi, int s_tdi)
{
        if (DELAY) delayMicroseconds(50);
        if (tck != IGNOREPIN) digitalWrite(tck, LOW);
        digitalWrite(tdi, s_tdi); 
        if (tck != IGNOREPIN) digitalWrite(tck, HIGH);
}
byte pulse_tdo(int tck, int tdo)
{
        byte tdo_read;
        if (DELAY) delayMicroseconds(50);
        digitalWrite(tck, LOW); // read in TDO on falling edge
        tdo_read = digitalRead(tdo);
        digitalWrite(tck, HIGH);
        return tdo_read;
}
/*
 * Initialize all pins to a default state
 * default with no arguments: all pins as INPUTs
 */
void init_pins (int tck=IGNOREPIN, int tms=IGNOREPIN, int tdi=IGNOREPIN) 
{ 
        // default all to INPUT state
        for (int i=0; i 1)
                return bitstoggled; // no match but activity found
        else
                return 0; // no match and no activity on tdo
}
/*
 * Shift JTAG TAP to ShiftIR state. Send pattern to TDI and check
 * for output on TDO
 */
static void scan()
{
        int tck, tms, tdo, tdi;
        int checkdataret=0;
        Serial.print(
        	"================================\n"
                "Starting scan for pattern:\n");
        Serial.println(pattern);
        for(tck=0;tck 1) {
                                                Serial.print("active ");
                                                Serial.print(" tck:");
                                                Serial.print(pinnames[tck]);
                                                Serial.print(" tms:");
                                                Serial.print(pinnames[tms]);
                                                Serial.print(" tdo:");
                                                Serial.print(pinnames[tdo]);
                                                Serial.print(" tdi:");
                                                Serial.print(pinnames[tdi]);
                                                Serial.print("  bits toggled:");
                                                Serial.println(checkdataret);
                                        }
                                        else if(VERBOSE) Serial.println();                                        
                                }
                        }
                }
        }
        Serial.print("================================\n");
}
/*
 * Check for pins that pass pattern[] between tdi and tdo
 * regardless of JTAG TAP state (tms, tck ignored).
 *
 * TDO, TDI pairs that match indicate possible shorts between
 * pins. Pins that do not match but are active might indicate
 * that the patch cable used is not shielded well enough. Run
 * the test again without the cable connected between controller
 * and target. Run with the verbose flag to examine closely.
 */
static void loopback_check()
{
        int tdo, tdi;
        int checkdataret=0;
        Serial.print(
        	"================================\n"
                "Starting loopback check...\n");
        for(tdo=0;tdo 1) {
                                Serial.print("active ");
                                Serial.print(" tdo:");
                                Serial.print(pinnames[tdo]);
                                Serial.print(" tdi:");
                                Serial.print(pinnames[tdi]);
                                Serial.print("  bits toggled:");
                                Serial.println(checkdataret);
                        }
                        else if(VERBOSE) Serial.println();
                }
        }
        Serial.print("================================\n");
}
/*
 * Scan TDI for IDCODE
 * no need for TDO stimulation
 */
static void scan_idcode()
{
        int tck, tms, tdo, i;
        int bitstoggled;
        byte prevbit, tdo_read;
        Serial.print(
        	"================================\n"
                "Starting scan for IDCODE...\n"
                //"(if activity found, examine for IDCODE. Pits printed in shift right order with MSB first)\n"
	);
        char idcodestr[] = "                                ";
        int idcode_i=31; // TODO: artifact that might need to be configurable
        uint32_t idcode;
        for(tck=0;tck 0) {
                                                idcode |= ((uint32_t)tdo_read) << (31-idcode_i);
                                                idcodestr[idcode_i--] = tdo_read+'0';
                                                Serial.print(tdo_read,DEC);
                                                if (i % 32 == 31) Serial.print(" ");
                                        }
                                        prevbit = tdo_read;
                                }
                                if(bitstoggled >= IDCODETHRESHOLD) {
                                        Serial.print("\n tck:");
                                        Serial.print(pinnames[tck]);
                                        Serial.print(" tms:");
                                        Serial.print(pinnames[tms]);
                                        Serial.print(" tdo:");
                                        Serial.print(pinnames[tdo]);
                                        Serial.print("\n bits toggled:");
                                        Serial.print(bitstoggled);
                                        Serial.print("\n idcode buffer: ");
                                        Serial.print(idcodestr);
                                        Serial.print("  0x");
                                        Serial.println(idcode,HEX);
                                }
                                else if (bitstoggled || VERBOSE)
                                        Serial.println();
                        }
                }
        }
        Serial.print("================================\n");
}
static void shift_bypass()
{
        int tdi, tdo;
        int checkdataret;
        Serial.print(
        	"================================\n"
                "Starting shift of pattern through bypass...\n"
                "(assuming TDI->bypassreg->TDO state (no tck or tms))\n");
        for(tdi=0;tdi 1) {
                                Serial.print("active ");
                                Serial.print(" tdo:");
                                Serial.print(pinnames[tdo]);
                                Serial.print(" tdi:");
                                Serial.print(pinnames[tdi]);
                                Serial.print("  bits toggled:");
                                Serial.println(checkdataret);
                        }
                        else if(VERBOSE) Serial.println();
                }
        }
        Serial.print("================================\n");
}
void ir_state(char state[], int tck, int tms, int tdi) 
{
        tap_state(TAP_SHIFTIR, tck, tms);
#ifdef DEBUGIR
        Serial.print("ir set to: ");
#endif
        for (int i=0; i < IR_LEN; i++) {
                if (DELAY) delayMicroseconds(50);
                // TAP/TMS changes to Exit IR state (1) must be executed
                // at same time that the last TDI bit is sent:
                if (i == IR_LEN-1) {
                        digitalWrite(tms, HIGH); // ExitIR
#ifdef DEBUGIR
                        Serial.print("ExitIR");
#endif
                }
                pulse_tdi(tck, tdi, *state-'0');
                //                digitalWrite(tck, LOW);                
                //                digitalWrite(tdi, *state-'0'); // conv from ascii pattern
#ifdef DEBUGIR
                Serial.print(*state-'0', DEC);
#endif
                // TMS already set to 0 "shiftir" state to shift in bit to IR
                *state++;
        }
#ifdef DEBUGIR
        Serial.print("\nUpdateIR with ");
#endif
        // a reset would cause IDCODE instruction to be selected again
        tap_state("11", tck, tms); // UpdateIR & SelectDR
        tap_state("00", tck, tms); // CaptureDR & ShiftDR
}
static void sample(int iterations, int tck, int tms, int tdi, int tdo)
{
        Serial.print("================================\n"
                     "Starting sample (boundary scan)...\n"); 
        init_pins(tck, tms ,tdi);  
        // send instruction and go to ShiftDR
        ir_state(IR_SAMPLE, tck, tms, tdi);
        // Tell TAP to go to shiftout of selected data register (DR)
        // is determined by the instruction we sent, in our case 
        // SAMPLE/boundary scan
        for (int i=0; i ");
        i = 0;
        while(1) {
                c = Serial.read();
                switch(c) {
                case '0':
                case '1':
                        if(i < (PATTERN_LEN-1)) {
                                pattern[i++] = c;
                                Serial.print(c);
                        }
                        break;
                case '\n':
                case '\r':
                case '.': // bah. for the arduino serial console
                        pattern[i] = 0;
                        Serial.println();
                        Serial.print("new pattern set [");
                        Serial.print(pattern);
                        Serial.println("]");
                        return;
                }
        }
}
/*
 * main()
 */
void loop() {
        char c;
        if (Serial.available() > 0) {   
                c = Serial.read();
                byte result = 0;
                Serial.println(c);
                switch (c) {
                case 's':
                        scan();
                        break;
                case 'p':
                        set_pattern();
                        break;
                case '1':
                        init_pins(pins[1], pins[2], PIN_D2);
                        Serial.println(check_data(pattern, (2*PATTERN_LEN), pins[1],PIN_D1, PIN_D2) 
                                ? "found pattern or other" : "no pattern found");
                        init_pins(pins[1], pins[2], PIN_D2);
                        Serial.println(check_data(pattern, (2*PATTERN_LEN), pins[1],PIN_D2, PIN_D1) 
                                ? "found pattern or other" : "no pattern found");
                        break;
                case 'l':
                        loopback_check();
                        break;
                case 'i':
                        scan_idcode();
                        break;
                case 'b':
                        shift_bypass();
                        break;
                case 'x':
                        Serial.print("pins tck tms tdi tdo: ");
                        Serial.print(pinnames[0]); 
                        Serial.print(pinnames[1]); 
                        Serial.print(pinnames[3]);
                        Serial.println(pinnames[2]);
                        sample(SCAN_LEN+100, pins[0]/*tck*/, pins[1]/*tms*/, pins[3]/*tdi*/, pins[2]/*tdo*/);
                        break;
                case 'y':
                        brute_ir(SCAN_LEN, pins[0]/*tck*/, pins[1]/*tms*/, pins[3]/*tdi*/, pins[2]/*tdo*/);
                        break;
                case 'v':
                        VERBOSE = ~VERBOSE;
                        Serial.println(VERBOSE ? "Verbose ON" : "Verbose OFF");
                        break;
                case 'd':
                        DELAY = ~DELAY;
                        Serial.println(DELAY ? "Delay ON" : "Delay OFF");
                        break;
                case '-':
                        Serial.print("Delay microseconds: ");
                        if (DELAYUS != 0 && DELAYUS > 1000) DELAYUS-=1000;
                        else if (DELAYUS != 0 && DELAYUS <= 1000) DELAYUS-=100;
                        Serial.println(DELAYUS,DEC);
                        break;
                case '+':
                        Serial.print("Delay microseconds: ");
                        if (DELAYUS < 1000) DELAYUS+=100;
                        else DELAYUS+=1000;
                        Serial.println(DELAYUS,DEC);
                        break;
                case 'r':
                        PULLUP = ~PULLUP;
                        Serial.println(PULLUP ? "Pullups ON" : "Pullups OFF");
                        break;
                default:
                        Serial.println("unknown command");
                case 'h':
                        Serial.print("\n"
                                "s > scan\n"
                                "\n"
                                "l > loopback\n"
                                "    ignores tck,tms. if patterns passed to tdo pins are\n"
                                "    connected there is a short or a false-possitive\n"
                                "    condition exists that should be taken into account\n"
                                "\n"
                                "i > idcode scan\n"
                                "    ignores tdi. assumes IDCODE is default on reset state.\n"
                                "    sets TAP state to DR_SHIFT and prints TDO to console\n"
                                "    if TDO appears active. Human examination required to\n"
                                "    determine if actual IDCODE is present. Run several\n"
                                "    times to check for consistancy or compare against\n"
                                "    active tdo lines found with loopback test.\n"
                                "\n"
                                "b > shift_bypass\n"
                                "    currently broken. need to add tck\n"
                                "\n"
                                "x > sample (aka boundary scan)\n"
                                "\n"
                                "y > brute force IR search\n"
                                "\n"
                                "1 > single check\n"
                                "    runs a full check on one code-defined tdi<>tdo pair and\n"
                                "    you will need to look at the main()/loop() code to specify.\n"
                                "r > pullup resistors on inputs on/off\n"
                                "    might increase stability when using a bad patch cable.\n"
                                "v > verbose on/off\n"
                                "    print tdo bits to console during testing. will slow\n"
                                "    down scan.\n"
                                "d > delay on/off\n"
                                "    will slow down scan.\n"
                                "- > delay - 1000us (or 100us)\n"
                                "+ > delay + 1000us\n"
                                "p > set pattern ["
                                );
                        Serial.print(pattern);
                        Serial.println("]\n\n"
                                "h > help");
                        break;
                }
                Serial.print("\n> ");
        }
}