Support for driving RISC-V DM via Arty's own JTAG chain using BSCAN tunnel (#370)

Including adjustments in response to review comments.
reverse-resume-order
Greg Savin 2019-04-23 16:25:22 -07:00 committed by GitHub
parent 15f2d35bc6
commit 5190dd4cef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 268 additions and 2 deletions

View File

@ -9502,6 +9502,11 @@ When utilizing version 0.11 of the RISC-V Debug Specification,
and DBUS registers, respectively. and DBUS registers, respectively.
@end deffn @end deffn
@deffn Command {riscv use_bscan_tunnel} value
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.
@end deffn
@subsection RISC-V Authentication Commands @subsection RISC-V Authentication Commands
The following commands can be used to authenticate to a RISC-V system. Eg. a The following commands can be used to authenticate to a RISC-V system. Eg. a

View File

@ -406,6 +406,10 @@ static void dump_field(int idle, const struct scan_field *field)
static void select_dmi(struct target *target) 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); 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 in_value[4];
uint8_t out_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); buf_set_u32(out_value, 0, 32, out);
jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE); 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, .out_value = out,
.in_value = in .in_value = in
}; };
uint8_t tunneled_dr_width;
struct scan_field tunneled_dr[4];
if (r->reset_delays_wait >= 0) { if (r->reset_delays_wait >= 0) {
r->reset_delays_wait--; 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_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out);
buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out); buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out);
/* 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. */ /* Assume dbus is already selected. */
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
}
int idle_count = info->dmi_busy_delay; int idle_count = info->dmi_busy_delay;
if (exec) if (exec)
@ -502,6 +547,11 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
return DMI_STATUS_FAILED; 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) if (data_in)
*data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); *data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);

View File

@ -170,6 +170,44 @@ struct scan_field select_idcode = {
.out_value = ir_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 { struct trigger {
uint64_t address; uint64_t address;
uint32_t length; uint32_t length;
@ -201,12 +239,101 @@ range_t *expose_custom;
static int riscv_resume_go_all_harts(struct target *target); 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) static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{ {
struct scan_field field; struct scan_field field;
uint8_t in_value[4]; uint8_t in_value[4];
uint8_t out_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); buf_set_u32(out_value, 0, 32, out);
jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE); 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_dbus.num_bits = target->tap->ir_length;
select_idcode.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); riscv_semihosting_init(target);
target->debug_reason = DBG_REASON_DBGRQ; 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[] = { static const struct command_registration riscv_exec_command_handlers[] = {
{ {
.name = "test_compliance", .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", .usage = "riscv set_ir_idcode [idcode|dtmcs|dmi] value",
.help = "Set IR value for specified JTAG register." .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 COMMAND_REGISTRATION_DONE
}; };

View File

@ -166,6 +166,16 @@ extern struct scan_field select_dbus;
extern uint8_t ir_idcode[4]; extern uint8_t ir_idcode[4];
extern struct scan_field select_idcode; 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 */ /*** OpenOCD Interface */
int riscv_openocd_poll(struct target *target); int riscv_openocd_poll(struct target *target);

View File

@ -0,0 +1,10 @@
#
# Be sure you include the speed and interface before this file
# Example:
# -c "adapter_khz 5000" -f "interface/ftdi/arty-onboard-ftdi.cfg" -f "board/sifive-e31arty-onboard-ftdi.cfg"
set _CHIPNAME arty
jtag newtap $_CHIPNAME bs -irlen 6 -expected-id 0x0362d093
echo "Ready for Remote Connections"

View File

@ -0,0 +1,25 @@
#
# Be sure you include the speed and interface before this file
# Example:
# -c "adapter_khz 5000" -f "interface/ftdi/arty-onboard-ftdi.cfg" -f "board/sifive-e31arty-onboard-ftdi.cfg"
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 6; # -expected-id 0x0362d093
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
riscv use_bscan_tunnel 5
# Uncomment if hardware has flash
# flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000
init
if {[ info exists pulse_srst]} {
ftdi_set_signal nSRST 0
ftdi_set_signal nSRST z
}
halt
# Uncomment if hardware has flash
# flash protect 0 64 last off
echo "Ready for Remote Connections"

View File

@ -0,0 +1,7 @@
interface ftdi
# ftdi_device_desc "Arty On-board FTDI interface"
ftdi_vid_pid 0x0403 0x6010
ftdi_channel 0
ftdi_layout_init 0x0088 0x008b
reset_config none