forked from auracaster/openocd
drivers/ch347: limit SWD packet size by processing time
CH347 chip firmware probably does not service USB poll/requests from the host during processing of SWD packet. If the processing takes longer than approx 8 ms, USB host disconnects the adapter from the bus as if it were dead. To avoid disconnect, compute approximate processing time of SWD transactions and flush the queue if the total time gets over the limit (use 7 ms to keep some safety margin). Allow ch347_swd_send_idle() to split the requested delay to more partial sequences. It prevents USB disconnect at beginning of target examine, when memaccess 255 clk is used. Also memaccess > 255 clk is now possible. Set the maximal divisor to fit the longest ADI sequence into 7 ms processing time. Tested on a linux x64 PC and RPi 5. Change-Id: Ibdcff4de52e3eb4d86ed83af81a1c64f1f9b5d24 Signed-off-by: Tomas Vanek <vanekt@fbl.cz> Reviewed-on: https://review.openocd.org/c/openocd/+/8746 Tested-by: jenkins Reviewed-by: ZhiYuanNJ <871238103@qq.com>
This commit is contained in:
@@ -115,13 +115,15 @@
|
||||
#define CH347_CMD_SWD_REG_W 0xA0 // SWD Interface write reg
|
||||
#define CH347_CMD_SWD_SEQ_W 0xA1 // SWD Interface write spec seq
|
||||
#define CH347_CMD_SWD_REG_R 0xA2 // SWD Interface read reg
|
||||
#define CH347_MAX_SEND_CMD 19 // max send cmd number
|
||||
#define CH347_MAX_SEND_BUF 0X200
|
||||
#define CH347_MAX_RECV_BUF 0X200
|
||||
#define CH347_MAX_PROCESSING_US 7000 // max time in us for packet processing
|
||||
// USB hosts disconnect the adapter if SWD processing takes more!
|
||||
#define CH347_MAX_SEND_BUF USBC_PACKET_USBHS
|
||||
#define CH347_MAX_RECV_BUF USBC_PACKET_USBHS
|
||||
#define CH347_MAX_CMD_BUF 128
|
||||
#define CH347_SWD_CLOCK_MAX 5000
|
||||
#define CH347_SWD_CLOCK_BASE 1000
|
||||
#define CH347_SWD_CLOCK_MAX_DIVISOR 8
|
||||
// limited by the longest sequence processing time
|
||||
#define CH347_SWD_CLOCK_MAX_DIVISOR (CH347_MAX_PROCESSING_US / swd_seq_dormant_to_swd_len)
|
||||
|
||||
#define CH347_EPOUT 0x06u // the usb endpoint number for writing
|
||||
#define CH347_EPIN 0x86u // the usb endpoint number for reading
|
||||
@@ -240,6 +242,8 @@ struct ch347_swd_context {
|
||||
int need_recv_len;
|
||||
int queued_retval;
|
||||
int sent_cmd_count;
|
||||
unsigned int clk_divisor;
|
||||
unsigned int total_swd_clk;
|
||||
struct list_head send_cmd_head;
|
||||
struct list_head free_cmd_head;
|
||||
struct ch347_swd_io ch347_cmd_buf[CH347_MAX_CMD_BUF];
|
||||
@@ -1640,6 +1644,7 @@ static int ch347_swd_init_cmd(uint8_t clock_divisor)
|
||||
retval = ch347_single_read_get_byte(0, &init_result);
|
||||
LOG_DEBUG("SWD init clk div %" PRIu8 ", result %02" PRIx8,
|
||||
clock_divisor, init_result);
|
||||
ch347_swd_context.clk_divisor = clock_divisor;
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -1734,7 +1739,7 @@ static int ch347_speed_get_index(int khz, int *speed_idx)
|
||||
// Don't allow too low clk speeds: packet processing is limited to ~8 msec
|
||||
// or triggers host USB disconnect
|
||||
*speed_idx = MIN(DIV_ROUND_UP(CH347_SWD_CLOCK_BASE, khz),
|
||||
CH347_SWD_CLOCK_MAX_DIVISOR);
|
||||
(int)CH347_SWD_CLOCK_MAX_DIVISOR);
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
@@ -1990,6 +1995,7 @@ static void ch347_write_swd_reg(uint8_t cmd, const uint32_t out)
|
||||
ch347_swd_context.send_buf[ch347_swd_context.send_len++] = parity_u32(out);
|
||||
// 0xA0 + 1 byte(3bit ACK)
|
||||
ch347_swd_context.need_recv_len += (1 + 1);
|
||||
ch347_swd_context.total_swd_clk += 46;
|
||||
}
|
||||
|
||||
static void ch347_write_spec_seq(const uint8_t *out, uint8_t out_len)
|
||||
@@ -2001,6 +2007,7 @@ static void ch347_write_spec_seq(const uint8_t *out, uint8_t out_len)
|
||||
for (uint8_t i = 0; i < DIV_ROUND_UP(out_len, 8); i++)
|
||||
ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out ? out[i] : 0x00;
|
||||
ch347_swd_context.need_recv_len += 1; // 0xA1
|
||||
ch347_swd_context.total_swd_clk += out_len;
|
||||
}
|
||||
|
||||
static void ch347_read_swd_reg(uint8_t cmd)
|
||||
@@ -2011,6 +2018,7 @@ static void ch347_read_swd_reg(uint8_t cmd)
|
||||
ch347_swd_context.send_buf[ch347_swd_context.send_len++] = cmd;
|
||||
// 0xA2 + 1 byte(3bit ACK) + 4 byte(data) + 1 byte(1bit parity+1bit trn)
|
||||
ch347_swd_context.need_recv_len += 1 + 1 + 4 + 1;
|
||||
ch347_swd_context.total_swd_clk += 46;
|
||||
}
|
||||
|
||||
static int ch347_swd_switch_out(enum swd_special_seq seq, const uint8_t *out, unsigned int out_len)
|
||||
@@ -2080,22 +2088,81 @@ static int ch347_swd_run_queue_inner(void);
|
||||
|
||||
static int ch347_swd_send_idle(uint32_t ap_delay_clk)
|
||||
{
|
||||
struct ch347_swd_io *pswd_io = ch347_get_one_swd_io();
|
||||
if (!pswd_io) {
|
||||
int retval = ch347_swd_run_queue_inner();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
bool run_q = false;
|
||||
struct ch347_swd_io *pswd_io = NULL;
|
||||
unsigned int max_processing_clk = ch347_swd_context.clk_divisor
|
||||
? CH347_MAX_PROCESSING_US / ch347_swd_context.clk_divisor
|
||||
: CH347_MAX_PROCESSING_US * 5;
|
||||
bool more_q_runs =
|
||||
1 + 1 + 1 + DIV_ROUND_UP(ap_delay_clk, 8) > CH347_MAX_SEND_BUF
|
||||
&& ap_delay_clk > max_processing_clk;
|
||||
|
||||
pswd_io = ch347_get_one_swd_io();
|
||||
if (!pswd_io) {
|
||||
LOG_ERROR("ch347 SWD queue not empty after ch347_swd_run_queue");
|
||||
ch347_swd_context.queued_retval = ERROR_FAIL;
|
||||
return ERROR_FAIL;
|
||||
if (ch347_swd_context.sent_cmd_count) {
|
||||
unsigned int expected_total_clk = ch347_swd_context.total_swd_clk + ap_delay_clk;
|
||||
unsigned int expected_send_len = ch347_swd_context.send_len
|
||||
+ 1 + 1 + 1 + DIV_ROUND_UP(ap_delay_clk, 8);
|
||||
// 0xA1 + Len + rev + n byte(delay)
|
||||
unsigned int expected_recv_len = ch347_swd_context.need_recv_len + 1;
|
||||
// 0xA1
|
||||
unsigned int expected_time = ch347_swd_context.clk_divisor
|
||||
? expected_total_clk * ch347_swd_context.clk_divisor
|
||||
: expected_total_clk / 5;
|
||||
if (expected_time > CH347_MAX_PROCESSING_US
|
||||
|| expected_send_len > CH347_MAX_SEND_BUF
|
||||
|| expected_recv_len > CH347_MAX_RECV_BUF) {
|
||||
int send_room = CH347_MAX_SEND_BUF - ch347_swd_context.send_len - 1 - 1 - 1;
|
||||
if (more_q_runs
|
||||
&& send_room > 0
|
||||
&& expected_recv_len <= CH347_MAX_RECV_BUF
|
||||
&& ch347_swd_context.total_swd_clk < max_processing_clk) {
|
||||
pswd_io = ch347_get_one_swd_io();
|
||||
if (pswd_io) {
|
||||
// fill the rest of queue/time by part of delay
|
||||
unsigned int this_delay_clk = MIN(ap_delay_clk, 255);
|
||||
if ((unsigned int)send_room * 8 < this_delay_clk)
|
||||
this_delay_clk = send_room * 8;
|
||||
if (max_processing_clk - ch347_swd_context.total_swd_clk < this_delay_clk)
|
||||
this_delay_clk = max_processing_clk - ch347_swd_context.total_swd_clk;
|
||||
LOG_DEBUG_IO("partial delay %u clk", this_delay_clk);
|
||||
ch347_write_spec_seq(NULL, this_delay_clk);
|
||||
list_add_tail(&pswd_io->list_entry, &ch347_swd_context.send_cmd_head);
|
||||
ap_delay_clk -= this_delay_clk;
|
||||
}
|
||||
}
|
||||
run_q = true;
|
||||
}
|
||||
}
|
||||
|
||||
ch347_write_spec_seq(NULL, ap_delay_clk);
|
||||
list_add_tail(&pswd_io->list_entry, &ch347_swd_context.send_cmd_head);
|
||||
do {
|
||||
if (!run_q)
|
||||
pswd_io = ch347_get_one_swd_io();
|
||||
|
||||
if (!pswd_io) {
|
||||
int retval = ch347_swd_run_queue_inner();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
pswd_io = ch347_get_one_swd_io();
|
||||
if (!pswd_io) {
|
||||
LOG_ERROR("ch347 SWD queue not empty after ch347_swd_run_queue");
|
||||
ch347_swd_context.queued_retval = ERROR_FAIL;
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int send_room = CH347_MAX_SEND_BUF - 1 - 1 - 1;
|
||||
unsigned int this_delay_clk = MIN(ap_delay_clk, 255);
|
||||
if (send_room * 8 < this_delay_clk)
|
||||
this_delay_clk = send_room * 8;
|
||||
if (max_processing_clk < this_delay_clk)
|
||||
this_delay_clk = max_processing_clk;
|
||||
LOG_DEBUG_IO("delay %u clk", this_delay_clk);
|
||||
ch347_write_spec_seq(NULL, this_delay_clk);
|
||||
list_add_tail(&pswd_io->list_entry, &ch347_swd_context.send_cmd_head);
|
||||
ap_delay_clk -= this_delay_clk;
|
||||
run_q = true;
|
||||
pswd_io = NULL;
|
||||
} while (ap_delay_clk);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -2236,6 +2303,7 @@ skip:
|
||||
ch347_swd_context.need_recv_len = CH347_CMD_HEADER;
|
||||
ch347_swd_context.recv_len = 0;
|
||||
ch347_swd_context.sent_cmd_count = 0;
|
||||
ch347_swd_context.total_swd_clk = 0;
|
||||
retval = ch347_swd_context.queued_retval;
|
||||
ch347_swd_context.queued_retval = ERROR_OK;
|
||||
return retval;
|
||||
@@ -2255,22 +2323,28 @@ static int ch347_swd_run_queue(void)
|
||||
static int ch347_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, uint32_t ap_delay_clk)
|
||||
{
|
||||
int retval = ERROR_OK;
|
||||
if (ap_delay_clk > 255)
|
||||
ap_delay_clk = 255; // limit imposed by spec. seq. size in one byte
|
||||
|
||||
if (ch347_swd_context.sent_cmd_count >= CH347_MAX_SEND_CMD) {
|
||||
retval = ch347_swd_run_queue_inner();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
bool run_q = false;
|
||||
if (ch347_swd_context.sent_cmd_count) {
|
||||
unsigned int expected_total_clk = ch347_swd_context.total_swd_clk
|
||||
+ 46 // SWD transaction
|
||||
+ ap_delay_clk
|
||||
+ 8; // 8 idle cycles at the end of queue
|
||||
unsigned int expected_time = ch347_swd_context.clk_divisor
|
||||
? expected_total_clk * ch347_swd_context.clk_divisor
|
||||
: expected_total_clk / 5;
|
||||
if (expected_time > CH347_MAX_PROCESSING_US) {
|
||||
LOG_DEBUG_IO("Expected queue run %u cycles, with this cmd %u",
|
||||
ch347_swd_context.total_swd_clk, expected_total_clk);
|
||||
run_q = true;
|
||||
} else if (!ch347_chk_buf_size(cmd, ap_delay_clk)) {
|
||||
run_q = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ch347_chk_buf_size(cmd, ap_delay_clk)) {
|
||||
retval = ch347_swd_run_queue_inner();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
}
|
||||
struct ch347_swd_io *pswd_io = NULL;
|
||||
if (!run_q)
|
||||
pswd_io = ch347_get_one_swd_io();
|
||||
|
||||
struct ch347_swd_io *pswd_io = ch347_get_one_swd_io();
|
||||
if (!pswd_io) {
|
||||
retval = ch347_swd_run_queue_inner();
|
||||
if (retval != ERROR_OK)
|
||||
|
||||
Reference in New Issue
Block a user