diff --git a/Makefile b/Makefile index cb8c826..7d9ac33 100644 --- a/Makefile +++ b/Makefile @@ -146,6 +146,9 @@ OBJ=\ drivers/pif.o \ drivers/loadbsl.o \ drivers/loadbsl_fw.o \ + drivers/hal_proto.o \ + drivers/v3hil.o \ + drivers/fet3.o \ formats/binfile.o \ formats/coff.o \ formats/elf32.o \ diff --git a/drivers/fet3.c b/drivers/fet3.c new file mode 100644 index 0000000..04a4ba4 --- /dev/null +++ b/drivers/fet3.c @@ -0,0 +1,308 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * + * 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 +#include +#include "util/ctrlc.h" +#include "dis.h" +#include "output.h" +#include "comport.h" +#include "cdc_acm.h" +#include "fet3.h" +#include "chipinfo.h" +#include "v3hil.h" + +struct fet3 { + struct device base; + struct v3hil hil; +}; + +static int fet3_readmem(device_t dev_base, address_t addr, + uint8_t *mem, address_t len) +{ + struct fet3 *fet = (struct fet3 *)dev_base; + + if (addr & 1) { + uint8_t word[2]; + + if (v3hil_read(&fet->hil, addr - 1, word, 2) < 0) + return -1; + + mem[0] = word[1]; + addr++; + len--; + } + + while (len >= 2) { + int r = 128; + + if (r > len) + r = len; + + r = v3hil_read(&fet->hil, addr, mem, r & ~1); + if (r < 0) + return -1; + + addr += r; + mem += r; + len -= r; + } + + if (len) { + uint8_t word[2]; + + if (v3hil_read(&fet->hil, addr, word, 2) < 0) + return -1; + + mem[0] = word[0]; + addr++; + len--; + } + + return 0; +} + +static int fet3_writemem(device_t dev_base, address_t addr, + const uint8_t *mem, address_t len) +{ + struct fet3 *fet = (struct fet3 *)dev_base; + + if (addr & 1) { + uint8_t word[2]; + + if (v3hil_read(&fet->hil, addr - 1, word, 2) < 0) + return -1; + + word[1] = mem[0]; + if (v3hil_write(&fet->hil, addr - 1, word, 2) < 0) + return -1; + + addr++; + len--; + } + + while (len >= 2) { + int r = 128; + + if (r > len) + r = len; + + r = v3hil_write(&fet->hil, addr, mem, r & ~1); + if (r < 0) + return -1; + + addr += r; + mem += r; + len -= r; + } + + if (len) { + uint8_t word[2]; + + if (v3hil_read(&fet->hil, addr, word, 2) < 0) + return -1; + + word[0] = mem[0]; + if (v3hil_write(&fet->hil, addr, word, 2) < 0) + return -1; + + addr++; + len--; + } + + return 0; +} + +static int fet3_setregs(device_t dev_base, const address_t *regs) +{ + struct fet3 *fet = (struct fet3 *)dev_base; + + memcpy(fet->hil.regs, regs, sizeof(fet->hil.regs)); + return -1; +} + +static int fet3_getregs(device_t dev_base, address_t *regs) +{ + struct fet3 *fet = (struct fet3 *)dev_base; + + memcpy(regs, fet->hil.regs, sizeof(fet->hil.regs)); + return 0; +} + +static int fet3_ctl(device_t dev_base, device_ctl_t type) +{ + struct fet3 *fet = (struct fet3 *)dev_base; + + switch (type) { + case DEVICE_CTL_RESET: + if (v3hil_sync(&fet->hil) < 0) + return -1; + return v3hil_update_regs(&fet->hil); + + case DEVICE_CTL_RUN: + if (v3hil_flush_regs(&fet->hil) < 0) + return -1; + return v3hil_context_restore(&fet->hil, 0); + + case DEVICE_CTL_HALT: + if (v3hil_context_save(&fet->hil) < 0) + return -1; + return v3hil_update_regs(&fet->hil); + + case DEVICE_CTL_STEP: + if (v3hil_flush_regs(&fet->hil) < 0) + return -1; + if (v3hil_single_step(&fet->hil) < 0) + return -1; + return v3hil_update_regs(&fet->hil); + } + + return 0; +} + +static device_status_t fet3_poll(device_t dev_base) +{ + /* We don't support breakpoints yet, so there's nothing to poll + * for. + */ + delay_ms(500); + if (ctrlc_check()) + return DEVICE_STATUS_INTR; + + return DEVICE_STATUS_RUNNING; +} + +static int fet3_erase(device_t dev_base, device_erase_type_t type, + address_t addr) +{ + struct fet3 *fet = (struct fet3 *)dev_base; + + if (type == DEVICE_ERASE_ALL) { + printc_err("fet3: mass erase is not supported\n"); + return -1; + } + + return v3hil_erase(&fet->hil, + (type == DEVICE_ERASE_MAIN) ? ADDRESS_NONE : addr); +} + +static int debug_init(struct fet3 *fet, const struct device_args *args) +{ + if (v3hil_comm_init(&fet->hil) < 0) + return -1; + + printc_dbg("Set VCC: %d mV\n", args->vcc_mv); + if (v3hil_set_vcc(&fet->hil, args->vcc_mv) < 0) + return -1; + + printc_dbg("Starting interface...\n"); + if (v3hil_start_jtag(&fet->hil, + (args->flags & DEVICE_FLAG_JTAG) ? + V3HIL_JTAG_JTAG : V3HIL_JTAG_SPYBIWIRE) < 0) + return -1; + + if (args->forced_chip_id) { + fet->hil.chip = chipinfo_find_by_name(args->forced_chip_id); + if (!fet->hil.chip) { + printc_err("fet3: unknown chip: %s\n", + args->forced_chip_id); + goto fail_stop_jtag; + } + } else { + if (v3hil_identify(&fet->hil) < 0) + goto fail_stop_jtag; + } + + if (v3hil_configure(&fet->hil) < 0) + goto fail_stop_jtag; + if (v3hil_update_regs(&fet->hil) < 0) + goto fail_stop_jtag; + + return 0; + +fail_stop_jtag: + v3hil_stop_jtag(&fet->hil); + return -1; +} + +static device_t fet3_open(const struct device_args *args) +{ + struct fet3 *fet; + transport_t trans; + + if (args->flags & DEVICE_FLAG_TTY) + trans = comport_open(args->path, 460800); + else + trans = cdc_acm_open(args->path, args->requested_serial, + 460800, 0x2047, 0x0013); + + if (!trans) { + printc_err("fet3: failed to open transport\n"); + return NULL; + } + + fet = malloc(sizeof(*fet)); + if (!fet) { + printc_err("fet3: malloc: %s\n", last_error()); + trans->ops->destroy(trans); + return NULL; + } + + memset(fet, 0, sizeof(*fet)); + fet->base.type = &device_ezfet; + + /* Breakpoints aren't supported yet */ + fet->base.max_breakpoints = 0; + + v3hil_init(&fet->hil, trans, 0); + + if (debug_init(fet, args) < 0) { + trans->ops->destroy(trans); + free(fet); + return NULL; + } + + return &fet->base; +} + +static void fet3_destroy(device_t dev_base) +{ + struct fet3 *fet = (struct fet3 *)dev_base; + transport_t tr = fet->hil.hal.trans; + + v3hil_flush_regs(&fet->hil); + v3hil_context_restore(&fet->hil, 1); + v3hil_stop_jtag(&fet->hil); + + tr->ops->destroy(tr); + free(fet); +} + +const struct device_class device_ezfet = { + .name = "ezfet", + .help = "Texas Instruments eZ-FET", + .open = fet3_open, + .destroy = fet3_destroy, + .readmem = fet3_readmem, + .writemem = fet3_writemem, + .getregs = fet3_getregs, + .setregs = fet3_setregs, + .ctl = fet3_ctl, + .poll = fet3_poll, + .erase = fet3_erase +}; diff --git a/drivers/fet3.h b/drivers/fet3.h new file mode 100644 index 0000000..988d421 --- /dev/null +++ b/drivers/fet3.h @@ -0,0 +1,27 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * + * 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 FET3_H_ +#define FET3_H_ + +#include "device.h" + +/* eZ-FET */ +extern const struct device_class device_ezfet; + +#endif diff --git a/drivers/hal_proto.c b/drivers/hal_proto.c new file mode 100644 index 0000000..2ff2d93 --- /dev/null +++ b/drivers/hal_proto.c @@ -0,0 +1,196 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2013 Daniel Beer + * + * 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 +#include "util.h" +#include "output.h" +#include "hal_proto.h" + +void hal_proto_init(struct hal_proto *p, transport_t trans, + hal_proto_flags_t flags) +{ + memset(p, 0, sizeof(*p)); + + p->trans = trans; + p->flags = flags; + p->ref_id = 0; +} + +int hal_proto_send(struct hal_proto *p, hal_proto_type_t type, + const uint8_t *data, int length) +{ + uint8_t buf[512]; + size_t len = 0; + + if (length > HAL_MAX_PAYLOAD) { + printc_err("hal_proto_send: payload too long: %d\n", length); + return -1; + } + + buf[len++] = length + 3; + buf[len++] = type; + buf[len++] = p->ref_id; + buf[len++] = 0; + + p->ref_id = (p->ref_id + 1) & 0x7f; + + memcpy(buf + len, data, length); + len += length; + + if (len & 1) + buf[len++] = 0; + + if (p->flags & HAL_PROTO_CHECKSUM) { + size_t i; + uint8_t sum_l = 0xff; + uint8_t sum_h = 0xff; + + for (i = 0; i < len; i += 2) { + sum_l ^= buf[i]; + sum_h ^= buf[i + 1]; + } + + buf[len++] = sum_l; + buf[len++] = sum_h; + } + + if (p->trans->ops->send(p->trans, buf, len) < 0) { + printc_err("hal_proto_send: type: 0x%02x\n", type); + return -1; + } + + return 0; +} + +int hal_proto_receive(struct hal_proto *p, uint8_t *buf, int max_len) +{ + uint8_t rx_buf[512]; + uint8_t sum_h = 0xff; + uint8_t sum_l = 0xff; + int rx_len = 0; + int len; + int i; + + for (;;) { + int r = p->trans->ops->recv(p->trans, rx_buf + rx_len, + sizeof(rx_buf) - rx_len); + + if (r <= 0) { + printc_err("hal_proto_recv: read error\n"); + return -1; + } + + rx_len += r; + + if (rx_len) { + const size_t expect_len = + rx_buf[0] + 4 - (rx_buf[0] & 1); + + if (rx_len == expect_len) + break; + + if (rx_len > expect_len) { + printc_err("hal_proto_recv: length " + "mismatch\n"); + return -1; + } + } + } + + if (rx_len < 6) { + printc_err("hal_proto_recv: short read: %d\n", rx_len); + return -1; + } + + for (i = 0; i < rx_len; i += 2) { + sum_h ^= rx_buf[i]; + sum_l ^= rx_buf[i + 1]; + } + + if (sum_h || sum_l) { + printc_err("hal_proto_recv: bad checksum\n"); + return -1; + } + + len = rx_buf[0] - 3; + p->type = rx_buf[1]; + p->ref = rx_buf[2]; + p->seq = rx_buf[3]; + + if (len > max_len) { + printc_err("hal_proto_recv: reply too long\n"); + return -1; + } + + memcpy(buf, rx_buf + 4, len); + return len; +} + +int hal_proto_execute(struct hal_proto *p, uint8_t fid, + const uint8_t *data, int len) +{ + uint8_t fdata[HAL_MAX_PAYLOAD]; + + if (len + 2 > HAL_MAX_PAYLOAD) { + printc_err("hal_proto_execute: payload too big: %d\n", len); + return -1; + } + + fdata[0] = fid; + fdata[1] = 0; + memcpy(fdata + 2, data, len); + + if (hal_proto_send(p, HAL_PROTO_TYPE_CMD_EXECUTE, fdata, len + 2) < 0) + goto fail; + + p->length = 0; + + do { + int r = hal_proto_receive(p, p->payload + p->length, + sizeof(p->payload) - p->length); + + if (r < 0) + goto fail; + + if ((p->type == HAL_PROTO_TYPE_EXCEPTION) && (r >= 2)) { + printc_err("hal_proto_execute: HAL exception: 0x%04x\n", + LE_WORD(p->payload, p->length)); + goto fail; + } + + if (p->type == HAL_PROTO_TYPE_ACKNOWLEDGE) + break; + + if (p->type != HAL_PROTO_TYPE_DATA) { + printc_err("hal_proto_execute: no data " + "(got type 0x%02x)\n", p->type); + goto fail; + } + + if (hal_proto_send(p, HAL_PROTO_TYPE_ACKNOWLEDGE, NULL, 0) < 0) + goto fail; + + p->length += r; + } while (p->ref & 0x80); + + return 0; + +fail: + printc_err("hal_proto_execute: fid: 0x%02x\n", fid); + return -1; +} diff --git a/drivers/hal_proto.h b/drivers/hal_proto.h new file mode 100644 index 0000000..7ce4322 --- /dev/null +++ b/drivers/hal_proto.h @@ -0,0 +1,100 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2013 Daniel Beer + * + * 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 HAL_PROTO_H_ +#define HAL_PROTO_H_ + +#include +#include +#include "transport.h" + +/* Low-level HAL message types */ +typedef enum { + HAL_PROTO_TYPE_UP_INIT = 0x51, + HAL_PROTO_TYPE_UP_ERASE = 0x52, + HAL_PROTO_TYPE_UP_WRITE = 0x53, + HAL_PROTO_TYPE_UP_READ = 0x54, + HAL_PROTO_TYPE_UP_CORE = 0x55, + HAL_PROTO_TYPE_DCDC_CALIBRATE = 0x56, + HAL_PROTO_TYPE_DCDC_INIT_INTERFACE = 0x57, + HAL_PROTO_TYPE_DCDC_SUB_MCU_VERSION = 0x58, + HAL_PROTO_TYPE_DCDC_LAYER_VERSION = 0x59, + HAL_PROTO_TYPE_DCDC_POWER_DOWN = 0x60, + HAL_PROTO_TYPE_DCDC_SET_VCC = 0x61, + HAL_PROTO_TYPE_DCDC_RESTART = 0x62, + HAL_PROTO_TYPE_CMD_LEGACY = 0x7e, + HAL_PROTO_TYPE_CMD_SYNC = 0x80, + HAL_PROTO_TYPE_CMD_EXECUTE = 0x81, + HAL_PROTO_TYPE_CMD_EXECUTE_LOOP = 0x82, + HAL_PROTO_TYPE_CMD_LOAD = 0x83, + HAL_PROTO_TYPE_CMD_LOAD_CONTINUED = 0x84, + HAL_PROTO_TYPE_CMD_DATA = 0x85, + HAL_PROTO_TYPE_CMD_KILL = 0x86, + HAL_PROTO_TYPE_CMD_MOVE = 0x87, + HAL_PROTO_TYPE_CMD_UNLOAD = 0x88, + HAL_PROTO_TYPE_CMD_BYPASS = 0x89, + HAL_PROTO_TYPE_CMD_EXECUTE_LOOP_CONT = 0x8a, + HAL_PROTO_TYPE_CMD_COM_RESET = 0x8b, + HAL_PROTO_TYPE_CMD_PAUSE_LOOP = 0x8c, + HAL_PROTO_TYPE_CMD_RESUME_LOOP = 0x8d, + HAL_PROTO_TYPE_ACKNOWLEDGE = 0x91, + HAL_PROTO_TYPE_EXCEPTION = 0x92, + HAL_PROTO_TYPE_DATA = 0x93, + HAL_PROTO_TYPE_DATA_REQUEST = 0x94, + HAL_PROTO_TYPE_STATUS = 0x95 +} hal_proto_type_t; + +typedef enum { + HAL_PROTO_CHECKSUM = 0x01 +} hal_proto_flags_t; + +#define HAL_MAX_PAYLOAD 253 + +struct hal_proto { + transport_t trans; + hal_proto_flags_t flags; + uint8_t ref_id; + + /* Receive parameters */ + hal_proto_type_t type; + uint8_t ref; + uint8_t seq; + + /* Execute data */ + int length; + uint8_t payload[4096]; +}; + +/* Initialize a HAL protocol interpreter */ +void hal_proto_init(struct hal_proto *p, transport_t trans, + hal_proto_flags_t flags); + +/* Send a low-level HAL command */ +int hal_proto_send(struct hal_proto *p, hal_proto_type_t type, + const uint8_t *data, int length); + +/* Receive a low-level HAL response */ +int hal_proto_receive(struct hal_proto *p, uint8_t *buf, int max_len); + +/* Execute a high-level function. The reply data is kept in the payload + * buffer. + */ +int hal_proto_execute(struct hal_proto *p, uint8_t fid, + const uint8_t *data, int len); + +#endif diff --git a/drivers/v3hil.c b/drivers/v3hil.c new file mode 100644 index 0000000..638869a --- /dev/null +++ b/drivers/v3hil.c @@ -0,0 +1,1103 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * + * 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 +#include "bytes.h" +#include "v3hil.h" +#include "dis.h" +#include "output.h" +#include "opdb.h" + +/* HAL function IDs */ +typedef enum { + HAL_PROTO_FID_INIT = 0x01, + HAL_PROTO_FID_SET_VCC = 0x02, + HAL_PROTO_FID_GET_VCC = 0x03, + HAL_PROTO_FID_START_JTAG = 0x04, + HAL_PROTO_FID_START_JTAG_ACT_CODE = 0x05, + HAL_PROTO_FID_STOP_JTAG = 0x06, + HAL_PROTO_FID_CONFIGURE = 0x07, + HAL_PROTO_FID_GET_FUSES = 0x08, + HAL_PROTO_FID_BLOW_FUSE = 0x09, + HAL_PROTO_FID_WAIT_FOR_EEM = 0x0a, + HAL_PROTO_FID_BIT_SEQUENCE = 0x0b, + HAL_PROTO_FID_GET_JTAG_ID = 0x0c, + HAL_PROTO_FID_SET_DEVICE_CHAIN_INFO = 0x0d, + HAL_PROTO_FID_SET_CHAIN_CONFIGURATION = 0x0e, + HAL_PROTO_FID_GET_NUM_DEVICES = 0x0f, + HAL_PROTO_FID_GET_INTERFACE_MODE = 0x10, + HAL_PROTO_FID_SJ_ASSERT_POR_SC = 0x11, + HAL_PROTO_FID_SJ_CONDITIONAL_SC = 0x12, + HAL_PROTO_FID_RC_RELEASE_JTAG = 0x13, + HAL_PROTO_FID_READ_MEM_BYTES = 0x14, + HAL_PROTO_FID_READ_MEM_WORDS = 0x15, + HAL_PROTO_FID_READ_MEM_QUICK = 0x16, + HAL_PROTO_FID_WRITE_MEM_BYTES = 0x17, + HAL_PROTO_FID_WRITE_MEM_WORDS = 0x18, + HAL_PROTO_FID_EEM_DX = 0x19, + HAL_PROTO_FID_EEM_DX_AFE2XX = 0x1a, + HAL_PROTO_FID_SINGLE_STEP = 0x1b, + HAL_PROTO_FID_READ_ALL_CPU_REGS = 0x1c, + HAL_PROTO_FID_WRITE_ALL_CPU_REGS = 0x1d, + HAL_PROTO_FID_PSA = 0x1e, + HAL_PROTO_FID_EXECUTE_FUNCLET = 0x1f, + HAL_PROTO_FID_EXECUTE_FUNCLET_JTAG = 0x20, + HAL_PROTO_FID_GET_DCO_FREQUENCY = 0x21, + HAL_PROTO_FID_GET_DCO_FREQUENCY_JTAG = 0x22, + HAL_PROTO_FID_GET_FLL_FREQUENCY = 0x23, + HAL_PROTO_FID_GET_FLL_FREQUENCY_JTAG = 0x24, + HAL_PROTO_FID_WAIT_FOR_STORAGE = 0x25, + HAL_PROTO_FID_SJ_ASSERT_POR_SC_X = 0x26, + HAL_PROTO_FID_SJ_CONDITIONAL_SC_X = 0x27, + HAL_PROTO_FID_RC_RELEASE_JTAG_X = 0x28, + HAL_PROTO_FID_READ_MEM_BYTES_X = 0x29, + HAL_PROTO_FID_READ_MEM_WORDS_X = 0x2a, + HAL_PROTO_FID_READ_MEM_QUICK_X = 0x2b, + HAL_PROTO_FID_WRITE_MEM_BYTES_X = 0x2c, + HAL_PROTO_FID_WRITE_MEM_WORDS_X = 0x2d, + HAL_PROTO_FID_EEM_DX_X = 0x2e, + HAL_PROTO_FID_SINGLE_STEP_X = 0x2f, + HAL_PROTO_FID_READ_ALL_CPU_REGS_X = 0x30, + HAL_PROTO_FID_WRITE_ALL_CPU_REGS_X = 0x31, + HAL_PROTO_FID_PSA_X = 0x32, + HAL_PROTO_FID_EXECUTE_FUNCLET_X = 0x33, + HAL_PROTO_FID_GET_DCO_FREQUENCY_X = 0x34, + HAL_PROTO_FID_GET_FLL_FREQUENCY_X = 0x35, + HAL_PROTO_FID_WAIT_FOR_STORAGE_X = 0x36, + HAL_PROTO_FID_BLOW_FUSE_XV2 = 0x37, + HAL_PROTO_FID_BLOW_FUSE_FRAM = 0x38, + HAL_PROTO_FID_SJ_ASSERT_POR_SC_XV2 = 0x39, + HAL_PROTO_FID_SJ_CONDITIONAL_SC_XV2 = 0x3a, + HAL_PROTO_FID_RC_RELEASE_JTAG_XV2 = 0x3b, + HAL_PROTO_FID_READ_MEM_WORDS_XV2 = 0x3c, + HAL_PROTO_FID_READ_MEM_QUICK_XV2 = 0x3d, + HAL_PROTO_FID_WRITE_MEM_WORDS_XV2 = 0x3e, + HAL_PROTO_FID_EEM_DX_XV2 = 0x3f, + HAL_PROTO_FID_SINGLE_STEP_XV2 = 0x40, + HAL_PROTO_FID_READ_ALL_CPU_REGS_XV2 = 0x41, + HAL_PROTO_FID_WRITE_ALL_CPU_REGS_XV2 = 0x42, + HAL_PROTO_FID_PSA_XV2 = 0x43, + HAL_PROTO_FID_EXECUTE_FUNCLET_XV2 = 0x44, + HAL_PROTO_FID_UNLOCK_DEVICE_XV2 = 0x45, + HAL_PROTO_FID_MAGIC_PATTERN = 0x46, + HAL_PROTO_FID_UNLOCK_C092 = 0x47, + HAL_PROTO_FID_HIL_COMMAND = 0x48, + HAL_PROTO_FID_POLL_JSTATE_REG = 0x49, + HAL_PROTO_FID_POLL_JSTATE_REG_FR57XX = 0x4a, + HAL_PROTO_FID_IS_JTAG_FUSE_BLOWN = 0x4b, + HAL_PROTO_FID_RESET_XV2 = 0x4c, + HAL_PROTO_FID_WRITE_FRAM_QUICK_XV2 = 0x4d, + HAL_PROTO_FID_SEND_JTAG_MAILBOX_XV2 = 0x4e, + HAL_PROTO_FID_SINGLE_STEP_JSTATE_XV2 = 0x4f, + HAL_PROTO_FID_POLL_JSTATE_REG_ET8 = 0x50, + HAL_PROTO_FID_RESET_STATIC_GLOBAL_VARS = 0x51, + HAL_PROTO_FID_RESET_430I = 0x52, + HAL_PROTO_FID_POLL_JSTATE_REG_430I = 0x53 +} hal_proto_fid_t; + +/* Argument types for HAL_PROTO_FID_CONFIGURE */ +typedef enum { + HAL_PROTO_CONFIG_ENHANCED_PSA = 0x01, + HAL_PROTO_CONFIG_PSA_TCKL_HIGH = 0x02, + HAL_PROTO_CONFIG_DEFAULT_CLK_CONTROL = 0x03, + HAL_PROTO_CONFIG_POWER_TESTREG_MASK = 0x04, + HAL_PROTO_CONFIG_TESTREG_ENABLE_LPMX5 = 0x05, + HAL_PROTO_CONFIG_TESTREG_DISABLE_LPMX5 = 0x06, + HAL_PROTO_CONFIG_POWER_TESTREG3V_MASK = 0x07, + HAL_PROTO_CONFIG_TESTREG3V_ENABLE_LPMX5 = 0x08, + HAL_PROTO_CONFIG_TESTREG3V_DISABLE_LPMX5 = 0x09, + HAL_PROTO_CONFIG_CLK_CONTROL_TYPE = 0x0a, + HAL_PROTO_CONFIG_JTAG_SPEED = 0x0b, + HAL_PROTO_CONFIG_SFLLDEH = 0x0c, + HAL_PROTO_CONFIG_NO_BSL = 0x0d, + HAL_PROTO_CONFIG_ALT_ROM_ADDR_FOR_CPU_READ = 0x0e, + HAL_PROTO_CONFIG_ASSERT_BSL_VALID_BIT = 0x0f +} hal_proto_config_t; + +static hal_proto_fid_t map_fid(const struct v3hil *h, hal_proto_fid_t src) +{ + hal_proto_fid_t dst = h->chip->v3_functions[src]; + + return dst ? dst : src; +} + +/* Given an address range, specified by a start and a size (in bytes), + * return a size which is trimmed so as to not overrun a region boundary + * in the chip's memory map. + * + * The single region occupied is optionally returned in m_ret. If the + * range doesn't start in a valid region, it's trimmed to the start of + * the next valid region, and m_ret is NULL. + */ +static address_t check_range(const struct chipinfo *chip, + address_t addr, address_t size, + const struct chipinfo_memory **m_ret) +{ + const struct chipinfo_memory *m = + chipinfo_find_mem_by_addr(chip, addr); + + if (m) { + if (m->offset > addr) { + address_t n = m->offset - addr; + + if (size > n) + size = n; + + m = NULL; + } else if (addr + size > m->offset + m->size) { + size = m->offset + m->size - addr; + } + } + + if (m) + *m_ret = m; + + return size; +} + +void v3hil_init(struct v3hil *h, transport_t trans, + hal_proto_flags_t flags) +{ + memset(h, 0, sizeof(*h)); + hal_proto_init(&h->hal, trans, flags); +} + +int v3hil_set_vcc(struct v3hil *h, int vcc_mv) +{ + uint8_t data[2]; + + w16le(data, vcc_mv); + return hal_proto_execute(&h->hal, HAL_PROTO_FID_SET_VCC, data, 2); +} + +int v3hil_comm_init(struct v3hil *h) +{ + const uint8_t ver_payload = 0; + + printc_dbg("Reset communications...\n"); + if (hal_proto_send(&h->hal, HAL_PROTO_TYPE_EXCEPTION, NULL, 0) < 0) + return -1; + + if (hal_proto_execute(&h->hal, 0, &ver_payload, 1) < 0) + return -1; + if (h->hal.length < 8) { + printc_err("warning: v3hil: short reply to version request\n"); + } else { + const uint8_t major = h->hal.payload[1] >> 6; + const uint8_t minor = h->hal.payload[1] & 0x3f; + const uint8_t patch = h->hal.payload[0]; + const uint16_t flavour = r16le(h->hal.payload + 2); + + printc_dbg("Version: %d.%d.%d.%d, HW: 0x%04x\n", + major, minor, patch, flavour, + r32le(h->hal.payload + 4)); + } + + printc_dbg("Reset firmware...\n"); + if (hal_proto_execute(&h->hal, + HAL_PROTO_FID_RESET_STATIC_GLOBAL_VARS, NULL, 0) < 0) + return -1; + + return 0; +} + +int v3hil_start_jtag(struct v3hil *h, v3hil_jtag_type_t type) +{ + uint8_t data = type; + uint8_t chain_id[2] = {0, 0}; + + if (hal_proto_execute(&h->hal, HAL_PROTO_FID_START_JTAG, + &data, 1) < 0) + return -1; + + if (!h->hal.length) { + printc_err("v3hil: short reply\n"); + return -1; + } + + if (!h->hal.payload[0]) { + printc_err("v3hil: no devices present\n"); + return -1; + } + + printc_dbg("Device count: %d\n", h->hal.payload[0]); + return hal_proto_execute(&h->hal, HAL_PROTO_FID_SET_DEVICE_CHAIN_INFO, + chain_id, 2); +} + +int v3hil_stop_jtag(struct v3hil *h) +{ + return hal_proto_execute(&h->hal, HAL_PROTO_FID_STOP_JTAG, NULL, 0); +} + +int v3hil_sync(struct v3hil *h) +{ + uint8_t data[32]; + + h->cal.is_cal = 0; + + memset(data, 0, sizeof(data)); + data[0] = (h->jtag_id == 0x89) ? 0x20 : 0x5c; /* WDTCTL */ + data[1] = 0x01; + data[2] = 0x80; /* WDTHOLD */ + data[3] = 0x5a; /* WDTPW */ + data[4] = h->jtag_id; + + /* ETW codes (?) */ + if (h->chip) { + int i; + + for (i = 0; i < 16; i++) + data[i + 20 - i] = h->chip->clock_map[i].value; + } else { + data[5] = 1; + data[15] = 40; + } + + /* We can't use map_fid() because h->chip might be NULL -- this + * function will be called before identification is complete. + */ + if (hal_proto_execute(&h->hal, + (h->jtag_id == 0x89) + ? HAL_PROTO_FID_SJ_ASSERT_POR_SC + : HAL_PROTO_FID_SJ_ASSERT_POR_SC_XV2, + data, 21) < 0) + return -1; + + if (h->hal.length < 8) { + printc_err("v3hil: short reply: %d\n", h->hal.length); + return -1; + } + + h->wdtctl = h->hal.payload[0]; + h->regs[MSP430_REG_PC] = r32le(h->hal.payload + 2); + h->regs[MSP430_REG_SR] = r16le(h->hal.payload + 6); + return 0; +} + +int v3hil_read(struct v3hil *h, address_t addr, + uint8_t *mem, address_t size) +{ + const struct chipinfo_memory *m = NULL; + uint8_t req[12]; + + if (h->chip) { + size = check_range(h->chip, addr, size, &m); + if (!m) { + memset(mem, 0x55, size); + return size; + } + } + + w32le(req, addr); + w32le(req + 4, (m->bits == 8) ? size : (size >> 1)); + w32le(req + 8, h->regs[MSP430_REG_PC]); + + if (hal_proto_execute(&h->hal, + map_fid(h, (m->bits == 8) ? HAL_PROTO_FID_READ_MEM_BYTES : + HAL_PROTO_FID_READ_MEM_WORDS), + req, 8) < 0) + goto fail; + + if (h->hal.length < size) { + printc_err("v3hil: short reply: %d\n", h->hal.length); + goto fail; + } + + memcpy(mem, h->hal.payload, size); + return size; +fail: + printc_err("v3hil: failed reading %d bytes from 0x%05x\n", + size, addr); + return -1; +} + +const struct chipinfo_memory *find_ram(const struct chipinfo *c) +{ + const struct chipinfo_memory *m; + const struct chipinfo_memory *best = NULL; + + if (!c) + goto fail; + + for (m = c->memory; m->name; m++) { + if (m->type != CHIPINFO_MEMTYPE_RAM) + continue; + if (!best || m->size > best->size) + best = m; + } + + if (!best) + goto fail; + + return best; + +fail: + printc_err("v3hil: can't find RAM region in chip database\n"); + return NULL; +} + +static int calibrate_dco(struct v3hil *h, uint8_t max_bcs) +{ + const struct chipinfo_memory *ram = find_ram(h->chip); + uint8_t data[4]; + uint8_t mem_write[16]; + + if (!ram) + goto fail; + + printc_dbg("Calibrate DCO...\n"); + + w16le(data, ram->offset); + w16le(data + 2, max_bcs); + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_GET_DCO_FREQUENCY), + data, 4) < 0) + goto fail; + if (h->hal.length < 6) { + printc_err("v3hil: short reply: %d\n", h->hal.length); + goto fail; + } + + h->cal.cal0 = r16le(data); + h->cal.cal1 = r16le(data + 2); + + w32le(mem_write, 0x56); /* addr of DCO */ + w32le(mem_write + 4, 3); + mem_write[8] = data[0]; /* DCO */ + mem_write[9] = data[2]; /* BCS1 */ + mem_write[10] = data[4]; /* BCS2 */ + mem_write[11] = 0; /* pad */ + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_WRITE_MEM_BYTES), + data, 12) < 0) { + printc_err("v3hil: failed to load DCO settings\n"); + goto fail; + } + + return 0; + +fail: + printc_err("v3hil: DCO calibration failed\n"); + return -1; +} + +static int calibrate_fll(struct v3hil *h) +{ + const struct chipinfo_memory *ram = find_ram(h->chip); + uint8_t data[4]; + uint8_t mem_write[16]; + + if (!ram) + goto fail; + + printc_dbg("Calibrate FLL...\n"); + + w16le(data, ram->offset); + w16le(data + 2, 0); + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_GET_DCO_FREQUENCY), + data, 4) < 0) + goto fail; + if (h->hal.length < 10) { + printc_err("v3hil: short reply: %d\n", h->hal.length); + goto fail; + } + + h->cal.cal0 = 0; + h->cal.cal1 = r16le(data + 2); + + w32le(mem_write, 0x50); /* addr of SCFI0 */ + w32le(mem_write + 4, 5); + mem_write[8] = data[0]; /* SCFI0 */ + mem_write[9] = data[2]; /* SCFI1 */ + mem_write[10] = data[4]; /* SCFQCTL */ + mem_write[11] = data[6]; /* FLLCTL0 */ + mem_write[12] = data[8]; /* FLLCTL1 */ + mem_write[13] = 0; /* pad */ + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_WRITE_MEM_BYTES), + data, 14) < 0) { + printc_err("v3hil: failed to load FLL settings\n"); + goto fail; + } + + return 0; + +fail: + printc_err("v3hil: FLL calibration failed\n"); + return -1; +} + +static int calibrate(struct v3hil *h) +{ + int r; + + if (h->cal.is_cal) + return 0; + + switch (h->chip->clock_sys) { + case CHIPINFO_CLOCK_SYS_BC_1XX: + r = calibrate_dco(h, 0x7); + break; + + case CHIPINFO_CLOCK_SYS_BC_2XX: + r = calibrate_dco(h, 0xf); + break; + + case CHIPINFO_CLOCK_SYS_FLL_PLUS: + r = calibrate_fll(h); + break; + + default: + r = 0; + h->cal.cal0 = 0; + h->cal.cal1 = 0; + break; + } + + if (r < 0) + return -1; + + h->cal.is_cal = 1; + return 0; +} + +static int upload_funclet(struct v3hil *h, + const struct chipinfo_memory *ram, + const struct chipinfo_funclet *f) +{ + uint32_t addr = ram->offset; + const uint16_t *code = f->code; + uint16_t num_words = f->code_size; + + if (num_words * 2 > ram->size) { + printc_err("v3hil: funclet too big for RAM\n"); + return -1; + } + + while (num_words) { + uint8_t data[512]; + uint16_t n = num_words > 112 ? 112 : num_words; + int i; + + w32le(data, addr); + w32le(data + 4, n); + for (i = 0; i < n; i++) + w16le(data + 8 + i * 2, code[i]); + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_WRITE_MEM_WORDS), + data, n * 2 + 8) < 0) { + printc_err("v3hil: funclet upload " + "failed at 0x%04x (%d words)\n", + addr, n); + return -1; + } + + addr += n * 2; + code += n; + num_words -= n; + } + + return 0; +} + +static int write_flash(struct v3hil *h, address_t addr, + const uint8_t *mem, address_t size) +{ + const struct chipinfo_memory *ram = find_ram(h->chip); + const struct chipinfo_funclet *f = h->chip->v3_write; + uint8_t data[256]; + uint16_t avail; + + if (!ram) + return -1; + + if (!f) { + printc_err("v3hil: no funclet defined for flash write\n"); + return -1; + } + + if (calibrate(h) < 0) + return -1; + if (upload_funclet(h, ram, f) < 0) + return -1; + + if (size > 128) + size = 128; + + avail = ram->size - f->code_size * 2; + if (avail > f->max_payload) + avail = f->max_payload; + + w16le(data, ram->offset); + w16le(data + 2, avail); + w16le(data + 4, ram->offset + f->entry_point); + + w32le(data + 6, addr); + w32le(data + 10, size >> 1); + w32le(data + 14, 0); + /* If FPERM_LOCKED_FLASH is set, info A is UNLOCKED */ + w16le(data + 16, (opdb_read_fperm() & FPERM_LOCKED_FLASH) ? + 0xa548 : 0xa508); + w16le(data + 18, h->cal.cal0); + w16le(data + 20, h->cal.cal1); + memcpy(data + 22, mem, size); + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_EXECUTE_FUNCLET), + data, size + 22) < 0) { + printc_err("v3hil: failed to program %d bytes at 0x%04x\n", + size, addr); + return -1; + } + + return size; +} + +static int write_ram(struct v3hil *h, const struct chipinfo_memory *m, + address_t addr, const uint8_t *mem, address_t size) +{ + uint8_t data[256]; + + w32le(data, addr); + w32le(data + 4, (m->bits == 8) ? size : (size >> 1)); + + memcpy(data + 8, mem, size); + + if (hal_proto_execute(&h->hal, + map_fid(h, (m->bits == 8) ? HAL_PROTO_FID_WRITE_MEM_BYTES + : HAL_PROTO_FID_WRITE_MEM_WORDS), + data, size + 8) < 0) { + printc_err("v3hil: failed writing %d bytes to 0x%05x\n", + size, addr); + return -1; + } + + return size; +} + +int v3hil_write(struct v3hil *h, address_t addr, + const uint8_t *mem, address_t size) +{ + const struct chipinfo_memory *m = NULL; + + if (h->chip) { + size = check_range(h->chip, addr, size, &m); + if (!m) + return size; + } + + if (size > 128) + size = 128; + + if (m->type == CHIPINFO_MEMTYPE_FLASH) + return write_flash(h, addr, mem, size); + + return write_ram(h, m, addr, mem, size); +} + +static int call_erase(struct v3hil *h, + const struct chipinfo_memory *ram, + const struct chipinfo_funclet *f, + address_t addr, uint16_t type) +{ + uint8_t data[32]; + + printc_dbg("Erase segment @ 0x%04x\n", addr); + + w16le(data, ram->offset); + w16le(data + 2, 0); + w16le(data + 4, ram->offset + f->entry_point); + w32le(data + 6, addr); + w32le(data + 10, 2); + w16le(data + 14, type); + w16le(data + 16, (opdb_read_fperm() & FPERM_LOCKED_FLASH) ? + 0xa548 : 0xa508); + w16le(data + 18, h->cal.cal0); + w16le(data + 20, h->cal.cal1); + w32le(data + 22, 0xdeadbeef); + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_EXECUTE_FUNCLET), + data, 26) < 0) { + printc_err("v3hil: failed to erase at 0x%04x\n", + addr); + return -1; + } + + return 0; +} + +int v3hil_erase(struct v3hil *h, address_t segment) +{ + const struct chipinfo_memory *ram = find_ram(h->chip); + const struct chipinfo_funclet *f = h->chip->v3_erase; + const struct chipinfo_memory *flash; + + if (!ram) + return -1; + + if (!f) { + printc_err("v3hil: no funclet defined for flash erase\n"); + return -1; + } + + if (segment == ADDRESS_NONE) + flash = chipinfo_find_mem_by_name(h->chip, "main"); + else + flash = chipinfo_find_mem_by_addr(h->chip, segment); + + if (!flash) + printc_err("v3hil: can't find appropriate flash region\n"); + + if (calibrate(h) < 0) + return -1; + if (upload_funclet(h, ram, f) < 0) + return -1; + + if (segment == ADDRESS_NONE) { + int bank_size = flash->size; + int i; + + if (flash->banks) + bank_size /= flash->banks; + + for (i = flash->banks; i >= 0; i--) + if (call_erase(h, ram, f, + flash->offset + i * bank_size - 2, 0xa502) < 0) + return -1; + } else { + segment &= ~(flash->seg_size - 1); + segment |= flash->seg_size - 2; + + if (call_erase(h, ram, f, segment, 0xa502) < 0) + return -1; + } + + return 0; +} + +int v3hil_update_regs(struct v3hil *h) +{ + const hal_proto_fid_t fid = + map_fid(h, HAL_PROTO_FID_READ_ALL_CPU_REGS); + const int reg_size = (fid == HAL_PROTO_FID_READ_ALL_CPU_REGS) ? 2 : 3; + int i; + int sptr = 0; + + if (hal_proto_execute(&h->hal, fid, NULL, 0) < 0) { + printc_err("v3hil: can't read CPU registers\n"); + return -1; + } + + if (h->hal.length < reg_size * 13) { + printc_err("v3hil: short read: %d\n", h->hal.length); + return -1; + } + + for (i = 0; i < DEVICE_NUM_REGS; i++){ + address_t r = 0; + int j; + + if ((i == MSP430_REG_PC) || + (i == MSP430_REG_SR) || + (i == MSP430_REG_R3)) + continue; + + for (j = 0; j < reg_size; j++) + r |= ((address_t)(h->hal.payload[sptr++])) << + (j << 3); + + h->regs[i] = r; + } + + return 0; +} + +int v3hil_flush_regs(struct v3hil *h) +{ + const hal_proto_fid_t fid = + map_fid(h, HAL_PROTO_FID_WRITE_ALL_CPU_REGS); + const int reg_size = (fid == HAL_PROTO_FID_WRITE_ALL_CPU_REGS) ? 2 : 3; + int i; + int dptr = 0; + uint8_t data[64]; + + for (i = 0; i < DEVICE_NUM_REGS; i++){ + address_t r = h->regs[i]; + int j; + + if ((i == MSP430_REG_PC) || + (i == MSP430_REG_SR) || + (i == MSP430_REG_R3)) + continue; + + for (j = 0; j < reg_size; j++) { + data[dptr++] = r; + r >>= 8; + } + } + + if (hal_proto_execute(&h->hal, fid, data, reg_size * 13) < 0) { + printc_err("v3hil: can't write CPU registers\n"); + return -1; + } + + return 0; +} + +int v3hil_context_restore(struct v3hil *h, int free) +{ + uint8_t data[32]; + + memset(data, 0, sizeof(data)); + data[0] = (h->jtag_id == 0x89) ? 0x20 : 0x5c; /* WDTCTL */ + data[1] = 0x01; + data[2] = h->wdtctl; + data[3] = 0x5a; /* WDTPW */ + w32le(data + 4, h->regs[MSP430_REG_PC]); + data[8] = h->regs[MSP430_REG_SR]; + data[9] = h->regs[MSP430_REG_SR] >> 8; + data[10] = free ? 7 : 6; + data[14] = free ? 1 : 0; + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_RC_RELEASE_JTAG), + data, 18) < 0) { + printc_err("v3hil: failed to restore context\n"); + return -1; + } + + return 0; +} + +int v3hil_context_save(struct v3hil *h) +{ + uint8_t data[32]; + + h->cal.is_cal = 0; + + memset(data, 0, sizeof(data)); + data[0] = (h->jtag_id == 0x89) ? 0x20 : 0x5c; /* WDTCTL */ + data[1] = 0x01; + data[2] = h->wdtctl | 0x80; + data[3] = 0x5a; /* WDTPW */ + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_SJ_CONDITIONAL_SC), + data, 8) < 0) + return -1; + if (h->hal.length < 8) { + printc_err("v3hil: short reply: %d\n", h->hal.length); + return -1; + } + + h->wdtctl = r16le(h->hal.payload); + h->regs[MSP430_REG_PC] = r32le(h->hal.payload + 2); + h->regs[MSP430_REG_SR] = r16le(h->hal.payload + 6); + return 0; +} + +int v3hil_single_step(struct v3hil *h) +{ + uint8_t data[32]; + + h->cal.is_cal = 0; + + memset(data, 0, sizeof(data)); + data[0] = (h->jtag_id == 0x89) ? 0x20 : 0x5c; /* WDTCTL */ + data[1] = 0x01; + data[2] = h->wdtctl; + data[3] = 0x5a; /* WDTPW */ + w32le(data + 4, h->regs[MSP430_REG_PC]); + data[8] = h->regs[MSP430_REG_SR]; + data[9] = h->regs[MSP430_REG_SR] >> 8; + data[10] = 7; + + if (hal_proto_execute(&h->hal, + map_fid(h, HAL_PROTO_FID_SINGLE_STEP), + data, 18) < 0) { + printc_err("do_step: single-step failed\n"); + return -1; + } + + if (h->hal.length < 8) { + printc_err("do_step: short reply: %d\n", h->hal.length); + return -1; + } + + h->wdtctl = r16le(h->hal.payload); + h->regs[MSP430_REG_PC] = r32le(h->hal.payload + 2); + h->regs[MSP430_REG_SR] = r16le(h->hal.payload + 6); + return 0; +} + +/************************************************************************ + * Identification/config + */ + +static int set_param(struct v3hil *fet, hal_proto_config_t cfg, + uint32_t value) +{ + uint8_t data[8] = {0}; + int i; + + for (i = 0; i < 4; i++) { + data[i + 4] = value; + value >>= 8; + } + + data[0] = cfg; + if (hal_proto_execute(&fet->hal, HAL_PROTO_FID_CONFIGURE, + data, 8) < 0) { + printc_err("v3hil: can't set param 0x%02x to 0x%08x\n", + cfg, value); + return -1; + } + + return 0; +} + +static int idproc_89(struct v3hil *fet, uint32_t id_data_addr, + struct chipinfo_id *id) +{ + uint8_t data[32]; + + printc_dbg("Identify (89)...\n"); + printc_dbg("Read device ID bytes at 0x%05x...\n", id_data_addr); + memset(data, 0, 8); + w32le(data, id_data_addr); + data[4] = 8; + if (hal_proto_execute(&fet->hal, HAL_PROTO_FID_READ_MEM_WORDS, + data, 8) < 0) + return -1; + if (fet->hal.length < 16) { + printc_err("v3hil: short reply: %d\n", fet->hal.length); + return -1; + } + + id->ver_id = r16le(fet->hal.payload); + id->ver_sub_id = 0; + id->revision = r16le(fet->hal.payload + 2); + id->fab = fet->hal.payload[3]; + id->self = r16le(fet->hal.payload + 4); + id->config = fet->hal.payload[13] & 0x7f; + + printc_dbg("Read fuses...\n"); + if (hal_proto_execute(&fet->hal, HAL_PROTO_FID_GET_FUSES, NULL, 0) < 0) + return -1; + if (!fet->hal.length) { + printc_err("v3hil: short reply: %d\n", fet->hal.length); + return -1; + } + + id->fuses = fet->hal.payload[0]; + return 0; +} + +static int idproc_9x(struct v3hil *fet, uint32_t dev_id_ptr, + struct chipinfo_id *id) +{ + uint8_t data[32]; + uint8_t info_len; + int i; + int tlv_size; + + printc_dbg("Identify (9x)...\n"); + printc_dbg("Read device ID bytes at 0x%05x...\n", dev_id_ptr); + memset(data, 0, 8); + w32le(data, dev_id_ptr); + data[4] = 4; + if (hal_proto_execute(&fet->hal, HAL_PROTO_FID_READ_MEM_QUICK_XV2, + data, 8) < 0) + return -1; + if (fet->hal.length < 8) { + printc_err("v3hil: short reply: %d\n", fet->hal.length); + return -1; + } + + info_len = fet->hal.payload[0]; + id->ver_id = r16le(fet->hal.payload + 4); + id->revision = fet->hal.payload[6]; + id->config = fet->hal.payload[7]; + id->fab = 0x55; + id->self = 0x5555; + id->fuses = 0x55; + + if ((info_len < 1) || (info_len > 11)) + return 0; + + printc_dbg("Read TLV...\n"); + tlv_size = ((1 << info_len) - 2) << 2; + w32le(data, dev_id_ptr); + w32le(data + 4, tlv_size >> 1); + w32le(data + 8, fet->regs[MSP430_REG_PC]); + if (hal_proto_execute(&fet->hal, HAL_PROTO_FID_READ_MEM_QUICK_XV2, + data, 8) < 0) + return -1; + if (fet->hal.length < tlv_size) { + printc_err("v3hil: short reply: %d\n", fet->hal.length); + return -1; + } + + /* Search TLV for sub-ID */ + i = 8; + while (i + 3 < tlv_size) { + uint8_t tag = fet->hal.payload[i++]; + uint8_t len = fet->hal.payload[i++]; + + if (tag == 0xff) + break; + + if ((tag == 0x14) && (len >= 2)) + id->ver_sub_id = r16le(fet->hal.payload); + + i += len; + } + + return 0; +} + +int v3hil_identify(struct v3hil *fet) +{ + struct chipinfo_id id; + uint32_t dev_id_ptr; + uint32_t id_data_addr; + int i; + + printc_dbg("Fetching JTAG ID...\n"); + if (hal_proto_execute(&fet->hal, HAL_PROTO_FID_GET_JTAG_ID, + NULL, 0) < 0) + return -1; + + if (fet->hal.length < 12) { + printc_err("v3hil: short reply: %d\n", fet->hal.length); + return -1; + } + + printc_dbg("ID:"); + for (i = 0; i < fet->hal.length; i++) + printc_dbg(" %02x", fet->hal.payload[i]); + printc_dbg("\n"); + + /* Byte at 0 is JTAG ID. 0x91, 0x95, 0x99 means CPUxV2. 0x89 + * means old CPU. + */ + fet->jtag_id = fet->hal.payload[0]; + dev_id_ptr = r32le(fet->hal.payload + 4); + id_data_addr = r32le(fet->hal.payload + 8); + + /* Pick fail-safe configuration */ + printc_dbg("Reset parameters...\n"); + if (set_param(fet, HAL_PROTO_CONFIG_CLK_CONTROL_TYPE, 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_SFLLDEH, 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_DEFAULT_CLK_CONTROL, 0x040f) || + set_param(fet, HAL_PROTO_CONFIG_ENHANCED_PSA, 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_PSA_TCKL_HIGH, 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_POWER_TESTREG_MASK, 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_POWER_TESTREG3V_MASK, 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_NO_BSL, 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_ALT_ROM_ADDR_FOR_CPU_READ, 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_ASSERT_BSL_VALID_BIT, 0) < 0) + return -1; + + printc_dbg("Check JTAG fuse...\n"); + if (hal_proto_execute(&fet->hal, HAL_PROTO_FID_IS_JTAG_FUSE_BLOWN, + NULL, 0) < 0) + return -1; + if ((fet->hal.length >= 2) && + (fet->hal.payload[0] == 0x55) && + (fet->hal.payload[1] == 0x55)) { + printc_err("v3hil: JTAG fuse is blown!\n"); + return -1; + } + + memset(&id, 0, sizeof(id)); + printc_dbg("Sync JTAG...\n"); + if (v3hil_sync(fet) < 0) + return -1; + + if (fet->jtag_id == 0x89) { + if (idproc_89(fet, id_data_addr, &id) < 0) + return -1; + } else { + if (idproc_9x(fet, dev_id_ptr, &id) < 0) + return -1; + } + + printc_dbg(" ver_id: %04x\n", id.ver_id); + printc_dbg(" ver_sub_id: %04x\n", id.ver_sub_id); + printc_dbg(" revision: %02x\n", id.revision); + printc_dbg(" fab: %02x\n", id.fab); + printc_dbg(" self: %04x\n", id.self); + printc_dbg(" config: %02x\n", id.config); + printc_dbg(" fuses: %02x\n", id.fuses); + printc_dbg(" activation_key: %08x\n", id.activation_key); + + fet->chip = chipinfo_find_by_id(&id); + if (!fet->chip) { + printc_err("v3hil: unknown chip ID\n"); + return -1; + } + + return 0; +} + +int v3hil_configure(struct v3hil *fet) +{ + printc_dbg("Configuring for %s...\n", fet->chip->name); + + if (set_param(fet, HAL_PROTO_CONFIG_CLK_CONTROL_TYPE, + fet->chip->clock_control) < 0 || + set_param(fet, HAL_PROTO_CONFIG_SFLLDEH, + (fet->chip->features & + CHIPINFO_FEATURE_SFLLDH) ? 1 : 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_DEFAULT_CLK_CONTROL, + fet->chip->mclk_control) < 0 || + set_param(fet, HAL_PROTO_CONFIG_ENHANCED_PSA, + (fet->chip->psa == CHIPINFO_PSA_ENHANCED) ? 1 : 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_PSA_TCKL_HIGH, + (fet->chip->features & + CHIPINFO_FEATURE_PSACH) ? 1 : 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_POWER_TESTREG_MASK, + fet->chip->power.reg_mask) < 0 || + set_param(fet, HAL_PROTO_CONFIG_TESTREG_ENABLE_LPMX5, + fet->chip->power.enable_lpm5) < 0 || + set_param(fet, HAL_PROTO_CONFIG_TESTREG_DISABLE_LPMX5, + fet->chip->power.disable_lpm5) < 0 || + set_param(fet, HAL_PROTO_CONFIG_POWER_TESTREG3V_MASK, + fet->chip->power.reg_mask_3v) < 0 || + set_param(fet, HAL_PROTO_CONFIG_TESTREG3V_ENABLE_LPMX5, + fet->chip->power.enable_lpm5_3v) < 0 || + set_param(fet, HAL_PROTO_CONFIG_TESTREG3V_DISABLE_LPMX5, + fet->chip->power.disable_lpm5_3v) < 0 || + set_param(fet, HAL_PROTO_CONFIG_NO_BSL, + (fet->chip->features & + CHIPINFO_FEATURE_NO_BSL) ? 1 : 0) < 0 || + set_param(fet, HAL_PROTO_CONFIG_ALT_ROM_ADDR_FOR_CPU_READ, + (fet->chip->features & + CHIPINFO_FEATURE_1337) ? 1 : 0) < 0) + return -1; + + return 0; +} diff --git a/drivers/v3hil.h b/drivers/v3hil.h new file mode 100644 index 0000000..855defd --- /dev/null +++ b/drivers/v3hil.h @@ -0,0 +1,113 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * + * 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 V3HIL_H_ +#define V3HIL_H_ + +#include "hal_proto.h" +#include "chipinfo.h" +#include "util.h" +#include "device.h" +#include "transport.h" + +/* Clock calibration data */ +struct v3hil_calibrate { + uint8_t is_cal; + + /* Calibration parameters for write/erase funclets */ + uint16_t cal0; + uint16_t cal1; +}; + +struct v3hil { + struct hal_proto hal; + const struct chipinfo *chip; + + /* 0x89 is old-style CPU */ + uint8_t jtag_id; + + /* Lower 8 bits of saved WDTCTL */ + uint8_t wdtctl; + + /* Register cache: this must be flushed before restoring context + * and updated after saving context. + */ + address_t regs[DEVICE_NUM_REGS]; + + struct v3hil_calibrate cal; +}; + +/* Initialize data, associate transport */ +void v3hil_init(struct v3hil *h, transport_t trans, + hal_proto_flags_t flags); + +/* Reset communications and probe HAL. */ +int v3hil_comm_init(struct v3hil *h); + +/* Set voltage */ +int v3hil_set_vcc(struct v3hil *h, int vcc_mv); + +/* Start/stop JTAG controller */ +typedef enum { + V3HIL_JTAG_JTAG = 0, + V3HIL_JTAG_SPYBIWIRE = 1 +} v3hil_jtag_type_t; + +int v3hil_start_jtag(struct v3hil *h, v3hil_jtag_type_t); +int v3hil_stop_jtag(struct v3hil *h); + +/* Synchronize JTAG and reset the chip. This is the only operation which + * can be done pre-configuration. + */ +int v3hil_sync(struct v3hil *h); + +/* Run the chip identification procedure. chip will be filled out if + * this is successful. This calls v3hil_sync(). + */ +int v3hil_identify(struct v3hil *h); + +/* Configure for the current chip */ +int v3hil_configure(struct v3hil *h); + +/* Read/write memory. LSB of address and size are ignored. Number of + * bytes read is returned, which may be less than requested if a memory + * map boundary is crossed. + */ +int v3hil_read(struct v3hil *h, address_t addr, + uint8_t *mem, address_t size); +int v3hil_write(struct v3hil *h, address_t addr, + const uint8_t *mem, address_t size); + +/* Erase flash. If address is specified, a segment erase is performed. + * Otherwise, ADDRESS_NONE indicates that a main memory erase should be + * performed. + */ +int v3hil_erase(struct v3hil *h, address_t segment); + +/* Read/write register cache. */ +int v3hil_update_regs(struct v3hil *h); +int v3hil_flush_regs(struct v3hil *h); + +/* Restore context (run) and save context (halt). */ +int v3hil_context_restore(struct v3hil *h, int free); +int v3hil_context_save(struct v3hil *h); + +/* Single-step the CPU. You must handle the register cache yourself. */ +int v3hil_single_step(struct v3hil *h); + +#endif diff --git a/mspdebug.man b/mspdebug.man index 1d2c776..24af561 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -184,6 +184,9 @@ supported for Linux. A parallel port device must be specified via the .IP "\fBload-bsl\fR" Connect to a USB bootloader. The stub bootloader will be used to load a fuller-featured bootloader into RAM for execution. +.IP "\fBezfet\fR" +This driver is for Texas Instruments' eZ-FET devices. It supports USB +and TTY access. It does not support breakpoint control. .SH COMMANDS MSPDebug can accept commands either through an interactive prompt, or non-interactively when specified on the command line. The supported diff --git a/ui/main.c b/ui/main.c index a84db63..ddbe847 100644 --- a/ui/main.c +++ b/ui/main.c @@ -55,6 +55,7 @@ #include "input_async.h" #include "pif.h" #include "loadbsl.h" +#include "fet3.h" #ifdef __CYGWIN__ #include @@ -84,7 +85,8 @@ static const struct device_class *const driver_table[] = { &device_tilib, &device_goodfet, &device_pif, - &device_loadbsl + &device_loadbsl, + &device_ezfet }; static const char *version_text =