diff --git a/meson_options.txt b/meson_options.txt index 8608c8361b0..bf7165325c6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -33,6 +33,7 @@ option( 'apollo3', 'at32f4', 'ch32', + 'ch32v', 'ch579', 'efm', 'gd32', @@ -148,3 +149,8 @@ option( value: false, description: 'Enable firmware-side protocol acceleration of RISC-V Debug' ) +option( + 'rvswd_support', + type: 'boolean', + value: 'false' +) diff --git a/src/command.c b/src/command.c index 52a60c6c721..ac414c9f80a 100644 --- a/src/command.c +++ b/src/command.c @@ -56,6 +56,9 @@ static bool cmd_help(target_s *target, int argc, const char **argv); static bool cmd_jtag_scan(target_s *target, int argc, const char **argv); static bool cmd_swd_scan(target_s *target, int argc, const char **argv); +#if defined(ENABLE_RVSWD) && defined(PLATFORM_HAS_RVSWD) +static bool cmd_rvswd_scan(target_s *target, int argc, const char **argv); +#endif static bool cmd_auto_scan(target_s *target, int argc, const char **argv); static bool cmd_frequency(target_s *target, int argc, const char **argv); static bool cmd_targets(target_s *target, int argc, const char **argv); @@ -94,6 +97,9 @@ const command_s cmd_list[] = { {"jtag_scan", cmd_jtag_scan, "Scan JTAG chain for devices"}, {"swd_scan", cmd_swd_scan, "Scan SWD interface for devices: [TARGET_ID]"}, {"swdp_scan", cmd_swd_scan, "Deprecated: use swd_scan instead"}, +#if defined(ENABLE_RVSWD) && defined(PLATFORM_HAS_RVSWD) + {"rvswd_scan", cmd_rvswd_scan, "Scan RVSWD for devices"}, +#endif {"auto_scan", cmd_auto_scan, "Automatically scan all chain types for devices"}, {"frequency", cmd_frequency, "set minimum high and low times: [FREQ]"}, {"targets", cmd_targets, "Display list of available targets"}, @@ -316,6 +322,52 @@ bool cmd_swd_scan(target_s *target, int argc, const char **argv) return true; } +#if defined(ENABLE_RVSWD) && defined(PLATFORM_HAS_RVSWD) +bool cmd_rvswd_scan(target_s *target, int argc, const char **argv) +{ + (void)target; + (void)argc; + (void)argv; + + if (platform_target_voltage()) + gdb_outf("Target voltage: %s\n", platform_target_voltage()); + + if (connect_assert_nrst) + platform_nrst_set_val(true); /* will be deasserted after attach */ + + bool scan_result = false; + TRY (EXCEPTION_ALL) { +#if CONFIG_BMDA == 1 + scan_result = bmda_rvswd_scan(); +#else + scan_result = false; +#endif + } + CATCH () { + case EXCEPTION_TIMEOUT: + gdb_outf("Timeout during scan. Is target stuck in WFI?\n"); + break; + case EXCEPTION_ERROR: + gdb_outf("Exception: %s\n", exception_frame.msg); + break; + default: + break; + } + + if (!scan_result) { + platform_target_clk_output_enable(false); + platform_nrst_set_val(false); + gdb_out("RVSWD scan failed!\n"); + return false; + } + + cmd_targets(NULL, 0, NULL); + platform_target_clk_output_enable(false); + morse(NULL, false); + return true; +} +#endif + bool cmd_auto_scan(target_s *target, int argc, const char **argv) { (void)target; @@ -342,8 +394,18 @@ bool cmd_auto_scan(target_s *target, int argc, const char **argv) #else scan_result = adiv5_swd_scan(0); #endif - if (!scan_result) + if (!scan_result) { gdb_out("SWD scan found no devices.\n"); +#if defined(ENABLE_RVSWD) && defined(PLATFORM_HAS_RVSWD) +#if CONFIG_BMDA == 1 + scan_result = bmda_rvswd_scan(); +#else + scan_result = false; +#endif + if (!scan_result) + gdb_out("RVSWD scan found no devices.\n"); +#endif + } } } CATCH () { diff --git a/src/include/target.h b/src/include/target.h index 182c745ce35..ce4ea69e345 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -45,6 +45,9 @@ typedef struct target_controller target_controller_s; #if CONFIG_BMDA == 1 bool bmda_swd_scan(uint32_t targetid); bool bmda_jtag_scan(void); +#if defined(ENABLE_RVSWD) && defined(PLATFORM_HAS_RVSWD) +bool bmda_rvswd_scan(void); +#endif #endif bool adiv5_swd_scan(uint32_t targetid); bool jtag_scan(void); diff --git a/src/meson.build b/src/meson.build index 6ebf83ea501..88c54270600 100644 --- a/src/meson.build +++ b/src/meson.build @@ -79,6 +79,13 @@ if rtt_support endif endif +# RVSWD support handling +rvswd_support = get_option('rvswd_support') +if rtt_support + libbmd_core_args += ['-DENABLE_RVSWD=1'] + bmd_core_args += ['-DENABLE_RVSWD=1'] +endif + # Advertise QStartNoAckMode advertise_noackmode = get_option('advertise_noackmode') if advertise_noackmode @@ -117,6 +124,7 @@ summary( { 'Debug output': debug_output, 'RTT support': rtt_support, + 'RVSWD support': rvswd_support, 'Advertise QStartNoAckMode': advertise_noackmode, }, bool_yn: true, diff --git a/src/platforms/hosted/bmp_libusb.c b/src/platforms/hosted/bmp_libusb.c index c0d060cbb13..9b5237d9e5f 100644 --- a/src/platforms/hosted/bmp_libusb.c +++ b/src/platforms/hosted/bmp_libusb.c @@ -76,6 +76,7 @@ static const debugger_device_s debugger_devices[] = { {VENDOR_ID_STLINK, PRODUCT_ID_STLINKV3, PROBE_TYPE_STLINK_V2, NULL, "ST-Link v3"}, {VENDOR_ID_STLINK, PRODUCT_ID_STLINKV3E, PROBE_TYPE_STLINK_V2, NULL, "ST-Link v3E"}, {VENDOR_ID_SEGGER, PRODUCT_ID_ANY, PROBE_TYPE_JLINK, NULL, "Segger J-Link"}, + {VENDOR_ID_WCH, PRODUCT_ID_WCHLINK_RV, PROBE_TYPE_WCHLINK, NULL, "WCH-Link"}, {VENDOR_ID_FTDI, PRODUCT_ID_FTDI_FT2232, PROBE_TYPE_FTDI, NULL, "FTDI FT2232"}, {VENDOR_ID_FTDI, PRODUCT_ID_FTDI_FT4232, PROBE_TYPE_FTDI, NULL, "FTDI FT4232"}, {VENDOR_ID_FTDI, PRODUCT_ID_FTDI_FT232, PROBE_TYPE_FTDI, NULL, "FTDI FT232"}, @@ -118,7 +119,7 @@ const debugger_device_s *get_debugger_device_from_vid_pid(const uint16_t probe_v void bmp_ident(bmda_probe_s *info) { DEBUG_INFO("Black Magic Debug App " FIRMWARE_VERSION "\n for Black Magic Probe, ST-Link v2 and v3, CMSIS-DAP, " - "J-Link and FTDI (MPSSE)\n"); + "J-Link, FTDI (MPSSE) and WCH-Link\n"); if (info && info->vid && info->pid) { DEBUG_INFO("Using %04x:%04x %s %s\n %s %s\n", info->vid, info->pid, (info->serial[0]) ? info->serial : NO_SERIAL_NUMBER, info->manufacturer, info->product, info->version); diff --git a/src/platforms/hosted/meson.build b/src/platforms/hosted/meson.build index 4b679c2c217..13c0d511113 100644 --- a/src/platforms/hosted/meson.build +++ b/src/platforms/hosted/meson.build @@ -54,6 +54,9 @@ bmda_sources = files( 'jlink.c', 'jlink_jtag.c', 'jlink_swd.c', + 'wchlink.c', + 'wchlink_rvswd.c', + 'wchlink_riscv_dtm.c', ) subdir('remote') diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index ad508cd58e5..a6f90ee4251 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -51,6 +51,7 @@ #include "stlinkv2.h" #include "ftdi_bmp.h" #include "jlink.h" +#include "wchlink.h" #include "cmsis_dap.h" #endif @@ -163,6 +164,11 @@ void platform_init(int argc, char **argv) if (!jlink_init()) exit(1); break; + + case PROBE_TYPE_WCHLINK: + if (!wchlink_init()) + exit(-1); + break; #endif #ifdef ENABLE_GPIOD @@ -309,6 +315,21 @@ bool bmda_jtag_init(void) } } +bool bmda_rvswd_scan() +{ + bmda_probe_info.is_jtag = false; + + switch (bmda_probe_info.type) { +#if HOSTED_BMP_ONLY == 0 + case PROBE_TYPE_WCHLINK: + return wchlink_rvswd_scan(); +#endif + + default: + return false; + } +} + void bmda_adiv5_dp_init(adiv5_debug_port_s *const dp) { switch (bmda_probe_info.type) { @@ -411,6 +432,9 @@ char *bmda_adaptor_ident(void) case PROBE_TYPE_JLINK: return "J-Link"; + case PROBE_TYPE_WCHLINK: + return "WCH-Link"; + case PROBE_TYPE_GPIOD: return "GPIOD"; diff --git a/src/platforms/hosted/platform.h b/src/platforms/hosted/platform.h index 3747ef1169f..e220166dffb 100644 --- a/src/platforms/hosted/platform.h +++ b/src/platforms/hosted/platform.h @@ -48,6 +48,7 @@ void platform_buffer_flush(void); do { \ } while (0) #define PLATFORM_HAS_POWER_SWITCH +#define PLATFORM_HAS_RVSWD #define PRODUCT_ID_ANY 0xffffU @@ -77,6 +78,11 @@ void platform_buffer_flush(void); #define VENDOR_ID_ORBCODE 0x1209U #define PRODUCT_ID_ORBTRACE 0x3443U +#define VENDOR_ID_WCH 0x1a86U +#define PRODUCT_ID_WCHLINK_RV 0x8010U /* WCH-Link and WCH-LinkE in mode RV */ +#define PRODUCT_ID_WCHLINK_DAP 0x8011U /* WCH-Link in mode DAP */ +#define PRODUCT_ID_WCHLINKE_DAP 0x8012U /* WCH-LinkE in mode DAP */ + typedef enum probe_type { PROBE_TYPE_NONE = 0, PROBE_TYPE_BMP, @@ -84,6 +90,7 @@ typedef enum probe_type { PROBE_TYPE_FTDI, PROBE_TYPE_CMSIS_DAP, PROBE_TYPE_JLINK, + PROBE_TYPE_WCHLINK, PROBE_TYPE_GPIOD, } probe_type_e; diff --git a/src/platforms/hosted/wchlink.c b/src/platforms/hosted/wchlink.c new file mode 100644 index 00000000000..78c58c9126d --- /dev/null +++ b/src/platforms/hosted/wchlink.c @@ -0,0 +1,387 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "wchlink.h" +#include "wchlink_protocol.h" +#include "buffer_utils.h" + +typedef struct wchlink { + struct wchlink_fw_version { + uint8_t major; + uint8_t minor; + } fw_version; /* Firmware version */ + + uint8_t hw_type; /* Hardware type */ + + uint8_t riscvchip; /* The attached RISC-V chip code */ +} wchlink_s; + +static wchlink_s wchlink; + +/* WCH-Link USB protocol functions */ + +static char *wchlink_command_error(const uint8_t command, const uint8_t subcommand, const uint8_t error) +{ + /* Only this error is formally known, so we hack it's identification a bit for now */ + if (command == WCH_CMD_CONTROL && subcommand == WCH_CONTROL_SUBCMD_ATTACH && error == WCH_ERR_ATTACH) + return "Failed to attach to target"; + return "Unknown"; +} + +/* + * Send a command to the WCH-Link. + * + * ┌─────────────┬────────┬─────────┬──────────────┬──────────────────────────────┐ + * │ Byte │ 0 │ 1 │ 2 │ 3:End │ + * ├─────────────┼────────┼─────────┼──────────────┼──────────────────────────────┤ + * │ Description │ Header │ Command │ Payload Size │ Payload (Sub-command + Data) │ + * └─────────────┴────────┴─────────┴──────────────┴──────────────────────────────┘ + * See wchlink_protocol.h for more information. + * + * Returns true for success, false for failure. + */ +bool wchlink_command_send_recv(const uint8_t command, const uint8_t subcommand, const void *const payload, + const size_t payload_length, void *const response, const size_t response_length) +{ + /* + * Total request size is packet header + command + payload size + payload (for which we always add the subcommand byte) + * Total response size is packet header + command/error + payload size + payload + */ + const size_t request_size = 4U + payload_length; + const size_t response_size = 3U + response_length; + + /* Stack buffer for the transfer, this is much larger than we need */ + uint8_t buffer[256U] = {0}; + if (request_size > sizeof(buffer) || response_size > sizeof(buffer)) + return false; + + /* Prepare the command packet */ + buffer[WCH_CMD_PACKET_HEADER_OFFSET] = WCH_CMD_PACKET_HEADER_OUT; /* Command packet header */ + buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET] = command; /* Command */ + buffer[WCH_CMD_PACKET_SIZE_OFFSET] = payload_length + 1U; /* Payload size */ + buffer[WCH_CMD_PACKET_PAYLOAD_OFFSET] = subcommand; /* Subcommand as the first byte of the payload */ + + /* Copy in the payload if any */ + if (payload_length && payload) + memcpy(buffer + WCH_CMD_PACKET_PAYLOAD_OFFSET + 1U, payload, payload_length); + + /* Send the command and receive the response */ + if (bmda_usb_transfer(bmda_probe_info.usb_link, buffer, request_size, buffer, response_size, WCH_USB_TIMEOUT) < 0) + return false; + + /* Check the response */ + if (buffer[WCH_CMD_PACKET_HEADER_OFFSET] != WCH_CMD_PACKET_HEADER_IN) { + DEBUG_ERROR("wchlink protocol error: malformed response\n"); + return false; + } + if (buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET] != command) { + DEBUG_ERROR("wchlink protocol error: 0x%02x - %s\n", buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET], + wchlink_command_error(command, subcommand, buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET])); + return false; + } + if (buffer[WCH_CMD_PACKET_SIZE_OFFSET] != response_length) { + DEBUG_ERROR("wchlink protocol error: response payload size mismatch\n"); + return false; + } + + /* Copy the response payload if requested */ + if (response_length && response) + memcpy(response, buffer + WCH_CMD_PACKET_PAYLOAD_OFFSET, response_length); + + return true; +} + +/* + * Do a DMI transfer. + * + * ┌────────────────────────────┐ + * │ Payload │ + * ├─────────┬──────┬───────────┤ + * │ 0 │ 1:4 │ 5 │ + * ├─────────┼──────┼───────────┤ + * │ Address │ Data │ Operation │ + * └─────────┴──────┴───────────┘ + * ┌────────────────────────────┐ + * │ Response payload │ + * ├─────────┬──────┬───────────┤ + * │ 0 │ 1:4 │ 5 │ + * ├─────────┼──────┼───────────┤ + * │ Address │ Data │ Status │ + * └─────────┴──────┴───────────┘ + * See wchlink_protocol.h for more information. + * + * Returns true for success, false for failure. + */ +bool wchlink_transfer_dmi(const uint8_t operation, const uint32_t address, const uint32_t data_in, + uint32_t *const data_out, uint8_t *const status) +{ + /* The DMI register address must be a 7 or 8-bit address */ + if (address & ~0xffU) { + DEBUG_ERROR("wchlink protocol error: DMI address 0x%08" PRIx32 " is out of range\n", address); + return false; + } + + /* Stack buffer for the transfer */ + uint8_t buffer[9U] = {0}; + + /* Prepare the command packet */ + buffer[WCH_CMD_PACKET_HEADER_OFFSET] = WCH_CMD_PACKET_HEADER_OUT; /* Command packet header */ + buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET] = WCH_CMD_DMI; /* Command */ + buffer[WCH_CMD_PACKET_SIZE_OFFSET] = 6U; /* Payload size */ + + /* Construct the payload */ + buffer[WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_ADDR_OFFSET] = address & 0xffU; /* Address */ + write_be4(buffer, WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_DATA_OFFSET, data_in); /* Data */ + buffer[WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_OP_STATUS_OFFSET] = operation; /* Operation */ + + /* Send the command and receive the response */ + if (bmda_usb_transfer(bmda_probe_info.usb_link, buffer, sizeof(buffer), buffer, sizeof(buffer), WCH_USB_TIMEOUT) < + 0) + return false; + + /* Check the response */ + if (buffer[WCH_CMD_PACKET_HEADER_OFFSET] != WCH_CMD_PACKET_HEADER_IN) { + DEBUG_ERROR("wchlink protocol error: malformed response\n"); + return false; + } + if (buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET] != WCH_CMD_DMI) { + DEBUG_ERROR("wchlink protocol error: 0x%02x - %s\n", buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET], + wchlink_command_error(WCH_CMD_DMI, 0, buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET])); + return false; + } + if (buffer[WCH_CMD_PACKET_SIZE_OFFSET] != 6U) { + DEBUG_ERROR("wchlink protocol error: response payload size mismatch\n"); + return false; + } + + /* Copy over the result */ + if (data_out) + *data_out = read_be4(buffer, WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_DATA_OFFSET); + if (status) + *status = buffer[WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_OP_STATUS_OFFSET]; + + return true; +} + +/* + * Try to claim the debugging interface of a WCH-Link. + * On success this copies the command endpoint addresses identified into the + * usb_link_s sub-structure of bmda_probe_s (bmda_probe_info.usb_link) for later use. + * Returns true for success, false for failure. + */ +static bool wchlink_claim_interface(void) +{ + libusb_config_descriptor_s *config = NULL; + const int result = libusb_get_active_config_descriptor(bmda_probe_info.libusb_dev, &config); + if (result != LIBUSB_SUCCESS) { + DEBUG_ERROR("Failed to get configuration descriptor: %s\n", libusb_error_name(result)); + return false; + } + const libusb_interface_descriptor_s *descriptor = NULL; + for (size_t idx = 0; idx < config->bNumInterfaces; ++idx) { + const libusb_interface_s *const interface = &config->interface[idx]; + const libusb_interface_descriptor_s *const interface_desc = &interface->altsetting[0]; + if (interface_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + interface_desc->bInterfaceSubClass == WCH_USB_INTERFACE_SUBCLASS && interface_desc->bNumEndpoints > 1U) { + const int claim_result = libusb_claim_interface(bmda_probe_info.usb_link->device_handle, (int)idx); + if (claim_result) { + DEBUG_ERROR("Can not claim handle: %s\n", libusb_error_name(claim_result)); + return false; + } + bmda_probe_info.usb_link->interface = idx; + descriptor = interface_desc; + break; + } + } + if (!descriptor) { + DEBUG_ERROR("No suitable interface found\n"); + libusb_free_config_descriptor(config); + return false; + } + for (size_t i = 0; i < descriptor->bNumEndpoints; i++) { + const libusb_endpoint_descriptor_s *endpoint = &descriptor->endpoint[i]; + if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK) == WCH_USB_MODE_RV_CMD_EPT_ADDR) { + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + bmda_probe_info.usb_link->ep_rx = endpoint->bEndpointAddress; + else + bmda_probe_info.usb_link->ep_tx = endpoint->bEndpointAddress; + } + } + libusb_free_config_descriptor(config); + return true; +} + +/* WCH-Link command functions */ + +static char *wchlink_hw_type_to_string(const uint8_t hardware_id) +{ + switch (hardware_id) { + case WCH_HARDWARE_TYPE_WCHLINK: + return "WCH-Link (CH549)"; + case WCH_HARDWARE_TYPE_WCHLINKE2: + case WCH_HARDWARE_TYPE_WCHLINKE: + return "WCH-LinkE (CH32V305)"; + case WCH_HARDWARE_TYPE_WCHLINKS: + return "WCH-LinkS (CH32V203)"; + case WCH_HARDWARE_TYPE_WCHLINKB: + return "WCH-LinkB"; + case WCH_HARDWARE_TYPE_WCHLINKW: + return "WCH-LinkW (CH32V208)"; + default: + return "Unknown"; + } +} + +static char *wchlink_riscvchip_to_string(const uint8_t hardware_id) +{ + switch (hardware_id) { + case WCH_RISCVCHIP_CH32V103: + return "CH32V103 RISC-V3A series"; + case WCH_RISCVCHIP_CH57X: + return "CH571/CH573 RISC-V3A BLE 4.2 series"; + case WCH_RISCVCHIP_CH56X: + return "CH565/CH569 RISC-V3A series"; + case WCH_RISCVCHIP_CH32V20X: + return "CH32V20X RISC-V4B/V4C series"; + case WCH_RISCVCHIP_CH32V30X: + return "CH32V30X RISC-V4C/V4F series"; + case WCH_RISCVCHIP_CH58X: + return "CH581/CH582/CH583 RISC-V4A BLE 5.3 series"; + case WCH_RISCVCHIP_CH32V003: + return "CH32V003 RISC-V2A series"; + case WCH_RISCVCHIP_CH59X: + return "CH59x RISC-V4C BLE 5.4 series"; + case WCH_RISCVCHIP_CH32X035: + return "CH32X035 RISC-V4C series"; + default: + return "Unknown"; + } +} + +static bool wchlink_get_version(void) +{ + uint8_t response[4U]; + if (!wchlink_command_send_recv( + WCH_CMD_CONTROL, WCH_CONTROL_SUBCMD_GET_PROBE_INFO, NULL, 0, response, sizeof(response))) + return false; + + wchlink.fw_version.major = response[WCH_VERSION_MAJOR_OFFSET]; + wchlink.fw_version.minor = response[WCH_VERSION_MINOR_OFFSET]; + DEBUG_INFO("Firmware version: v%" PRIu32 ".%" PRIu32 "\n", wchlink.fw_version.major, wchlink.fw_version.minor); + + const uint8_t hardware_type = response[WCH_HARDWARE_TYPE_OFFSET]; + DEBUG_INFO("Hardware type: %s\n", wchlink_hw_type_to_string(hardware_type)); + + /* Build version string onto info struct for version command */ + snprintf(bmda_probe_info.version, sizeof(bmda_probe_info.version), "%s v%" PRIu32 ".%" PRIu32, + wchlink_hw_type_to_string(hardware_type), wchlink.fw_version.major, wchlink.fw_version.minor); + + return true; +} + +/* + * This function is called when the WCH-Link attaches to certain types of RISC-V chip + * It is unknown what this function does, but the official WCH-Link software calls it + * + * Removing this function still allows the WCH-Link to work and the scan is successful + * but it is unknown if it might required for some chips or states + */ +static bool wchlink_after_attach_unknown() +{ + DEBUG_INFO("Sending unknown WCH-Link command after attach\n"); + + /* Response seems to be WCH_CONTROL_SUBCMD_UNKNOWN, but without knowing what the command does we won't check it blindly */ + uint8_t response = 0; + return wchlink_command_send_recv(WCH_CMD_CONTROL, WCH_CONTROL_SUBCMD_UNKNOWN, NULL, 0, &response, sizeof(response)); +} + +/* WCH-Link attach routine, attempts to detect and attach to a connected RISC-V chip */ +bool wchlink_attach() +{ + uint8_t response[5U]; + if (!wchlink_command_send_recv(WCH_CMD_CONTROL, WCH_CONTROL_SUBCMD_ATTACH, NULL, 0, response, sizeof(response))) + return false; + + wchlink.riscvchip = response[WCH_RISCVCHIP_OFFSET]; + const uint32_t idcode = read_be4(response, WCH_IDCODDE_OFFSET); + + DEBUG_INFO("WCH-Link attached to RISC-V chip: %s\n", wchlink_riscvchip_to_string(wchlink.riscvchip)); + DEBUG_INFO("ID code: 0x%08" PRIx32 "\n", idcode); + + /* Some RISC-V chips require* an additional command to be sent after attach */ + switch (wchlink.riscvchip) { + case WCH_RISCVCHIP_CH32V103: + case WCH_RISCVCHIP_CH32V20X: + case WCH_RISCVCHIP_CH32V30X: + case WCH_RISCVCHIP_CH32V003: + if (!wchlink_after_attach_unknown()) + return false; + break; + default: + break; + } + + return true; +} + +bool wchlink_init(void) +{ + usb_link_s *link = calloc(1U, sizeof(usb_link_s)); + if (!link) + return false; + bmda_probe_info.usb_link = link; + link->context = bmda_probe_info.libusb_ctx; + const int result = libusb_open(bmda_probe_info.libusb_dev, &link->device_handle); + if (result != LIBUSB_SUCCESS) { + DEBUG_ERROR("libusb_open() failed (%d): %s\n", result, libusb_error_name(result)); + return false; + } + if (!wchlink_claim_interface()) { + libusb_close(bmda_probe_info.usb_link->device_handle); + return false; + } + if (!link->ep_tx || !link->ep_rx) { + DEBUG_ERROR("Device setup failed\n"); + libusb_release_interface(bmda_probe_info.usb_link->device_handle, bmda_probe_info.usb_link->interface); + libusb_close(bmda_probe_info.usb_link->device_handle); + return false; + } + if (!wchlink_get_version()) { + DEBUG_ERROR("Failed to read WCH-Link information\n"); + libusb_release_interface(bmda_probe_info.usb_link->device_handle, bmda_probe_info.usb_link->interface); + libusb_close(bmda_probe_info.usb_link->device_handle); + return false; + } + return true; +} diff --git a/src/platforms/hosted/wchlink.h b/src/platforms/hosted/wchlink.h new file mode 100644 index 00000000000..8e6a283dca2 --- /dev/null +++ b/src/platforms/hosted/wchlink.h @@ -0,0 +1,42 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PLATFORMS_HOSTED_WCHLINK_H +#define PLATFORMS_HOSTED_WCHLINK_H + +#include "bmp_hosted.h" + +bool wchlink_init(void); +bool wchlink_rvswd_scan(void); +void wchlink_riscv_dtm_handler(void); + +#endif /* PLATFORMS_HOSTED_WCHLINK_H */ diff --git a/src/platforms/hosted/wchlink_protocol.h b/src/platforms/hosted/wchlink_protocol.h new file mode 100644 index 00000000000..4de7b70695d --- /dev/null +++ b/src/platforms/hosted/wchlink_protocol.h @@ -0,0 +1,324 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H +#define PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H + +#include +#include +#include + +/* + * This file contains the definitions for the WCH-Link USB Protocol, no public documentation is available + * so these definitions are the result of reverse engineering the protocol and trial and error. + * + * !!! THIS IS LARGELY INCOMPLETE AND UNTESTED, DO NOT TAKE THIS AS A DEFINITIVE SOURCE OF INFORMATION !!! + * + * The WCH-Link has two modes of operation, DAPLink and RV (i.e. RISC-V). + * This refers to the RV mode of operation only, changing the mode of operation is also out of scope. + * This was based on probes with firmware v2.5 and v2.8, differences are expected on untested/future versions. + * + * Overview + * + * WCH-Link uses USB Bulk Transfers to communicate with the host + * + * The WCH-Link exposes 4 endpoints through a Vendor interface: + * 0x82: EP 2 IN (Raw data) + * 0x02: EP 2 OUT (Raw data) + * 0x81: EP 1 IN (Command packets) + * 0x01: EP 1 OUT (Command packets) + * EP 1 IN/OUT is used for most of the communication, EP 2 IN/OUT is used for some flash related operations. + * + * Command packet format: + * ┌─────────────┬────────┬─────────┬──────────────┬──────────────────────────────┐ + * │ Byte │ 0 │ 1 │ 2 │ 3:End │ + * ├─────────────┼────────┼─────────┼──────────────┼──────────────────────────────┤ + * │ Description │ Header │ Command │ Payload Size │ Payload (Sub-command + Data) │ + * └─────────────┴────────┴─────────┴──────────────┴──────────────────────────────┘ + * + * Header: + * - 0x81 for host command packets + * - 0x82 for device response packets + * + * Command: command, used to identify how the payload will be interpreted. + * Payload Size: length in bytes of the remaining command data. + * Payload: command data, interpreted according to the command, most command have a subcommand as the 1st byte. + * + * Responses are sent in the same format, with the header set to 0x82 and the same command. + * In case of an error, the response will contain the error value instead of the requested command in the command field. + */ + +/* USB protocol */ +#define WCH_USB_MODE_RV_CMD_EPT_ADDR 0x1U +#define WCH_USB_MODE_RV_RAW_EPT_ADDR 0x2U +#define WCH_USB_MODE_DAP_OUT_EPT_ADDR 0x2U +#define WCH_USB_MODE_DAP_IN_EPT_ADDR 0x3U + +#define WCH_USB_TIMEOUT 5000U + +#define WCH_USB_INTERFACE_SUBCLASS 0x80U + +/* Command packet */ +#define WCH_CMD_PACKET_HEADER_OFFSET 0U +#define WCH_CMD_PACKET_HEADER_OUT 0x81U +#define WCH_CMD_PACKET_HEADER_IN 0x82U +#define WCH_CMD_PACKET_CMD_ERROR_OFFSET 1U +#define WCH_CMD_PACKET_SIZE_OFFSET 2U +#define WCH_CMD_PACKET_PAYLOAD_OFFSET 3U + +/* Error */ +#define WCH_ERR_ATTACH 0x55U /* Failed to attach to target */ + +/* RISC-V targets AKA "riscvchip" */ +#define WCH_RISCVCHIP_CH32V103 0x01U /* CH32V103 RISC-V3A series */ +#define WCH_RISCVCHIP_CH57X 0x02U /* CH571/CH573 RISC-V3A BLE 4.2 series */ +#define WCH_RISCVCHIP_CH56X 0x03U /* CH565/CH569 RISC-V3A series */ +#define WCH_RISCVCHIP_CH32V20X 0x05U /* CH32V20X RISC-V4B/V4C series */ +#define WCH_RISCVCHIP_CH32V30X 0x06U /* CH32V30X RISC-V4C/V4F series */ +#define WCH_RISCVCHIP_CH58X 0x07U /* CH581/CH582/CH583 RISC-V4A BLE 5.3 series */ +#define WCH_RISCVCHIP_CH32V003 0x09U /* CH32V003 RISC-V2A series */ +#define WCH_RISCVCHIP_CH59X 0x0bU /* CH59x RISC-V4C BLE 5.4 series */ +#define WCH_RISCVCHIP_CH32X035 0x0dU /* CH32X035 RISC-V4C series */ + +/* Commands */ +#define WCH_CMD_ADDR_N_SIZE 0x01U /* Set address and size command */ +#define WCH_CMD_FLASH 0x02U /* Flash command */ +#define WCH_CMD_READ_MEM 0x03U /* Memory read command */ +#define WCH_CMD_PROTECT 0x06U /* Flash potection command */ +#define WCH_CMD_DMI 0x08U /* DMI transfer command */ +#define WCH_CMD_RESET 0x0bU /* Reset command */ +#define WCH_CMD_PROBE_CONTROL 0x0cU /* Probe control command */ +#define WCH_CMD_CONTROL 0x0dU /* Control command */ +#define WCH_CMD_RV_DIS_DBG 0x0eU /* RV disable debug command */ +#define WCH_CMD_VERIFY 0x0fU /* Verify command */ +#define WCH_CMD_UID 0x11U /* Chip UID command */ +#define WCH_CMD_MODDE_SWITCH 0xffU /* Switch probe mode command */ + +/* + * Set address and size command - WCH_CMD_ADDR_N_SIZE + * + * This command does not have a sub-command byte, + * the payload is the address followed by the size + * + * ┌──────┬──────┐ + * │ 0:4 │ 5:8 │ + * ├──────┼──────┤ + * │ ADDR │ SIZE │ + * └──────┴──────┘ + */ + +/* Flash command - WCH_CMD_FLASH */ +#define WCH_FLASH_SUBCMD_CHIPERASE 0x01U /* Chip erase */ +#define WCH_FLASH_SUBCMD_BEGIN_WRITE_FLASH 0x02U /* Begin write flash - ?? */ +#define WCH_FLASH_SUBCMD_EXEC_RAM 0x03U /* Execute ram - ?? */ +#define WCH_FLASH_SUBCMD_BEGIN_WRITE_MEM 0x05U /* Begin transfer - ?? */ +#define WCH_FLASH_SUBCMD_PREPARE 0x06U /* ?? */ +#define WCH_FLASH_SUBCMD_EXEC_MEM 0x07U /* ?? */ +#define WCH_FLASH_SUBCMD_TERMINATE 0x08U /* ?? */ +#define WCH_FLASH_SUBCMD_READY_WRITE 0x09U /* End transfer - ?? */ +#define WCH_FLASH_SUBCMD_VERIFY2 0x0aU /* Verify - ?? */ +#define WCH_FLASH_SUBCMD_RV_VERIFY 0x0bU /* Verify - ?? */ +#define WCH_FLASH_SUBCMD_BEGIN_READ_MEM 0x0cU /* ?? */ + +/* + * Memory read commands - WCH_CMD_READ_MEM + * + * This command does not have a sub-command byte, + * the payload is the address to read from followed by the number of bytes to read + * + * ┌──────┬────────┐ + * │ 0:4 │ 5:8 │ + * ├──────┼────────┤ + * │ ADDR │ LENGTH │ + * └──────┴────────┘ + */ + +/* + * Flash potection command - WCH_CMD_PROTECT + * + * Not supported on riscvchip: + * - 0x01: CH32V103 + * - 0x09: CH32V003 + * - 0x05: CH32V20X + * - 0x06: CH32V30X + * - 0x0d: CH32X035 + */ +#define WCH_PROTECT_SUBCMD_CHECK 0x01U /* Check if flash is read-protected */ +#define WCH_PROTECT_SUBCMD_FLASH_UNPROTECT 0x02U /* Set flash read unprotected */ +#define WCH_PROTECT_SUBCMD_FLASH_PROTECT 0x03U /* Set flash read protected */ +#define WCH_PROTECT_SUBCMD_CHECK_V2 0x04U /* Check if flash is read-protected - Firmware >= v2.9 */ +/* PROTECT_V2 and UNPROTECT_V2 require `0xbf ff ff ff ff ff ff` as payload */ +#define WCH_PROTECT_SUBCMD_FLASH_UNPROTECT_V2 0xf2U /* Set flash read unprotected - Firmware >= v2.9 */ +#define WCH_PROTECT_SUBCMD_FLASH_PROTECT_V2 0xf3U /* Set flash read protected - Firmware >= v2.9 */ + +#define WCH_PROTECTED 0x01U /* Protected, memory read returns random data */ +#define WCH_UNPROTECTED 0x02U /* Unprotected */ +#define WCH_PROTECTED_V2 0x01U /* Protected, memory read returns random data */ +#define WCH_UNPROTECTED_V2 0x00U /* Unprotected */ + +/* + * DMI transfer command - WCH_CMD_DMI + * + * This command does not have a sub-command byte + * + * ┌────────────────────────────┐ + * │ Payload │ + * ├─────────┬──────┬───────────┤ + * │ 0 │ 1:4 │ 5 │ + * ├─────────┼──────┼───────────┤ + * │ Address │ Data │ Operation │ + * └─────────┴──────┴───────────┘ + * ┌────────────────────────────┐ + * │ Response payload │ + * ├─────────┬──────┬───────────┤ + * │ 0 │ 1:4 │ 5 │ + * ├─────────┼──────┼───────────┤ + * │ Address │ Data │ Status │ + * └─────────┴──────┴───────────┘ + * + * Operation and Status correspond to the same values + * found in the JTAG implementation of RISC-V DMI: + * + * Operation: + * - 0x00: no-op + * - 0x01: read + * - 0x02: write + * + * Status: + * - 0x00: success + * - 0x01: error + * - 0x03: busy + */ +#define WCH_DMI_ADDR_OFFSET 0U +#define WCH_DMI_DATA_OFFSET 1U +#define WCH_DMI_OP_STATUS_OFFSET 5U + +/* Reset command - WCH_CMD_RESET */ +#define WCH_RESET_SUBCMD_RELEASE 0x01U /* Release reset (after 300ms delay) */ +/* + * There are two _SUBCMD_ASSERT sub-commands, used depending on the riscvchip: + * + * ASSERT2 used for riscvchip: + * - 0x02: CH57X + * - 0x07: CH58X + * - 0x0b: CH59X + */ +#define WCH_RESET_SUBCMD_ASSERT 0x03U /* Reset */ +#define WCH_RESET_SUBCMD_ASSERT2 0x02U /* Reset */ + +/* + * Probe constrol command - WCH_CMD_PROBE_CONTROL + * + * This command does not have a sub-command byte, + * the payload is the the riscvchip number followed by the speed + * + * ┌───────────┬───────┐ + * │ 0 │ 1 │ + * ├───────────┼───────┤ + * │ RISCVCHIP │ Speed │ + * └───────────┴───────┘ + * + * Response is one byte, 0x01 meaning success + */ +#define WCH_SPEED_LOW 0x03U +#define WCH_SPEED_MEDIUM 0x02U +#define WCH_SPEED_HIGH 0x01U +#define WCH_SPEED_VERYHIGH 0x00U + +#define WCH_PROBE_CONTROL_OK 0x01U /* Success response */ + +/* Control command - WCH_CMD_CONTROL */ +#define WCH_CONTROL_SUBCMD_GET_PROBE_INFO 0x01U /* Firmware version and hardware type */ +#define WCH_CONTROL_SUBCMD_ATTACH 0x02U /* Attach to target */ +/* + * On some riscvchip targets, a _SUBCMD_UNKNOWN is issued after attach + * + * Issued on riscvchip: + * - 0x01: CH32V103 + * - 0x05: CH32V20X + * - 0x06: CH32V30X + * - 0x09: CH32V003 + */ +#define WCH_CONTROL_SUBCMD_UNKNOWN 0x03U /* ?? - issued after attach */ +/* + * _GET_MEMORY_INFO + * + * Supported on riscvchip: + * - 0x05: CH32V20X + * - 0x06: CH32V30X + */ +#define WCH_CONTROL_SUBCMD_GET_MEMORY_INFO 0x04U /* RAM size, flash size and addr */ +#define WCH_CONTROL_SUBCMD_CLOSE 0xffU /* Terminate connection (unsure what this entails) */ + +/* Probe info subcommand - WCH_SYS_SUBCMD_GET_PROBE_INFO */ +#define WCH_VERSION_MAJOR_OFFSET 0U /* 8 bit */ +#define WCH_VERSION_MINOR_OFFSET 1U /* 8 bit */ + +#define WCH_HARDWARE_TYPE_OFFSET 2U +#define WCH_HARDWARE_TYPE_WCHLINK 1U /* WCH-Link (CH549) *does not support SDIO (single wire debug) */ +#define WCH_HARDWARE_TYPE_WCHLINKE 2U /* WCH-LinkE (CH32V305) */ +#define WCH_HARDWARE_TYPE_WCHLINKS 3U /* WCH-LinkS (CH32V203) */ +#define WCH_HARDWARE_TYPE_WCHLINKB 4U /* WCH-LinkB */ +#define WCH_HARDWARE_TYPE_WCHLINKW 5U /* WCH-LinkW (CH32V208) *wireless */ +#define WCH_HARDWARE_TYPE_WCHLINKE2 18U /* WCH-LinkE (CH32V305) - ?? */ + +/* Attach to target subcommand - WCH_CONTROL_SUBCMD_ATTACH */ +#define WCH_RISCVCHIP_OFFSET 0U /* 8 bit */ +#define WCH_IDCODDE_OFFSET 1U /* 32 bit */ + +/* + * RV disable debug command - WCH_CMD_RV_DIS_DBG + * + * Supported on riscvchip: + * - 0x02: CH57X + * - 0x03: CH56X + * - 0x07: CH58X + * - 0x0b: CH59X + */ +#define WCH_RV_DIS_DBG_SUBCMD_DISABLE 0x01U /* Disable debug */ + +/* + * Verify command - WCH_CMD_VERIFY + * FIXME: TBD + */ + +/* + * Chip UID command - WCH_CMD_UID + * + * The reply does not use the standard format. + * + * Raw response: ffff00 20 aeb4abcd 16c6bc45 e339e339e339e339 + * Corresponding UID: 0xcdabb4ae45bcc616 + * Unknown value: e339e339e339e339 -> inital value for erased flash + */ +#define WCH_UID_SUBCMD_GET 0x09U /* Get chip UID */ +#define WCH_UID_SUBCMD_GET_V2 0x06U /* Get chip UID - Firmware >= v2.9 */ + +/* Switch probe mode command - WCH_CMD_MODDE_SWITCH */ +#define WCH_MODDE_SWITCH_SUBCMD_SUPPORTED 0x41U /* Check if mode switching is supported - ?? */ +#define WCH_MODDE_SWITCH_SUBCMD_DAP_TO_RV 0x52U /* Switch to RV mode - ?? */ + +bool wchlink_command_send_recv(uint8_t command, uint8_t subcommand, const void *payload, size_t payload_length, + void *response, size_t response_length); +bool wchlink_transfer_dmi(uint8_t operation, uint32_t address, uint32_t data_in, uint32_t *data_out, uint8_t *status); + +bool wchlink_attach(void); + +#endif /* PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H */ diff --git a/src/platforms/hosted/wchlink_riscv_dtm.c b/src/platforms/hosted/wchlink_riscv_dtm.c new file mode 100644 index 00000000000..0531655b7b7 --- /dev/null +++ b/src/platforms/hosted/wchlink_riscv_dtm.c @@ -0,0 +1,94 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "jep106.h" +#include "riscv_debug.h" +#include "wchlink_protocol.h" + +static void wchlink_riscv_dtm_init(riscv_dmi_s *dmi); +static bool wchlink_riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +static bool wchlink_riscv_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); + +void wchlink_riscv_dtm_handler(void) +{ + riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); + if (!dmi) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + wchlink_riscv_dtm_init(dmi); + /* If we failed to find any DMs or Harts, free the structure */ + if (!dmi->ref_count) + free(dmi); +} + +static void wchlink_riscv_dtm_init(riscv_dmi_s *const dmi) +{ + /* WCH-Link doesn't have any mechanism to identify the DTM manufacturer, so we'll just assume it's WCH */ + dmi->designer_code = JEP106_MANUFACTURER_WCH; + + /* This DTM/DMI is not part of any official spec */ + dmi->version = RISCV_DEBUG_NONSTANDARD; + + /* + * WCH-Link has a fixed address width of 7 bits, + * technically only limited by the USB protocol to 8 bits but the underlying protocols are 7 bits + */ + dmi->address_width = 7U; + + dmi->read = wchlink_riscv_dmi_read; + dmi->write = wchlink_riscv_dmi_write; + + riscv_dmi_init(dmi); +} + +static bool wchlink_riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +{ + uint8_t status = 0; + const bool result = wchlink_transfer_dmi(RV_DMI_OP_READ, address, 0, value, &status); + + /* Translate error 1 into RV_DMI_FAILURE per the spec, also write RV_DMI_FAILURE if the transfer failed */ + dmi->fault = !result || status == RV_DMI_RESERVED ? RV_DMI_FAILURE : status; + return dmi->fault == RV_DMI_SUCCESS; +} + +static bool wchlink_riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) +{ + uint8_t status = 0; + const bool result = wchlink_transfer_dmi(RV_DMI_OP_WRITE, address, value, NULL, &status); + + /* Translate error 1 into RV_DMI_FAILURE per the spec, also write RV_DMI_FAILURE if the transfer failed */ + dmi->fault = !result || status == RV_DMI_RESERVED ? RV_DMI_FAILURE : status; + return dmi->fault == RV_DMI_SUCCESS; +} diff --git a/src/platforms/hosted/wchlink_rvswd.c b/src/platforms/hosted/wchlink_rvswd.c new file mode 100644 index 00000000000..249aa7f49f9 --- /dev/null +++ b/src/platforms/hosted/wchlink_rvswd.c @@ -0,0 +1,53 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target_internal.h" +#include "wchlink.h" +#include "wchlink_protocol.h" + +bool wchlink_rvswd_scan(void) +{ + target_list_free(); + + /* + * This is redundant as we will run a generic scan routine after this + * but without it the scan will fail + */ + if (!wchlink_attach()) + return false; + + /* deffer to WCH-Link riscv_dtm_handler */ + wchlink_riscv_dtm_handler(); + + return target_list != NULL; +} diff --git a/src/target/ch32vx.c b/src/target/ch32vx.c new file mode 100644 index 00000000000..e220797e8f1 --- /dev/null +++ b/src/target/ch32vx.c @@ -0,0 +1,164 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 1BitSquared + * Written by Rafael Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements RISC-V CH32Vx target specific functions */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "buffer_utils.h" + +/* + * IDCODE register + * [31:16] - REVID + * [15:0] - DEVID + */ +#define CH32V003X_IDCODE 0x1ffff7c4U + +/* IDCODE register */ +#define CH32VX_IDCODE 0x1ffff704U +#define CH32VX_IDCODE_MASK 0x0ffffff0f +#define CH32VX_IDCODE_FAMILY_OFFSET 20U +#define CH32VX_IDCODE_FAMILY_MASK (0xfffU << CH32VX_IDCODE_FAMILY_OFFSET) + +#define CH32V203_IDCODE_FAMILY 0x203U +#define CH32V208_IDCODE_FAMILY 0x208U +#define CH32V305_IDCODE_FAMILY 0x305U +#define CH32V303_IDCODE_FAMILY 0x303U +#define CH32V307_IDCODE_FAMILY 0x307U + +/* Electronic Signature (ESIG) registers */ +#define CH32VX_ESIG_FLASH_CAP 0x1ffff7e0U /* Flash capacity register, 16 bits, KiB units */ +#define CH32VX_ESIG_UID1 0x1ffff7e8U /* Unique ID register, bits 0:31 */ +#define CH32VX_ESIG_UID2 0x1ffff7ecU /* Unique ID register, bits 32:63 */ +#define CH32VX_ESIG_UID3 0x1ffff7f0U /* Unique ID register, bits 64:95 */ + +static bool ch32vx_uid_cmd(target_s *t, int argc, const char **argv); + +const command_s ch32vx_cmd_list[] = { + {"uid", ch32vx_uid_cmd, "Prints 96 bit unique id"}, + {NULL, NULL, NULL}, +}; + +static size_t ch32vx_read_flash_size(target_s *const t) +{ + return target_mem32_read16(t, CH32VX_ESIG_FLASH_CAP) * 1024U; +} + +static void ch32vx_read_uid(target_s *const t, uint8_t *const uid) +{ + for (size_t uid_reg_offset = 0; uid_reg_offset < 12U; uid_reg_offset += 4U) + write_be4(uid, uid_reg_offset, target_mem32_read32(t, CH32VX_ESIG_UID1 + uid_reg_offset)); +} + +bool ch32v003x_probe(target_s *const target) +{ + const uint32_t idcode = target_mem32_read32(target, CH32V003X_IDCODE); + + switch (idcode & CH32VX_IDCODE_MASK) { + case 0x00300500U: /* CH32V003F4P6 */ + case 0x00310500U: /* CH32V003F4U6 */ + case 0x00320500U: /* CH32V003A4M6 */ + case 0x00330500U: /* CH32V003J4M6 */ + break; + default: + DEBUG_INFO("Unrecognized CH32V003x IDCODE: 0x%08" PRIx32 "\n", idcode); + return false; + break; + } + + target->driver = "CH32V003"; + + const size_t flash_size = ch32vx_read_flash_size(target); + DEBUG_INFO("CH32V003x flash size: %zu\n", flash_size); + (void)flash_size; + + target->part_id = idcode; + + target_add_commands(target, ch32vx_cmd_list, "CH32Vx"); + + return true; +} + +bool ch32vx_probe(target_s *const target) +{ + const uint32_t idcode = target_mem32_read32(target, CH32VX_IDCODE); + + switch (idcode & CH32VX_IDCODE_MASK) { + case 0x30330504U: /* CH32V303CBT6 */ + case 0x30320504U: /* CH32V303RBT6 */ + case 0x30310504U: /* CH32V303RCT6 */ + case 0x30300504U: /* CH32V303VCT6 */ + case 0x30520508U: /* CH32V305FBP6 */ + case 0x30500508U: /* CH32V305RBT6 */ + case 0x30730508U: /* CH32V307WCU6 */ + case 0x30720508U: /* CH32V307FBP6 */ + case 0x30710508U: /* CH32V307RCT6 */ + case 0x30700508U: /* CH32V307VCT6 */ + break; + default: + DEBUG_INFO("Unrecognized CH32Vx IDCODE: 0x%08" PRIx32 "\n", idcode); + return false; + break; + } + + const uint16_t family = (idcode & CH32VX_IDCODE_FAMILY_MASK) >> CH32VX_IDCODE_FAMILY_OFFSET; + switch (family) { + case CH32V303_IDCODE_FAMILY: + target->driver = "CH32V303"; + break; + case CH32V305_IDCODE_FAMILY: + target->driver = "CH32V305"; + break; + case CH32V307_IDCODE_FAMILY: + target->driver = "CH32V307"; + break; + default: + return false; + break; + } + + const size_t flash_size = ch32vx_read_flash_size(target); + DEBUG_INFO("CH32Vx flash size: %zu\n", flash_size); + (void)flash_size; + + target->part_id = idcode; + + target_add_commands(target, ch32vx_cmd_list, "CH32Vx"); + + return true; +} + +/* Reads the 96 bit unique id */ +static bool ch32vx_uid_cmd(target_s *const target, const int argc, const char **const argv) +{ + (void)argc; + (void)argv; + + uint8_t uid[12U]; + ch32vx_read_uid(target, uid); + + tc_printf(target, "Unique id: 0x"); + for (size_t i = 0U; i < sizeof(uid); i++) + tc_printf(target, "%02" PRIx8, uid[i]); + tc_printf(target, "\n"); + + return true; +} diff --git a/src/target/jep106.h b/src/target/jep106.h index f9225067151..1dd626b7669 100644 --- a/src/target/jep106.h +++ b/src/target/jep106.h @@ -67,7 +67,6 @@ #define JEP106_MANUFACTURER_GIGADEVICE 0x751U /* GigaDevice */ #define JEP106_MANUFACTURER_RASPBERRY 0x913U /* Raspberry Pi */ #define JEP106_MANUFACTURER_RENESAS 0x423U /* Renesas */ -#define JEP106_MANUFACTURER_WCH 0x72aU /* "Nanjing Yihuo Technology", used by CH579 */ #define JEP106_MANUFACTURER_XILINX 0x309U /* Xilinx - Technically 0x049, but they use Ikanos Communications' code */ /* * This JEP code should belong to "Andes Technology Corporation", but is used on RISC-V by GigaDevice, @@ -75,6 +74,12 @@ */ #define JEP106_MANUFACTURER_RV_GIGADEVICE 0x61eU +/* + * Used by WCH (WinChipHead) aka Nanjing Qinheng Microelectronics + * Not the same company but related + */ +#define JEP106_MANUFACTURER_WCH 0x72aU /* Nanjing Yihuo Technology */ + /* * This code is not listed in the JEP106 standard, but is used by some stm32f1 clones * since we're not using this code elsewhere let's switch to the stm code. diff --git a/src/target/meson.build b/src/target/meson.build index 4e11cb99ef0..f0c0c693973 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -293,6 +293,13 @@ target_ch32 = declare_dependency( dependencies: target_stm32f1, ) +target_ch32v = declare_dependency( + sources: files( + 'ch32vx.c', + ), + dependencies: target_riscv32, +) + target_stm = declare_dependency( sources: files( 'stm32g0.c', diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 0d98a1b1596..aee1d676974 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -97,6 +97,10 @@ bool riscv32_probe(target_s *const target) break; default: break; + case JEP106_MANUFACTURER_WCH: + PROBE(ch32v003x_probe); + PROBE(ch32vx_probe); + break; } #if CONFIG_BMDA == 0 diff --git a/src/target/riscv_adi_dtm.c b/src/target/riscv_adi_dtm.c index 6c9cce48c62..f46693311cd 100644 --- a/src/target/riscv_adi_dtm.c +++ b/src/target/riscv_adi_dtm.c @@ -51,7 +51,7 @@ void riscv_adi_dtm_handler(adiv5_access_port_s *const ap) dmi_ap->dmi.dev_index = 0xffU; dmi_ap->dmi.idle_cycles = 0xffU; dmi_ap->dmi.designer_code = ap->dp->designer_code; - dmi_ap->dmi.version = RISCV_DEBUG_0_13; /* The DMI version doesn't actually matter, so just make it spec v0.13 */ + dmi_ap->dmi.version = RISCV_DEBUG_NONSTANDARD; /* This DTM/DMI is not part of any official spec */ dmi_ap->dmi.address_width = ap->flags & ADIV5_AP_FLAGS_64BIT ? 64U : 32U; dmi_ap->dmi.read = riscv_adi_dmi_read; diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 97a55590428..735e6da5017 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -290,13 +290,22 @@ static void riscv_reset(target_s *target); void riscv_dmi_init(riscv_dmi_s *const dmi) { - /* If we don't currently know how to talk to this DMI, warn and fail */ + /* + * The DMI version does not actually matter here, the implementation details have already been + * abstracted away at this point and we have a generic DMI to work with + * + * But the dminfo register (at 0x11) of v0.11 DM is incompatible with dmstatus (also at 0x11) of + * later versions, meaning we can't easily/reliably determine the version of the DM. + * We ignore all v0.11 DMI's in the hope we don't encounter a v0.11 DM with a later version DMI. + */ if (dmi->version == RISCV_DEBUG_UNKNOWN) return; if (dmi->version == RISCV_DEBUG_0_11) { - DEBUG_INFO("RISC-V debug v0.11 not presently supported\n"); + DEBUG_INFO("RISC-V v0.11 DMI is not presently supported\n"); return; } + if (dmi->version == RISCV_DEBUG_NONSTANDARD) + DEBUG_INFO("RISC-V non-standard DMI, proceeding anyway\n"); /* Iterate through the possible DMs and probe implemented ones */ /* The first DM is always at base address 0 */ @@ -310,8 +319,11 @@ void riscv_dmi_init(riscv_dmi_s *const dmi) } const riscv_debug_version_e dm_version = riscv_dm_version(dm_status); - /* If the DM is not unimplemented, allocate a structure for it and do further processing */ - if (dm_version != RISCV_DEBUG_UNIMPL) { + /* + * If the DM is not unimplemented and is a supported version, + * allocate a structure for it and do further processing + */ + if (dm_version == RISCV_DEBUG_0_13 || dm_version == RISCV_DEBUG_1_0) { riscv_dm_s *dbg_module = calloc(1, sizeof(*dbg_module)); if (!dbg_module) { /* calloc failed: heap exhaustion */ DEBUG_WARN("calloc: failed in %s\n", __func__); @@ -325,7 +337,8 @@ void riscv_dmi_init(riscv_dmi_s *const dmi) /* If we failed to discover any Harts, free the structure */ if (!dbg_module->ref_count) free(dbg_module); - } + } else if (dm_version != RISCV_DEBUG_UNIMPL) + DEBUG_INFO("Ignoring DM with unsupported version\n"); /* Read out the address of the next DM */ if (!riscv_dmi_read(dmi, base_addr + RV_DM_NEXT_DM, &base_addr)) { @@ -557,16 +570,30 @@ static void riscv_hart_free(void *const priv) static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { - const bool result = dmi->read(dmi, address, value); + bool result = false; + do { + result = dmi->read(dmi, address, value); + } while (dmi->fault == RV_DMI_TOO_SOON); + if (result) DEBUG_PROTO("%s: %08" PRIx32 " -> %08" PRIx32 "\n", __func__, address, *value); + else + DEBUG_WARN("%s: %08" PRIx32 " failed: %u\n", __func__, address, dmi->fault); return result; } static bool riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) { DEBUG_PROTO("%s: %08" PRIx32 " <- %08" PRIx32 "\n", __func__, address, value); - return dmi->write(dmi, address, value); + + bool result = false; + do { + result = dmi->write(dmi, address, value); + } while (dmi->fault == RV_DMI_TOO_SOON); + + if (!result) + DEBUG_WARN("%s: %08" PRIx32 " failed: %u\n", __func__, address, dmi->fault); + return result; } bool riscv_dm_read(riscv_dm_s *dbg_module, const uint8_t address, uint32_t *const value) @@ -586,18 +613,21 @@ static riscv_debug_version_e riscv_dm_version(const uint32_t status) case 0: return RISCV_DEBUG_UNIMPL; case 1: - DEBUG_INFO("RISC-V debug v0.11 DM\n"); + DEBUG_INFO("RISC-V v0.11 Debug Module\n"); return RISCV_DEBUG_0_11; case 2: - DEBUG_INFO("RISC-V debug v0.13 DM\n"); + DEBUG_INFO("RISC-V v0.13 Debug Module\n"); return RISCV_DEBUG_0_13; case 3: - DEBUG_INFO("RISC-V debug v1.0 DM\n"); + DEBUG_INFO("RISC-V v1.0 Debug Module\n"); return RISCV_DEBUG_1_0; + case 15: + DEBUG_INFO("RISC-V non-standard Debug Module\n"); + return RISCV_DEBUG_NONSTANDARD; default: break; } - DEBUG_INFO("Please report part with unknown RISC-V debug DM version %x\n", version); + DEBUG_INFO("Please report part with unknown RISC-V Debug Module version %x\n", version); return RISCV_DEBUG_UNKNOWN; } diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 9db24a4993e..0c89137b06d 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -41,10 +41,16 @@ typedef enum riscv_debug_version { RISCV_DEBUG_UNKNOWN, + /* No Debug Module [Interface] present */ RISCV_DEBUG_UNIMPL, + /* Debug Module [Interface] conforming to version 0.11 of the spec */ RISCV_DEBUG_0_11, + /* Debug Module [Interface] conforming to version 0.13 of the spec */ RISCV_DEBUG_0_13, + /* Debug Module [Interface] conforming to version 1.0 of the spec */ RISCV_DEBUG_1_0, + /* Debug Module [Interface] not conforming to any available version of the spec */ + RISCV_DEBUG_NONSTANDARD, } riscv_debug_version_e; /* This enum describes the Hart status (eg after a CSR read/write) */ @@ -158,6 +164,16 @@ typedef struct riscv_hart { #define RV_STATUS_VERSION_MASK 0x0000000fU +#define RV_DMI_OP_NOOP 0U /* Ignore data and address */ +#define RV_DMI_OP_READ 1U /* Read data from address */ +#define RV_DMI_OP_WRITE 2U /* Write data to address */ +#define RV_DMI_OP_RESERVED 3U /* Reserved */ + +#define RV_DMI_SUCCESS 0U /* The previous operation completed successfully */ +#define RV_DMI_RESERVED 1U /* Reserved */ +#define RV_DMI_FAILURE 2U /* A previous operation failed */ +#define RV_DMI_TOO_SOON 3U /* An operation was attempted while a DMI request is still in progress */ + #define RV_DM_DATA0 0x04U #define RV_DM_DATA1 0x05U #define RV_DM_DATA2 0x06U diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 6b41971146a..f5e43803863 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -50,13 +50,6 @@ #define RV_DTMCS_ADDRESS_MASK 0x000003f0U #define RV_DTMCS_ADDRESS_SHIFT 4U -#define RV_DMI_NOOP 0U -#define RV_DMI_READ 1U -#define RV_DMI_WRITE 2U -#define RV_DMI_SUCCESS 0U -#define RV_DMI_FAILURE 2U -#define RV_DMI_TOO_SOON 3U - #ifdef CONFIG_RISCV static void riscv_jtag_dtm_init(riscv_dmi_s *dmi); static uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); @@ -146,10 +139,8 @@ static uint8_t riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, jtag_proc.jtagtap_tdi_seq(!device->dr_postscan, (const uint8_t *)&address, dmi->address_width); jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); jtagtap_return_idle(dmi->idle_cycles); - /* Translate error 1 into RV_DMI_FAILURE per the spec */ - if (status == 1U) - return RV_DMI_FAILURE; - return status; + /* Translate error 1 (Reserved) into RV_DMI_FAILURE per the spec */ + return status == RV_DMI_RESERVED ? RV_DMI_FAILURE : status; } static bool riscv_dmi_transfer(riscv_dmi_s *const dmi, const uint8_t operation, const uint32_t address, @@ -183,33 +174,21 @@ static bool riscv_dmi_transfer(riscv_dmi_s *const dmi, const uint8_t operation, bool riscv_jtag_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { - bool result = true; - do { - /* Setup the location to read from */ - result = riscv_dmi_transfer(dmi, RV_DMI_READ, address, 0U, NULL); - if (result) - /* If that worked, read back the value and check the operation status */ - result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0U, 0U, value); - } while (dmi->fault == RV_DMI_TOO_SOON); - - if (!result) - DEBUG_WARN("DMI read at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); + /* Setup the location to read from */ + bool result = riscv_dmi_transfer(dmi, RV_DMI_OP_READ, address, 0U, NULL); + if (result) + /* If that worked, read back the value and check the operation status */ + result = riscv_dmi_transfer(dmi, RV_DMI_OP_NOOP, 0U, 0U, value); return result; } bool riscv_jtag_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) { - bool result = true; - do { - /* Write a value to the requested register */ - result = riscv_dmi_transfer(dmi, RV_DMI_WRITE, address, value, NULL); - if (result) - /* If that worked, read back the operation status to ensure the write actually worked */ - result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0U, 0U, NULL); - } while (dmi->fault == RV_DMI_TOO_SOON); - - if (!result) - DEBUG_WARN("DMI write at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); + /* Write a value to the requested register */ + bool result = riscv_dmi_transfer(dmi, RV_DMI_OP_WRITE, address, value, NULL); + if (result) + /* If that worked, read back the operation status to ensure the write actually worked */ + result = riscv_dmi_transfer(dmi, RV_DMI_OP_NOOP, 0U, 0U, NULL); return result; } diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 2fee2a324e1..5be60806147 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -104,6 +104,8 @@ CORTEXM_PROBE_WEAK_NOP(rp2040_rescue_probe) TARGET_PROBE_WEAK_NOP(apollo_3_probe) TARGET_PROBE_WEAK_NOP(at32f40x_probe) TARGET_PROBE_WEAK_NOP(at32f43x_probe) +TARGET_PROBE_WEAK_NOP(ch32v003x_probe) +TARGET_PROBE_WEAK_NOP(ch32vx_probe) TARGET_PROBE_WEAK_NOP(ch32f1_probe) TARGET_PROBE_WEAK_NOP(ch579_probe) TARGET_PROBE_WEAK_NOP(efm32_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index a68693dea8c..9976a23cd5a 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -58,6 +58,8 @@ bool at32f43x_probe(target_s *target); bool ch32f1_probe(target_s *target); // will catch all the clones bool ch579_probe(target_s *target); bool efm32_probe(target_s *target); +bool ch32v003x_probe(target_s *target); +bool ch32vx_probe(target_s *target); bool gd32f1_probe(target_s *target); bool gd32f4_probe(target_s *target); bool gd32vf1_probe(target_s *target);