mspdebug/drivers/jtaglib_cpuxv2.c

632 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdbool.h>
#include "jtaglib.h"
#include "jtaglib_defs.h"
#include "output.h"
#define SAFE_FRAM_PC 0x0004
// FIXME:
// * read mem (broken)
// * read mem quick (consecutive ones break)
// * write mem (no effect)
// * read reg (nonsense values)
// -> also loses FES
// * write reg?
// * single step: check if it works
// * breakpoints are a big TODO
static int jlfxv2_check_full_emu_state_ex(struct jtdev *p, const char* fnname)
{ // see SLAU320AJ ExecutePOR
uint16_t dr;
uint8_t jtag_id;
jtag_id = jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
if (((dr = jtag_dr_shift_16(p, 0)) & 0x0301) == 0x0301) {
return 1; // OK
}
printc_err("jlfxv2: %s: not in full emu state, while expected!"
" (dr=%04x jid=%02x)\n", fnname, dr, jtag_id);
p->failed = 1;
return 0;
}
#define jlfxv2_check_full_emu_state(p) jlfxv2_check_full_emu_state_ex((p), __func__)
static void jlfxv2_set_pc(struct jtdev *p, address_t addr)
{
printc_dbg("set pc %06x\n", addr);
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
jtag_dr_shift_16(p, 0);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_16BIT);
jtag_tclk_set(p);
jtag_dr_shift_16(p, 0x0080 | ((addr >> 8) & 0x0f00));
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x1400);
jtag_ir_shift(p, IR_DATA_16BIT);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_dr_shift_16(p, addr & 0xffff);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_dr_shift_16(p, 0x4303);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_ADDR_CAPTURE);
jtag_dr_shift_20(p, 0);
}
/* 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
*/
static int jlfxv2_verify_mem(struct jtdev *p,
unsigned int start_address,
unsigned int length,
const uint16_t *data)
{ // SLAU320AJ name: VerifyMem/VerifyPSA
uint16_t crc, psaval;
jtag_execute_puc(p);
crc = start_address - 2;
jlfxv2_set_pc(p, start_address);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0501);
jtag_ir_shift(p, IR_DATA_16BIT);
jtag_dr_shift_16(p, start_address - 2);
jtag_ir_shift(p, IR_DATA_PSA);
for (unsigned int addr = 0; addr < length; ++addr) {
if (crc & 0x8000) {
crc ^= 0x0805;
crc <<= 1;
crc |= 1;
} else {
crc <<= 1;
}
if (data) crc ^= data[addr];
else crc ^= 0xffff;
jtag_tclk_clr(p);
/* Go through DR path without shifting data in/out */
jtag_tms_sequence(p, 6, 0x19); /* TMS=1 0 0 1 1 0 ; 6 clocks */
jtag_tclk_set(p);
}
jtag_ir_shift(p, IR_SHIFT_OUT_PSA);
psaval = jtag_dr_shift_16(p, 0);
jtag_execute_puc(p);
return (psaval == crc) ? 1 : 0;
}
/* ------------------------------------------------------------------------- */
static int jlfxv2_erase_check(struct jtdev *p, unsigned int start_address,
unsigned int length)
{
return jlfxv2_verify_mem(p, start_address, length, NULL);
}
static unsigned int jlfxv2_get_device(struct jtdev *p)
{ // SLAU320AJ name: GetDevice (and SyncJtag_AssertPor)
unsigned int jtag_id = 0;
int i;
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x1501);
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
for (i = 0; i < 50; ++i) {
if ((jtag_dr_shift_16(p, 0) & (1<<9)) == (1<<9))
break;
}
if (i == 50) {
printc_err("jlfxv2_get_device: failed\n");
p->failed = 1;
return 0;
}
jtag_id = jtag_execute_puc(p);
return jtag_id;
}
/* Reads one byte/word from a given address
* format : 8-byte, 16-word
* address: address of memory
* return : content of memory
*/
static void jlfxv2_read_mem_quick(struct jtdev *p, address_t address,
unsigned int length, uint16_t *data);
static uint16_t jlfxv2_read_mem(struct jtdev *p, unsigned int format, address_t address)
{ // SLAU320AJ name: ReadMem
uint16_t r=0;
printc_dbg("read mem %d %06x\n", format, address);
jlfxv2_read_mem_quick(p, address ^ (address & 1), 1, &r);
if (format == 8) {
if (address & 1) return r >> 8;
else return r & 0xff;
} else return r;
if (!jlfxv2_check_full_emu_state(p))
return 0;
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, (format == 16) ? 0x0501 : 0x0511);
jtag_ir_shift(p, IR_ADDR_16BIT);
jtag_dr_shift_20(p, address);
jtag_ir_shift(p, IR_DATA_TO_ADDR);
jtag_tclk_set(p);
jtag_tclk_clr(p);
r = jtag_dr_shift_16(p, 0);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
return r;
}
/* Reads an array of words from target memory
* address: address to read from
* length : number of word to read
* data : memory to write to
*/
static void jlfxv2_read_mem_quick(struct jtdev *p, address_t address,
unsigned int length, uint16_t *data)
{ // SLAU320AJ name: ReadMemQuick
if (!jlfxv2_check_full_emu_state(p))
return;
jlfxv2_set_pc(p, address);
//jtag_tclk_set(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0501);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_ADDR_CAPTURE);
jtag_ir_shift(p, IR_DATA_QUICK);
for (unsigned int i = 0; i < length; ++i) {
jtag_tclk_set(p);
jtag_tclk_clr(p);
data[i] = jtag_dr_shift_16(p, 0);
}
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
jtag_dr_shift_16(p, 0);
if (p->jtag_id == 0x99) {
jlfxv2_set_pc(p, SAFE_FRAM_PC);
}
jtag_tclk_set(p);
}
/* Writes one byte/word at a given address
* format : 8-byte, 16-word
* address: address to be written
* data : data to write
*/
static void jlfxv2_write_mem(struct jtdev *p, unsigned int format,
address_t address, uint16_t data)
{ // SLAU320AJ name: WriteMem
if (!jlfxv2_check_full_emu_state(p))
return;
printc_dbg("write mem %d: %06x<-%04x\n", format, address, data);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, (format == 16) ? 0x0500 : 0x0510);
jtag_ir_shift(p, IR_ADDR_16BIT);
jtag_dr_shift_20(p, address);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_DATA_TO_ADDR);
jtag_dr_shift_16(p, data);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0501);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
}
/* Writes an array of words into target memory
* address: address to write to
* length : number of word to write
* data : data to write
*/
static void jlfxv2_write_mem_quick(struct jtdev *p, address_t address,
unsigned int length, const uint16_t *data)
{ // SLAU320AJ name: WriteMemQuick
for (unsigned int i = 0; i < length; ++i) {
jlfxv2_write_mem(p, 16, address + i*2, data[i]);
}
}
/* Execute a Power-Up Clear (PUC) using JTAG CNTRL SIG register
* return: JTAG ID
*/
static unsigned int jlfxv2_execute_puc(struct jtdev *p)
{ // SLAU320AJ name: ExecutePOR
unsigned int jtag_id;
jtag_id = jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
// empty CPU pipeline
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
/* Apply and remove reset */
jtag_dr_shift_16(p, 0x0C01);
jtag_dr_shift_16(p, 0x0401);
if (jtag_id == 0x91) { // TODO: is this correct?
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p); // two more cycles to release CPU internal POR delay signals
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
} else { // FRAM
jtag_ir_shift(p, IR_DATA_16BIT);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_dr_shift_16(p, SAFE_FRAM_PC);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_DATA_CAPTURE);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
}
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0501);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_write_mem(p, 16, 0x015c, 0x5a80); // disable WDT
// TODO: disable WDT
// Disable Watchdog Timer on target device now by setting the HOLD signal
// in the WDT_CNTRL register (i.e. by using WriteMem_430Xv2 note
// different WDT addresses for individual FRAM device groups)
//
// Initialize Test Memory with default values to ensure consistency between
// PC value and MAB (MAB is +2 after sync) Use WriteMem_430Xv2 to write
// 0x3FFF to addresses 0x06 and 0x08 (this is only applicable for devices
// with JTAG ID 0x91 or 0x99)
return jtag_id;
}
/* 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
*/
static void jlfxv2_release_device(struct jtdev *p, address_t address)
{ // SLAU320AJ name: ReleaseDevice. from Replicator430 code, not in the PDF
switch (address) {
case 0xffff: // BOR
jtag_ir_shift(p, IR_TEST_REG);
jtag_dr_shift_16(p, 0x0200);
delay_ms(5);
break;
case 0xfffe: // reset
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0C01);
jtag_dr_shift_16(p, 0x0401);
jtag_ir_shift(p, IR_CNTRL_SIG_RELEASE);
break;
default:
jlfxv2_set_pc(p, address);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0401);
jtag_ir_shift(p, IR_ADDR_CAPTURE);
jtag_ir_shift(p, IR_CNTRL_SIG_RELEASE);
break;
}
}
/* 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
*/
static void jlfxv2_write_flash(struct jtdev *p, address_t start_address,
unsigned int length, const uint16_t *data)
{ // SLAU320AJ name: WriteFLASH
}
/* 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
*/
static void jlfxv2_erase_flash(struct jtdev *p, unsigned int erase_mode,
address_t erase_address)
{ // SLAU320AJ name: EraseFLASH
}
/* Reads a register from the target CPU */
static address_t jlfxv2_read_reg(struct jtdev *p, int reg)
{ // libmsp430 BIOS name: ReadCpuReg
uint16_t reglo, reghi;
uint16_t jtag_id, jmb_addr;
if (reg == 3) return 0; // CG
printc_dbg("read reg %d\n", reg);
if (!jlfxv2_check_full_emu_state(p))
return 0;
jtag_tclk_clr(p);
jtag_id = jtag_ir_shift(p, IR_DATA_16BIT);
jtag_tclk_set(p);
jtag_dr_shift_16(p, reg);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x1401);
jtag_ir_shift(p, IR_DATA_16BIT);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jmb_addr = (jtag_id == 0x98) ? 0x14c : 0x18c;
jtag_dr_shift_16(p, jmb_addr);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_dr_shift_16(p, 0x3ffd);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_CAPTURE);
jtag_tclk_set(p);
reglo = jtag_dr_shift_16(p, 0);
jtag_tclk_clr(p);
jtag_tclk_set(p);
reghi = jtag_dr_shift_16(p, 0);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0501);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_CAPTURE);
jtag_tclk_set(p);
printc_dbg("read reg %d: lo=%04x hi=%04x\n", reg, reglo, reghi);
return reglo | ((address_t)reghi << 16);
}
/* Writes a value into a register of the target CPU */
static void jlfxv2_write_reg(struct jtdev *p, int reg, address_t value)
{ // SLAU320AJ name: SetPC
jlfxv2_check_full_emu_state(p);
printc_dbg("write reg %d %06x\n", reg, value);
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
jtag_dr_shift_16(p, 0);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_16BIT);
jtag_tclk_set(p);
jtag_dr_shift_16(p, 0x0080 | ((value >> 8) & 0x0f00));
//jtag_tclk_clr(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x1401/*1400*/);
jtag_ir_shift(p, IR_DATA_16BIT);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_dr_shift_16(p, value & 0xffff);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_dr_shift_16(p, 0x3ffd); // rewind PC
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_ADDR_CAPTURE);
jtag_dr_shift_20(p, 0);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, /*0x4303*/0x0501); // insert NOP to be prefetched by the CPU
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_CAPTURE);
jtag_tclk_set(p);
//jtag_dr_shift_20(p, 0);
}
/*----------------------------------------------------------------------------*/
static void jlfxv2_single_step( struct jtdev *p )
{ // libmsp430 BIOS name: SingleStep
int i, timeout;
uint16_t tmp;
printc_dbg("jlfxv2: single step\n");
/*jtag_ir_shift(p, IR_EMEX_READ_CONTROL);
timeout = 3000;
for (i = 0; i < timeout; ++i)
if (jtag_dr_shift_16(p, 0) & EEM_STOPPED)
break;
if (i == timeout) {
printc_err("jlfxv2_single_step: EEM timeout\n");
goto err;
}*/
jtag_ir_shift(p, IR_EMEX_WRITE_CONTROL);
jtag_dr_shift_16(p, EMU_CLK_EN | EEM_EN);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x1501);
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
timeout = 50;
for (i = 0; i < timeout; ++i) {
tmp = jtag_dr_shift_16(p, 0);
if (tmp != 0xffff && (tmp & 0x200) == 0x0200)
break;
}
if (i == timeout) {
printc_err("jlfxv2_single_step: JTAG sync timeout\n");
goto err;
}
jtag_ir_shift(p, IR_EMEX_WRITE_CONTROL);
jtag_dr_shift_16(p, EMU_CLK_EN | CLEAR_STOP);
jtag_dr_shift_16(p, EMU_CLK_EN | CLEAR_STOP | EEM_EN);
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x1501);
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
timeout = 30000;
for (i = 0; i < timeout; ++i) {
if ((jtag_dr_shift_16(p, 0) & 8) == 0) break;
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
}
if (i == timeout) {
printc_err("jlfxv2_single_step: single-step timeout\n");
goto err;
}
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
timeout = 10000;
for (i = 0; i < timeout; ++i) {
jtag_tclk_clr(p);
tmp = jtag_dr_shift_16(p, 0);
jtag_tclk_set(p);
if ((tmp & CNTRL_SIG_CPUSUSP) == CNTRL_SIG_CPUSUSP) break;
}
if (i == timeout) {
printc_err("jlfxv2_single_step: pipeline empty timeout\n");
goto err;
}
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0501);
return;
err:
p->failed = 1;
}
/*----------------------------------------------------------------------------*/
static unsigned int jlfxv2_set_breakpoint( struct jtdev *p,int bp_num, address_t bp_addr )
{
/* The breakpoint logic is explained in 'SLAU414c EEM.pdf' */
/* A good overview is given with Figure 1-1 */
/* MBx is TBx in EEM_defs.h */
/* CPU Stop is BREAKREACT in EEM_defs.h */
/* State Storage is STOR_REACT in EEM_defs.h */
/* Cycle Counter is EVENT_REACT in EEM_defs.h */
return 0;
}
/*----------------------------------------------------------------------------*/
static unsigned int jlfxv2_cpu_state( struct jtdev *p )
{ // libmsp430 BIOS name: WaitForEem(?)
// TODO: does this need an update?
jtag_ir_shift(p, IR_EMEX_READ_CONTROL);
if ((jtag_dr_shift_16(p, 0x0000) & EEM_STOPPED) == EEM_STOPPED) {
printc_dbg("jlfxv2_cpu_state: halted\n");
return 1; /* halted */
} else {
printc_dbg("jlfxv2_cpu_state: running\n");
return 0; /* running */
}
}
/*----------------------------------------------------------------------------*/
static int jlfxv2_get_config_fuses( struct jtdev *p )
{ // always the same?
// TODO: does this need an update?
jtag_ir_shift(p, IR_CONFIG_FUSES);
return jtag_dr_shift_8(p, 0);
}
/* ------------------------------------------------------------------------- */
const struct jtaglib_funcs jlf_cpuxv2 = {
.jlf_get_device = jlfxv2_get_device,
.jlf_read_mem = jlfxv2_read_mem,
.jlf_read_mem_quick = jlfxv2_read_mem_quick,
.jlf_write_mem = jlfxv2_write_mem,
.jlf_write_mem_quick = jlfxv2_write_mem_quick,
.jlf_execute_puc = jlfxv2_execute_puc,
.jlf_release_device = jlfxv2_release_device,
.jlf_verify_mem = jlfxv2_verify_mem,
.jlf_erase_check = jlfxv2_erase_check,
.jlf_write_flash = jlfxv2_write_flash,
.jlf_erase_flash = jlfxv2_erase_flash,
.jlf_read_reg = jlfxv2_read_reg,
.jlf_write_reg = jlfxv2_write_reg,
.jlf_single_step = jlfxv2_single_step,
.jlf_set_breakpoint = jlfxv2_set_breakpoint,
.jlf_cpu_state = jlfxv2_cpu_state,
.jlf_get_config_fuses = jlfxv2_get_config_fuses,
};