Add SWD protocol support to bitbang

This is based on the initial work by Paul Fertser with addition of the
switch sequences and new ACK handling.  In case of WAIT response, the
sticky bits are cleared and the last operation is repeated. The ACK
handling is based on the interpretation of the 8 February 2006 ARM
Debug Interface v5 Architecture Specification

Change-Id: Id50855b1ffff310177ccf9883dc9eb0d1b4458c8
Signed-off-by: Jean-Christian de Rivaz <jc@eclis.ch>
Reviewed-on: http://openocd.zylin.com/2437
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>
This commit is contained in:
Jean-Christian de Rivaz 2014-12-11 19:11:49 +01:00 committed by Paul Fertser
parent 750f8cd0c5
commit e971c7f423
2 changed files with 222 additions and 0 deletions

View File

@ -21,6 +21,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
/* 2014-12: Addition of the SWD protocol support is based on the initial work
* by Paul Fertser and modifications by Jean-Christian de Rivaz. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@ -29,6 +32,9 @@
#include <jtag/interface.h>
#include <jtag/commands.h>
/* YUK! - but this is currently a global.... */
extern struct jtag_interface *jtag_interface;
/**
* Function bitbang_stableclocks
* issues a number of clock cycles while staying in a stable state.
@ -337,3 +343,209 @@ int bitbang_execute_queue(void)
return retval;
}
bool swd_mode;
static int queued_retval;
static int bitbang_swd_init(void)
{
LOG_DEBUG("bitbang_swd_init");
swd_mode = true;
return ERROR_OK;
}
static void bitbang_exchange(bool rnw, uint8_t buf[], unsigned int offset, unsigned int bit_cnt)
{
LOG_DEBUG("bitbang_exchange");
int tdi;
for (unsigned int i = offset; i < bit_cnt + offset; i++) {
int bytec = i/8;
int bcval = 1 << (i % 8);
tdi = !rnw && (buf[bytec] & bcval);
bitbang_interface->write(0, 0, tdi);
if (rnw && buf) {
if (bitbang_interface->swdio_read())
buf[bytec] |= bcval;
else
buf[bytec] &= ~bcval;
}
bitbang_interface->write(1, 0, tdi);
}
}
int bitbang_swd_switch_seq(struct adiv5_dap *dap, enum swd_special_seq seq)
{
LOG_DEBUG("bitbang_swd_switch_seq");
switch (seq) {
case LINE_RESET:
LOG_DEBUG("SWD line reset");
bitbang_exchange(false, (uint8_t *)swd_seq_line_reset, 0, swd_seq_line_reset_len);
break;
case JTAG_TO_SWD:
LOG_DEBUG("JTAG-to-SWD");
bitbang_exchange(false, (uint8_t *)swd_seq_jtag_to_swd, 0, swd_seq_jtag_to_swd_len);
break;
case SWD_TO_JTAG:
LOG_DEBUG("SWD-to-JTAG");
bitbang_exchange(false, (uint8_t *)swd_seq_swd_to_jtag, 0, swd_seq_swd_to_jtag_len);
break;
default:
LOG_ERROR("Sequence %d not supported", seq);
return ERROR_FAIL;
}
return ERROR_OK;
}
void bitbang_switch_to_swd(void)
{
LOG_DEBUG("bitbang_switch_to_swd");
bitbang_exchange(false, (uint8_t *)swd_seq_jtag_to_swd, 0, swd_seq_jtag_to_swd_len);
}
static void swd_clear_sticky_errors(struct adiv5_dap *dap)
{
const struct swd_driver *swd = jtag_interface->swd;
assert(swd);
swd->write_reg(dap, swd_cmd(false, false, DP_ABORT),
STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR);
}
static void bitbang_swd_read_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t *value)
{
LOG_DEBUG("bitbang_swd_read_reg");
assert(cmd & SWD_CMD_RnW);
if (queued_retval != ERROR_OK) {
LOG_DEBUG("Skip bitbang_swd_read_reg because queued_retval=%d", queued_retval);
return;
}
for (;;) {
uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)];
cmd |= SWD_CMD_START | (1 << 7);
bitbang_exchange(false, &cmd, 0, 8);
bitbang_interface->swdio_drive(false);
bitbang_exchange(true, trn_ack_data_parity_trn, 0, 1 + 3 + 32 + 1 + 1);
bitbang_interface->swdio_drive(true);
int ack = buf_get_u32(trn_ack_data_parity_trn, 1, 3);
uint32_t data = buf_get_u32(trn_ack_data_parity_trn, 1 + 3, 32);
int parity = buf_get_u32(trn_ack_data_parity_trn, 1 + 3 + 32, 1);
LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
cmd & SWD_CMD_APnDP ? "AP" : "DP",
cmd & SWD_CMD_RnW ? "read" : "write",
(cmd & SWD_CMD_A32) >> 1,
data);
switch (ack) {
case SWD_ACK_OK:
if (parity != parity_u32(data)) {
LOG_DEBUG("Wrong parity detected");
queued_retval = ERROR_FAIL;
return;
}
if (value)
*value = data;
if (cmd & SWD_CMD_APnDP)
bitbang_exchange(true, NULL, 0, dap->memaccess_tck);
return;
case SWD_ACK_WAIT:
LOG_DEBUG("SWD_ACK_WAIT");
swd_clear_sticky_errors(dap);
break;
case SWD_ACK_FAULT:
LOG_DEBUG("SWD_ACK_FAULT");
queued_retval = ack;
return;
default:
LOG_DEBUG("No valid acknowledge: ack=%d", ack);
queued_retval = ack;
return;
}
}
}
static void bitbang_swd_write_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t value)
{
LOG_DEBUG("bitbang_swd_write_reg");
assert(!(cmd & SWD_CMD_RnW));
if (queued_retval != ERROR_OK) {
LOG_DEBUG("Skip bitbang_swd_write_reg because queued_retval=%d", queued_retval);
return;
}
for (;;) {
uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)];
buf_set_u32(trn_ack_data_parity_trn, 1 + 3 + 1, 32, value);
buf_set_u32(trn_ack_data_parity_trn, 1 + 3 + 1 + 32, 1, parity_u32(value));
cmd |= SWD_CMD_START | (1 << 7);
bitbang_exchange(false, &cmd, 0, 8);
bitbang_interface->swdio_drive(false);
bitbang_exchange(true, trn_ack_data_parity_trn, 0, 1 + 3 + 1);
bitbang_interface->swdio_drive(true);
bitbang_exchange(false, trn_ack_data_parity_trn, 1 + 3 + 1, 32 + 1);
int ack = buf_get_u32(trn_ack_data_parity_trn, 1, 3);
LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
cmd & SWD_CMD_APnDP ? "AP" : "DP",
cmd & SWD_CMD_RnW ? "read" : "write",
(cmd & SWD_CMD_A32) >> 1,
buf_get_u32(trn_ack_data_parity_trn, 1 + 3 + 1, 32));
switch (ack) {
case SWD_ACK_OK:
if (cmd & SWD_CMD_APnDP)
bitbang_exchange(true, NULL, 0, dap->memaccess_tck);
return;
case SWD_ACK_WAIT:
LOG_DEBUG("SWD_ACK_WAIT");
swd_clear_sticky_errors(dap);
break;
case SWD_ACK_FAULT:
LOG_DEBUG("SWD_ACK_FAULT");
queued_retval = ack;
return;
default:
LOG_DEBUG("No valid acknowledge: ack=%d", ack);
queued_retval = ack;
return;
}
}
}
static int bitbang_swd_run_queue(struct adiv5_dap *dap)
{
LOG_DEBUG("bitbang_swd_run_queue");
/* A transaction must be followed by another transaction or at least 8 idle cycles to
* ensure that data is clocked through the AP. */
bitbang_exchange(true, NULL, 0, 8);
int retval = queued_retval;
queued_retval = ERROR_OK;
LOG_DEBUG("SWD queue return value: %02x", retval);
return retval;
}
const struct swd_driver bitbang_swd = {
.init = bitbang_swd_init,
.switch_seq = bitbang_swd_switch_seq,
.read_reg = bitbang_swd_read_reg,
.write_reg = bitbang_swd_write_reg,
.run = bitbang_swd_run_queue,
};

View File

@ -24,6 +24,8 @@
#ifndef BITBANG_H
#define BITBANG_H
#include <jtag/swd.h>
struct bitbang_interface {
/* low level callbacks (for bitbang)
*/
@ -31,10 +33,18 @@ struct bitbang_interface {
void (*write)(int tck, int tms, int tdi);
void (*reset)(int trst, int srst);
void (*blink)(int on);
int (*swdio_read)(void);
void (*swdio_drive)(bool on);
};
const struct swd_driver bitbang_swd;
extern bool swd_mode;
int bitbang_execute_queue(void);
extern struct bitbang_interface *bitbang_interface;
void bitbang_switch_to_swd(void);
int bitbang_swd_switch_seq(struct adiv5_dap *dap, enum swd_special_seq seq);
#endif /* BITBANG_H */