Yes, that's what it says. This is very much an advanced, "really gotta know how to compile stuff" method. But it does work, as evidenced by the now-8.5-Gh/s Jalapeno sitting right next to me still plugged in to the Arduino. It just took about 3 hours to actually run the flashing process.
You need:
- Arduino - any kind with at least 5 I/O ports and a serial connection capable of a solid, error-free high speed 115,200bps connection
- Access to a Linux environment
- A way to plug 6 pins from the JTAG connector into an Arduino - either a 10-pin ribbon cable and some jumper wires, or some M/F jumper wires.
Pins:
..| 2 | 4 | 6 | 8 |10| ... | x | x...
..| 1 | 3 | 5 | 7 | 9 | ... | x | x...
.(x)..(x).(x).(x).(x)...................
You know the story here, but probably only half of it. The JTAG connector might be under your heat sink or, as is the case with mine, a nice notch is already cut out of the heat sink just for the occasion. As in the fine ASCII art above, the connector's viewed with the unusually-cramped unpopulated 5-pin header towards you.
(pinout from the
Raspberry Pi guide)
Signal | Jalapeno JTAG pin | Arduino pin |
JTAG_TCK | 1 | 3 |
JTAG_TMS | 5 | 2 |
JTAG_TDI | 9 | 4 |
JTAG_TDO | 3 | 6 |
MASTER_RESET | 6 | 7 (optional) |
GND | 2 or 10 | GND |
And now, the braindump. Here's the Arduino program:
/*
This file is part of arduiggler.
arduiggler 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.
arduiggler 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 arduiggler. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Arduino Wiggler cable simulator
ver 1.0 originally written by jtrimble for tjtag
http://www.dd-wrt.com/phpBB2/viewtopic.php?p=435578&sid=0c7c7b114dc7239be2620514cecbe2d8
*/
////////////////////////////////////////////////////////////////////////////////
// Pins 0-7 are part of PORTD
// pins 0 and 1 are RX and TX, respectively
const int pinGP0 = 7;
const int pinTMS0 = 2;
const int pinTCK0 = 3;
const int pinTDI0 = 4;
const int pinTRST0 = 5;
const int pinTDO0 = 6;
const int pinLED = 13;
const long DEFAULT_RS232_RATE = 115200L;
const char *APP_NAME = "arduiggler";
const char *APP_VER = "3.00";
////////////////////////////////////////////////////////////////////////////////
const byte CMD_RESET = 0x74; //ASCII for t
const byte CMD_STATUS = 0x3F; //ASCII for ?
const byte CMD_GETVER = 0x61; //ASCII for a
const byte CMD_CLOCK = 0x63; //ASCII for c
const byte CMD_HITDI = 0x68; //ASCII for h
const byte CMD_LOTDI = 0x6C; //ASCII for l
const byte CMD_HITDIMS= 0x48; //ASCII for H
const byte CMD_LOTDIMS= 0x4C; //ASCII for L
const byte CMD_READ = 0x72; //ASCII for r
const byte CMD_FORCE = 0x66; //ASCII for f
const int STATUS_OK = 0x6B; //ASCII for ok
const int STATUS_ERR1 = 0x31; //ASCII for e1
const int STATUS_ERR2 = 0x32; //ASCII for e2
const byte MASK_TDI = 0x01;
const byte MASK_TCK = 0x02;
const byte MASK_TMS = 0x04;
const byte MASK_TRST = 0x08;
const byte MASK_GP0 = 0x10;
const byte MASK_GP1 = 0x20;
const byte MASK_GP2 = 0x40;
const byte MASK_GP3 = 0x80;
byte status;
////////////////////////////////////////////////////////////////////////////////
/*
Wapper functions for clear/set individual pins.
Just in case it needs to be replaced with direct pins access
*/
inline void setGP0() {
digitalWrite(pinGP0, HIGH);
}
inline void clrGP0() {
digitalWrite(pinGP0, LOW);
}
inline void setTRST0() {
digitalWrite(pinTRST0, HIGH);
}
inline void clrTRST0() {
digitalWrite(pinTRST0, LOW);
}
inline void setTMS0() {
digitalWrite(pinTMS0, HIGH);
}
inline void clrTMS0() {
digitalWrite(pinTMS0, LOW);
}
inline void setTCK0() {
digitalWrite(pinTCK0, HIGH);
}
inline void clrTCK0() {
digitalWrite(pinTCK0, LOW);
}
inline void toggleTCK0() {
//assume it is LOW by default
digitalWrite(pinTCK0, HIGH);
digitalWrite(pinTCK0, LOW);
}
inline void setTDI0() {
digitalWrite(pinTDI0, HIGH);
}
inline void clrTDI0() {
digitalWrite(pinTDI0, LOW);
}
////////////////////////////////////////////////////////////////////////////////
void setup() {
pinMode(pinGP0, OUTPUT);
pinMode(pinTRST0, OUTPUT);
pinMode(pinTMS0, OUTPUT);
pinMode(pinTCK0, OUTPUT);
pinMode(pinTDI0, OUTPUT);
pinMode(pinTDO0, INPUT);
pinMode(pinLED, OUTPUT);
digitalWrite(pinLED, LOW);
clrGP0();
clrTRST0();
clrTMS0();
clrTCK0();
clrTDI0();
Serial.begin(DEFAULT_RS232_RATE);
status = STATUS_OK;
}
////////////////////////////////////////////////////////////////////////////////
void loop() {
if(Serial.available() > 0) {
digitalWrite(pinLED, HIGH);
byte cmd = (byte)Serial.read();
//Serial.print("cmd received: ");
//Serial.println(cmd, HEX);
switch(cmd) {
case CMD_HITDI: {
Serial.write(0x30 + digitalRead(pinTDO0));
clrTMS0();
setTDI0();
toggleTCK0();
break;
}
case CMD_LOTDI: {
Serial.write(0x30 + digitalRead(pinTDO0));
clrTMS0();
clrTDI0();
toggleTCK0();
break;
}
case CMD_HITDIMS: {
Serial.write(0x30 + digitalRead(pinTDO0));
setTMS0();
setTDI0();
toggleTCK0();
break;
}
case CMD_LOTDIMS: {
Serial.write(0x30 + digitalRead(pinTDO0));
setTMS0();
clrTDI0();
toggleTCK0();
break;
}
case CMD_CLOCK: {
byte data;
while(Serial.available() == 0);
data = (byte)Serial.read();
while (data > 0) {
toggleTCK0();
data--;
}
Serial.write(STATUS_OK);
break;
}
case CMD_RESET: {
//Serial.println("CMD_RESET");
clrGP0();
clrTRST0();
clrTMS0();
clrTCK0();
clrTDI0();
Serial.write(STATUS_OK);
break;
}
case CMD_READ: {
Serial.write(0x30 + digitalRead(pinTDO0));
break;
}
case CMD_FORCE: {
//Serial.println("CMD_FORCE");
byte data;
while(Serial.available() == 0);
data = (byte)Serial.read();
if(data & MASK_TDI)
setTDI0();
else
clrTDI0();
if(data & MASK_TMS)
setTMS0();
else
clrTMS0();
if(data & MASK_TCK)
setTCK0();
else
clrTCK0();
if(data & MASK_TRST)
setTRST0();
else
clrTRST0();
if(data & MASK_GP0)
setGP0();
else
clrGP0();
Serial.write(STATUS_OK);
break;
}
case CMD_GETVER: {
//Serial.println("CMD_GETVER");
Serial.print(APP_VER);
Serial.write(STATUS_OK);
break;
}
case CMD_STATUS: {
Serial.write(STATUS_OK);
break;
}
default: {
//Serial.println(cmd, HEX);
Serial.write(STATUS_ERR1);
break;
}
}
/*if(Serial.available() > 0) {
lcd.setCursor(0,1);
lcd.print("Overflow: ");
lcd.print(Serial.peek(),HEX);
lastLcdErr = millis();
}*/
// Serial.flush();
} else {
digitalWrite(pinLED, LOW);
}
/* if ((lastLcdErr + 1000) < millis()) {
lcd.setCursor(0,1);
lcd.print(" ");
lastLcdErr = 0xFFFF0000;
}*/
}
Hackish as hell and code written like it's a programmer's last day alive, but it works. Plenty of debugging was done using an LCD for data display, but that was mostly removed here (some comments left in as a debugging "breadcrumb trail").
Next, you need the modified UrJTAG program from LukeJr. That thread is here:
https://bitcointalk.org/index.php?topic=262558.0 - jump to "Step 3: Flashing" and look under the JTAG pinout table. Grab that UrJTAG clone, then apply these changes:
https://gitorious.org/urjtag-arduiggler/urjtag-arduiggler/commit/4949a49270c3ff10aa4629b7872dbb63b55612b6You can skip creating Arduiggler.c from that page, as you'll need this version instead. The location of Arduiggler.c is mentioned in that git commit page above. Create that file, but use this instead (deep breath...):
/*
* $Id: usbblaster.c 1863 2010-10-19 21:10:14Z vapier $
*
* Arduino JTAG USB Cable Driver
* Copyright (C) 2006 K. Waschk
*
* 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.
*
* Written by Kolja Waschk, 2006; http://www.ixo.de
*
*/
#include <sysdep.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ftdi.h>
#include <urjtag/cable.h>
#include <urjtag/chain.h>
#include <urjtag/cmd.h>
#include "generic.h"
#include "generic_usbconn.h"
#include "cmd_xfer.h"
#include "usbconn/libftdx.h"
#ifndef UCHAR_MAX
#define UCHAR_MAX 255
#endif
const int BAUD_RATE = 115200;
const uint8_t CMD_RESET = 0x74;
const uint8_t CMD_STATUS = 0x3F;
const uint8_t CMD_GETVER = 0x61;
const uint8_t CMD_CLOCK = 0x63;
const uint8_t CMD_HITDI = 0x68;
const uint8_t CMD_LOTDI = 0x6C;
const uint8_t CMD_HITDIMS= 0x48;
const uint8_t CMD_LOTDIMS= 0x4C;
const uint8_t CMD_READ = 0x72;
const uint8_t CMD_FORCE = 0x66;
const int STATUS_OK = 0x6B;
const int STATUS_ERR1 = 0x31;
const int STATUS_ERR2 = 0x32;
/* Cable params_t structure with our data */
typedef struct
{
urj_tap_cable_cx_cmd_root_t cmd_root;
} params_t;
static int
arduiggler_get_status (urj_cable_t *cable)
{
uint8_t ar_status = 0;
uint8_t ar_rply;
ar_status = urj_tap_cable_cx_xfer_recv (cable);
return (int)ar_status;
}
static int
arduiggler_connect (urj_cable_t *cable, const urj_param_t *params[])
{
params_t *cable_params;
/* perform urj_tap_cable_generic_usbconn_connect */
if (urj_tap_cable_generic_usbconn_connect (cable, params) != URJ_STATUS_OK)
return URJ_STATUS_FAIL;
cable_params = malloc (sizeof (*cable_params));
if (!cable_params)
{
urj_error_set (URJ_ERROR_OUT_OF_MEMORY, _("malloc(%zd) fails"),
sizeof (*cable_params));
/* NOTE:
* Call the underlying usbport driver (*free) routine directly
* not urj_tap_cable_generic_usbconn_free() since it also free's cable->params
* (which is not established) and cable (which the caller will do)
*/
cable->link.usb->driver->free (cable->link.usb);
return URJ_STATUS_FAIL;
}
urj_tap_cable_cx_cmd_init (&cable_params->cmd_root);
/* exchange generic cable parameters with our private parameter set */
free (cable->params);
cable->params = cable_params;
return URJ_STATUS_OK;
}
static int
arduiggler_init (urj_cable_t *cable)
{
params_t *params = cable->params;
urj_tap_cable_cx_cmd_root_t *cmd_root = ¶ms->cmd_root;
if (urj_tap_usbconn_open (cable->link.usb) != URJ_STATUS_OK)
return URJ_STATUS_FAIL;
/* need to change the default baud rate from libftdi.c
* to the actual one used by the cable
*/
ftdi_param_t *fp = cable->link.usb->params;
int r = ftdi_set_baudrate(fp->fc, BAUD_RATE);
if (r != 0) {
urj_warning (_("cannot change baud rate\n"));
return URJ_STATUS_FAIL;
}
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_RESET);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
int ar_status = arduiggler_get_status(cable);
if (ar_status != STATUS_OK) {
urj_warning (_("cable not initialized properly\n"));
return URJ_STATUS_FAIL;
}
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_GETVER);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
char ar_swver[] = " ";
for (int i = 0; i < strlen(ar_swver); i++) {
ar_swver[i] = urj_tap_cable_cx_xfer_recv (cable);
}
urj_log (URJ_LOG_LEVEL_NORMAL, "Arduiggler firmware: %s\n", ar_swver);
ar_status = arduiggler_get_status(cable);
if (ar_status != STATUS_OK) {
urj_warning (_("cable not initialized properly\n"));
return URJ_STATUS_FAIL;
}
//arduiggler_set_frequency (cable, 0);
return URJ_STATUS_OK;
}
static void
arduiggler_cable_free (urj_cable_t *cable)
{
params_t *params = cable->params;
urj_tap_cable_cx_cmd_deinit (¶ms->cmd_root);
urj_tap_cable_generic_usbconn_free (cable);
}
static void
arduiggler_set_frequency (urj_cable_t *cable, uint32_t new_frequency)
{
urj_warning (_("Arduiggler does not support configurable frequency\n"));
}
static void
arduiggler_clock (urj_cable_t *cable, int tms, int tdi, int n)
{
uint8_t ar_data, ar_rply;
int ar_clk = n;
if(tms) {
if (tdi)
ar_data = CMD_HITDIMS;
else
ar_data = CMD_LOTDIMS;
} else {
if (tdi)
ar_data = CMD_HITDI;
else
ar_data = CMD_LOTDI;
}
params_t *params = cable->params;
urj_tap_cable_cx_cmd_root_t *cmd_root = ¶ms->cmd_root;
int ar_status;
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, ar_data);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
ar_rply = urj_tap_cable_cx_xfer_recv (cable);
ar_clk--;
while(ar_clk > UCHAR_MAX) {
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_CLOCK);
urj_tap_cable_cx_cmd_push (cmd_root, UCHAR_MAX);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
ar_status = arduiggler_get_status(cable);
if (ar_status != STATUS_OK) {
urj_log (URJ_LOG_LEVEL_WARNING, "arduiggler_clock - ar_status = %X\n", ar_status);
return;
}
ar_clk -= UCHAR_MAX;
}
if(ar_clk > 0) {
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_CLOCK);
urj_tap_cable_cx_cmd_push (cmd_root, (uint8_t)ar_clk);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
ar_status = arduiggler_get_status(cable);
if (ar_status != STATUS_OK) {
urj_log (URJ_LOG_LEVEL_WARNING, "arduiggler_clock - ar_status = %X\n", ar_status);
return;
}
}
}
int
arduiggler_generic_transfer (urj_cable_t *cable, int len, const char *in, char *out)
{
uint8_t ar_data, ar_rply;
int ar_clk;
params_t *params = cable->params;
urj_tap_cable_cx_cmd_root_t *cmd_root = ¶ms->cmd_root;
int ar_status;
if (out) {
for (ar_clk = 0; ar_clk < len; ar_clk++) {
if (in[ar_clk]) {
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_HITDI);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
ar_rply = urj_tap_cable_cx_xfer_recv (cable);
out[ar_clk] = (int)(ar_rply & 0x01);
} else {
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_LOTDI);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
ar_rply = urj_tap_cable_cx_xfer_recv (cable);
out[ar_clk] = (int)(ar_rply & 0x01);
}
}
} else {
for (ar_clk = 0; ar_clk < len; ar_clk++) {
if (in[ar_clk]) {
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_HITDI);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
ar_rply = urj_tap_cable_cx_xfer_recv (cable);
} else {
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_LOTDI);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
ar_rply = urj_tap_cable_cx_xfer_recv (cable);
}
}
}
ar_status = STATUS_OK;
return ar_clk;
}
static int
arduiggler_get_tdo (urj_cable_t *cable)
{
params_t *params = cable->params;
urj_tap_cable_cx_cmd_root_t *cmd_root = ¶ms->cmd_root;
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_READ);
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
uint8_t ar_rply = urj_tap_cable_cx_xfer_recv (cable);
int tdo = (int)(ar_rply & 0x01);
int ar_status = STATUS_OK;
//TODO: handle cable failure
urj_log (URJ_LOG_LEVEL_DEBUG, "arduiggler_get_tdo - ar_status = %X\n", ar_status);
return tdo;
}
static int
arduiggler_set_signal (urj_cable_t *cable, int mask, int val)
{
params_t *params = cable->params;
urj_tap_cable_cx_cmd_root_t *cmd_root = ¶ms->cmd_root;
mask &= ( URJ_POD_CS_RESET |
URJ_POD_CS_TRST |
URJ_POD_CS_TMS |
URJ_POD_CS_TCK |
URJ_POD_CS_TDI );
urj_tap_cable_cx_cmd_queue (cmd_root, 0);
urj_tap_cable_cx_cmd_push (cmd_root, CMD_FORCE);
urj_tap_cable_cx_cmd_push (cmd_root, (uint8_t)(val & mask));
urj_tap_cable_cx_xfer (cmd_root, NULL, cable, URJ_TAP_CABLE_COMPLETELY);
int ar_status = arduiggler_get_status(cable);
//TODO: handle cable failure
urj_log (URJ_LOG_LEVEL_DEBUG, "arduiggler_set_signal - ar_status = %X\n", ar_status);
return 0;
}
const const urj_cable_driver_t urj_tap_cable_arduiggler_driver = {
"Arduiggler",
N_("Arduino JTAG USB Cable (FT232)"),
URJ_CABLE_DEVICE_USB,
{ .usb = arduiggler_connect, },
urj_tap_cable_generic_disconnect,
arduiggler_cable_free,
arduiggler_init,
urj_tap_cable_generic_usbconn_done,
arduiggler_set_frequency,
arduiggler_clock,
arduiggler_get_tdo,
arduiggler_generic_transfer,
//urj_tap_cable_generic_transfer, // TODO
arduiggler_set_signal,
urj_tap_cable_generic_get_signal, // TODO
urj_tap_cable_generic_flush_one_by_one,
urj_tap_cable_generic_usbconn_help
};
URJ_DECLARE_FTDX_CABLE(0x0403, 0x6001, "", "arduiggler", arduiggler)
And finally, once all that's in place, one last piece to make the whole thing work. In urjtag/data/atmel, open up "PARTS" and add the line at the bottom:
0001111011100011 at32uc3a AT32UC3A1128
The '1128 part is supported by exactly the same "at32uc3a" code that's already there, but the part definition was missing. This fixes that.
Compile, then... you're set! Use the same exact directions from
the rest of LukeJr's post for Flashing, substituting "cable Arduiggler" for the first line (instead of "cable ft2232..."), no parameters needed.
Then go take a nap, because it will literally take a couple hours to program.