├── BitTwiddler.h ├── JTAGWhisperer.h ├── LICENSE ├── README.md ├── SerialComm.h ├── Utilities.h ├── examples └── JTAGWhisperer │ └── JTAGWhisperer.ino ├── keywords.txt ├── send_xsvf ├── test └── timer_test_1ms.xsvf └── xsvf └── XC9572XL ├── AlternateLEDsOnButton.xsvf ├── DeviceID.txt ├── DeviceID.xsvf └── ProgramButtonLED.xsvf /BitTwiddler.h: -------------------------------------------------------------------------------- 1 | /* 2 | The JTAG Whisperer: An Arduino library for JTAG. 3 | 4 | By Mike Tsao . 5 | 6 | Copyright © 2012 Mike Tsao. Use, modification, and distribution are 7 | subject to the BSD-style license as described in the accompanying 8 | LICENSE file. 9 | 10 | See README for complete attributions. 11 | */ 12 | 13 | #ifndef INCLUDE_JTAG_WHISPERER_BIT_TWIDDLER_H 14 | #define INCLUDE_JTAG_WHISPERER_BIT_TWIDDLER_H 15 | 16 | #include 17 | 18 | // Knows how to set MCU-specific pins in a JTAG-relevant way. 19 | class BitTwiddler { 20 | public: 21 | BitTwiddler() : portb_(0) { 22 | DDRB = TMS | TDI | TCK; 23 | } 24 | 25 | inline void pulse_clock() { 26 | clr_port(TCK); 27 | delayMicroseconds(1); 28 | set_port(TCK); 29 | } 30 | 31 | bool pulse_clock_and_read_tdo() { 32 | clr_port(TCK); 33 | delayMicroseconds(1); 34 | uint8_t pinb = PINB; 35 | set_port(TCK); 36 | return pinb & TDO; 37 | } 38 | 39 | void wait_time(unsigned long microsec) { 40 | unsigned long until = micros() + microsec; 41 | while (microsec--) { 42 | pulse_clock(); 43 | } 44 | while (micros() < until) { 45 | pulse_clock(); 46 | } 47 | } 48 | 49 | void set_tms() { 50 | set_port(TMS); 51 | } 52 | 53 | void clr_tms() { 54 | clr_port(TMS); 55 | } 56 | 57 | void set_tdi() { 58 | set_port(TDI); 59 | } 60 | 61 | void clr_tdi() { 62 | clr_port(TDI); 63 | } 64 | 65 | private: 66 | enum { 67 | TMS = _BV(PINB0), // Arduino 8 68 | TDI = _BV(PINB1), // Arduino 9 69 | TDO = _BV(PINB2), // Arduino 10 70 | TCK = _BV(PINB3) // Arduino 11 71 | }; 72 | 73 | inline void write_portb_if_tck(uint8_t pin) { 74 | if (pin == TCK) { 75 | PORTB = portb_; 76 | } 77 | } 78 | 79 | inline void set_port(uint8_t pin) { 80 | portb_ |= pin; 81 | write_portb_if_tck(pin); 82 | } 83 | 84 | inline void clr_port(uint8_t pin) { 85 | portb_ &= ~pin; 86 | write_portb_if_tck(pin); 87 | } 88 | 89 | // The current PORTB state. We write this only when we twiddle TCK. 90 | uint8_t portb_; 91 | }; 92 | 93 | #endif // INCLUDE_JTAG_WHISPERER_BIT_TWIDDLER_H 94 | -------------------------------------------------------------------------------- /JTAGWhisperer.h: -------------------------------------------------------------------------------- 1 | /* 2 | The JTAG Whisperer: An Arduino library for JTAG. 3 | 4 | By Mike Tsao . 5 | 6 | Copyright © 2012 Mike Tsao. Use, modification, and distribution are 7 | subject to the BSD-style license as described in the accompanying 8 | LICENSE file. 9 | 10 | See README for complete attributions. 11 | */ 12 | 13 | #ifndef INCLUDE_JTAG_WHISPERER_JTAG_WHISPERER_H 14 | #define INCLUDE_JTAG_WHISPERER_JTAG_WHISPERER_H 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | // These tables and the code that uses them are taken from 22 | // https://github.com/ben0109/XSVF-Player/. 23 | static const uint8_t tms_transitions[] = { 24 | 0x01, /* STATE_TLR */ 25 | 0x21, /* STATE_RTI */ 26 | 0x93, /* STATE_SELECT_DR_SCAN */ 27 | 0x54, /* STATE_CAPTURE_DR */ 28 | 0x54, /* STATE_SHIFT_DR */ 29 | 0x86, /* STATE_EXIT1_DR */ 30 | 0x76, /* STATE_PAUSE_DR */ 31 | 0x84, /* STATE_EXIT2_DR */ 32 | 0x21, /* STATE_UPDATE_DR */ 33 | 0x0a, /* STATE_SELECT_IR_SCAN */ 34 | 0xcb, /* STATE_CAPTURE_IR */ 35 | 0xcb, /* STATE_SHIFT_IR */ 36 | 0xfd, /* STATE_EXIT1_IR */ 37 | 0xed, /* STATE_PAUSE_IR */ 38 | 0xfb, /* STATE_EXIT2_IR */ 39 | 0x21, /* STATE_UPDATE_IR */ 40 | }; 41 | 42 | static const uint16_t tms_map[] = { 43 | 0x0000, /* STATE_TLR */ 44 | 0xfffd, /* STATE_RTI */ 45 | 0xfe03, /* STATE_SELECT_DR_SCAN */ 46 | 0xffe7, /* STATE_CAPTURE_DR */ 47 | 0xffef, /* STATE_SHIFT_DR */ 48 | 0xff0f, /* STATE_EXIT1_DR */ 49 | 0xffbf, /* STATE_PAUSE_DR */ 50 | 0xff0f, /* STATE_EXIT2_DR */ 51 | 0xfefd, /* STATE_UPDATE_DR */ 52 | 0x01ff, /* STATE_SELECT_IR_SCAN */ 53 | 0xf3ff, /* STATE_CAPTURE_IR */ 54 | 0xf7ff, /* STATE_SHIFT_IR */ 55 | 0x87ff, /* STATE_EXIT1_IR */ 56 | 0xdfff, /* STATE_PAUSE_IR */ 57 | 0x87ff, /* STATE_EXIT2_IR */ 58 | 0x7ffd, /* STATE_UPDATE_IR */ 59 | }; 60 | 61 | class JTAGWhisperer { 62 | public: 63 | JTAGWhisperer(SerialComm& serial_comm, BitTwiddler& twiddler) 64 | : serial_comm_(serial_comm), 65 | twiddler_(twiddler), 66 | current_state_(-1), 67 | sdrsize_bits_(0), 68 | sdrsize_bytes_(0), 69 | repeat_(32), 70 | runtest_(0), 71 | reached_xcomplete_(false) { 72 | } 73 | 74 | bool reached_xcomplete() { return reached_xcomplete_; } 75 | 76 | uint8_t read_next_instruction() { 77 | bp_ = instruction_buffer_; 78 | uint8_t instruction = serial_comm_.GetNextByte(); 79 | switch (instruction) { 80 | case XCOMPLETE: 81 | break; 82 | case XTDOMASK: 83 | get_next_bytes_from_stream(sdrsize_bytes_); 84 | break; 85 | case XSIR: { 86 | uint8_t length = serial_comm_.GetNextByte(); 87 | *bp_++ = length; 88 | get_next_bytes_from_stream(BYTES(length)); 89 | break; 90 | } 91 | case XSDR: 92 | case XSDRB: 93 | case XSDRC: 94 | case XSDRE: 95 | get_next_bytes_from_stream(sdrsize_bytes_); 96 | break; 97 | case XRUNTEST: 98 | get_next_bytes_from_stream(4); 99 | break; 100 | case XREPEAT: 101 | *bp_++ = serial_comm_.GetNextByte(); 102 | break; 103 | case XSDRSIZE: 104 | get_next_bytes_from_stream(4); 105 | break; 106 | case XSDRTDO: 107 | case XSDRTDOB: 108 | case XSDRTDOC: 109 | case XSDRTDOE: 110 | case XSETSDRMASKS: 111 | get_next_bytes_from_stream(sdrsize_bytes_ + sdrsize_bytes_); 112 | break; 113 | case XSTATE: 114 | *bp_++ = serial_comm_.GetNextByte(); 115 | break; 116 | case XWAIT: 117 | get_next_bytes_from_stream(6); 118 | break; 119 | case XSDRINC: 120 | default: 121 | serial_comm_.Important("Unexpected instruction %d", instruction); 122 | break; 123 | } 124 | return instruction; 125 | } 126 | 127 | bool handle_instruction(uint8_t instruction) { 128 | bp_ = instruction_buffer_; 129 | 130 | serial_comm_.Debug("Handling %s", instruction_name(instruction)); 131 | switch (instruction) { 132 | HANDLE(XCOMPLETE); 133 | HANDLE(XTDOMASK); 134 | HANDLE(XSIR); 135 | HANDLE(XSDR); 136 | HANDLE(XRUNTEST); 137 | HANDLE(XREPEAT); 138 | HANDLE(XSDRSIZE); 139 | HANDLE(XSDRTDO); 140 | HANDLE(XSTATE); 141 | HANDLE(XWAIT); 142 | default: 143 | serial_comm_.Debug("Unimplemented instruction: %d", instruction); 144 | return false; 145 | } 146 | } 147 | 148 | private: 149 | enum { 150 | XCOMPLETE = 0, 151 | XTDOMASK, 152 | XSIR, 153 | XSDR, 154 | XRUNTEST, 155 | XRESERVED_5, 156 | XRESERVED_6, 157 | XREPEAT, 158 | XSDRSIZE, 159 | XSDRTDO, 160 | XSETSDRMASKS, 161 | XSDRINC, 162 | XSDRB, 163 | XSDRC, 164 | XSDRE, 165 | XSDRTDOB, 166 | XSDRTDOC, 167 | XSDRTDOE, 168 | XSTATE, 169 | XENDIR, 170 | XENDDR, 171 | XSIR2, 172 | XCOMMENT, 173 | XWAIT 174 | }; 175 | const char *instruction_name(uint8_t instruction) { 176 | switch (instruction) { 177 | NAME_FOR(XCOMPLETE); 178 | NAME_FOR(XTDOMASK); 179 | NAME_FOR(XSIR); 180 | NAME_FOR(XSDR); 181 | NAME_FOR(XRUNTEST); 182 | NAME_FOR(XREPEAT); 183 | NAME_FOR(XSDRSIZE); 184 | NAME_FOR(XSDRTDO); 185 | NAME_FOR(XSETSDRMASKS); 186 | NAME_FOR(XSDRINC); 187 | NAME_FOR(XSDRB); 188 | NAME_FOR(XSDRC); 189 | NAME_FOR(XSDRE); 190 | NAME_FOR(XSDRTDOB); 191 | NAME_FOR(XSDRTDOC); 192 | NAME_FOR(XSDRTDOE); 193 | NAME_FOR(XSTATE); 194 | NAME_FOR(XENDIR); 195 | NAME_FOR(XENDDR); 196 | NAME_FOR(XSIR2); 197 | NAME_FOR(XCOMMENT); 198 | NAME_FOR(XWAIT); 199 | default: 200 | return "XWTF"; 201 | } 202 | } 203 | 204 | enum { 205 | STATE_TLR, 206 | STATE_RTI, 207 | STATE_SELECT_DR_SCAN, 208 | STATE_CAPTURE_DR, 209 | STATE_SHIFT_DR, 210 | STATE_EXIT1_DR, 211 | STATE_PAUSE_DR, 212 | STATE_EXIT2_DR, 213 | STATE_UPDATE_DR, 214 | STATE_SELECT_IR_SCAN, 215 | STATE_CAPTURE_IR, 216 | STATE_SHIFT_IR, 217 | STATE_EXIT1_IR, 218 | STATE_PAUSE_IR, 219 | STATE_EXIT2_IR, 220 | STATE_UPDATE_IR, 221 | }; 222 | const char *state_name(uint8_t state) { 223 | switch (state) { 224 | NAME_FOR(STATE_TLR); 225 | NAME_FOR(STATE_RTI); 226 | NAME_FOR(STATE_SELECT_DR_SCAN); 227 | NAME_FOR(STATE_CAPTURE_DR); 228 | NAME_FOR(STATE_SHIFT_DR); 229 | NAME_FOR(STATE_EXIT1_DR); 230 | NAME_FOR(STATE_PAUSE_DR); 231 | NAME_FOR(STATE_EXIT2_DR); 232 | NAME_FOR(STATE_UPDATE_DR); 233 | NAME_FOR(STATE_SELECT_IR_SCAN); 234 | NAME_FOR(STATE_CAPTURE_IR); 235 | NAME_FOR(STATE_SHIFT_IR); 236 | NAME_FOR(STATE_EXIT1_IR); 237 | NAME_FOR(STATE_PAUSE_IR); 238 | NAME_FOR(STATE_EXIT2_IR); 239 | NAME_FOR(STATE_UPDATE_IR); 240 | default: 241 | return "STATE_WTF"; 242 | } 243 | } 244 | 245 | void get_next_bytes_from_stream(uint8_t count) { 246 | while (count--) { 247 | *bp_++ = serial_comm_.GetNextByte(); 248 | } 249 | } 250 | 251 | uint8_t get_next_byte() { 252 | return *bp_++; 253 | } 254 | 255 | uint16_t get_next_word() { 256 | return (uint16_t)get_next_byte() << 8 | get_next_byte(); 257 | } 258 | 259 | uint32_t get_next_long() { 260 | return (uint32_t)get_next_byte() << 24 | 261 | (uint32_t)get_next_byte() << 16 | 262 | (uint32_t)get_next_byte() << 8 | 263 | get_next_byte(); 264 | } 265 | 266 | void get_next_bytes(uint8_t* data, uint8_t count) { 267 | while (count--) { 268 | *data++ = get_next_byte(); 269 | } 270 | } 271 | 272 | bool handle_XCOMPLETE() { 273 | serial_comm_.Important("XCOMPLETE"); 274 | reached_xcomplete_ = true; 275 | return false; 276 | } 277 | 278 | bool handle_XTDOMASK() { 279 | get_next_bytes(tdomask_, sdrsize_bytes_); 280 | serial_comm_.DebugBytes("... tdomask now ", tdomask_, sdrsize_bytes_); 281 | return true; 282 | } 283 | 284 | bool handle_XSIR() { 285 | uint8_t bits = get_next_byte(); 286 | get_next_bytes(tdi_, BYTES(bits)); 287 | state_goto(STATE_SHIFT_IR); 288 | shift_td(tdi_, bits, true); 289 | state_goto(STATE_RTI); 290 | return true; 291 | } 292 | 293 | bool handle_XSDR() { 294 | get_next_bytes(tdi_, sdrsize_bytes_); 295 | serial_comm_.DebugBytes("... sending ", tdi_, sdrsize_bytes_); 296 | return sdr(true, true, true); 297 | } 298 | 299 | bool handle_XRUNTEST() { 300 | runtest_ = get_next_long(); 301 | serial_comm_.Debug("... runtest now %ld", runtest_); 302 | return true; 303 | } 304 | 305 | bool handle_XREPEAT() { 306 | repeat_ = get_next_byte(); 307 | serial_comm_.Debug("... repeat now %d", repeat_); 308 | return true; 309 | } 310 | 311 | bool handle_XSDRSIZE() { 312 | sdrsize_bits_ = get_next_long(); 313 | sdrsize_bytes_ = BYTES(sdrsize_bits_); 314 | serial_comm_.Debug("... sdrsize now %d/%d", sdrsize_bits_, sdrsize_bytes_); 315 | return true; 316 | } 317 | 318 | bool handle_XSDRTDO() { 319 | get_next_bytes(tdi_, sdrsize_bytes_); 320 | get_next_bytes(tdo_expected_, sdrsize_bytes_); 321 | serial_comm_.DebugBytes("... sending ", tdi_, sdrsize_bytes_); 322 | serial_comm_.DebugBytes("... expecting ", tdo_expected_, sdrsize_bytes_); 323 | return sdr(true, true, true); 324 | } 325 | 326 | bool is_tdo_as_expected() { 327 | serial_comm_.DebugBytes("... received ", tdo_, sdrsize_bytes_); 328 | for (int i = 0; i < sdrsize_bytes_; ++i) { 329 | uint8_t expected = tdo_expected_[i] & tdomask_[i]; 330 | uint8_t actual = tdo_[i] & tdomask_[i]; 331 | if (expected != actual) { 332 | serial_comm_.Debug("... NO MATCH."); 333 | return false; 334 | } 335 | } 336 | serial_comm_.Debug("... match!"); 337 | return true; 338 | } 339 | 340 | bool handle_XSTATE() { 341 | state_goto(get_next_byte()); 342 | return true; 343 | } 344 | 345 | bool handle_XWAIT() { 346 | state_goto(get_next_byte()); 347 | uint8_t end_state = get_next_byte(); 348 | uint32_t wait_time_usec = get_next_long(); 349 | wait_time(wait_time_usec); 350 | state_goto(end_state); 351 | return true; 352 | } 353 | 354 | void shift_td(uint8_t *data, uint16_t data_length_bits, bool is_end) { 355 | int byte_count = BYTE_COUNT(data_length_bits); 356 | 357 | for (int i = 0; i < byte_count; ++i) { 358 | uint8_t byte_out = data[byte_count - 1 - i]; 359 | uint8_t tdo_byte = 0; 360 | for (int j = 0; j < 8 && data_length_bits-- > 0; ++j) { 361 | if (data_length_bits == 0 && is_end) { 362 | twiddler_.set_tms(); 363 | state_ack(1); 364 | } 365 | 366 | if (byte_out & 1) { 367 | twiddler_.set_tdi(); 368 | } else { 369 | twiddler_.clr_tdi(); 370 | } 371 | byte_out >>= 1; 372 | bool tdo = twiddler_.pulse_clock_and_read_tdo(); 373 | tdo_byte |= tdo << j; 374 | } 375 | tdo_[byte_count - 1 - i] = tdo_byte; 376 | } 377 | } 378 | 379 | bool sdr(bool should_begin, bool should_end, bool should_check) { 380 | int attempts_left = repeat_; 381 | bool matched = false; 382 | 383 | if (should_begin) { 384 | state_goto(STATE_SHIFT_DR); 385 | } 386 | 387 | while (!matched && attempts_left-- > 0) { 388 | shift_td(tdi_, sdrsize_bits_, should_end); 389 | if (should_check) { 390 | if (is_tdo_as_expected()) { 391 | matched = true; 392 | } else { 393 | state_goto(STATE_PAUSE_DR); 394 | state_goto(STATE_SHIFT_DR); 395 | state_goto(STATE_RTI); 396 | wait_time(runtest_); 397 | state_goto(STATE_SHIFT_DR); 398 | } 399 | } 400 | } 401 | if (should_check && !matched) { 402 | serial_comm_.Important("SDR check failed."); 403 | return false; 404 | } 405 | if (should_end) { 406 | state_goto(STATE_RTI); 407 | } 408 | wait_time(runtest_); 409 | return true; 410 | } 411 | 412 | void wait_time(uint32_t microseconds) { 413 | serial_comm_.Debug("Waiting %ld microseconds...", microseconds); 414 | uint32_t until = micros() + microseconds; 415 | while (microseconds--) { 416 | twiddler_.pulse_clock(); 417 | } 418 | while (micros() < until) { 419 | twiddler_.pulse_clock(); 420 | } 421 | } 422 | 423 | void set_state(int state) { 424 | current_state_ = state; 425 | } 426 | 427 | void state_ack(bool tms) { 428 | if (tms) { 429 | set_state((tms_transitions[current_state_] >> 4) & 0xf); 430 | } else { 431 | set_state(tms_transitions[current_state_] & 0xf); 432 | } 433 | } 434 | 435 | void state_step(bool tms) { 436 | if (tms) { 437 | twiddler_.set_tms(); 438 | } else { 439 | twiddler_.clr_tms(); 440 | } 441 | twiddler_.pulse_clock(); 442 | state_ack(tms); 443 | } 444 | 445 | void state_goto(int state) { 446 | if (state == STATE_TLR) { 447 | for (int i = 0; i < 5; ++i) { 448 | state_step(true); 449 | } 450 | } else { 451 | while (current_state_ != state) { 452 | state_step((tms_map[current_state_] >> state) & 1); 453 | } 454 | } 455 | } 456 | 457 | SerialComm serial_comm_; 458 | BitTwiddler twiddler_; 459 | uint8_t current_state_; 460 | uint8_t sdrsize_bits_; 461 | uint8_t sdrsize_bytes_; 462 | uint8_t repeat_; 463 | uint32_t runtest_; 464 | bool reached_xcomplete_; 465 | 466 | enum { 467 | BUFFER_SIZE = 32 468 | }; 469 | uint8_t instruction_buffer_[BUFFER_SIZE]; 470 | uint8_t* bp_; 471 | uint8_t tdi_[BUFFER_SIZE]; 472 | uint8_t tdo_[BUFFER_SIZE]; 473 | uint8_t tdomask_[BUFFER_SIZE]; 474 | uint8_t tdo_expected_[BUFFER_SIZE]; 475 | }; 476 | 477 | #endif // INCLUDE_JTAG_WHISPERER_JTAG_WHISPERER_H 478 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Mike Tsao (author). All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR 14 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 15 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 16 | EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 17 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 18 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are 25 | those of the authors and should not be interpreted as representing official 26 | policies, either expressed or implied, of the author. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The JTAG Whisperer 2 | ================== 3 | 4 | With this library, your Arduino is an XSVF player. Use it to program CPLDs and FPGAs! 5 | 6 | Warning 7 | ======= 8 | 9 | **Most Arduinos are 5-volt devices. Many JTAG devices are not 5-volt devices and will die if they get anywhere near 5 volts. Be sure your device is 5-volt tolerant or that you use an appropriate buffer for the I/O lines!** 10 | 11 | Instructions 12 | ============ 13 | 14 | You'll need Python on your desktop, as well as the Python library pyserial (try `easy_install pyserial` or `pip install pyserial` to get it). You'll also need a target JTAG-speaking board of some kind, an Arduino, and four jumpers to connect four of the Arduino pins to your target board's JTAG headers. 15 | 16 | 1. Find your Arduino IDE's sketches directory. This is the directory where you keep your own sketches. 17 | 18 | 1. If there isn't already one, create a `libraries` directory inside the sketches directory. 19 | 20 | 1. Unzip the project archive into `libraries`. It should end up looking like this: `[Your Arduino sketches directory]/libraries/JTAGWhisperer/JTAGWhisperer.h (etc.)` 21 | 22 | 1. Restart your Arduino IDE and you should see JTAGWhisperer among the libraries in the Sketch -> Import Library... menu. 23 | 24 | 1. Open the example JTAGWhisperer sketch. Upload it to your Arduino. 25 | 26 | 1. Get a Xilinx XC9572XL CPLD. Hook up your CPLD's JTAG pins to the Arduino: pins 8, 9, 10, 11 to TMS, TDI, TDO, and TCK, respectively. (If you have TCK on pin 11, you probably did it right.) On an Arduino MEGA or MEGA2560, use pins 53, 52, 51, 50 accordingly. **Re-read the warning above about I/O voltage. You just hooked up 5-volt I/O to your device, and that might let out the magic smoke!** 27 | 28 | 1. Apply power to the CPLD (note that the XC9500XL series are 3.3-volt devices!). Do not forget to set the '9572 VIO to 3.3V. **DID I REMIND YOU THAT VOLTAGES MATTER?** Try asking it its device ID as follows, from the root of the source directory: 29 | 30 | `./send_xsvf -p /dev/tty.your_arduino_serial_port xsvf/XC9572XL/DeviceID.xsvf` 31 | 32 | 1. You should see a back-and-forth that ends up with a "Success" message. If you rebuild the sketch with the DEBUG messages enabled in SerialComm.h, you'll see the actual device ID that's reported. 33 | 34 | 1. Now you're ready to write data to your CPLD. Execute either of these: 35 | 36 | `./send_xsvf -p /dev/tty.your_arduino_serial_port xsvf/XC9572XL/ProgramButtonLED.xsvf` 37 | 38 | `./send_xsvf -p /dev/tty.your_arduino_serial_port xsvf/XC9572XL/AlternateLEDsOnButton.xsvf` 39 | 40 | The first one blinks D1 when you press the button. The second one alternates between D1 and D2 when you hold down the button. 41 | 42 | Generate your own XSVF files using Xilinx iMPACT, or download them from the web. Just make sure they're for your device. 43 | 44 | Credits 45 | ======= 46 | 47 | By [Mike Tsao](http://www.sowbug.com/). Project on [GitHub](https://github.com/sowbug/JTAGWhisperer/). 48 | 49 | Many thanks to [Ben](https://github.com/ben0109/XSVF-Player/), who adapted the Xilinx XAPP058 code into something comprehensible. This code evolved from his project, and all the clever parts are his. 50 | 51 | The guys over at [Dangerous Prototypes](http://dangerousprototypes.com/) sell an XC9572XL breakout board. It's cheap, it's open-source hardware, and their Bus Pirate tool will also program CPLDs. The "AlternateLEDsOnButton" XSVF is taken directly from their CPLD tutorials. DP are the guys who got me into CPLDs in the first place. Check them out and buy their stuff! 52 | -------------------------------------------------------------------------------- /SerialComm.h: -------------------------------------------------------------------------------- 1 | /* 2 | The JTAG Whisperer: An Arduino library for JTAG. 3 | 4 | By Mike Tsao . 5 | 6 | Copyright © 2012 Mike Tsao. Use, modification, and distribution are 7 | subject to the BSD-style license as described in the accompanying 8 | LICENSE file. 9 | 10 | See README for complete attributions. 11 | */ 12 | 13 | #ifndef INCLUDE_JTAG_WHISPERER_SERIAL_COMM_H 14 | #define INCLUDE_JTAG_WHISPERER_SERIAL_COMM_H 15 | 16 | #include 17 | 18 | #define DEBUG 0 19 | 20 | class SerialComm { 21 | public: 22 | SerialComm() 23 | : read_ptr_(buffer_), 24 | write_ptr_(buffer_), 25 | stream_sum_(0), 26 | stream_count_(0) { 27 | Serial.begin(115200); 28 | Ready("XSVF"); 29 | } 30 | 31 | virtual ~SerialComm() { 32 | Important("Checksum %lx/%lx.", stream_sum_, stream_count_); 33 | Quit("Exiting!"); 34 | } 35 | 36 | uint8_t GetNextByte() { 37 | uint8_t c = get(); 38 | stream_sum_ += c; 39 | ++stream_count_; 40 | return c; 41 | } 42 | 43 | void Important(const char* format, ...) { 44 | va_list args; 45 | va_start(args, format); 46 | 47 | char tmp[128]; 48 | vsnprintf(tmp, 128, format, args); 49 | 50 | va_end(args); 51 | 52 | Serial.print("! "); 53 | Serial.println(tmp); 54 | } 55 | 56 | void Ready(const char* message) { 57 | Serial.print("\nR"); 58 | Serial.println(message); 59 | } 60 | 61 | void Quit(const char* message) { 62 | Serial.print("\nQ "); 63 | Serial.println(message); 64 | } 65 | 66 | #if DEBUG == 1 67 | void Debug(const char* format, ...) { 68 | va_list args; 69 | va_start(args, format); 70 | 71 | char tmp[128]; 72 | vsnprintf(tmp, 128, format, args); 73 | 74 | va_end(args); 75 | 76 | Serial.print("D "); 77 | Serial.println(tmp); 78 | } 79 | 80 | void DebugBytes(const char* s, const uint8_t* p, uint8_t n) { 81 | Serial.print("D "); 82 | Serial.print(s); 83 | print_bytes(p, n); 84 | Serial.println(); 85 | } 86 | #else 87 | void Debug(const char* format, ...) {} 88 | void DebugBytes(const char* s, const uint8_t* p, uint8_t n) {} 89 | #endif 90 | 91 | private: 92 | void p(const char *fmt, ... ) { 93 | char tmp[128]; 94 | va_list args; 95 | va_start(args, fmt); 96 | vsnprintf(tmp, 128, fmt, args); 97 | va_end(args); 98 | Serial.print(tmp); 99 | } 100 | 101 | void print_bytes(const uint8_t* pb, uint8_t count) { 102 | while (count--) { 103 | p("%02x", *pb++); 104 | } 105 | } 106 | 107 | uint8_t get() { 108 | if (!available()) { 109 | fill(); 110 | } 111 | uint8_t next_byte = *read_ptr_++; 112 | if (read_ptr_ == buffer_ + BUFFER_SIZE) { 113 | read_ptr_ -= BUFFER_SIZE; 114 | } 115 | return next_byte; 116 | } 117 | 118 | bool available() { 119 | return read_ptr_ != write_ptr_; 120 | } 121 | 122 | void fill() { 123 | load(); 124 | if (!available()) { 125 | Ready("SEND"); 126 | int delay_time = 5; 127 | do { 128 | delay(delay_time); 129 | load(); 130 | delay_time = delay_time + delay_time; 131 | } while (!available()); 132 | } 133 | } 134 | 135 | void load() { 136 | while (Serial.available() > 0) { 137 | uint8_t c = Serial.read(); 138 | *write_ptr_++ = c; 139 | if (write_ptr_ == buffer_ + BUFFER_SIZE) { 140 | write_ptr_ -= BUFFER_SIZE; 141 | } 142 | if (!available()) { 143 | Important("Overran serial buffer"); 144 | } 145 | } 146 | } 147 | 148 | enum { BUFFER_SIZE = 128 }; 149 | uint8_t buffer_[BUFFER_SIZE]; 150 | uint8_t* read_ptr_; 151 | uint8_t* write_ptr_; 152 | uint32_t stream_sum_; 153 | uint32_t stream_count_; 154 | }; 155 | 156 | #endif // INCLUDE_JTAG_WHISPERER_SERIAL_COMM_H 157 | -------------------------------------------------------------------------------- /Utilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | The JTAG Whisperer: An Arduino library for JTAG. 3 | 4 | By Mike Tsao . 5 | 6 | Copyright © 2012 Mike Tsao. Use, modification, and distribution are 7 | subject to the BSD-style license as described in the accompanying 8 | LICENSE file. 9 | 10 | See README for complete attributions. 11 | */ 12 | 13 | #ifndef INCLUDE_JTAG_WHISPERER_UTILITIES_H 14 | #define INCLUDE_JTAG_WHISPERER_UTILITIES_H 15 | 16 | #include 17 | #include 18 | 19 | #define BYTE_COUNT(bit_count) ((int)((bit_count + 7) >> 3)) 20 | #define BYTES(bits) ((int)((bits + 7) >> 3)) 21 | #define HANDLE(x) case x: return handle_##x() 22 | #define NAME_FOR(x) case x: return #x; 23 | 24 | #endif // INCLUDE_JTAG_WHISPERER_UTILITIES_H 25 | -------------------------------------------------------------------------------- /examples/JTAGWhisperer/JTAGWhisperer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | The JTAG Whisperer: An Arduino library for JTAG. 3 | 4 | By Mike Tsao . 5 | 6 | Copyright © 2012 Mike Tsao. Use, modification, and distribution are 7 | subject to the BSD-style license as described in the accompanying 8 | LICENSE file. 9 | 10 | See README for complete attributions. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | const int BLINK_PIN = 13; 18 | static bool is_pin_on; 19 | void blink() { 20 | digitalWrite(BLINK_PIN, is_pin_on); 21 | is_pin_on = !is_pin_on; 22 | } 23 | 24 | void setup() { 25 | pinMode(BLINK_PIN, OUTPUT); 26 | for (int i = 0; i < 10; ++i) { 27 | blink(); 28 | delay(50); 29 | } 30 | } 31 | 32 | // This is out of loop() so we get RAII without looping. 33 | void do_it() { 34 | SerialComm serial_comm; 35 | BitTwiddler twiddler; 36 | JTAGWhisperer whisperer(serial_comm, twiddler); 37 | uint32_t instruction_count = 0; 38 | 39 | while (true) { 40 | blink(); 41 | uint8_t instruction = whisperer.read_next_instruction(); 42 | if (!whisperer.handle_instruction(instruction)) { 43 | if (!whisperer.reached_xcomplete()) { 44 | serial_comm.Important("Failure at instruction #%d", instruction_count); 45 | } 46 | break; 47 | } 48 | ++instruction_count; 49 | } 50 | serial_comm.Important("Processed %d instructions.", instruction_count); 51 | } 52 | 53 | void loop() { 54 | do_it(); 55 | while (true) { 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sowbug/JTAGWhisperer/8fb46c40c5c3793befea3821523f99909dfa7e5f/keywords.txt -------------------------------------------------------------------------------- /send_xsvf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # The JTAG Whisperer https://github.com/sowbug/JTAGWhisperer 4 | # Copyright 2012 Mike Tsao http://www.sowbug.com/ 5 | # 6 | # See LICENSE file for BSD-style licensing information. 7 | # 8 | 9 | import binascii 10 | from time import time 11 | import getopt 12 | import sys 13 | 14 | try: 15 | import serial 16 | except ImportError: 17 | print """ 18 | You don't have pyserial installed. Please install it and try again. 19 | "easy_install pyserial" or "pip install pyserial" will probably work. 20 | """ 21 | 22 | total_bytes_sent = 0 23 | need_lf = False 24 | def send_xsvf_chunk(s, xsvf): 25 | global total_bytes_sent, need_lf 26 | if len(xsvf) == 0: 27 | print 'Problem: tried to send empty xsvf.' 28 | return xsvf 29 | chunk = xsvf[:32] 30 | xsvf = xsvf[len(chunk):] 31 | bytes_to_write = len(chunk) 32 | while bytes_to_write > 0: 33 | bytes_written = s.write(chunk) 34 | total_bytes_sent += bytes_written 35 | chunk = chunk[bytes_written:] 36 | bytes_to_write -= bytes_written 37 | print '\rSent: %8d bytes, %8d remaining' % (total_bytes_sent, len(xsvf)), 38 | need_lf = True 39 | sys.stdout.flush() 40 | return xsvf 41 | 42 | def get_xsvf_checksum(xsvf): 43 | xsvf_sum = 0 44 | for c in xsvf: 45 | xsvf_sum += ord(c) 46 | return (xsvf_sum, len(xsvf)) 47 | 48 | def maybe_print_lf(): 49 | global need_lf 50 | if need_lf: 51 | need_lf = False 52 | print 53 | 54 | def program(xsvf_filename, port, bps): 55 | global total_bytes_sent 56 | start_time = 0 57 | 58 | f = open(xsvf_filename, 'rb') 59 | xsvf = f.read() 60 | f.close() 61 | print 'Ready to send file of size %d bytes.' % (len(xsvf)) 62 | 63 | (xsvf_sum, xsvf_len) = get_xsvf_checksum(xsvf) 64 | 65 | s = serial.Serial(port=port, baudrate=bps, rtscts=True) 66 | s.flushInput() 67 | s.flushOutput() 68 | 69 | is_device_ready = False 70 | while True: 71 | line = s.readline().strip() 72 | if len(line) == 0: 73 | continue 74 | command = line[0] 75 | text = line[1:].strip() 76 | 77 | if command == 'R': 78 | if text == 'XSVF': 79 | if not is_device_ready: 80 | is_device_ready = True 81 | print 'Device is ready.' 82 | start_time = time() 83 | continue 84 | elif text == 'SEND': 85 | xsvf = send_xsvf_chunk(s, xsvf) 86 | continue 87 | else: 88 | print 'Unrecognized ready command:', text 89 | elif command == 'Q': 90 | maybe_print_lf() 91 | print 'Received device quit:', text 92 | break 93 | elif command == 'D': 94 | maybe_print_lf() 95 | print 'Device:', text 96 | elif command == '!': 97 | maybe_print_lf() 98 | print 'IMPORTANT:', text 99 | else: 100 | maybe_print_lf() 101 | print 'Unrecognized line:', line 102 | 103 | s.close() 104 | print 'Expected checksum: %lx/%lx.' % (xsvf_sum, xsvf_len) 105 | if start_time > 0: 106 | print 'Elapsed time: %.02f seconds.' % (time() - start_time) 107 | return 0 108 | 109 | def usage(): 110 | print 'send_xsvf [-b bps] -p /dev/your_arduino_serial_port xsvf_filename' 111 | 112 | def main(): 113 | try: 114 | opts, args = getopt.getopt(sys.argv[1:], 'b:p:', ['bps=', 'port=']) 115 | except getopt.GetoptError, err: 116 | print str(err) 117 | usage() 118 | sys.exit(2) 119 | xsvf_filename = args[0] 120 | port = None 121 | bps = 115200 122 | 123 | for o, a in opts: 124 | if o in ['-b', '--bps']: 125 | bps = int(a) 126 | elif o in ['-p', '--port']: 127 | port = a 128 | else: 129 | assert False, 'unhandled option' 130 | 131 | if port is None: 132 | usage() 133 | sys.exit(2) 134 | 135 | sys.exit(program(xsvf_filename, port, bps)) 136 | 137 | if __name__ == '__main__': 138 | main() 139 | -------------------------------------------------------------------------------- /test/timer_test_1ms.xsvf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sowbug/JTAGWhisperer/8fb46c40c5c3793befea3821523f99909dfa7e5f/test/timer_test_1ms.xsvf -------------------------------------------------------------------------------- /xsvf/XC9572XL/AlternateLEDsOnButton.xsvf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sowbug/JTAGWhisperer/8fb46c40c5c3793befea3821523f99909dfa7e5f/xsvf/XC9572XL/AlternateLEDsOnButton.xsvf -------------------------------------------------------------------------------- /xsvf/XC9572XL/DeviceID.txt: -------------------------------------------------------------------------------- 1 | 07 20 ...................... XREPEAT, 0x20 times 2 | 12 00 ...................... XSTATE Test-Logic-Reset 3 | 12 01 ...................... XSTATE Run-Test/Idle 4 | 04 00 00 00 00 ............. XRUNTEST 5 | 02 08 fe ................... XSIR, 8 bits, value 0xfe 6 | 08 00 00 00 20 ............. XSDRSIZE 32 bits 7 | 01 0f ff ff ff ............. XTDOMASK 8 | 09 00 00 00 00 f9 60 40 93 . XSDRTDO, TDI 00000000 TDO f9604093 9 | 02 08 ff ................... XSIR, 8 bits, value 0xff 10 | 02 08 fe ................... XSIR, 8 bits, value 0xfe 11 | 09 00 00 00 00 f9 60 40 93 : XSDRTDO, TDI 00000000 TDO f9604093 12 | 02 08 ff ................... XSIR, 8 bits, value 0xff 13 | 02 08 fe ................... XSIR, 8 bits, value 0xfe 14 | 09 00 00 00 00 f9 60 40 93 . XSDRTDO, TDI 00000000 TDO f9604093 15 | 07 00 ...................... XREPEAT, 0 times 16 | 07 20 ...................... XREPEAT, 0x20 times 17 | 12 00 ...................... XSTATE Test-Logic-Reset 18 | 12 01 ...................... XSTATE Run-Test/Idle 19 | 04 00 00 00 00 ............. XRUNTEST 20 | 02 08 ff ................... XSIR, 8 bits, value 0xff 21 | 08 00 00 00 01 ............. XSDRSIZE 1 bit 22 | 01 00 ...................... TDOMASK 0 23 | 09 00 00 ................... XSDRTDO, TDI 0, TDO 0 24 | 00 ......................... XCOMPLETE 25 | -------------------------------------------------------------------------------- /xsvf/XC9572XL/DeviceID.xsvf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sowbug/JTAGWhisperer/8fb46c40c5c3793befea3821523f99909dfa7e5f/xsvf/XC9572XL/DeviceID.xsvf -------------------------------------------------------------------------------- /xsvf/XC9572XL/ProgramButtonLED.xsvf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sowbug/JTAGWhisperer/8fb46c40c5c3793befea3821523f99909dfa7e5f/xsvf/XC9572XL/ProgramButtonLED.xsvf --------------------------------------------------------------------------------