Support for driving RISC-V DM via Arty's own JTAG chain using BSCAN tunnel (#370)
Including adjustments in response to review comments.
This commit is contained in:
@@ -406,6 +406,10 @@ static void dump_field(int idle, const struct scan_field *field)
|
||||
|
||||
static void select_dmi(struct target *target)
|
||||
{
|
||||
if (bscan_tunnel_ir_width != 0) {
|
||||
select_dmi_via_bscan(target);
|
||||
return;
|
||||
}
|
||||
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
|
||||
}
|
||||
|
||||
@@ -415,6 +419,9 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
|
||||
uint8_t in_value[4];
|
||||
uint8_t out_value[4];
|
||||
|
||||
if (bscan_tunnel_ir_width != 0)
|
||||
return dtmcontrol_scan_via_bscan(target, out);
|
||||
|
||||
buf_set_u32(out_value, 0, 32, out);
|
||||
|
||||
jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
|
||||
@@ -469,6 +476,8 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
|
||||
.out_value = out,
|
||||
.in_value = in
|
||||
};
|
||||
uint8_t tunneled_dr_width;
|
||||
struct scan_field tunneled_dr[4];
|
||||
|
||||
if (r->reset_delays_wait >= 0) {
|
||||
r->reset_delays_wait--;
|
||||
@@ -486,8 +495,44 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
|
||||
buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out);
|
||||
buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out);
|
||||
|
||||
/* Assume dbus is already selected. */
|
||||
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
|
||||
/* I wanted to place this code in a different function, but the way JTAG command
|
||||
queueing works in the jtag handling functions, the scan fields either have to be
|
||||
heap allocated, global/static, or else they need to stay on the stack until
|
||||
the jtag_execute_queue() call. Heap or static fields in this case doesn't seem
|
||||
the best fit. Declaring stack based field values in a subsidiary function call wouldn't
|
||||
work. */
|
||||
if (bscan_tunnel_ir_width != 0) {
|
||||
jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE);
|
||||
|
||||
/* I wanted to use struct initialization syntax, but that would involve either
|
||||
declaring the variable within this scope (which would go out of scope at runtime
|
||||
before the JTAG queue gets executed, which is an error waiting to happen), or
|
||||
initializing outside of the check for whether a BSCAN tunnel was active (which
|
||||
would be a waste of CPU time when BSCAN tunnel is not being used. So I declared the
|
||||
struct at the function's top-level, so its lifetime exceeds the point at which
|
||||
the queue is executed, and initializing with assignments here. */
|
||||
memset(tunneled_dr, 0, sizeof(tunneled_dr));
|
||||
tunneled_dr[0].num_bits = 1;
|
||||
tunneled_dr[0].out_value = bscan_one;
|
||||
|
||||
tunneled_dr[1].num_bits = 7;
|
||||
tunneled_dr_width = num_bits;
|
||||
tunneled_dr[1].out_value = &tunneled_dr_width;
|
||||
|
||||
/* for BSCAN tunnel, there is a one-TCK skew between shift in and shift out, so
|
||||
scanning num_bits + 1, and then will right shift the input field after executing the queues */
|
||||
tunneled_dr[2].num_bits = num_bits+1;
|
||||
tunneled_dr[2].out_value = out;
|
||||
tunneled_dr[2].in_value = in;
|
||||
|
||||
tunneled_dr[3].num_bits = 3;
|
||||
tunneled_dr[3].out_value = bscan_zero;
|
||||
|
||||
jtag_add_dr_scan(target->tap, DIM(tunneled_dr), tunneled_dr, TAP_IDLE);
|
||||
} else {
|
||||
/* Assume dbus is already selected. */
|
||||
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
|
||||
}
|
||||
|
||||
int idle_count = info->dmi_busy_delay;
|
||||
if (exec)
|
||||
@@ -502,6 +547,11 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
|
||||
return DMI_STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (bscan_tunnel_ir_width != 0) {
|
||||
/* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */
|
||||
buffer_shr(in, num_bytes, 1);
|
||||
}
|
||||
|
||||
if (data_in)
|
||||
*data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
|
||||
|
||||
|
||||
@@ -170,6 +170,44 @@ struct scan_field select_idcode = {
|
||||
.out_value = ir_idcode
|
||||
};
|
||||
|
||||
|
||||
int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */
|
||||
uint8_t bscan_zero[4] = {0};
|
||||
uint8_t bscan_one[4] = {1};
|
||||
|
||||
uint8_t ir_user4[4] = {0x23};
|
||||
struct scan_field select_user4 = {
|
||||
.in_value = NULL,
|
||||
.out_value = ir_user4
|
||||
};
|
||||
|
||||
|
||||
uint8_t bscan_tunneled_ir_width[4] = {5}; /* overridden by assignment in riscv_init_target */
|
||||
struct scan_field _bscan_tunneled_select_dmi[] = {
|
||||
{
|
||||
.num_bits = 1,
|
||||
.out_value = bscan_zero,
|
||||
.in_value = NULL,
|
||||
},
|
||||
{
|
||||
.num_bits = 7,
|
||||
.out_value = bscan_tunneled_ir_width,
|
||||
.in_value = NULL,
|
||||
},
|
||||
{
|
||||
.num_bits = 0, /* initialized in riscv_init_target to ir width of DM */
|
||||
.out_value = ir_dbus,
|
||||
.in_value = NULL,
|
||||
},
|
||||
{
|
||||
.num_bits = 3,
|
||||
.out_value = bscan_zero,
|
||||
.in_value = NULL,
|
||||
}
|
||||
};
|
||||
struct scan_field *bscan_tunneled_select_dmi = _bscan_tunneled_select_dmi;
|
||||
uint32_t bscan_tunneled_select_dmi_num_fields = DIM(_bscan_tunneled_select_dmi);
|
||||
|
||||
struct trigger {
|
||||
uint64_t address;
|
||||
uint32_t length;
|
||||
@@ -201,12 +239,101 @@ range_t *expose_custom;
|
||||
|
||||
static int riscv_resume_go_all_harts(struct target *target);
|
||||
|
||||
void select_dmi_via_bscan(struct target *target)
|
||||
{
|
||||
jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE);
|
||||
jtag_add_dr_scan(target->tap, bscan_tunneled_select_dmi_num_fields, bscan_tunneled_select_dmi, TAP_IDLE);
|
||||
}
|
||||
|
||||
|
||||
uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out)
|
||||
{
|
||||
/* On BSCAN TAP: Select IR=USER4, issue tunneled IR scan via BSCAN TAP's DR */
|
||||
uint8_t tunneled_ir_width[4] = {bscan_tunnel_ir_width};
|
||||
uint8_t tunneled_dr_width[4] = {32};
|
||||
uint8_t out_value[5] = {0};
|
||||
uint8_t in_value[5] = {0};
|
||||
|
||||
buf_set_u32(out_value, 0, 32, out);
|
||||
|
||||
struct scan_field tunneled_ir[] = {
|
||||
{
|
||||
.num_bits = 1,
|
||||
.out_value = bscan_zero,
|
||||
.in_value = NULL,
|
||||
},
|
||||
{
|
||||
.num_bits = 7,
|
||||
.out_value = tunneled_ir_width,
|
||||
.in_value = NULL,
|
||||
},
|
||||
{
|
||||
.num_bits = bscan_tunnel_ir_width,
|
||||
.out_value = ir_dtmcontrol,
|
||||
.in_value = NULL,
|
||||
},
|
||||
{
|
||||
.num_bits = 3,
|
||||
.out_value = bscan_zero,
|
||||
.in_value = NULL,
|
||||
}
|
||||
};
|
||||
struct scan_field tunneled_dr[] = {
|
||||
{
|
||||
.num_bits = 1,
|
||||
.out_value = bscan_one,
|
||||
.in_value = NULL,
|
||||
},
|
||||
{
|
||||
.num_bits = 7,
|
||||
.out_value = tunneled_dr_width,
|
||||
.in_value = NULL,
|
||||
},
|
||||
/* for BSCAN tunnel, there is a one-TCK skew between shift in and shift out,
|
||||
so scanning 33 bits and then right shifting the in_value after the scan is completed */
|
||||
{
|
||||
.num_bits = 32+1,
|
||||
.out_value = out_value,
|
||||
.in_value = in_value,
|
||||
},
|
||||
{
|
||||
.num_bits = 3,
|
||||
.out_value = bscan_zero,
|
||||
.in_value = NULL,
|
||||
}
|
||||
};
|
||||
|
||||
jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE);
|
||||
jtag_add_dr_scan(target->tap, DIM(tunneled_ir), tunneled_ir, TAP_IDLE);
|
||||
jtag_add_dr_scan(target->tap, DIM(tunneled_dr), tunneled_dr, TAP_IDLE);
|
||||
select_dmi_via_bscan(target);
|
||||
|
||||
int retval = jtag_execute_queue();
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("failed jtag scan: %d", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Note the starting offset is bit 1, not bit 0. In BSCAN tunnel, there is a one-bit TCK skew between
|
||||
output and input */
|
||||
uint32_t in = buf_get_u32(in_value, 1, 32);
|
||||
LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
|
||||
{
|
||||
struct scan_field field;
|
||||
uint8_t in_value[4];
|
||||
uint8_t out_value[4];
|
||||
|
||||
if (bscan_tunnel_ir_width != 0)
|
||||
return dtmcontrol_scan_via_bscan(target, out);
|
||||
|
||||
|
||||
buf_set_u32(out_value, 0, 32, out);
|
||||
|
||||
jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
|
||||
@@ -266,6 +393,12 @@ static int riscv_init_target(struct command_context *cmd_ctx,
|
||||
select_dbus.num_bits = target->tap->ir_length;
|
||||
select_idcode.num_bits = target->tap->ir_length;
|
||||
|
||||
if (bscan_tunnel_ir_width != 0) {
|
||||
select_user4.num_bits = target->tap->ir_length;
|
||||
bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width;
|
||||
bscan_tunneled_select_dmi[2].num_bits = bscan_tunnel_ir_width;
|
||||
}
|
||||
|
||||
riscv_semihosting_init(target);
|
||||
|
||||
target->debug_reason = DBG_REASON_DBGRQ;
|
||||
@@ -1827,6 +1960,23 @@ COMMAND_HANDLER(riscv_set_ir)
|
||||
}
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(riscv_use_bscan_tunnel)
|
||||
{
|
||||
int irwidth = 0;
|
||||
|
||||
if (CMD_ARGC > 1) {
|
||||
LOG_ERROR("Command takes at most one argument");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (CMD_ARGC == 1)
|
||||
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], irwidth);
|
||||
|
||||
bscan_tunnel_ir_width = irwidth;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
static const struct command_registration riscv_exec_command_handlers[] = {
|
||||
{
|
||||
.name = "test_compliance",
|
||||
@@ -1933,6 +2083,15 @@ static const struct command_registration riscv_exec_command_handlers[] = {
|
||||
.usage = "riscv set_ir_idcode [idcode|dtmcs|dmi] value",
|
||||
.help = "Set IR value for specified JTAG register."
|
||||
},
|
||||
{
|
||||
.name = "use_bscan_tunnel",
|
||||
.handler = riscv_use_bscan_tunnel,
|
||||
.mode = COMMAND_ANY,
|
||||
.usage = "riscv use_bscan_tunnel value",
|
||||
.help = "Enable or disable use of a BSCAN tunnel to reach DM. Supply "
|
||||
"the width of the DM transport TAP's instruction register to "
|
||||
"enable. Supply a value of 0 to disable."
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
|
||||
@@ -166,6 +166,16 @@ extern struct scan_field select_dbus;
|
||||
extern uint8_t ir_idcode[4];
|
||||
extern struct scan_field select_idcode;
|
||||
|
||||
extern struct scan_field select_user4;
|
||||
extern struct scan_field *bscan_tunneled_select_dmi;
|
||||
extern uint32_t bscan_tunneled_select_dmi_num_fields;
|
||||
extern uint8_t bscan_zero[4];
|
||||
extern uint8_t bscan_one[4];
|
||||
extern int bscan_tunnel_ir_width;
|
||||
|
||||
uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out);
|
||||
void select_dmi_via_bscan(struct target *target);
|
||||
|
||||
/*** OpenOCD Interface */
|
||||
int riscv_openocd_poll(struct target *target);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user