- protocol now correctly reports SWO and SWCLK clock rates on set - remove some debug printfs that were causing fw hangs (investigate later) - allow setting a 1MHz SWCLK rate - CMD_ATTACH now supports an argument to select one of several special reset or escape sequences: ATTACH_SWD_RESET: 60 HI, 4 LO, basic line reset ATTACH_JTAG_SWD: escape sequence for JTAG -> SWD mode switch ATTACH_DORMANT_TO_SWD: escape squence to exit dormant mode ATTACH_SWD_TO_DORMANT: and go back again - add special support for writing to DP TARGETSEL register (response phase must be ignored), needed for SWD multidrop support - bump protocol version to 1.3 - bump firmware version to 1.0 (it's long overdue)
321 lines
9.5 KiB
C
321 lines
9.5 KiB
C
/* rswd.c
|
|
*
|
|
* Copyright 2011-2015 Brian Swetland <swetland@frotz.net>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <lk/reg.h>
|
|
#include <lk/debug.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <printf.h>
|
|
|
|
#include <platform.h>
|
|
|
|
#include "swd.h"
|
|
#include "rswdp.h"
|
|
|
|
void usb_xmit(void *data, unsigned len);
|
|
int usb_recv(void *data, unsigned len);
|
|
|
|
unsigned swdp_trace = 0;
|
|
|
|
// indicates host knows about v1.0 protocol features
|
|
unsigned host_version = 0;
|
|
|
|
static u8 optable[16] = {
|
|
[OP_RD | OP_DP | OP_X0] = RD_IDCODE,
|
|
[OP_RD | OP_DP | OP_X4] = RD_DPCTRL,
|
|
[OP_RD | OP_DP | OP_X8] = RD_RESEND,
|
|
[OP_RD | OP_DP | OP_XC] = RD_BUFFER,
|
|
[OP_WR | OP_DP | OP_X0] = WR_ABORT,
|
|
[OP_WR | OP_DP | OP_X4] = WR_DPCTRL,
|
|
[OP_WR | OP_DP | OP_X8] = WR_SELECT,
|
|
[OP_WR | OP_DP | OP_XC] = WR_BUFFER,
|
|
[OP_RD | OP_AP | OP_X0] = RD_AP0,
|
|
[OP_RD | OP_AP | OP_X4] = RD_AP1,
|
|
[OP_RD | OP_AP | OP_X8] = RD_AP2,
|
|
[OP_RD | OP_AP | OP_XC] = RD_AP3,
|
|
[OP_WR | OP_AP | OP_X0] = WR_AP0,
|
|
[OP_WR | OP_AP | OP_X4] = WR_AP1,
|
|
[OP_WR | OP_AP | OP_X8] = WR_AP2,
|
|
[OP_WR | OP_AP | OP_XC] = WR_AP3,
|
|
};
|
|
|
|
static const char *board_str = TARGET;
|
|
static const char *build_str = "fw v1.00 (" __DATE__ ", " __TIME__ ")";
|
|
|
|
static void _reboot(void) {
|
|
platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET);
|
|
}
|
|
|
|
#define MODE_SWD 0
|
|
#define MODE_JTAG 1
|
|
static unsigned mode = MODE_SWD;
|
|
|
|
/* TODO bounds checking -- we trust the host far too much */
|
|
void process_txn(u32 txnid, u32 *rx, int rxc, u32 *tx) {
|
|
unsigned msg, op, n;
|
|
unsigned txc = 1;
|
|
unsigned count = 0;
|
|
unsigned status = 0;
|
|
void (*func)(void) = 0;
|
|
|
|
tx[0] = txnid;
|
|
|
|
while (rxc-- > 0) {
|
|
count++;
|
|
msg = *rx++;
|
|
op = RSWD_MSG_OP(msg);
|
|
n = RSWD_MSG_ARG(msg);
|
|
#if CONFIG_MDEBUG_TRACE
|
|
printf("> %02x %02x %04x <\n", RSWD_MSG_CMD(msg), op, n);
|
|
#endif
|
|
switch (RSWD_MSG_CMD(msg)) {
|
|
case CMD_NULL:
|
|
continue;
|
|
case CMD_SWD_WRITE:
|
|
while (n-- > 0) {
|
|
rxc--;
|
|
status = swd_write(optable[op], *rx++);
|
|
if (status) {
|
|
goto done;
|
|
}
|
|
}
|
|
continue;
|
|
case CMD_SWD_READ:
|
|
tx[txc++] = RSWD_MSG(CMD_SWD_DATA, 0, n);
|
|
while (n-- > 0) {
|
|
status = swd_read(optable[op], tx + txc);
|
|
if (status) {
|
|
txc++;
|
|
while (n-- > 0)
|
|
tx[txc++] = 0xfefefefe;
|
|
goto done;
|
|
}
|
|
txc++;
|
|
}
|
|
continue;
|
|
case CMD_SWD_DISCARD:
|
|
while (n-- > 0) {
|
|
u32 tmp;
|
|
status = swd_read(optable[op], &tmp);
|
|
if (status) {
|
|
goto done;
|
|
}
|
|
}
|
|
continue;
|
|
case CMD_ATTACH:
|
|
if (mode != MODE_SWD) {
|
|
mode = MODE_SWD;
|
|
swd_init();
|
|
}
|
|
swd_reset(op);
|
|
continue;
|
|
case CMD_JTAG_IO:
|
|
if (mode != MODE_JTAG) {
|
|
mode = MODE_JTAG;
|
|
jtag_init();
|
|
}
|
|
tx[txc++] = RSWD_MSG(CMD_JTAG_DATA, 0, n);
|
|
while (n > 0) {
|
|
unsigned xfer = (n > 32) ? 32 : n;
|
|
jtag_io(xfer, rx[0], rx[1], tx + txc);
|
|
rx += 2;
|
|
rxc -= 2;
|
|
txc += 1;
|
|
n -= xfer;
|
|
}
|
|
continue;
|
|
case CMD_JTAG_VRFY:
|
|
if (mode != MODE_JTAG) {
|
|
mode = MODE_JTAG;
|
|
jtag_init();
|
|
}
|
|
// (n/32) x 4 words: TMS, TDI, DATA, MASK
|
|
while (n > 0) {
|
|
unsigned xfer = (n > 32) ? 32 : n;
|
|
jtag_io(xfer, rx[0], rx[1], tx + txc);
|
|
if ((tx[txc] & rx[3]) != rx[2]) {
|
|
status = ERR_BAD_MATCH;
|
|
goto done;
|
|
}
|
|
rx += 4;
|
|
rxc -= 4;
|
|
n -= xfer;
|
|
}
|
|
continue;
|
|
case CMD_JTAG_TX: {
|
|
unsigned tms = (op & 1) ? 0xFFFFFFFF : 0;
|
|
if (mode != MODE_JTAG) {
|
|
mode = MODE_JTAG;
|
|
jtag_init();
|
|
}
|
|
while (n > 0) {
|
|
unsigned xfer = (n > 32) ? 32 : n;
|
|
jtag_io(xfer, tms, rx[0], rx);
|
|
rx++;
|
|
rxc--;
|
|
n -= xfer;
|
|
}
|
|
continue;
|
|
}
|
|
case CMD_JTAG_RX: {
|
|
unsigned tms = (op & 1) ? 0xFFFFFFFF : 0;
|
|
unsigned tdi = (op & 2) ? 0xFFFFFFFF : 0;
|
|
if (mode != MODE_JTAG) {
|
|
mode = MODE_JTAG;
|
|
jtag_init();
|
|
}
|
|
tx[txc++] = RSWD_MSG(CMD_JTAG_DATA, 0, n);
|
|
while (n > 0) {
|
|
unsigned xfer = (n > 32) ? 32 : n;
|
|
jtag_io(xfer, tms, tdi, tx + txc);
|
|
txc++;
|
|
n -= xfer;
|
|
}
|
|
continue;
|
|
}
|
|
case CMD_RESET:
|
|
swd_hw_reset(n);
|
|
continue;
|
|
case CMD_DOWNLOAD: {
|
|
//u32 *addr = (void*) *rx++;
|
|
rxc--;
|
|
while (n) {
|
|
//*addr++ = *rx++;
|
|
rx++;
|
|
rxc--;
|
|
}
|
|
continue;
|
|
}
|
|
case CMD_EXECUTE:
|
|
//func = (void*) *rx++;
|
|
rxc--;
|
|
continue;
|
|
case CMD_TRACE:
|
|
swdp_trace = op;
|
|
continue;
|
|
case CMD_BOOTLOADER:
|
|
func = _reboot;
|
|
continue;
|
|
case CMD_SET_CLOCK:
|
|
n = swd_set_clock(n);
|
|
if (host_version >= RSWD_VERSION_1_0) {
|
|
tx[txc++] = RSWD_MSG(CMD_CLOCK_KHZ, 0, n);
|
|
}
|
|
continue;
|
|
case CMD_SWO_CLOCK:
|
|
n = swo_set_clock(n);
|
|
if (host_version >= RSWD_VERSION_1_0) {
|
|
tx[txc++] = RSWD_MSG(CMD_CLOCK_KHZ, 1, n);
|
|
}
|
|
continue;
|
|
case CMD_VERSION:
|
|
host_version = n;
|
|
tx[txc++] = RSWD_MSG(CMD_VERSION, 0, RSWD_VERSION);
|
|
|
|
n = strlen(board_str);
|
|
memcpy(tx + txc + 1, board_str, n + 1);
|
|
n = (n + 4) / 4;
|
|
tx[txc++] = RSWD_MSG(CMD_BOARD_STR, 0, n);
|
|
txc += n;
|
|
|
|
n = strlen(build_str);
|
|
memcpy(tx + txc + 1, build_str, n + 1);
|
|
n = (n + 4) / 4;
|
|
tx[txc++] = RSWD_MSG(CMD_BUILD_STR, 0, n);
|
|
txc += n;
|
|
|
|
tx[txc++] = RSWD_MSG(CMD_RX_MAXDATA, 0, 8192);
|
|
txc += n;
|
|
continue;
|
|
default:
|
|
printf("unknown command %02x\n", RSWD_MSG_CMD(msg));
|
|
status = 1;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
tx[txc++] = RSWD_MSG(CMD_STATUS, status, count);
|
|
|
|
/* if we're about to send an even multiple of the packet size
|
|
* (64), add a NULL op on the end to create a short packet at
|
|
* the end.
|
|
*/
|
|
if ((txc & 0xf) == 0)
|
|
tx[txc++] = RSWD_MSG(CMD_NULL, 0, 0);
|
|
|
|
#if CONFIG_MDEBUG_TRACE
|
|
printf("[ send %d words ]\n", txc);
|
|
for (n = 0; n < txc; n+=4) {
|
|
printx("%08x %08x %08x %08x\n",
|
|
tx[n], tx[n+1], tx[n+2], tx[n+3]);
|
|
}
|
|
#endif
|
|
usb_xmit(tx, txc * 4);
|
|
|
|
if (func) {
|
|
for (n = 0; n < 1000000; n++) asm("nop");
|
|
func();
|
|
for (;;) ;
|
|
}
|
|
}
|
|
|
|
// io buffers in AHB SRAM
|
|
static u32 *rxbuffer = (void *) 0x20001000;
|
|
static u32 *txbuffer[2] = {(void *) 0x20003000, (void *) 0x20005000 };
|
|
|
|
#include <kernel/thread.h>
|
|
|
|
void handle_rswd(void) {
|
|
int rxc;
|
|
int toggle = 0;
|
|
|
|
#if CONFIG_MDEBUG_TRACE
|
|
printf("[ rswdp agent v0.9 ]\n");
|
|
printf("[ built " __DATE__ " " __TIME__ " ]\n");
|
|
#endif
|
|
|
|
for (;;) {
|
|
rxc = usb_recv(rxbuffer, 8192);
|
|
|
|
#if CONFIG_MDEBUG_TRACE
|
|
int n;
|
|
printx("[ recv %d words ]\n", rxc/4);
|
|
for (n = 0; n < (rxc/4); n+=4) {
|
|
printx("%08x %08x %08x %08x\n",
|
|
rxbuffer[n], rxbuffer[n+1],
|
|
rxbuffer[n+2], rxbuffer[n+3]);
|
|
}
|
|
#endif
|
|
|
|
if ((rxc < 4) || (rxc & 3)) {
|
|
printf("error, runt frame, or strange frame... %d\n", rxc);
|
|
continue;
|
|
}
|
|
|
|
rxc = rxc / 4;
|
|
|
|
if ((rxbuffer[0] & 0xFFFF0000) != 0xAA770000) {
|
|
printf("invalid frame %x\n", rxbuffer[0]);
|
|
continue;
|
|
}
|
|
|
|
process_txn(rxbuffer[0], rxbuffer + 1, rxc - 1, txbuffer[toggle]);
|
|
toggle ^= 1;
|
|
}
|
|
}
|