jtag/drivers/stlink_usb : implemented and repaired SWIM support

Fixed a bug in stlink_usb_read_mem/write_mem preventing large data transfers

The SWIM support in stlink_usb was basically non existent so I have
implemented the missing parts. The bCBWCBLength and dCBWDataTransferLength
for STLINK-V1 protocol was not correct so that was fixed. The reason for
adding SWIM support is to add STM8 support for OpenOCD.

I have tested the driver on:
STM8 discovery board with the built-in STLINK-V1
STM8 discovery board with STLINK-V2 dongle
STM32 vldiscovery board with the built-in STLINK-V1
STM32F1xxx processor with STLINK-V2 dongle

Change-Id: I4aa80a92fb0226174356adaf2f8ff949920a621f
Signed-off-by: Ake Rehnman <ake.rehnman@gmail.com>
Reviewed-on: http://openocd.zylin.com/3952
Tested-by: jenkins
Reviewed-by: Philipp Klaus Krause
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
This commit is contained in:
Ake Rehnman 2017-01-21 10:42:11 +01:00 committed by Paul Fertser
parent 779e95cc32
commit 3de6b5f6e5
1 changed files with 349 additions and 11 deletions

View File

@ -1,4 +1,8 @@
/*************************************************************************** /***************************************************************************
* SWIM contributions by Ake Rehnman *
* Copyright (C) 2017 Ake Rehnman *
* ake.rehnman(at)gmail.com *
* *
* Copyright (C) 2011-2012 by Mathias Kuester * * Copyright (C) 2011-2012 by Mathias Kuester *
* Mathias Kuester <kesmtp@freenet.de> * * Mathias Kuester <kesmtp@freenet.de> *
* * * *
@ -130,6 +134,8 @@ struct stlink_usb_handle_s {
bool reconnect_pending; bool reconnect_pending;
}; };
#define STLINK_SWIM_ERR_OK 0x00
#define STLINK_SWIM_BUSY 0x01
#define STLINK_DEBUG_ERR_OK 0x80 #define STLINK_DEBUG_ERR_OK 0x80
#define STLINK_DEBUG_ERR_FAULT 0x81 #define STLINK_DEBUG_ERR_FAULT 0x81
#define STLINK_SWD_AP_WAIT 0x10 #define STLINK_SWD_AP_WAIT 0x10
@ -167,8 +173,36 @@ struct stlink_usb_handle_s {
#define STLINK_DFU_EXIT 0x07 #define STLINK_DFU_EXIT 0x07
#define STLINK_SWIM_ENTER 0x00 /*
#define STLINK_SWIM_EXIT 0x01 STLINK_SWIM_ENTER_SEQ
1.3ms low then 750Hz then 1.5kHz
STLINK_SWIM_GEN_RST
STM8 DM pulls reset pin low 50us
STLINK_SWIM_SPEED
uint8_t (0=low|1=high)
STLINK_SWIM_WRITEMEM
uint16_t length
uint32_t address
STLINK_SWIM_RESET
send syncronization seq (16us low, response 64 clocks low)
*/
#define STLINK_SWIM_ENTER 0x00
#define STLINK_SWIM_EXIT 0x01
#define STLINK_SWIM_READ_CAP 0x02
#define STLINK_SWIM_SPEED 0x03
#define STLINK_SWIM_ENTER_SEQ 0x04
#define STLINK_SWIM_GEN_RST 0x05
#define STLINK_SWIM_RESET 0x06
#define STLINK_SWIM_ASSERT_RESET 0x07
#define STLINK_SWIM_DEASSERT_RESET 0x08
#define STLINK_SWIM_READSTATUS 0x09
#define STLINK_SWIM_WRITEMEM 0x0a
#define STLINK_SWIM_READMEM 0x0b
#define STLINK_SWIM_READBUF 0x0c
#define STLINK_DEBUG_ENTER_JTAG 0x00 #define STLINK_DEBUG_ENTER_JTAG 0x00
#define STLINK_DEBUG_GETSTATUS 0x01 #define STLINK_DEBUG_GETSTATUS 0x01
@ -252,6 +286,7 @@ static const struct {
}; };
static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size); static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size);
static int stlink_swim_status(void *handle);
/** */ /** */
static int stlink_usb_xfer_v1_get_status(void *handle) static int stlink_usb_xfer_v1_get_status(void *handle)
@ -342,7 +377,11 @@ static int stlink_usb_xfer_v1_get_sense(void *handle)
return ERROR_OK; return ERROR_OK;
} }
/** */ /*
transfers block in cmdbuf
<size> indicates number of bytes in the following
data phase.
*/
static int stlink_usb_xfer(void *handle, const uint8_t *buf, int size) static int stlink_usb_xfer(void *handle, const uint8_t *buf, int size)
{ {
int err, cmdsize = STLINK_CMD_SIZE_V2; int err, cmdsize = STLINK_CMD_SIZE_V2;
@ -350,8 +389,11 @@ static int stlink_usb_xfer(void *handle, const uint8_t *buf, int size)
assert(handle != NULL); assert(handle != NULL);
if (h->version.stlink == 1) if (h->version.stlink == 1) {
cmdsize = STLINK_SG_SIZE; cmdsize = STLINK_SG_SIZE;
/* put length in bCBWCBLength */
h->cmdbuf[14] = h->cmdidx-15;
}
err = stlink_usb_xfer_rw(handle, cmdsize, buf, size); err = stlink_usb_xfer_rw(handle, cmdsize, buf, size);
@ -373,7 +415,6 @@ static int stlink_usb_xfer(void *handle, const uint8_t *buf, int size)
return ERROR_OK; return ERROR_OK;
} }
/** /**
Converts an STLINK status code held in the first byte of a response Converts an STLINK status code held in the first byte of a response
to an openocd error, logs any error/wait status as debug output. to an openocd error, logs any error/wait status as debug output.
@ -388,6 +429,18 @@ static int stlink_usb_error_check(void *handle)
if (h->jtag_api == STLINK_JTAG_API_V1) if (h->jtag_api == STLINK_JTAG_API_V1)
h->databuf[0] = STLINK_DEBUG_ERR_OK; h->databuf[0] = STLINK_DEBUG_ERR_OK;
if (h->transport == HL_TRANSPORT_SWIM) {
switch (h->databuf[0]) {
case STLINK_SWIM_ERR_OK:
return ERROR_OK;
case STLINK_SWIM_BUSY:
return ERROR_WAIT;
default:
LOG_DEBUG("unknown/unexpected STLINK status code 0x%x", h->databuf[0]);
return ERROR_FAIL;
}
}
switch (h->databuf[0]) { switch (h->databuf[0]) {
case STLINK_DEBUG_ERR_OK: case STLINK_DEBUG_ERR_OK:
return ERROR_OK; return ERROR_OK;
@ -448,7 +501,7 @@ static int stlink_usb_error_check(void *handle)
/** Issue an STLINK command via USB transfer, with retries on any wait status responses. /** Issue an STLINK command via USB transfer, with retries on any wait status responses.
Works for commands where the STLINK_DEBUG status is returned in the first Works for commands where the STLINK_DEBUG status is returned in the first
byte of the response packet. byte of the response packet. For SWIM a SWIM_READSTATUS is requested instead.
Returns an openocd result code. Returns an openocd result code.
*/ */
@ -456,10 +509,21 @@ static int stlink_cmd_allow_retry(void *handle, const uint8_t *buf, int size)
{ {
int retries = 0; int retries = 0;
int res; int res;
struct stlink_usb_handle_s *h = handle;
while (1) { while (1) {
res = stlink_usb_xfer(handle, buf, size); if ((h->transport != HL_TRANSPORT_SWIM) || !retries) {
if (res != ERROR_OK) res = stlink_usb_xfer(handle, buf, size);
return res; if (res != ERROR_OK)
return res;
}
if (h->transport == HL_TRANSPORT_SWIM) {
res = stlink_swim_status(handle);
if (res != ERROR_OK)
return res;
}
res = stlink_usb_error_check(handle); res = stlink_usb_error_check(handle);
if (res == ERROR_WAIT && retries < MAX_WAIT_RETRIES) { if (res == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
usleep((1<<retries++) * 1000); usleep((1<<retries++) * 1000);
@ -487,7 +551,17 @@ static int stlink_usb_read_trace(void *handle, const uint8_t *buf, int size)
return ERROR_OK; return ERROR_OK;
} }
/** */ /*
this function writes transfer length in
the right place in the cb
*/
static void stlink_usb_set_cbw_transfer_datalength(void *handle, uint32_t size)
{
struct stlink_usb_handle_s *h = handle;
buf_set_u32(h->cmdbuf+8, 0, 32, size);
}
static void stlink_usb_xfer_v1_create_cmd(void *handle, uint8_t direction, uint32_t size) static void stlink_usb_xfer_v1_create_cmd(void *handle, uint8_t direction, uint32_t size)
{ {
struct stlink_usb_handle_s *h = handle; struct stlink_usb_handle_s *h = handle;
@ -496,12 +570,16 @@ static void stlink_usb_xfer_v1_create_cmd(void *handle, uint8_t direction, uint3
strcpy((char *)h->cmdbuf, "USBC"); strcpy((char *)h->cmdbuf, "USBC");
h->cmdidx += 4; h->cmdidx += 4;
/* csw tag not used */ /* csw tag not used */
buf_set_u32(h->cmdbuf+h->cmdidx, 0, 32, 0);
h->cmdidx += 4; h->cmdidx += 4;
/* cbw data transfer length (in the following data phase in or out) */
buf_set_u32(h->cmdbuf+h->cmdidx, 0, 32, size); buf_set_u32(h->cmdbuf+h->cmdidx, 0, 32, size);
h->cmdidx += 4; h->cmdidx += 4;
/* cbw flags */
h->cmdbuf[h->cmdidx++] = (direction == h->rx_ep ? ENDPOINT_IN : ENDPOINT_OUT); h->cmdbuf[h->cmdidx++] = (direction == h->rx_ep ? ENDPOINT_IN : ENDPOINT_OUT);
h->cmdbuf[h->cmdidx++] = 0; /* lun */ h->cmdbuf[h->cmdidx++] = 0; /* lun */
h->cmdbuf[h->cmdidx++] = STLINK_CMD_SIZE_V1; /* cdb clength (is filled in at xfer) */
h->cmdbuf[h->cmdidx++] = 0;
} }
/** */ /** */
@ -681,6 +759,8 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
case STLINK_MODE_DEBUG_SWIM: case STLINK_MODE_DEBUG_SWIM:
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER; h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER;
/* no answer for this function... */
rx_size = 0;
break; break;
case STLINK_MODE_DFU: case STLINK_MODE_DFU:
case STLINK_MODE_MASS: case STLINK_MODE_MASS:
@ -825,6 +905,12 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset)
} }
if (connect_under_reset) { if (connect_under_reset) {
/* if we are going to use swim we need to switch mode early */
if (emode == STLINK_MODE_DEBUG_SWIM) {
res = stlink_usb_mode_enter(handle, emode);
if (res != ERROR_OK)
return res;
}
res = stlink_usb_assert_srst(handle, 0); res = stlink_usb_assert_srst(handle, 0);
if (res != ERROR_OK) if (res != ERROR_OK)
return res; return res;
@ -845,6 +931,199 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset)
return ERROR_OK; return ERROR_OK;
} }
/* request status from last swim request */
static int stlink_swim_status(void *handle)
{
struct stlink_usb_handle_s *h = handle;
int res;
stlink_usb_init_buffer(handle, h->rx_ep, 4);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READSTATUS;
res = stlink_usb_xfer(handle, h->databuf, 4);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
/*
the purpose of this function is unknown...
capabilites? anyway for swim v6 it returns
0001020600000000
*/
__attribute__((unused))
static int stlink_swim_cap(void *handle, uint8_t *cap)
{
struct stlink_usb_handle_s *h = handle;
int res;
stlink_usb_init_buffer(handle, h->rx_ep, 8);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READ_CAP;
h->cmdbuf[h->cmdidx++] = 0x01;
res = stlink_usb_xfer(handle, h->databuf, 8);
if (res != ERROR_OK)
return res;
memcpy(cap, h->databuf, 8);
return ERROR_OK;
}
/* debug dongle assert/deassert sreset line */
static int stlink_swim_assert_reset(void *handle, int reset)
{
struct stlink_usb_handle_s *h = handle;
int res;
stlink_usb_init_buffer(handle, h->rx_ep, 0);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
if (!reset)
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ASSERT_RESET;
else
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_DEASSERT_RESET;
res = stlink_cmd_allow_retry(handle, h->databuf, 0);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
/*
send swim enter seq
1.3ms low then 750Hz then 1.5kHz
*/
static int stlink_swim_enter(void *handle)
{
struct stlink_usb_handle_s *h = handle;
int res;
stlink_usb_init_buffer(handle, h->rx_ep, 0);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER_SEQ;
res = stlink_cmd_allow_retry(handle, h->databuf, 0);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
/* switch high/low speed swim */
static int stlink_swim_speed(void *handle, int speed)
{
struct stlink_usb_handle_s *h = handle;
int res;
stlink_usb_init_buffer(handle, h->rx_ep, 0);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_SPEED;
if (speed)
h->cmdbuf[h->cmdidx++] = 1;
else
h->cmdbuf[h->cmdidx++] = 0;
res = stlink_cmd_allow_retry(handle, h->databuf, 0);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
/*
initiate srst from swim.
nrst is pulled low for 50us.
*/
static int stlink_swim_generate_rst(void *handle)
{
struct stlink_usb_handle_s *h = handle;
int res;
stlink_usb_init_buffer(handle, h->rx_ep, 0);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_GEN_RST;
res = stlink_cmd_allow_retry(handle, h->databuf, 0);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
/*
send resyncronize sequence
swim is pulled low for 16us
reply is 64 clks low
*/
static int stlink_swim_resync(void *handle)
{
struct stlink_usb_handle_s *h = handle;
int res;
stlink_usb_init_buffer(handle, h->rx_ep, 0);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_RESET;
res = stlink_cmd_allow_retry(handle, h->databuf, 0);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
static int stlink_swim_writebytes(void *handle, uint32_t addr, uint32_t len, const uint8_t *data)
{
struct stlink_usb_handle_s *h = handle;
int res;
unsigned int i;
unsigned int datalen = 0;
int cmdsize = STLINK_CMD_SIZE_V2;
if (len > STLINK_DATA_SIZE)
return ERROR_FAIL;
if (h->version.stlink == 1)
cmdsize = STLINK_SG_SIZE;
stlink_usb_init_buffer(handle, h->tx_ep, 0);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_WRITEMEM;
h_u16_to_be(h->cmdbuf+h->cmdidx, len);
h->cmdidx += 2;
h_u32_to_be(h->cmdbuf+h->cmdidx, addr);
h->cmdidx += 4;
for (i = 0; i < len; i++) {
if (h->cmdidx == cmdsize)
h->databuf[datalen++] = *(data++);
else
h->cmdbuf[h->cmdidx++] = *(data++);
}
if (h->version.stlink == 1)
stlink_usb_set_cbw_transfer_datalength(handle, datalen);
res = stlink_cmd_allow_retry(handle, h->databuf, datalen);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
static int stlink_swim_readbytes(void *handle, uint32_t addr, uint32_t len, uint8_t *data)
{
struct stlink_usb_handle_s *h = handle;
int res;
if (len > STLINK_DATA_SIZE)
return ERROR_FAIL;
stlink_usb_init_buffer(handle, h->rx_ep, 0);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READMEM;
h_u16_to_be(h->cmdbuf+h->cmdidx, len);
h->cmdidx += 2;
h_u32_to_be(h->cmdbuf+h->cmdidx, addr);
h->cmdidx += 4;
res = stlink_cmd_allow_retry(handle, h->databuf, 0);
if (res != ERROR_OK)
return res;
stlink_usb_init_buffer(handle, h->rx_ep, len);
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READBUF;
res = stlink_usb_xfer(handle, data, len);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
/** */ /** */
static int stlink_usb_idcode(void *handle, uint32_t *idcode) static int stlink_usb_idcode(void *handle, uint32_t *idcode)
{ {
@ -853,6 +1132,12 @@ static int stlink_usb_idcode(void *handle, uint32_t *idcode)
assert(handle != NULL); assert(handle != NULL);
/* there is no swim read core id cmd */
if (h->transport == HL_TRANSPORT_SWIM) {
*idcode = 0;
return ERROR_OK;
}
stlink_usb_init_buffer(handle, h->rx_ep, 4); stlink_usb_init_buffer(handle, h->rx_ep, 4);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
@ -971,6 +1256,18 @@ static enum target_state stlink_usb_state(void *handle)
assert(handle != NULL); assert(handle != NULL);
if (h->transport == HL_TRANSPORT_SWIM) {
res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport));
if (res != ERROR_OK)
return TARGET_UNKNOWN;
res = stlink_swim_resync(handle);
if (res != ERROR_OK)
return TARGET_UNKNOWN;
return ERROR_OK;
}
if (h->reconnect_pending) { if (h->reconnect_pending) {
LOG_INFO("Previous state query failed, trying to reconnect"); LOG_INFO("Previous state query failed, trying to reconnect");
res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport)); res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport));
@ -1014,6 +1311,9 @@ static int stlink_usb_assert_srst(void *handle, int srst)
assert(handle != NULL); assert(handle != NULL);
if (h->transport == HL_TRANSPORT_SWIM)
return stlink_swim_assert_reset(handle, srst);
if (h->version.stlink == 1) if (h->version.stlink == 1)
return ERROR_COMMAND_NOTFOUND; return ERROR_COMMAND_NOTFOUND;
@ -1088,6 +1388,9 @@ static int stlink_usb_reset(void *handle)
assert(handle != NULL); assert(handle != NULL);
if (h->transport == HL_TRANSPORT_SWIM)
return stlink_swim_generate_rst(handle);
stlink_usb_init_buffer(handle, h->rx_ep, 2); stlink_usb_init_buffer(handle, h->rx_ep, 2);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
@ -1440,6 +1743,11 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
if (count < bytes_remaining) if (count < bytes_remaining)
bytes_remaining = count; bytes_remaining = count;
if (h->transport == HL_TRANSPORT_SWIM) {
retval = stlink_swim_readbytes(handle, addr, bytes_remaining, buffer);
if (retval != ERROR_OK)
return retval;
} else
/* the stlink only supports 8/32bit memory read/writes /* the stlink only supports 8/32bit memory read/writes
* honour 32bit, all others will be handled as 8bit access */ * honour 32bit, all others will be handled as 8bit access */
if (size == 4) { if (size == 4) {
@ -1510,6 +1818,11 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
if (count < bytes_remaining) if (count < bytes_remaining)
bytes_remaining = count; bytes_remaining = count;
if (h->transport == HL_TRANSPORT_SWIM) {
retval = stlink_swim_writebytes(handle, addr, bytes_remaining, buffer);
if (retval != ERROR_OK)
return retval;
} else
/* the stlink only supports 8/32bit memory read/writes /* the stlink only supports 8/32bit memory read/writes
* honour 32bit, all others will be handled as 8bit access */ * honour 32bit, all others will be handled as 8bit access */
if (size == 4) { if (size == 4) {
@ -1574,6 +1887,20 @@ static int stlink_speed(void *handle, int khz, bool query)
int speed_diff = INT_MAX; int speed_diff = INT_MAX;
struct stlink_usb_handle_s *h = handle; struct stlink_usb_handle_s *h = handle;
if (h && (h->transport == HL_TRANSPORT_SWIM)) {
/*
we dont care what the khz rate is
we only have low and high speed...
before changing speed the SWIM_CSR HS bit
must be updated
*/
if (khz == 0)
stlink_swim_speed(handle, 0);
else
stlink_swim_speed(handle, 1);
return khz;
}
/* only supported by stlink/v2 and for firmware >= 22 */ /* only supported by stlink/v2 and for firmware >= 22 */
if (h && (h->version.stlink == 1 || h->version.jtag < 22)) if (h && (h->version.stlink == 1 || h->version.jtag < 22))
return khz; return khz;
@ -1778,6 +2105,17 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
goto error_open; goto error_open;
} }
if (h->transport == HL_TRANSPORT_SWIM) {
err = stlink_swim_enter(h);
if (err != ERROR_OK) {
LOG_ERROR("stlink_swim_enter_failed (unable to connect to the target)");
goto error_open;
}
*fd = h;
h->max_mem_packet = STLINK_DATA_SIZE;
return ERROR_OK;
}
/* clock speed only supported by stlink/v2 and for firmware >= 22 */ /* clock speed only supported by stlink/v2 and for firmware >= 22 */
if (h->version.stlink >= 2 && h->version.jtag >= 22) { if (h->version.stlink >= 2 && h->version.jtag >= 22) {
LOG_DEBUG("Supported clock speeds are:"); LOG_DEBUG("Supported clock speeds are:");