mspdebug/drivers/jtaglib_cpuxv2.c

890 lines
23 KiB
C

#include <stdbool.h>
#include "jtaglib.h"
#include "jtaglib_defs.h"
#include "output.h"
#define SAFE_FRAM_PC 0x0004
#ifdef DEBUG_JTAGLIB
#define dbg_printc(fmt, ...) printc_dbg("jlfxv2: %s:%d " fmt, __func__, __LINE__, ##__VA_ARGS__)
#else
#define dbg_printc(fmt, ...) do{}while(0)
#endif
static address_t jlfxv2_read_reg(struct jtdev *p, int reg);
static void jlfxv2_write_reg(struct jtdev *p, int reg, address_t value);
// FIXME:
// * context save/restore:
// * pc gets written correctly on single-step
// * pc does NOT get written on 'run'
// * pc does NOT get read properly on run break
// * implement flash write, erase, verify stuff
// * check how ^ works in practice on flash & FRAM devices
// * single step: not working!
// * 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;
for (int i = 0; i < 1/*10*/; ++i) {
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 int jlfxv2_wait_sync_ex(struct jtdev *p, const char* fnname) {
uint16_t dr;
uint8_t jtag_id;
jtag_id = jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
for (int i = 0; i < 50; ++i) {
if ((dr = jtag_dr_shift_16(p, 0)) & 0x0200)
return 1;
}
printc_err("jlfxv2: %s: couldn't sync! (dr=%04x jid=%02x)\n", fnname, dr, jtag_id);
return 0;
}
#define jlfxv2_wait_sync(p) jlfxv2_wait_sync_ex((p), __func__)
static uint8_t bitswap_nyb(uint8_t in) {
return ((in >> 3) & 1) | ((in >> 1) & 2) | ((in << 1) & 4) | ((in << 3) & 8);
}
static uint8_t bitswap(uint8_t in) {
return (bitswap_nyb(in&0xf) << 4) | bitswap_nyb(in>>4);
}
static address_t demangle_mab(address_t mab) {
address_t page = mab & 0xf0000;
address_t bot = mab & 0x0ff00;
address_t top = mab & 0x000ff;
address_t ret = page | (bot >> 8) | ((address_t)bitswap(top) << 8);
//dbg_printc("demangle: %05x -> %05x\n", mab, ret);
return ret;
}
static int jlfxv2_set_pc(struct jtdev *p, address_t addr)
{
address_t addr_rb;
dbg_printc("set pc %05x\n", addr);
if (!jlfxv2_wait_sync(p)) return -1;
/*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);
addr_rb = demangle_mab(jtag_dr_shift_20(p, 0));
dbg_printc("set pc MAB -> %05x%s\n", addr_rb,
(addr!=addr_rb)?", aieee!":"");
return (addr == addr_rb) ? 0 : -1;
}
/*static address_t jlfxv2_get_pc(struct jtdev *p)
{
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
jtag_dr_shift_16(p, 0);
jtag_ir_shift(p, IR_ADDR_CAPTURE);
jtag_dr_shift_20(p, 0);
address_t addr = jtag_dr_shift_20(p, 0);
dbg_printc("get pc %05x\n", addr);
return addr;
}*/
/* 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;
if (jlfxv2_set_pc(p, start_address) < 0) return -1;
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;
dbg_printc("jlfxv2: get_device\n");
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;
}
static void jlfxv2_read_mem_quick(struct jtdev *p, address_t address,
unsigned int length, uint16_t *data);
/* Reads one byte/word from a given address
* format : 8-byte, 16-word
* address: address of memory
* return : content of memory
*/
static uint16_t jlfxv2_read_mem(struct jtdev *p, unsigned int format, address_t address)
{ // SLAU320AJ name: ReadMem
uint16_t r = 0;
dbg_printc("read mem %05x\n", address);
if (!jlfxv2_check_full_emu_state(p))
return 0;
// the code below (an attempt at implementing the real read_mem) doesn't
// work, so let's use read_mem_quick as a backup
jlfxv2_read_mem_quick(p, address ^ (address & 1), 1, &r);
if (format == 8) {
if (address & 1) return (r >> 8) & 0xff;
else return r & 0xff;
} else return r;
/*//jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
//uint16_t dr = jtag_dr_shift_16(p, 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 ^ (address & 1));
//jtag_ir_shift(p, IR_DATA_TO_ADDR);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_CAPTURE);
r = jtag_dr_shift_16(p, 0);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
if (format == 8) {
if (address & 1) return (r >> 8) & 0xff;
else return r & 0xff;
} else 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;
dbg_printc("read mem quick %05x..%05x\n", address, address+length*2);
if (jlfxv2_set_pc(p, address) < 0) return;
//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);
jlfxv2_set_pc(p, SAFE_FRAM_PC);
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);
}
static void jlfxv2_write_mem_quick(struct jtdev *p, address_t address,
unsigned int length, const 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
*/
static void jlfxv2_write_mem(struct jtdev *p, unsigned int format,
address_t address, uint16_t data)
{ // SLAU320AJ name: WriteMem
dbg_printc("write mem: %d %06x <- %04x\n", format, address, data);
if (format == 8) {
p->failed = 1;
printc_err("jlfxv2 write mem: byte access not yet implemented!\n");
return;
}
// same story as with read_mem
jlfxv2_write_mem_quick(p, address, 1, &data);
if (!jlfxv2_check_full_emu_state(p))
return;
/*jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
uint16_t dr = jtag_dr_shift_16(p, 0);
dbg_printc("write mem %d: %06x<-%04x: dr=%04x\n", format, address, data, dr);
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]);
}*/
dbg_printc("write mem quick: %05x..%05x\n", address, address+length*2);
if (!jlfxv2_check_full_emu_state(p))
return;
if (jlfxv2_set_pc(p, address) < 0) return;
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0500);
jtag_tclk_set(p);
jtag_ir_shift(p, IR_DATA_QUICK);
for (unsigned int i = 0; i < length; ++i) {
jtag_tclk_set(p);
jtag_dr_shift_16(p, data[i]);
jtag_tclk_clr(p);
}
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0501);
jtag_tclk_set(p);
jlfxv2_set_pc(p, SAFE_FRAM_PC);
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);
}
/* 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;
dbg_printc("execute_puc\n");
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x1501);
if (!jlfxv2_wait_sync(p)) return -1;
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);
delay_ms(40);
jtag_dr_shift_16(p, 0x0401);
if (jtag_id == 0x91 || jtag_id == 0X99 || jtag_id == 0x98) {
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);
if (jtag_id == 0x91) {
jtag_tclk_clr(p);
jtag_tclk_set(p);
}
jtag_ir_shift(p, IR_DATA_CAPTURE);
} else { // TODO: this if jtag_id == 0x91, else other branch?
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
}
// two more cycles to release CPU internal POR delay signals
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);
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
dbg_printc("jlfxv2: release device: BOR\n");
jtag_ir_shift(p, IR_TEST_REG);
jtag_dr_shift_16(p, 0x0200);
delay_ms(5);
break;
case 0xfffe: // reset
dbg_printc("jlfxv2: release device: SRST\n");
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0C01);
delay_ms(40);
jtag_dr_shift_16(p, 0x0401);
jtag_ir_shift(p, IR_CNTRL_SIG_RELEASE);
break;
default:
dbg_printc("jlfxv2: release device: PC=%05x\n", address);
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_CAPTURE);
jtag_tclk_clr(p);
if (jtag_dr_shift_16(p, 0) & 2/*CNTRL_SIG_HALT*/) {
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0403);
}
jtag_tclk_set(p);
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
// TODO: implement!
}
/* 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
// TODO: implement!
}
/* 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;
const bool alt_addr = false;//true;
if (reg == 3) return 0; // CG
dbg_printc("read reg %d\n", reg);
if (!jlfxv2_check_full_emu_state(p))
return 0;
jtag_id = jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_16BIT);
jtag_tclk_set(p);
jtag_dr_shift_16(p, ((reg << 8) & 0x0f00) | 0x0060);
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);
if (alt_addr) {
jtag_dr_shift_16(p, 0x0ff6);
} else {
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);
if (alt_addr) {
jtag_ir_shift(p, IR_CNTRL_SIG_16BIT);
jtag_dr_shift_16(p, 0x0501);
}
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);
if (!alt_addr) {
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);
dbg_printc("read reg %d: lo=%04x hi=%04x\n", reg, reglo, reghi);
jlfxv2_set_pc(p, SAFE_FRAM_PC);
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);
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
if (reg == 0 || reg == 3) return; // pc, cg
dbg_printc("write reg %d %06x\n", reg, value);
if (!jlfxv2_check_full_emu_state(p)) return;
/*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) | (reg & 0xf));
//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, 0x0501);
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;
dbg_printc("single step\n");
// TODO: fix this. it only performs an instruction fetch but not much else
/*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 */
// TODO: implement
return 0;
}
/*----------------------------------------------------------------------------*/
static unsigned int jlfxv2_cpu_state( struct jtdev *p )
{ // libmsp430 BIOS name: WaitForEem(?)
jtag_ir_shift(p, IR_EMEX_READ_CONTROL);
if ((jtag_dr_shift_16(p, 0x0000) & EEM_STOPPED) == EEM_STOPPED) {
dbg_printc("cpu_state: halted\n");
return 1; /* halted */
} else {
dbg_printc("cpu_state: running\n");
return 0; /* running */
}
}
/*----------------------------------------------------------------------------*/
static int jlfxv2_get_config_fuses( struct jtdev *p )
{ // always the same?
jtag_ir_shift(p, IR_CONFIG_FUSES);
return jtag_dr_shift_8(p, 0);
}
/* ------------------------------------------------------------------------- */
static void jlfxv2_context_save(struct jtdev *p, bool after_puc) {
address_t wdtctl_a = (p->jtag_id == 0x89) ? 0x0120 : 0x015c,
rst_vec = 0x0fffe,
mab, entrypt_addr;
dbg_printc("context save, %s\n", after_puc?"after PUC":"normal");
jtag_ir_shift(p, IR_EMEX_WRITE_CONTROL);
jtag_dr_shift_16(p, EMU_FEAT_EN | EMU_CLK_EN | CLEAR_STOP);
jtag_ir_shift(p, IR_CNTRL_SIG_CAPTURE);
jtag_dr_shift_16(p, 0);
/* ReadMemWordXv2 */
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_ADDR_16BIT);
jtag_dr_shift_20(p, wdtctl_a);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_CAPTURE);
p->wdtctl = jtag_dr_shift_16(p, 0) & 0xff;
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
dbg_printc("WDTCTL: %04x\n", p->wdtctl);
/* ReadMemWordXv2 */
/* actually not using the result for this, but adding this makes it work
* somehow */
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_ADDR_16BIT);
jtag_dr_shift_20(p, rst_vec);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_ir_shift(p, IR_DATA_CAPTURE);
entrypt_addr = jtag_dr_shift_16(p, 0);
jtag_tclk_set(p);
jtag_tclk_clr(p);
jtag_tclk_set(p);
dbg_printc("ENTRYPOINT: %04x\n", entrypt_addr);
jtag_ir_shift(p, IR_ADDR_CAPTURE);
mab = demangle_mab(jtag_dr_shift_20(p, 0));
dbg_printc("PC MAB: %05x\n", mab);
//mab = rst_vec + 4;
/* back up program counter */
if (after_puc) {
p->regs[0] = rst_vec; /* yeah.. */
} else {
p->regs[0] = mab - 4;
}
if (p->regs[0] == rst_vec) {
p->regs[0] = jtag_read_mem(p, 16, rst_vec);
dbg_printc("ENTRY TRY1: %04x\n", p->regs[0]);
if (p->regs[0] == 0xffff || p->regs[0] == 0x3fff) {
p->regs[0] = jtag_read_mem(p, 16, rst_vec);
dbg_printc("ENTRY TRY2: %04x\n", p->regs[0]);
}
}
/* disable watchdog */
//p->wdtctl = jtag_read_mem(p, 16, wdtctl_a) & 0xff;
dbg_printc("WDTCTL: %04x\n", p->wdtctl);
jtag_write_mem(p, 16, wdtctl_a, p->wdtctl | 0x5a80);
/* also back up stack pointer & status register */
p->regs[1] = jtag_read_reg(p, 1);
p->regs[2] = jtag_read_reg(p, 2);
const static uint16_t data[] = {0x3fff,0x3fff,0x3fff};
jtag_write_mem_quick(p, 0x00004, sizeof(data)/sizeof(*data), data);
}
static void jlfxv2_context_restore(struct jtdev *p) {
dbg_printc("context restore\n");
/* basically the inverse of context_save */
address_t wdtctl_a = (p->jtag_id == 0x89) ? 0x0120 : 0x015c;
jtag_write_reg(p, 1, p->regs[1]);
jtag_write_reg(p, 2, p->regs[2]);
jtag_write_mem(p, 16, wdtctl_a, p->wdtctl | 0x5a00);
jlfxv2_set_pc(p, p->regs[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_context_save = jlfxv2_context_save,
.jlf_context_restore = jlfxv2_context_restore,
.jlf_regs_update = jtag_dev_default_regs_update,
.jlf_regs_flush = jtag_dev_default_regs_flush,
.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,
};