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
parent
15f2d35bc6
commit
5190dd4cef
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
/* Assume dbus is already selected. */
|
/* I wanted to place this code in a different function, but the way JTAG command
|
||||||
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
|
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;
|
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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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"
|
|
@ -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"
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue