907 lines
28 KiB
C
907 lines
28 KiB
C
/* 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);
|
||
}
|
||
|