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:
Greg Savin
2019-04-23 16:25:22 -07:00
committed by GitHub
parent 15f2d35bc6
commit 5190dd4cef
7 changed files with 268 additions and 2 deletions
+52 -2
View File
@@ -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);
+159
View File
@@ -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
};
+10
View File
@@ -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);