From 5190dd4cef6a8a71e592aa637f02e2b9cdbd80e3 Mon Sep 17 00:00:00 2001 From: Greg Savin <43152568+SiFiveGregS@users.noreply.github.com> Date: Tue, 23 Apr 2019 16:25:22 -0700 Subject: Support for driving RISC-V DM via Arty's own JTAG chain using BSCAN tunnel (#370) Including adjustments in response to review comments. --- doc/openocd.texi | 5 + src/target/riscv/riscv-013.c | 54 ++++++++- src/target/riscv/riscv.c | 159 ++++++++++++++++++++++++++ src/target/riscv/riscv.h | 10 ++ tcl/board/sifive-e31arty-onboard-ftdi-raw.cfg | 10 ++ tcl/board/sifive-e31arty-onboard-ftdi.cfg | 25 ++++ tcl/interface/ftdi/arty-onboard-ftdi.cfg | 7 ++ 7 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 tcl/board/sifive-e31arty-onboard-ftdi-raw.cfg create mode 100644 tcl/board/sifive-e31arty-onboard-ftdi.cfg create mode 100644 tcl/interface/ftdi/arty-onboard-ftdi.cfg diff --git a/doc/openocd.texi b/doc/openocd.texi index 9dfb04b..025e9e1 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -9502,6 +9502,11 @@ When utilizing version 0.11 of the RISC-V Debug Specification, and DBUS registers, respectively. @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 The following commands can be used to authenticate to a RISC-V system. Eg. a diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 86820df..27e20d7 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -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); diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 2fc3757..34854d0 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -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 }; diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index ade1bf0..0662efd 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -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); diff --git a/tcl/board/sifive-e31arty-onboard-ftdi-raw.cfg b/tcl/board/sifive-e31arty-onboard-ftdi-raw.cfg new file mode 100644 index 0000000..b7d1ee6 --- /dev/null +++ b/tcl/board/sifive-e31arty-onboard-ftdi-raw.cfg @@ -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" diff --git a/tcl/board/sifive-e31arty-onboard-ftdi.cfg b/tcl/board/sifive-e31arty-onboard-ftdi.cfg new file mode 100644 index 0000000..3d40cfa --- /dev/null +++ b/tcl/board/sifive-e31arty-onboard-ftdi.cfg @@ -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" diff --git a/tcl/interface/ftdi/arty-onboard-ftdi.cfg b/tcl/interface/ftdi/arty-onboard-ftdi.cfg new file mode 100644 index 0000000..825be7d --- /dev/null +++ b/tcl/interface/ftdi/arty-onboard-ftdi.cfg @@ -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 + -- cgit v1.1