Parallel JTAG driver (Linux only for now).
Based on a patch submitted by Peter Bägel <peter@baegel.de>.
This commit is contained in:
parent
4db2f9f499
commit
e14a578555
3
AUTHORS
3
AUTHORS
|
@ -74,3 +74,6 @@ Stanimir Bonev <bonev_st@abv.bg>:
|
||||||
* Olimex chip database.
|
* Olimex chip database.
|
||||||
* Improved identification/configuration system for Olimex
|
* Improved identification/configuration system for Olimex
|
||||||
debuggers.
|
debuggers.
|
||||||
|
|
||||||
|
Peter Bägel <peter@baegel.de>:
|
||||||
|
* JTAG interface library and parallel-port JTAG driver.
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -136,6 +136,9 @@ OBJ=\
|
||||||
drivers/obl.o \
|
drivers/obl.o \
|
||||||
drivers/devicelist.o \
|
drivers/devicelist.o \
|
||||||
drivers/fet_olimex_db.o \
|
drivers/fet_olimex_db.o \
|
||||||
|
drivers/jtdev.o \
|
||||||
|
drivers/jtaglib.o \
|
||||||
|
drivers/pif.o \
|
||||||
formats/binfile.o \
|
formats/binfile.o \
|
||||||
formats/coff.o \
|
formats/coff.o \
|
||||||
formats/elf32.o \
|
formats/elf32.o \
|
||||||
|
|
|
@ -0,0 +1,906 @@
|
||||||
|
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||||
|
* Copyright (C) 2009-2012 Daniel Beer
|
||||||
|
* Copyright (C) 2012 Peter Bägel
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jtag functions are taken from TIs SLAA149–September 2002
|
||||||
|
*
|
||||||
|
* 2012-10-03 Peter Bägel (DF5EQ)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*===== includes =============================================================*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "jtaglib.h"
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
/* JTAG identification value for all */
|
||||||
|
/* existing Flash-based MSP430 devices */
|
||||||
|
#define JTAG_ID 0x89
|
||||||
|
|
||||||
|
/* Instructions for the JTAG control signal register */
|
||||||
|
/* in reverse bit order */
|
||||||
|
#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 data register */
|
||||||
|
#define IR_DATA_16BIT 0x82 /* 0x41 */
|
||||||
|
#define IR_DATA_CAPTURE 0x42 /* 0x42 */
|
||||||
|
#define IR_DATA_QUICK 0xC2 /* 0x43 */
|
||||||
|
/* 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 */
|
||||||
|
/* Instructions for the JTAG PSA mode */
|
||||||
|
#define IR_DATA_PSA 0x22 /* 0x44 */
|
||||||
|
#define IR_SHIFT_OUT_PSA 0x62 /* 0x46 */
|
||||||
|
/* Instructions for the JTAG Fuse */
|
||||||
|
#define IR_PREPARE_BLOW 0x44 /* 0x22 */
|
||||||
|
#define IR_EX_BLOW 0x24 /* 0x24 */
|
||||||
|
/* Bypass instruction */
|
||||||
|
#define IR_BYPASS 0xFF /* 0xFF */
|
||||||
|
|
||||||
|
#define jtag_tms_set(p) jtdev_tms(p, 1)
|
||||||
|
#define jtag_tms_clr(p) jtdev_tms(p, 0)
|
||||||
|
#define jtag_tck_set(p) jtdev_tck(p, 1)
|
||||||
|
#define jtag_tck_clr(p) jtdev_tck(p, 0)
|
||||||
|
#define jtag_tdi_set(p) jtdev_tdi(p, 1)
|
||||||
|
#define jtag_tdi_clr(p) jtdev_tdi(p, 0)
|
||||||
|
#define jtag_tclk_set(p) jtdev_tclk(p, 1)
|
||||||
|
#define jtag_tclk_clr(p) jtdev_tclk(p, 0)
|
||||||
|
#define jtag_rst_set(p) jtdev_rst(p, 1)
|
||||||
|
#define jtag_rst_clr(p) jtdev_rst(p, 0)
|
||||||
|
#define jtag_tst_set(p) jtdev_tst(p, 1)
|
||||||
|
#define jtag_tst_clr(p) jtdev_tst(p, 0)
|
||||||
|
|
||||||
|
#define jtag_led_green_on(p) jtdev_led_green(p, 1)
|
||||||
|
#define jtag_led_green_off(p) jtdev_led_green(p, 0)
|
||||||
|
#define jtag_led_red_on(p) jtdev_led_red(p, 1)
|
||||||
|
#define jtag_led_red_off(p) jtdev_led_red(p, 0)
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static void jtag_reset_tap(struct jtdev *p)
|
||||||
|
/* Reset target JTAG interface and perform fuse-HW check */
|
||||||
|
{
|
||||||
|
int loop_counter;
|
||||||
|
|
||||||
|
jtag_tms_set(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* perform fuse check */
|
||||||
|
jtag_tms_clr(p);
|
||||||
|
jtag_tms_set(p);
|
||||||
|
jtag_tms_clr(p);
|
||||||
|
jtag_tms_set(p);
|
||||||
|
/* reset JTAG state machine */
|
||||||
|
for (loop_counter = 6; loop_counter > 0; loop_counter--) {
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
if (p->failed)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* set JTAG state machine to Run-Test/IDLE */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tms_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static void jtag_tclk_prep (struct jtdev *p)
|
||||||
|
/* This function sets the target JTAG state machine */
|
||||||
|
/* back into the Run-Test/Idle state after a shift access */
|
||||||
|
{
|
||||||
|
/* JTAG state = Exit-DR */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Update-DR */
|
||||||
|
jtag_tms_clr(p);
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Run-Test/Idle */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static unsigned int jtag_shift( struct jtdev *p,
|
||||||
|
unsigned char number_of_bits,
|
||||||
|
unsigned int data_out )
|
||||||
|
/* shift a value into TDI (MSB first) and simultaneously */
|
||||||
|
/* shift out a value from TDO (MSB first) */
|
||||||
|
/* number_of_bits: number of bits to shift */
|
||||||
|
/* data_out : data to be shifted out */
|
||||||
|
/* return : scanned TDO value */
|
||||||
|
{
|
||||||
|
unsigned int data_in;
|
||||||
|
unsigned int mask;
|
||||||
|
unsigned int tclk_save;
|
||||||
|
|
||||||
|
tclk_save = jtdev_tclk_get(p);
|
||||||
|
|
||||||
|
data_in = 0;
|
||||||
|
for (mask = (unsigned int)0x0001 << (number_of_bits-1);
|
||||||
|
mask != 0;
|
||||||
|
mask >>= 1) {
|
||||||
|
if ((data_out & mask) != 0) {
|
||||||
|
jtag_tdi_set(p);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jtag_tdi_clr(p);
|
||||||
|
}
|
||||||
|
if (mask == 1) {
|
||||||
|
jtag_tms_set(p);
|
||||||
|
}
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
if (jtdev_tdo_get(p) == 1) {
|
||||||
|
data_in |= mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jtdev_tclk(p, tclk_save);
|
||||||
|
|
||||||
|
/* Set JTAG state back to Run-Test/Idle */
|
||||||
|
jtag_tclk_prep(p);
|
||||||
|
return data_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static unsigned int jtag_ir_shift(struct jtdev *p, unsigned int instruction)
|
||||||
|
/* shifts a new instruction into the JTAG instruction register through TDI */
|
||||||
|
/* MSB first, with interchanged MSB/LSB, to use the shifting function */
|
||||||
|
/* instruction: 8 bit instruction */
|
||||||
|
/* return : scanned TDO value */
|
||||||
|
{
|
||||||
|
/* JTAG state = Run-Test/Idle */
|
||||||
|
jtag_tms_set(p);
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Select DR-Scan */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Select IR-Scan */
|
||||||
|
jtag_tms_clr(p);
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Capture-IR */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Shift-IR, Shift in TDI (8-bit) */
|
||||||
|
return jtag_shift(p, 8, instruction);
|
||||||
|
/* JTAG state = Run-Test/Idle */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static unsigned int jtag_dr_shift( struct jtdev *p, unsigned int data )
|
||||||
|
/* shifts a given 16-bit word into the JTAG data register through TDI. */
|
||||||
|
/* data : 16 bit data */
|
||||||
|
/* return: scanned TDO value */
|
||||||
|
{
|
||||||
|
/* JTAG state = Run-Test/Idle */
|
||||||
|
jtag_tms_set(p);
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Select DR-Scan */
|
||||||
|
jtag_tms_clr(p);
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Capture-DR */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
/* JTAG state = Shift-DR, Shift in TDI (16-bit) */
|
||||||
|
return jtag_shift(p, 16, data);
|
||||||
|
/* JTAG state = Run-Test/Idle */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int jtag_set_instruction_fetch(struct jtdev *p)
|
||||||
|
/* set target CPU JTAG state machine into the instruction fetch state */
|
||||||
|
/* return: 1 - instruction fetch was set */
|
||||||
|
/* 0 - otherwise */
|
||||||
|
{
|
||||||
|
unsigned int loop_counter;
|
||||||
|
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
|
||||||
|
/* wait until CPU is in instruction fetch state */
|
||||||
|
/* timeout after limited attempts */
|
||||||
|
for (loop_counter = 50; loop_counter > 0; loop_counter--) {
|
||||||
|
if ((jtag_dr_shift(p, 0x0000) & 0x0080) == 0x0080) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
jtag_tclk_clr(p); /* the TCLK pulse befor jtag_dr_shift leads to */
|
||||||
|
jtag_tclk_set(p); /* problems at MEM_QUICK_READ, it's from SLAU265 */
|
||||||
|
}
|
||||||
|
printc_err("jtag_set_instruction_fetch: failed\n");
|
||||||
|
p->failed = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static void jtag_halt_cpu(struct jtdev *p)
|
||||||
|
/* set the CPU into a controlled stop state */
|
||||||
|
{
|
||||||
|
/* set CPU into instruction fetch mode */
|
||||||
|
jtag_set_instruction_fetch(p);
|
||||||
|
/* set device into JTAG mode + read */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2401);
|
||||||
|
/* send JMP $ instruction to keep CPU from changing the state */
|
||||||
|
jtag_ir_shift(p, IR_DATA_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x3FFF);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* set JTAG_HALT bit */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2409);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static void jtag_release_cpu(struct jtdev *p)
|
||||||
|
/* release the target CPU from the controlled stop state */
|
||||||
|
{
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* clear the HALT_JTAG bit */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2401);
|
||||||
|
jtag_ir_shift(p, IR_ADDR_CAPTURE);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int jtag_verify_psa( struct jtdev *p,
|
||||||
|
unsigned int start_address,
|
||||||
|
unsigned int length,
|
||||||
|
const uint16_t *data )
|
||||||
|
/* compares the computed PSA (Pseudo Signature Analysis) value to the PSA */
|
||||||
|
/* value shifted out from the target device. It is used for very fast data */
|
||||||
|
/* block write or erasure verification. */
|
||||||
|
/* start_address: start of data */
|
||||||
|
/* length : number of data */
|
||||||
|
/* data : pointer to data, 0 for erase check */
|
||||||
|
/* RETURN : 1 - comparison was successful */
|
||||||
|
/* 0 - otherwise */
|
||||||
|
{
|
||||||
|
unsigned int psa_value;
|
||||||
|
unsigned int index;
|
||||||
|
|
||||||
|
/* Polynom value for PSA calculation */
|
||||||
|
unsigned int polynom = 0x0805;
|
||||||
|
/* Start value for PSA calculation */
|
||||||
|
unsigned int psa_crc = start_address-2;
|
||||||
|
|
||||||
|
jtag_execute_puc(p);
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2401);
|
||||||
|
jtag_set_instruction_fetch(p);
|
||||||
|
jtag_ir_shift(p, IR_DATA_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x4030);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_dr_shift(p, start_address-2);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_ir_shift(p, IR_ADDR_CAPTURE);
|
||||||
|
jtag_dr_shift(p, 0x0000);
|
||||||
|
jtag_ir_shift(p, IR_DATA_PSA);
|
||||||
|
for (index = 0; index < length; index++) {
|
||||||
|
/* Calculate the PSA value */
|
||||||
|
if ((psa_crc & 0x8000) == 0x8000) {
|
||||||
|
psa_crc ^= polynom;
|
||||||
|
psa_crc <<= 1;
|
||||||
|
psa_crc |= 0x0001;
|
||||||
|
} else {
|
||||||
|
psa_crc <<= 1;
|
||||||
|
}
|
||||||
|
if ( data == 0) {
|
||||||
|
/* use erase check mask */
|
||||||
|
psa_crc ^= 0xFFFF;
|
||||||
|
} else {
|
||||||
|
/* use data */
|
||||||
|
psa_crc ^= data[index];
|
||||||
|
}
|
||||||
|
/* Clock through the PSA */
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tms_set(p);
|
||||||
|
jtag_tck_set(p); /* select DR scan */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tms_clr(p);
|
||||||
|
jtag_tck_set(p); /* capture DR */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p); /* shift DR */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tms_set(p);
|
||||||
|
jtag_tck_set(p); /* exit DR */
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
jtag_tms_clr(p);
|
||||||
|
jtag_tck_clr(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
}
|
||||||
|
/* read out the PSA value */
|
||||||
|
jtag_ir_shift(p, IR_SHIFT_OUT_PSA);
|
||||||
|
psa_value = jtag_dr_shift(p, 0x0000);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
return (psa_value == psa_crc) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===== public functions =====================================================*/
|
||||||
|
|
||||||
|
unsigned int jtag_init (struct jtdev *p)
|
||||||
|
/* Take target device under JTAG control. */
|
||||||
|
/* Disable the target watchdog. */
|
||||||
|
/* return: 0 - fuse is blown */
|
||||||
|
/* >0 - jtag id */
|
||||||
|
{
|
||||||
|
unsigned int jtag_id;
|
||||||
|
|
||||||
|
jtag_rst_clr(p);
|
||||||
|
jtdev_power_on(p);
|
||||||
|
jtag_tst_set(p);
|
||||||
|
jtag_tdi_set(p);
|
||||||
|
jtag_tms_set(p);
|
||||||
|
jtag_tck_set(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_rst_clr(p);
|
||||||
|
jtdev_connect(p);
|
||||||
|
jtag_rst_set(p);
|
||||||
|
jtag_reset_tap(p);
|
||||||
|
/* check fuse */
|
||||||
|
if (jtag_is_fuse_blown(p)) {
|
||||||
|
printc_err("jtag_init: fuse is blown\n");
|
||||||
|
p->failed = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Set device into JTAG mode */
|
||||||
|
jtag_id = jtag_get_device(p);
|
||||||
|
if ( jtag_id == 0 ) {
|
||||||
|
printc_err("jtag_init: invalid jtag_id: 0x%02x\n", jtag_id);
|
||||||
|
p->failed = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Perform PUC, includes target watchdog disable */
|
||||||
|
if ( jtag_execute_puc(p) != jtag_id ) {
|
||||||
|
printc_err("jtag_init: PUC failed\n");
|
||||||
|
p->failed = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return jtag_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
unsigned int jtag_get_device(struct jtdev *p)
|
||||||
|
{
|
||||||
|
unsigned int jtag_id = 0;
|
||||||
|
unsigned int loop_counter;
|
||||||
|
|
||||||
|
/* Set device into JTAG mode + read */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2401);
|
||||||
|
/* Wait until CPU is synchronized, */
|
||||||
|
/* timeout after a limited number of attempts */
|
||||||
|
jtag_id = jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
|
||||||
|
for ( loop_counter = 50;
|
||||||
|
loop_counter > 0 && ((jtag_dr_shift(p, 0x0000) & 0x0200) != 0x0200);
|
||||||
|
loop_counter--);
|
||||||
|
if (loop_counter == 0){
|
||||||
|
printc_err("jtag_get_device: timed out\n");
|
||||||
|
p->failed = 1;
|
||||||
|
/* timeout reached */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
jtag_led_green_on(p);
|
||||||
|
return jtag_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
unsigned int jtag_chip_id(struct jtdev *p)
|
||||||
|
/* Read the target chip id. */
|
||||||
|
/* return: chip id */
|
||||||
|
{
|
||||||
|
unsigned short chip_id;
|
||||||
|
|
||||||
|
/* read id from address 0x0ff0 */
|
||||||
|
chip_id = jtag_read_mem(p, 16, 0x0FF0);
|
||||||
|
/* high / low byte are stored in reverse order */
|
||||||
|
chip_id = (chip_id << 8) + (chip_id >> 8);
|
||||||
|
return chip_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
uint16_t jtag_read_mem( struct jtdev *p,
|
||||||
|
unsigned int format,
|
||||||
|
address_t address )
|
||||||
|
/* reads one byte/word from a given address */
|
||||||
|
/* format : 8-byte, 16-word */
|
||||||
|
/* address: address of memory */
|
||||||
|
/* return : content of memory */
|
||||||
|
{
|
||||||
|
uint16_t content;
|
||||||
|
|
||||||
|
jtag_halt_cpu(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
if (format == 16) {
|
||||||
|
/* set word read */
|
||||||
|
jtag_dr_shift(p, 0x2409);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* set byte read */
|
||||||
|
jtag_dr_shift(p, 0x2419);
|
||||||
|
}
|
||||||
|
/* set address */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, address);
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* shift out 16 bits */
|
||||||
|
content = jtag_dr_shift(p, 0x0000);
|
||||||
|
jtag_tclk_set(p); /* is also the first instruction in jtag_release_cpu() */
|
||||||
|
jtag_release_cpu(p);
|
||||||
|
if (format == 8) {
|
||||||
|
content &= 0x00ff;
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
void jtag_read_mem_quick( struct jtdev *p,
|
||||||
|
address_t address,
|
||||||
|
unsigned int length,
|
||||||
|
uint16_t *data )
|
||||||
|
/* reads an array of words from target memory */
|
||||||
|
/* address: address to read from */
|
||||||
|
/* length : number of word to read */
|
||||||
|
/* data : memory to write to */
|
||||||
|
{
|
||||||
|
unsigned int index;
|
||||||
|
|
||||||
|
/* Initialize reading: */
|
||||||
|
jtag_write_reg(p, 0,address-4);
|
||||||
|
jtag_halt_cpu(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* set RW to read */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2409);
|
||||||
|
jtag_ir_shift(p, IR_DATA_QUICK);
|
||||||
|
for (index = 0; index < length; index++) {
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* shift out the data from the target */
|
||||||
|
data[index] = jtag_dr_shift(p, 0x0000);
|
||||||
|
}
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_release_cpu(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
void jtag_write_mem( struct jtdev *p,
|
||||||
|
unsigned int format,
|
||||||
|
address_t address,
|
||||||
|
uint16_t data )
|
||||||
|
/* writes one byte/word at a given address */
|
||||||
|
/* format : 8-byte, 16-word */
|
||||||
|
/* address: address to be written */
|
||||||
|
/* data : data to write */
|
||||||
|
{
|
||||||
|
jtag_halt_cpu(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
if (format == 16) {
|
||||||
|
/* set word write */
|
||||||
|
jtag_dr_shift(p, 0x2408);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* set byte write */
|
||||||
|
jtag_dr_shift(p, 0x2418);
|
||||||
|
}
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
/* set addr */
|
||||||
|
jtag_dr_shift(p, address);
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
/* shift in 16 bits */
|
||||||
|
jtag_dr_shift(p, data);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_release_cpu(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
void jtag_write_mem_quick( struct jtdev *p,
|
||||||
|
address_t address,
|
||||||
|
unsigned int length,
|
||||||
|
const uint16_t *data )
|
||||||
|
/* writes an array of words into target memory */
|
||||||
|
/* address: address to write to */
|
||||||
|
/* length : number of word to write */
|
||||||
|
/* data : data to write */
|
||||||
|
{
|
||||||
|
unsigned int index;
|
||||||
|
|
||||||
|
/* Initialize writing: */
|
||||||
|
jtag_write_reg(p, 0, address-4);
|
||||||
|
jtag_halt_cpu(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
/* set RW to write */
|
||||||
|
jtag_dr_shift(p, 0x2408);
|
||||||
|
jtag_ir_shift(p, IR_DATA_QUICK);
|
||||||
|
for (index = 0; index < length; index++) {
|
||||||
|
/* write data */
|
||||||
|
jtag_dr_shift(p, data[index]);
|
||||||
|
/* increment PC by 2 */
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
}
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_release_cpu(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
int jtag_is_fuse_blown (struct jtdev *p)
|
||||||
|
/* This function checks if the JTAG access security fuse is blown */
|
||||||
|
/* return: 1 - fuse is blown */
|
||||||
|
/* 0 - otherwise */
|
||||||
|
{
|
||||||
|
unsigned int loop_counter;
|
||||||
|
|
||||||
|
/* First trial could be wrong */
|
||||||
|
for (loop_counter = 3; loop_counter > 0; loop_counter--) {
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
|
||||||
|
if (jtag_dr_shift(p, 0xAAAA) == 0x5555) {
|
||||||
|
/* Fuse is blown */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* fuse is not blown */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
unsigned int jtag_execute_puc(struct jtdev *p)
|
||||||
|
/* execute a Power-Up Clear (PUC) using JTAG CNTRL SIG register */
|
||||||
|
/* return: JTAG ID */
|
||||||
|
{
|
||||||
|
unsigned int jtag_id;
|
||||||
|
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
/* apply and remove reset */
|
||||||
|
jtag_dr_shift(p, 0x2C01);
|
||||||
|
jtag_dr_shift(p, 0x2401);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
/* read jtag id */
|
||||||
|
jtag_id = jtag_ir_shift(p, IR_ADDR_CAPTURE);
|
||||||
|
/* disable watchdog on target device */
|
||||||
|
jtag_write_mem(p, 16, 0x0120, 0x5A80);
|
||||||
|
return jtag_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
void jtag_release_device( struct jtdev *p, address_t address )
|
||||||
|
/* release the target device from JTAG control */
|
||||||
|
/* address: 0xFFFE - perform Reset, */
|
||||||
|
/* load Reset Vector into PC */
|
||||||
|
/* 0xFFFF - start execution at current */
|
||||||
|
/* PC position */
|
||||||
|
/* other - load Address into PC */
|
||||||
|
{
|
||||||
|
jtag_led_green_off(p);
|
||||||
|
switch (address) {
|
||||||
|
case 0xffff: /* nothing to do */
|
||||||
|
break;
|
||||||
|
case 0xfffe: /* perform reset */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2C01);
|
||||||
|
jtag_dr_shift(p, 0x2401);
|
||||||
|
break;
|
||||||
|
default: /* set target CPU's PC */
|
||||||
|
jtag_write_reg(p, 0, address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_RELEASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
int jtag_verify_mem( struct jtdev *p,
|
||||||
|
address_t start_address,
|
||||||
|
unsigned int length,
|
||||||
|
const uint16_t *data )
|
||||||
|
/* performs a verification over the given memory range */
|
||||||
|
/* return: 1 - verification was successful */
|
||||||
|
/* 0 - otherwise */
|
||||||
|
{
|
||||||
|
return jtag_verify_psa( p, start_address, length, data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
int jtag_erase_check( struct jtdev *p,
|
||||||
|
address_t start_address,
|
||||||
|
unsigned int length )
|
||||||
|
/* performs an erase check over the given memory range */
|
||||||
|
/* return: 1 - erase check was successful */
|
||||||
|
/* 0 - otherwise */
|
||||||
|
{
|
||||||
|
return jtag_verify_psa (p, start_address, length, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
void jtag_write_flash( struct jtdev *p,
|
||||||
|
address_t start_address,
|
||||||
|
unsigned int length,
|
||||||
|
const uint16_t *data )
|
||||||
|
/* programs/verifies an array of words into a FLASH by using the */
|
||||||
|
/* FLASH controller. The JTAG FLASH register isn't needed. */
|
||||||
|
/* start_address: start in FLASH */
|
||||||
|
/* length : number of words */
|
||||||
|
/* data : pointer to data */
|
||||||
|
{
|
||||||
|
unsigned int index;
|
||||||
|
unsigned int address;
|
||||||
|
|
||||||
|
jtag_led_red_on(p);
|
||||||
|
|
||||||
|
address = start_address;
|
||||||
|
jtag_halt_cpu(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* set RW to write */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2408);
|
||||||
|
/* FCTL1 register */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x0128);
|
||||||
|
/* enable FLASH write */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, 0xA540);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* FCTL2 register */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x012A);
|
||||||
|
/* select MCLK as source, DIV=1 */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, 0xA540);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* FCTL3 register */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x012C);
|
||||||
|
/* clear FCTL3 register */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, 0xA500);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
for (index = 0; index < length; index++) {
|
||||||
|
/* set RW to write */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2408);
|
||||||
|
/* set address */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, address);
|
||||||
|
/* set data */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, data[index]);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* set RW to read */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2409);
|
||||||
|
/* provide TCLKs */
|
||||||
|
/* min. 33 for F149 and F449 */
|
||||||
|
jtdev_tclk_strobe(p, 35);
|
||||||
|
address += 2;
|
||||||
|
|
||||||
|
if (p->failed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* set RW to write */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2408);
|
||||||
|
/* FCTL1 register */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x0128);
|
||||||
|
/* disable FLASH write */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, 0xA500);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_release_cpu(p);
|
||||||
|
|
||||||
|
jtag_led_red_off(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
void jtag_erase_flash( struct jtdev *p, unsigned int erase_mode,
|
||||||
|
address_t erase_address )
|
||||||
|
/* performs a mass erase (with and w/o info memory) or a segment erase of a */
|
||||||
|
/* FLASH module specified by the given mode and address. Large memory devices */
|
||||||
|
/* get additional mass erase operations to meet the spec. */
|
||||||
|
/* erase_mode : ERASE_MASS, ERASE_MAIN, ERASE_SGMT */
|
||||||
|
/* erase_address: address within the selected segment */
|
||||||
|
{
|
||||||
|
unsigned int number_of_strobes = 4820; /* default for segment erase */
|
||||||
|
unsigned int loop_counter;
|
||||||
|
unsigned int max_loop_count = 1; /* erase cycle repeating for mass erase */
|
||||||
|
|
||||||
|
jtag_led_red_on(p);
|
||||||
|
|
||||||
|
if ((erase_mode == JTAG_ERASE_MASS) ||
|
||||||
|
(erase_mode == JTAG_ERASE_MAIN)) {
|
||||||
|
number_of_strobes = 5300; /* Larger Flash memories require */
|
||||||
|
max_loop_count = 19; /* additional cycles for erase. */
|
||||||
|
erase_address = 0xfffe; /* overwrite given address */
|
||||||
|
}
|
||||||
|
for (loop_counter = max_loop_count; loop_counter > 0; loop_counter--) {
|
||||||
|
jtag_halt_cpu(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* set RW to write */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2408);
|
||||||
|
/* FCTL1 address */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x0128);
|
||||||
|
/* enable erase mode */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, erase_mode);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* FCTL2 address */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x012A);
|
||||||
|
/* MCLK is source, DIV=1 */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, 0xA540);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* FCTL3 address */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x012C);
|
||||||
|
/* clear FCTL3 */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, 0xA500);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* set erase address */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, erase_address);
|
||||||
|
/* dummy write to start erase */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, 0x55AA);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* set RW to read */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2409);
|
||||||
|
/* provide TCLKs */
|
||||||
|
jtdev_tclk_strobe(p, number_of_strobes);
|
||||||
|
/* set RW to write */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2408);
|
||||||
|
/* FCTL1 address */
|
||||||
|
jtag_ir_shift(p, IR_ADDR_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x0128);
|
||||||
|
/* disable erase */
|
||||||
|
jtag_ir_shift(p, IR_DATA_TO_ADDR);
|
||||||
|
jtag_dr_shift(p, 0xA500);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_release_cpu(p);
|
||||||
|
}
|
||||||
|
jtag_led_red_off(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
address_t jtag_read_reg( struct jtdev *p, int reg )
|
||||||
|
/* reads a register from the target CPU */
|
||||||
|
{
|
||||||
|
unsigned int value;
|
||||||
|
|
||||||
|
/* CPU controls RW & BYTE */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x3401);
|
||||||
|
|
||||||
|
/* set CPU into instruction fetch mode */
|
||||||
|
jtag_set_instruction_fetch(p);
|
||||||
|
|
||||||
|
jtag_ir_shift(p, IR_DATA_16BIT);
|
||||||
|
/* "sub #8,PC" instruction */
|
||||||
|
/* PC - 8 -> PC */
|
||||||
|
/* PC is advanced 4 bytes by this instruction */
|
||||||
|
/* needs 3 clock cycles */
|
||||||
|
jtag_dr_shift(p, 0x8030);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_dr_shift(p, 0x0008);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* "mov Rn,&0x01fe" instruction */
|
||||||
|
/* Rn -> &0x01fe */
|
||||||
|
/* PC is advanced 4 bytes by this instruction */
|
||||||
|
/* needs 4 clock cycles */
|
||||||
|
/* it's a ROM address, write has no effect, but */
|
||||||
|
/* the registers value is placed on the databus */
|
||||||
|
jtag_dr_shift(p, 0x4082 | (((unsigned int)reg << 8) & 0x0f00) );
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_dr_shift(p, 0x01fe);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
|
||||||
|
/* read databus which contains the registers value */
|
||||||
|
jtag_ir_shift(p, IR_DATA_CAPTURE);
|
||||||
|
value = jtag_dr_shift(p, 0x0000);
|
||||||
|
|
||||||
|
/* JTAG controls RW & BYTE */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2401);
|
||||||
|
|
||||||
|
/* return value read from register */
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
void jtag_write_reg( struct jtdev *p, int reg, address_t value )
|
||||||
|
/* writes a value into a register of the target CPU */
|
||||||
|
{
|
||||||
|
/* CPU controls RW & BYTE */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x3401);
|
||||||
|
|
||||||
|
/* set CPU into instruction fetch mode */
|
||||||
|
jtag_set_instruction_fetch(p);
|
||||||
|
|
||||||
|
jtag_ir_shift(p, IR_DATA_16BIT);
|
||||||
|
/* "sub #8,PC" instruction */
|
||||||
|
/* PC-8 -> PC */
|
||||||
|
/* PC is advanced 4 bytes by this instruction */
|
||||||
|
/* needs 3 clock cycles */
|
||||||
|
jtag_dr_shift(p, 0x8030);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_dr_shift(p, 0x0008);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
/* "mov #value,Rn" instruction */
|
||||||
|
/* value -> Rn */
|
||||||
|
/* PC is advanced 4 bytes by this instruction */
|
||||||
|
/* needs 2 clock cycles */
|
||||||
|
jtag_dr_shift(p, 0x4030 | (reg & 0x000f) );
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
jtag_dr_shift(p, reg==0 ? value-4 : value );
|
||||||
|
jtag_tclk_set(p);
|
||||||
|
jtag_tclk_clr(p);
|
||||||
|
|
||||||
|
/* JTAG controls RW & BYTE */
|
||||||
|
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
|
||||||
|
jtag_dr_shift(p, 0x2401);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||||
|
* Copyright (C) 2009-2012 Daniel Beer
|
||||||
|
* Copyright (C) 2012 Peter Bägel
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jtag functions are taken from TIs SLAA149–September 2002
|
||||||
|
*
|
||||||
|
* 2012-10-03 Peter Bägel (DF5EQ)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JTAGLIB_H_
|
||||||
|
#define JTAGLIB_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "jtdev.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/*===== public symbols ====================================================== */
|
||||||
|
|
||||||
|
/* flash erasing modes */
|
||||||
|
#define JTAG_ERASE_MASS 0xA506
|
||||||
|
#define JTAG_ERASE_MAIN 0xA504
|
||||||
|
#define JTAG_ERASE_SGMT 0xA502
|
||||||
|
|
||||||
|
/*===== public functions =====================================================*/
|
||||||
|
|
||||||
|
unsigned int jtag_init (struct jtdev *p);
|
||||||
|
unsigned int jtag_get_device (struct jtdev *p);
|
||||||
|
unsigned int jtag_chip_id (struct jtdev *p);
|
||||||
|
uint16_t jtag_read_mem (struct jtdev *p, unsigned int format,
|
||||||
|
address_t address);
|
||||||
|
void jtag_read_mem_quick (struct jtdev *p, address_t start_address,
|
||||||
|
unsigned int word_count, uint16_t *data);
|
||||||
|
void jtag_write_mem (struct jtdev *p, unsigned int format,
|
||||||
|
address_t address, uint16_t data);
|
||||||
|
void jtag_write_mem_quick(struct jtdev *p, address_t start_address,
|
||||||
|
unsigned int word_count,
|
||||||
|
const uint16_t *data);
|
||||||
|
int jtag_is_fuse_blown (struct jtdev *p);
|
||||||
|
unsigned int jtag_execute_puc (struct jtdev *p);
|
||||||
|
void jtag_release_device (struct jtdev *p, address_t address);
|
||||||
|
int jtag_verify_mem (struct jtdev *p, address_t start_address,
|
||||||
|
unsigned int word_count,
|
||||||
|
const uint16_t *data);
|
||||||
|
int jtag_erase_check (struct jtdev *p, address_t start_address,
|
||||||
|
unsigned int word_count);
|
||||||
|
void jtag_write_flash (struct jtdev *p, address_t start_address,
|
||||||
|
unsigned int word_count,
|
||||||
|
const uint16_t *data);
|
||||||
|
void jtag_erase_flash (struct jtdev *p, unsigned int erase_mode,
|
||||||
|
address_t erase_address);
|
||||||
|
address_t jtag_read_reg (struct jtdev *p, int reg);
|
||||||
|
void jtag_write_reg (struct jtdev *p, int reg, address_t value);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,297 @@
|
||||||
|
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||||
|
* Copyright (C) 2009-2012 Daniel Beer
|
||||||
|
* Copyright (C) 2012 Peter Bägel
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "jtdev.h"
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
/*===== includes =============================================================*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/ppdev.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/*===== private symbols ======================================================*/
|
||||||
|
|
||||||
|
/*--- port data (out) ---*/
|
||||||
|
#define DATA0 ((unsigned char)0x01)
|
||||||
|
#define DATA1 ((unsigned char)0x02)
|
||||||
|
#define DATA2 ((unsigned char)0x04)
|
||||||
|
#define DATA3 ((unsigned char)0x08)
|
||||||
|
#define DATA4 ((unsigned char)0x10)
|
||||||
|
#define DATA5 ((unsigned char)0x20)
|
||||||
|
#define DATA6 ((unsigned char)0x40)
|
||||||
|
#define DATA7 ((unsigned char)0x80)
|
||||||
|
|
||||||
|
/*--- port status (in) ---*/
|
||||||
|
#define ERR ((unsigned char)0x08)
|
||||||
|
#define SEL ((unsigned char)0x10)
|
||||||
|
#define PE ((unsigned char)0x20)
|
||||||
|
#define ACK ((unsigned char)0x40)
|
||||||
|
#define BUSY ((unsigned char)0x80)
|
||||||
|
|
||||||
|
/*--- port control (out) ---*/
|
||||||
|
#define STROBE ((unsigned char)0x01) /* inverted by PC-hardware */
|
||||||
|
#define AUTOFEED ((unsigned char)0x02)
|
||||||
|
#define INIT ((unsigned char)0x04)
|
||||||
|
#define SELECTIN ((unsigned char)0x08)
|
||||||
|
#define IRQEN ((unsigned char)0x10)
|
||||||
|
|
||||||
|
/*--- JTAG signal mapping ---*/
|
||||||
|
#define TEST INIT
|
||||||
|
#define TDO PE
|
||||||
|
#define TDI DATA0
|
||||||
|
#define TMS DATA1
|
||||||
|
#define TCK DATA2
|
||||||
|
#define XOUT DATA3
|
||||||
|
#define POWER (DATA4 | DATA7)
|
||||||
|
#define RESET STROBE
|
||||||
|
#define ENABLE (SELECTIN | AUTOFEED)
|
||||||
|
#define LED_GREEN DATA5
|
||||||
|
#define LED_RED DATA6
|
||||||
|
|
||||||
|
#define TCLK TDI
|
||||||
|
|
||||||
|
/*===== public functions =====================================================*/
|
||||||
|
|
||||||
|
static void do_ppwdata(struct jtdev *p)
|
||||||
|
{
|
||||||
|
if (ioctl(p->port, PPWDATA, &p->data_register) < 0) {
|
||||||
|
pr_error("ioctl: PPWDATA");
|
||||||
|
p->failed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_ppwcontrol(struct jtdev *p)
|
||||||
|
{
|
||||||
|
if (ioctl(p->port, PPWCONTROL, &p->control_register) < 0) {
|
||||||
|
pr_error("ioctl: PPWCONTROL");
|
||||||
|
p->failed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int jtdev_open(struct jtdev *p, const char *device)
|
||||||
|
{
|
||||||
|
p->port = open(device, O_RDWR);
|
||||||
|
if (p->port < 0) {
|
||||||
|
printc_err("jtdev: can't open %s: %s\n",
|
||||||
|
device, last_error());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(p->port, PPCLAIM, NULL) < 0) {
|
||||||
|
printc_err("jtdev: PPCLAIM: %s: %s\n",
|
||||||
|
device, last_error());
|
||||||
|
close(p->port);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->data_register = 0;
|
||||||
|
p->control_register = 0;
|
||||||
|
p->failed = 0;
|
||||||
|
|
||||||
|
do_ppwdata(p);
|
||||||
|
do_ppwcontrol(p);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_close(struct jtdev *p)
|
||||||
|
{
|
||||||
|
if (ioctl(p->port, PPRELEASE, NULL) < 0)
|
||||||
|
pr_error("warning: jtdev: failed to release port");
|
||||||
|
|
||||||
|
close(p->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_power_on(struct jtdev *p)
|
||||||
|
{
|
||||||
|
/* power supply on */
|
||||||
|
p->data_register |= POWER;
|
||||||
|
do_ppwdata(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_power_off(struct jtdev *p)
|
||||||
|
{
|
||||||
|
/* power supply off */
|
||||||
|
p->data_register &= ~POWER;
|
||||||
|
do_ppwdata(p);
|
||||||
|
|
||||||
|
/* a high, inactive reset also powers the target */
|
||||||
|
/* reset pin is inverted by PC hardware */
|
||||||
|
p->control_register |= RESET;
|
||||||
|
do_ppwcontrol(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_connect(struct jtdev *p)
|
||||||
|
{
|
||||||
|
p->control_register |= (TEST | ENABLE);
|
||||||
|
do_ppwcontrol(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_release(struct jtdev *p)
|
||||||
|
{
|
||||||
|
p->control_register &= ~(TEST | ENABLE);
|
||||||
|
do_ppwcontrol(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_tck(struct jtdev *p, int out)
|
||||||
|
{
|
||||||
|
if (out)
|
||||||
|
p->data_register |= TCK;
|
||||||
|
else
|
||||||
|
p->data_register &= ~TCK;
|
||||||
|
|
||||||
|
do_ppwdata(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_tms(struct jtdev *p, int out)
|
||||||
|
{
|
||||||
|
if (out)
|
||||||
|
p->data_register |= TMS;
|
||||||
|
else
|
||||||
|
p->data_register &= ~TMS;
|
||||||
|
|
||||||
|
do_ppwdata(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_tdi(struct jtdev *p, int out)
|
||||||
|
{
|
||||||
|
if (out)
|
||||||
|
p->data_register |= TDI;
|
||||||
|
else
|
||||||
|
p->data_register &= ~TDI;
|
||||||
|
|
||||||
|
do_ppwdata(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_rst(struct jtdev *p, int out)
|
||||||
|
{
|
||||||
|
/* reset pin is inverted by PC hardware */
|
||||||
|
if (out)
|
||||||
|
p->control_register &= ~RESET;
|
||||||
|
else
|
||||||
|
p->control_register |= RESET;
|
||||||
|
|
||||||
|
do_ppwcontrol(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_tst(struct jtdev *p, int out)
|
||||||
|
{
|
||||||
|
if (out)
|
||||||
|
p->control_register |= TEST;
|
||||||
|
else
|
||||||
|
p->control_register &= ~TEST;
|
||||||
|
|
||||||
|
do_ppwcontrol(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int jtdev_tdo_get(struct jtdev *p)
|
||||||
|
{
|
||||||
|
uint8_t input;
|
||||||
|
|
||||||
|
if (ioctl(p->port, PPRSTATUS, &input) < 0) {
|
||||||
|
pr_error("ioctl: PPRSTATUS");
|
||||||
|
p->failed = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (input & TDO) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_tclk(struct jtdev *p, int out)
|
||||||
|
{
|
||||||
|
if (out)
|
||||||
|
p->data_register |= TCLK;
|
||||||
|
else
|
||||||
|
p->data_register &= ~TCLK;
|
||||||
|
|
||||||
|
do_ppwdata(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int jtdev_tclk_get(struct jtdev *p)
|
||||||
|
{
|
||||||
|
return (p->data_register & TCLK) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_tclk_strobe(struct jtdev *p, unsigned int count)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
jtdev_tclk(p, 1);
|
||||||
|
jtdev_tclk(p, 0);
|
||||||
|
|
||||||
|
if (p->failed)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_led_green(struct jtdev *p, int out)
|
||||||
|
{
|
||||||
|
if (out)
|
||||||
|
p->data_register |= LED_GREEN;
|
||||||
|
else
|
||||||
|
p->data_register &= ~LED_GREEN;
|
||||||
|
|
||||||
|
do_ppwdata(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_led_red(struct jtdev *p, int out)
|
||||||
|
{
|
||||||
|
if (out)
|
||||||
|
p->data_register |= LED_RED;
|
||||||
|
else
|
||||||
|
p->data_register &= ~LED_RED;
|
||||||
|
|
||||||
|
do_ppwdata(p);
|
||||||
|
}
|
||||||
|
#else /* __linux__ */
|
||||||
|
int jtdev_open(struct jtdev *p, const char *device)
|
||||||
|
{
|
||||||
|
printc_err("jtdev: driver is only supported for Linux\n");
|
||||||
|
p->failed = 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtdev_close(struct jtdev *p) { }
|
||||||
|
|
||||||
|
void jtdev_power_on(struct jtdev *p) { }
|
||||||
|
void jtdev_power_off(struct jtdev *p) { }
|
||||||
|
void jtdev_connect(struct jtdev *p) { }
|
||||||
|
void jtdev_release(struct jtdev *p) { }
|
||||||
|
|
||||||
|
void jtdev_tck(struct jtdev *p, int out) { }
|
||||||
|
void jtdev_tms(struct jtdev *p, int out) { }
|
||||||
|
void jtdev_tdi(struct jtdev *p, int out) { }
|
||||||
|
void jtdev_rst(struct jtdev *p, int out) { }
|
||||||
|
void jtdev_tst(struct jtdev *p, int out) { }
|
||||||
|
int jtdev_tdo_get(struct jtdev *p) { return 0; }
|
||||||
|
|
||||||
|
void jtdev_tclk(struct jtdev *p, int out) { }
|
||||||
|
int jtdev_tclk_get(struct jtdev *p) { return 0; }
|
||||||
|
void jtdev_tclk_strobe(struct jtdev *p, unsigned int count) { }
|
||||||
|
|
||||||
|
void jtdev_led_green(struct jtdev *p, int out) { }
|
||||||
|
void jtdev_led_red(struct jtdev *p, int out) { }
|
||||||
|
#endif
|
|
@ -0,0 +1,62 @@
|
||||||
|
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||||
|
* Copyright (C) 2009-2012 Daniel Beer
|
||||||
|
* Copyright (C) 2012 Peter Bägel
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JTDEV_H_
|
||||||
|
#define JTDEV_H_
|
||||||
|
|
||||||
|
struct jtdev {
|
||||||
|
int port;
|
||||||
|
uint8_t data_register;
|
||||||
|
uint8_t control_register;
|
||||||
|
int failed;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialize/destroy a parallel-port JTAG interface. jtdev_open()
|
||||||
|
* returns 0 on success or -1 if an error occurs.
|
||||||
|
*
|
||||||
|
* All other JTAG IO functions indicate errors by setting the failed
|
||||||
|
* field in the jtdev structure.
|
||||||
|
*/
|
||||||
|
int jtdev_open(struct jtdev *p, const char *device);
|
||||||
|
void jtdev_close(struct jtdev *p);
|
||||||
|
|
||||||
|
/* Connect/release JTAG */
|
||||||
|
void jtdev_power_on(struct jtdev *p);
|
||||||
|
void jtdev_power_off(struct jtdev *p);
|
||||||
|
void jtdev_connect(struct jtdev *p);
|
||||||
|
void jtdev_release(struct jtdev *p);
|
||||||
|
|
||||||
|
/* Low-level IO */
|
||||||
|
void jtdev_tck(struct jtdev *p, int out);
|
||||||
|
void jtdev_tms(struct jtdev *p, int out);
|
||||||
|
void jtdev_tdi(struct jtdev *p, int out);
|
||||||
|
void jtdev_rst(struct jtdev *p, int out);
|
||||||
|
void jtdev_tst(struct jtdev *p, int out);
|
||||||
|
int jtdev_tdo_get(struct jtdev *p);
|
||||||
|
|
||||||
|
/* TCLK management */
|
||||||
|
void jtdev_tclk(struct jtdev *p, int out);
|
||||||
|
int jtdev_tclk_get(struct jtdev *p);
|
||||||
|
void jtdev_tclk_strobe(struct jtdev *p, unsigned int count);
|
||||||
|
|
||||||
|
/* LED indicators */
|
||||||
|
void jtdev_led_green(struct jtdev *p, int out);
|
||||||
|
void jtdev_led_red(struct jtdev *p, int out);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,401 @@
|
||||||
|
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||||
|
* Copyright (C) 2009-2012 Daniel Beer
|
||||||
|
* Copyright (C) 2012 Peter Bägel
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Driver for parallel port interface like the Olimex MSP430-JTAG
|
||||||
|
* Starting point was the goodfet driver
|
||||||
|
*
|
||||||
|
* 2012-10-03 Peter Bägel (DF5EQ)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "pif.h"
|
||||||
|
#include "jtaglib.h"
|
||||||
|
#include "ctrlc.h"
|
||||||
|
|
||||||
|
/*============================================================================*/
|
||||||
|
/* pif MSP430 JTAG operations */
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
/* Read a word-aligned block from any kind of memory */
|
||||||
|
static int read_words( struct jtdev *p, address_t addr,
|
||||||
|
address_t len,
|
||||||
|
uint8_t* data )
|
||||||
|
{
|
||||||
|
unsigned int index;
|
||||||
|
unsigned int word;
|
||||||
|
|
||||||
|
for ( index = 0; index < len; index += 2 ) {
|
||||||
|
word = jtag_read_mem( p, 16, addr+index );
|
||||||
|
data[index] = word & 0x00ff;
|
||||||
|
data[index+1] = (word >> 8) & 0x00ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p->failed ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
/* Write a word to RAM */
|
||||||
|
int write_ram_word( struct jtdev *p, address_t addr,
|
||||||
|
uint16_t value )
|
||||||
|
{
|
||||||
|
unsigned int word;
|
||||||
|
|
||||||
|
word = ((value & 0x00ff) << 8) | ((value & 0xff00) >> 8);
|
||||||
|
jtag_write_mem( p, 16, addr, word );
|
||||||
|
|
||||||
|
return p->failed ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
/* Write a word-aligned flash block. */
|
||||||
|
/* The starting address must be within the flash memory range. */
|
||||||
|
|
||||||
|
static int write_flash_block( struct jtdev *p, address_t addr,
|
||||||
|
address_t len,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
uint16_t *word;
|
||||||
|
|
||||||
|
word = malloc( len / 2 * sizeof(*word) );
|
||||||
|
if (!word) {
|
||||||
|
pr_error("pif: failed to allocate memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < len/2; i++) {
|
||||||
|
word[i]=data[2*i] + (((uint16_t)data[2*i+1]) << 8);
|
||||||
|
}
|
||||||
|
jtag_write_flash( p, addr, len/2, word );
|
||||||
|
|
||||||
|
free(word);
|
||||||
|
|
||||||
|
return p->failed ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
/* Write a single byte by reading and rewriting a word. */
|
||||||
|
static int write_byte( struct jtdev *p,
|
||||||
|
address_t addr,
|
||||||
|
uint8_t value )
|
||||||
|
{
|
||||||
|
address_t aligned = addr & ~1;
|
||||||
|
uint8_t data[2];
|
||||||
|
unsigned int word;
|
||||||
|
|
||||||
|
read_words(p, aligned, 2, data);
|
||||||
|
data[addr & 1] = value;
|
||||||
|
|
||||||
|
if ( (addr >= 0x1000 && addr <= 0x10ff)
|
||||||
|
||
|
||||||
|
addr >= 0x4000) {
|
||||||
|
/* program in FLASH */
|
||||||
|
write_flash_block(p, aligned, 2, data);
|
||||||
|
} else {
|
||||||
|
/* write to RAM */
|
||||||
|
word = (uint16_t)data[1] | ((uint16_t)data[0] << 8);
|
||||||
|
write_ram_word(p, aligned, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
return p->failed ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int init_device(struct jtdev *p)
|
||||||
|
{
|
||||||
|
unsigned int jtag_id;
|
||||||
|
unsigned int chip_id;
|
||||||
|
|
||||||
|
printc_dbg("Starting JTAG\n");
|
||||||
|
jtag_id = jtag_init(p);
|
||||||
|
printc("JTAG ID: 0x%02x\n", jtag_id);
|
||||||
|
if (jtag_id != 0x89 && jtag_id != 0x91) {
|
||||||
|
printc_err("pif: unexpected JTAG ID: 0x%02x\n", jtag_id);
|
||||||
|
jtag_release_device(p, 0xfffe);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip_id =jtag_chip_id(p);
|
||||||
|
printc_dbg("Chip ID: %04X\n", chip_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===== MSPDebug Device interface ============================================*/
|
||||||
|
|
||||||
|
struct pif_device {
|
||||||
|
struct device base;
|
||||||
|
struct jtdev jtag;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int pif_readmem( device_t dev_base,
|
||||||
|
address_t addr,
|
||||||
|
uint8_t* mem,
|
||||||
|
address_t len )
|
||||||
|
{
|
||||||
|
struct pif_device *dev = (struct pif_device *)dev_base;
|
||||||
|
uint8_t data[2];
|
||||||
|
|
||||||
|
dev->jtag.failed = 0;
|
||||||
|
|
||||||
|
if ( len > 0 ) {
|
||||||
|
/* Handle unaligned start */
|
||||||
|
if (addr & 1) {
|
||||||
|
if (read_words(&dev->jtag, addr & ~1, 2, data) < 0)
|
||||||
|
return -1;
|
||||||
|
mem[0] = data[1];
|
||||||
|
addr++;
|
||||||
|
mem++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read aligned blocks */
|
||||||
|
if (len >= 2) {
|
||||||
|
if (read_words(&dev->jtag, addr, len & ~1, mem) < 0)
|
||||||
|
return -1;
|
||||||
|
addr += len & ~1;
|
||||||
|
mem += len & ~1;
|
||||||
|
len &= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle unaligned end */
|
||||||
|
if (len == 1) {
|
||||||
|
if (read_words(&dev->jtag, addr, 2, data) < 0)
|
||||||
|
return -1;
|
||||||
|
mem[0] = data[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int pif_writemem( device_t dev_base,
|
||||||
|
address_t addr,
|
||||||
|
const uint8_t* mem,
|
||||||
|
address_t len )
|
||||||
|
{
|
||||||
|
struct pif_device *dev = (struct pif_device *)dev_base;
|
||||||
|
|
||||||
|
dev->jtag.failed = 0;
|
||||||
|
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
/* Handle unaligned start */
|
||||||
|
if (addr & 1) {
|
||||||
|
if (write_byte(&dev->jtag, addr, mem[0]) < 0)
|
||||||
|
return -1;
|
||||||
|
addr++;
|
||||||
|
mem++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write aligned blocks */
|
||||||
|
while (len >= 2) {
|
||||||
|
if ( (addr >= 0x1000 && addr <= 0x10ff)
|
||||||
|
||
|
||||||
|
addr >= 0x4000) {
|
||||||
|
if (write_flash_block(&dev->jtag, addr, len & ~1, mem) < 0)
|
||||||
|
return -1;
|
||||||
|
addr += len & ~1;
|
||||||
|
mem += len & ~1;
|
||||||
|
len &= 1;
|
||||||
|
} else {
|
||||||
|
if (write_ram_word(&dev->jtag, addr,
|
||||||
|
(uint16_t)mem[1] | ((uint16_t)mem[0] << 8)) < 0)
|
||||||
|
return -1;
|
||||||
|
addr += 2;
|
||||||
|
mem += 2;
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle unaligned end */
|
||||||
|
if (len == 1) {
|
||||||
|
if (write_byte(&dev->jtag, addr, mem[0]) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int pif_getregs(device_t dev_base, address_t *regs)
|
||||||
|
{
|
||||||
|
struct pif_device *dev = (struct pif_device *)dev_base;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dev->jtag.failed = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < DEVICE_NUM_REGS; i++)
|
||||||
|
regs[i] = jtag_read_reg(&dev->jtag, i);
|
||||||
|
|
||||||
|
return dev->jtag.failed ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int pif_setregs( device_t dev_base, const address_t* regs )
|
||||||
|
{
|
||||||
|
struct pif_device *dev = (struct pif_device *)dev_base;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dev->jtag.failed = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < DEVICE_NUM_REGS; i++) {
|
||||||
|
jtag_write_reg( &dev->jtag, i, regs[i] );
|
||||||
|
}
|
||||||
|
return dev->jtag.failed ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int pif_ctl(device_t dev_base, device_ctl_t type)
|
||||||
|
{
|
||||||
|
struct pif_device *dev = (struct pif_device *)dev_base;
|
||||||
|
|
||||||
|
dev->jtag.failed = 0;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case DEVICE_CTL_RESET:
|
||||||
|
/* perform soft reset */
|
||||||
|
jtag_execute_puc(&dev->jtag);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DEVICE_CTL_RUN:
|
||||||
|
/* start program execution at current PC */
|
||||||
|
jtag_release_device(&dev->jtag, 0xffff);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DEVICE_CTL_HALT:
|
||||||
|
/* take device under JTAG control */
|
||||||
|
jtag_get_device(&dev->jtag);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DEVICE_CTL_STEP:
|
||||||
|
printc_err("pif: single-stepping not implemented\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev->jtag.failed ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static device_status_t pif_poll(device_t dev_base)
|
||||||
|
{
|
||||||
|
if (delay_ms(100) < 0 || ctrlc_check())
|
||||||
|
return DEVICE_STATUS_INTR;
|
||||||
|
|
||||||
|
return DEVICE_STATUS_RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static int pif_erase( device_t dev_base,
|
||||||
|
device_erase_type_t type,
|
||||||
|
address_t addr )
|
||||||
|
{
|
||||||
|
struct pif_device *dev = (struct pif_device *)dev_base;
|
||||||
|
|
||||||
|
dev->jtag.failed = 0;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case DEVICE_ERASE_MAIN:
|
||||||
|
jtag_erase_flash ( &dev->jtag, JTAG_ERASE_MAIN, addr );
|
||||||
|
break;
|
||||||
|
case DEVICE_ERASE_ALL:
|
||||||
|
jtag_erase_flash ( &dev->jtag, JTAG_ERASE_MASS, addr );
|
||||||
|
break;
|
||||||
|
case DEVICE_ERASE_SEGMENT:
|
||||||
|
jtag_erase_flash ( &dev->jtag, JTAG_ERASE_SGMT, addr );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev->jtag.failed ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static device_t pif_open(const struct device_args *args)
|
||||||
|
{
|
||||||
|
struct pif_device *dev;
|
||||||
|
|
||||||
|
if (!(args->flags & DEVICE_FLAG_TTY)) {
|
||||||
|
printc_err("pif: this driver does not support raw USB access\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(args->flags & DEVICE_FLAG_JTAG)) {
|
||||||
|
printc_err("pif: this driver does not support Spy-Bi-Wire\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = malloc(sizeof(*dev));
|
||||||
|
if (!dev) {
|
||||||
|
printc_err("pif: malloc: %s\n", last_error());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(dev, 0, sizeof(*dev));
|
||||||
|
dev->base.type = &device_pif;
|
||||||
|
dev->base.max_breakpoints = 0;
|
||||||
|
|
||||||
|
if (jtdev_open(&dev->jtag, args->path) < 0) {
|
||||||
|
printc_err("pif: can't open port\n");
|
||||||
|
free(dev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_device(&dev->jtag) < 0) {
|
||||||
|
printc_err("pif: initialization failed\n");
|
||||||
|
free(dev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dev->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
static void pif_destroy(device_t dev_base)
|
||||||
|
{
|
||||||
|
struct pif_device *dev = (struct pif_device *)dev_base;
|
||||||
|
|
||||||
|
dev->jtag.failed = 0;
|
||||||
|
|
||||||
|
jtag_release_device(&dev->jtag, 0xfffe);
|
||||||
|
jtdev_close(&dev->jtag);
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
const struct device_class device_pif = {
|
||||||
|
.name = "pif",
|
||||||
|
.help = "Parallel Port JTAG",
|
||||||
|
.open = pif_open,
|
||||||
|
.destroy = pif_destroy,
|
||||||
|
.readmem = pif_readmem,
|
||||||
|
.writemem = pif_writemem,
|
||||||
|
.getregs = pif_getregs,
|
||||||
|
.setregs = pif_setregs,
|
||||||
|
.ctl = pif_ctl,
|
||||||
|
.poll = pif_poll,
|
||||||
|
.erase = pif_erase
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||||
|
* Copyright (C) 2009-2012 Daniel Beer
|
||||||
|
* Copyright (C) 2012 Peter Bägel
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Driver for parallel port interface like the Olimex MSP430-JTAG
|
||||||
|
* Starting point was the goodfet driver
|
||||||
|
*
|
||||||
|
* 2012-10-03 Peter Bägel (DF5EQ)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PIF_H_
|
||||||
|
#define PIF_H_
|
||||||
|
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
/* pif implementation */
|
||||||
|
extern const struct device_class device_pif;
|
||||||
|
|
||||||
|
#endif
|
|
@ -168,6 +168,10 @@ Connect to a GoodFET device. JTAG mode must be used, and only TTY access
|
||||||
is supported. This device can be used for memory access (read, erase and
|
is supported. This device can be used for memory access (read, erase and
|
||||||
program), but CPU control is limited. The CPU may be halted, run and
|
program), but CPU control is limited. The CPU may be halted, run and
|
||||||
reset, but register access and breakpoints aren't supported.
|
reset, but register access and breakpoints aren't supported.
|
||||||
|
.IP "\fBpif\fR"
|
||||||
|
Connect to a parallel-port JTAG controller. Currently, this driver is only
|
||||||
|
supported for Linux. A parallel port device must be specified via the
|
||||||
|
\fB-d\fR option.
|
||||||
.SH COMMANDS
|
.SH COMMANDS
|
||||||
MSPDebug can accept commands either through an interactive prompt, or
|
MSPDebug can accept commands either through an interactive prompt, or
|
||||||
non-interactively when specified on the command line. The supported
|
non-interactively when specified on the command line. The supported
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include "goodfet.h"
|
#include "goodfet.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "input_async.h"
|
#include "input_async.h"
|
||||||
|
#include "pif.h"
|
||||||
|
|
||||||
#define OPT_NO_RC 0x01
|
#define OPT_NO_RC 0x01
|
||||||
#define OPT_EMBEDDED 0x02
|
#define OPT_EMBEDDED 0x02
|
||||||
|
@ -75,7 +76,8 @@ static const struct device_class *const driver_table[] = {
|
||||||
&device_flash_bsl,
|
&device_flash_bsl,
|
||||||
&device_gdbc,
|
&device_gdbc,
|
||||||
&device_tilib,
|
&device_tilib,
|
||||||
&device_goodfet
|
&device_goodfet,
|
||||||
|
&device_pif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *version_text =
|
static const char *version_text =
|
||||||
|
|
Loading…
Reference in New Issue