ipdbg: improve ipdbg-host speed

By queuing multiple jtag transfers the connection speed between
JTAG-Host and JTAG-Hub is improved. This is due to much less
calls to OS functions. An improvement of about x30 has been
measured with ftdi-based jtag adapters

For this to work the JTAG-Host server needs to know if flow control
is enabled on the JTAG-Hub ports. This is possible with newer
JTAG-Hub/JtagCDC. For old JTAG-Hubs the queuing is not enabled so
this change is backwards compatible.

Change-Id: I8a5108adbe2a2c1e3d3620b5c9ff77a546bfc14e
Signed-off-by: Daniel Anselmi <danselmi@gmx.ch>
Reviewed-on: https://review.openocd.org/c/openocd/+/7978
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
Daniel Anselmi 2023-03-12 01:43:32 +01:00 committed by Antonio Borneo
parent 44e02e1f49
commit d2b2ac28d9
1 changed files with 210 additions and 68 deletions

View File

@ -20,6 +20,17 @@
#define IPDBG_MIN_DR_LENGTH 11
#define IPDBG_MAX_DR_LENGTH 13
#define IPDBG_TCP_PORT_STR_MAX_LENGTH 6
#define IPDBG_SCRATCH_MEMORY_SIZE 1024
#define IPDBG_EMPTY_DOWN_TRANSFERS 1024
#define IPDBG_CONSECUTIVE_UP_TRANSFERS 1024
#if IPDBG_SCRATCH_MEMORY_SIZE < IPDBG_EMPTY_DOWN_TRANSFERS
#error "scratch Memory must be at least IPDBG_EMPTY_DOWN_TRANSFERS"
#endif
#if IPDBG_SCRATCH_MEMORY_SIZE < IPDBG_CONSECUTIVE_UP_TRANSFERS
#error "scratch Memory must be at least IPDBG_CONSECUTIVE_UP_TRANSFERS"
#endif
/* private connection data for IPDBG */
struct ipdbg_fifo {
@ -48,6 +59,13 @@ struct ipdbg_virtual_ir_info {
uint32_t value;
};
struct ipdbg_hub_scratch_memory {
uint8_t *dr_out_vals;
uint8_t *dr_in_vals;
uint8_t *vir_out_val;
struct scan_field *fields;
};
struct ipdbg_hub {
uint32_t user_instruction;
uint32_t max_tools;
@ -62,7 +80,9 @@ struct ipdbg_hub {
struct connection **connections;
uint8_t data_register_length;
uint8_t dn_xoff;
uint8_t flow_control_enabled;
struct ipdbg_virtual_ir_info *virtual_ir;
struct ipdbg_hub_scratch_memory scratch_memory;
};
static struct ipdbg_hub *ipdbg_first_hub;
@ -236,20 +256,28 @@ static int ipdbg_create_hub(struct jtag_tap *tap, uint32_t user_instruction, uin
{
*hub = NULL;
struct ipdbg_hub *new_hub = calloc(1, sizeof(struct ipdbg_hub));
if (!new_hub) {
free(virtual_ir);
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
if (!new_hub)
goto mem_err_hub;
const size_t dreg_buffer_size = DIV_ROUND_UP(data_register_length, 8);
new_hub->max_tools = ipdbg_max_tools_from_data_register_length(data_register_length);
new_hub->scratch_memory.dr_out_vals = calloc(IPDBG_SCRATCH_MEMORY_SIZE, dreg_buffer_size);
new_hub->scratch_memory.dr_in_vals = calloc(IPDBG_SCRATCH_MEMORY_SIZE, dreg_buffer_size);
new_hub->scratch_memory.fields = calloc(IPDBG_SCRATCH_MEMORY_SIZE, sizeof(struct scan_field));
new_hub->connections = calloc(new_hub->max_tools, sizeof(struct connection *));
if (!new_hub->connections) {
free(virtual_ir);
free(new_hub);
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
if (virtual_ir)
new_hub->scratch_memory.vir_out_val = calloc(1, DIV_ROUND_UP(virtual_ir->length, 8));
if (!new_hub->scratch_memory.dr_out_vals || !new_hub->scratch_memory.dr_in_vals ||
!new_hub->scratch_memory.fields || (virtual_ir && !new_hub->scratch_memory.vir_out_val) ||
!new_hub->connections)
goto mem_err2;
if (virtual_ir)
buf_set_u32(new_hub->scratch_memory.vir_out_val, 0, virtual_ir->length, virtual_ir->value);
new_hub->tap = tap;
new_hub->user_instruction = user_instruction;
new_hub->data_register_length = data_register_length;
@ -260,8 +288,19 @@ static int ipdbg_create_hub(struct jtag_tap *tap, uint32_t user_instruction, uin
new_hub->virtual_ir = virtual_ir;
*hub = new_hub;
return ERROR_OK;
mem_err2:
free(new_hub->scratch_memory.vir_out_val);
free(new_hub->connections);
free(new_hub->scratch_memory.fields);
free(new_hub->scratch_memory.dr_in_vals);
free(new_hub->scratch_memory.dr_out_vals);
free(new_hub);
mem_err_hub:
free(virtual_ir);
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
static void ipdbg_free_hub(struct ipdbg_hub *hub)
@ -270,6 +309,10 @@ static void ipdbg_free_hub(struct ipdbg_hub *hub)
return;
free(hub->connections);
free(hub->virtual_ir);
free(hub->scratch_memory.dr_out_vals);
free(hub->scratch_memory.dr_in_vals);
free(hub->scratch_memory.fields);
free(hub->scratch_memory.vir_out_val);
free(hub);
}
@ -348,20 +391,11 @@ static int ipdbg_shift_vir(struct ipdbg_hub *hub)
if (!tap)
return ERROR_FAIL;
uint8_t *dr_out_val = calloc(DIV_ROUND_UP(hub->virtual_ir->length, 8), 1);
if (!dr_out_val) {
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
buf_set_u32(dr_out_val, 0, hub->virtual_ir->length, hub->virtual_ir->value);
struct scan_field fields;
ipdbg_init_scan_field(&fields, NULL, hub->virtual_ir->length, dr_out_val);
jtag_add_dr_scan(tap, 1, &fields, TAP_IDLE);
ipdbg_init_scan_field(hub->scratch_memory.fields, NULL,
hub->virtual_ir->length, hub->scratch_memory.vir_out_val);
jtag_add_dr_scan(tap, 1, hub->scratch_memory.fields, TAP_IDLE);
retval = jtag_execute_queue();
free(dr_out_val);
return retval;
}
@ -374,33 +408,15 @@ static int ipdbg_shift_data(struct ipdbg_hub *hub, uint32_t dn_data, uint32_t *u
if (!tap)
return ERROR_FAIL;
uint8_t *dr_out_val = calloc(DIV_ROUND_UP(hub->data_register_length, 8), 1);
if (!dr_out_val) {
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
buf_set_u32(dr_out_val, 0, hub->data_register_length, dn_data);
buf_set_u32(hub->scratch_memory.dr_out_vals, 0, hub->data_register_length, dn_data);
uint8_t *dr_in_val = NULL;
if (up_data) {
dr_in_val = calloc(DIV_ROUND_UP(hub->data_register_length, 8), 1);
if (!dr_in_val) {
LOG_ERROR("Out of memory");
free(dr_out_val);
return ERROR_FAIL;
}
}
struct scan_field fields;
ipdbg_init_scan_field(&fields, dr_in_val, hub->data_register_length, dr_out_val);
jtag_add_dr_scan(tap, 1, &fields, TAP_IDLE);
ipdbg_init_scan_field(hub->scratch_memory.fields, hub->scratch_memory.dr_in_vals,
hub->data_register_length, hub->scratch_memory.dr_out_vals);
jtag_add_dr_scan(tap, 1, hub->scratch_memory.fields, TAP_IDLE);
int retval = jtag_execute_queue();
if (up_data && retval == ERROR_OK)
*up_data = buf_get_u32(dr_in_val, 0, hub->data_register_length);
free(dr_out_val);
free(dr_in_val);
*up_data = buf_get_u32(hub->scratch_memory.dr_in_vals, 0, hub->data_register_length);
return retval;
}
@ -432,6 +448,60 @@ static int ipdbg_distribute_data_from_hub(struct ipdbg_hub *hub, uint32_t up)
return ERROR_OK;
}
static void ipdbg_check_for_xoff(struct ipdbg_hub *hub, size_t tool,
uint32_t rx_data)
{
if ((rx_data & hub->xoff_mask) && hub->last_dn_tool != hub->max_tools) {
hub->dn_xoff |= BIT(hub->last_dn_tool);
LOG_INFO("tool %d sent xoff", hub->last_dn_tool);
}
hub->last_dn_tool = tool;
}
static int ipdbg_shift_empty_data(struct ipdbg_hub *hub)
{
if (!hub)
return ERROR_FAIL;
struct jtag_tap *tap = hub->tap;
if (!tap)
return ERROR_FAIL;
const size_t dreg_buffer_size = DIV_ROUND_UP(hub->data_register_length, 8);
memset(hub->scratch_memory.dr_out_vals, 0, dreg_buffer_size);
for (size_t i = 0; i < IPDBG_EMPTY_DOWN_TRANSFERS; ++i) {
ipdbg_init_scan_field(hub->scratch_memory.fields + i,
hub->scratch_memory.dr_in_vals + i * dreg_buffer_size,
hub->data_register_length,
hub->scratch_memory.dr_out_vals);
jtag_add_dr_scan(tap, 1, hub->scratch_memory.fields + i, TAP_IDLE);
}
int retval = jtag_execute_queue();
if (retval == ERROR_OK) {
uint32_t up_data;
for (size_t i = 0; i < IPDBG_EMPTY_DOWN_TRANSFERS; ++i) {
up_data = buf_get_u32(hub->scratch_memory.dr_in_vals +
i * dreg_buffer_size, 0,
hub->data_register_length);
int rv = ipdbg_distribute_data_from_hub(hub, up_data);
if (rv != ERROR_OK)
retval = rv;
if (i == 0) {
/* check if xoff sent is only needed on the first transfer which
may contain the xoff of the prev down transfer.
*/
ipdbg_check_for_xoff(hub, hub->max_tools, up_data);
}
}
}
return retval;
}
static int ipdbg_jtag_transfer_byte(struct ipdbg_hub *hub, size_t tool, struct ipdbg_connection *connection)
{
uint32_t dn = hub->valid_mask | ((tool & hub->tool_mask) << 8) |
@ -445,16 +515,65 @@ static int ipdbg_jtag_transfer_byte(struct ipdbg_hub *hub, size_t tool, struct i
if (ret != ERROR_OK)
return ret;
if ((up & hub->xoff_mask) && (hub->last_dn_tool != hub->max_tools)) {
hub->dn_xoff |= BIT(hub->last_dn_tool);
LOG_INFO("tool %d sent xoff", hub->last_dn_tool);
}
hub->last_dn_tool = tool;
ipdbg_check_for_xoff(hub, tool, up);
return ERROR_OK;
}
static int ipdbg_jtag_transfer_bytes(struct ipdbg_hub *hub,
size_t tool, struct ipdbg_connection *connection)
{
if (!hub)
return ERROR_FAIL;
struct jtag_tap *tap = hub->tap;
if (!tap)
return ERROR_FAIL;
const size_t dreg_buffer_size = DIV_ROUND_UP(hub->data_register_length, 8);
size_t num_tx = (connection->dn_fifo.count < IPDBG_CONSECUTIVE_UP_TRANSFERS) ?
connection->dn_fifo.count : IPDBG_CONSECUTIVE_UP_TRANSFERS;
for (size_t i = 0; i < num_tx; ++i) {
uint32_t dn_data = hub->valid_mask | ((tool & hub->tool_mask) << 8) |
(0x00fful & ipdbg_get_from_fifo(&connection->dn_fifo));
buf_set_u32(hub->scratch_memory.dr_out_vals + i * dreg_buffer_size, 0,
hub->data_register_length, dn_data);
ipdbg_init_scan_field(hub->scratch_memory.fields + i,
hub->scratch_memory.dr_in_vals +
i * dreg_buffer_size,
hub->data_register_length,
hub->scratch_memory.dr_out_vals +
i * dreg_buffer_size);
jtag_add_dr_scan(tap, 1, hub->scratch_memory.fields + i, TAP_IDLE);
}
int retval = jtag_execute_queue();
if (retval == ERROR_OK) {
uint32_t up_data;
for (size_t i = 0; i < num_tx; ++i) {
up_data = buf_get_u32(hub->scratch_memory.dr_in_vals +
i * dreg_buffer_size,
0, hub->data_register_length);
int rv = ipdbg_distribute_data_from_hub(hub, up_data);
if (rv != ERROR_OK)
retval = rv;
if (i == 0) {
/* check if xoff sent is only needed on the first transfer which
may contain the xoff of the prev down transfer.
No checks for this channel because this function is only
called for channels without enabled flow control.
*/
ipdbg_check_for_xoff(hub, tool, up_data);
}
}
}
return retval;
}
static int ipdbg_polling_callback(void *priv)
{
struct ipdbg_hub *hub = priv;
@ -468,33 +587,25 @@ static int ipdbg_polling_callback(void *priv)
return ret;
/* transfer dn buffers to jtag-hub */
unsigned int num_transfers = 0;
for (size_t tool = 0; tool < hub->max_tools; ++tool) {
struct connection *conn = hub->connections[tool];
if (conn && conn->priv) {
struct ipdbg_connection *connection = conn->priv;
while (((hub->dn_xoff & BIT(tool)) == 0) && !ipdbg_fifo_is_empty(&connection->dn_fifo)) {
ret = ipdbg_jtag_transfer_byte(hub, tool, connection);
if (hub->flow_control_enabled & BIT(tool))
ret = ipdbg_jtag_transfer_byte(hub, tool, connection);
else
ret = ipdbg_jtag_transfer_bytes(hub, tool, connection);
if (ret != ERROR_OK)
return ret;
++num_transfers;
}
}
}
/* some transfers to get data from jtag-hub in case there is no dn data */
while (num_transfers++ < hub->max_tools) {
uint32_t dn = 0;
uint32_t up = 0;
int retval = ipdbg_shift_data(hub, dn, &up);
if (retval != ERROR_OK)
return ret;
retval = ipdbg_distribute_data_from_hub(hub, up);
if (retval != ERROR_OK)
return ret;
}
ret = ipdbg_shift_empty_data(hub);
if (ret != ERROR_OK)
return ret;
/* write from up fifos to sockets */
for (size_t tool = 0; tool < hub->max_tools; ++tool) {
@ -510,6 +621,33 @@ static int ipdbg_polling_callback(void *priv)
return ERROR_OK;
}
static int ipdbg_get_flow_control_info_from_hub(struct ipdbg_hub *hub)
{
uint32_t up_data;
/* on older implementations the flow_control_enable_word is not sent to us.
so we don't know -> assume it's enabled on all channels */
hub->flow_control_enabled = 0x7f;
int ret = ipdbg_shift_data(hub, 0UL, &up_data);
if (ret != ERROR_OK)
return ret;
const bool valid_up_data = up_data & hub->valid_mask;
if (valid_up_data) {
const size_t tool = (up_data >> 8) & hub->tool_mask;
/* the first valid data from hub is flow_control_enable_word */
if (tool == hub->tool_mask)
hub->flow_control_enabled = up_data & 0x007f;
else
ipdbg_distribute_data_from_hub(hub, up_data);
}
LOG_INFO("Flow control enabled on IPDBG JTAG Hub: 0x%02x", hub->flow_control_enabled);
return ERROR_OK;
}
static int ipdbg_start_polling(struct ipdbg_service *service, struct connection *connection)
{
struct ipdbg_hub *hub = service->hub;
@ -536,6 +674,10 @@ static int ipdbg_start_polling(struct ipdbg_service *service, struct connection
if (ret != ERROR_OK)
return ret;
ret = ipdbg_get_flow_control_info_from_hub(hub);
if (ret != ERROR_OK)
return ret;
LOG_INFO("IPDBG start_polling");
const int time_ms = 20;