From 7a2134cf98bf4de4a7add5fd8a48141ca7f1dc83 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 10 Aug 2023 17:25:34 +0100 Subject: [PATCH 01/24] hosted: barebones wch-link implementation --- src/platforms/hosted/bmp_libusb.c | 3 +- src/platforms/hosted/meson.build | 1 + src/platforms/hosted/platform.c | 9 + src/platforms/hosted/platform.h | 6 + src/platforms/hosted/wchlink.c | 241 ++++++++++++++++++ src/platforms/hosted/wchlink.h | 40 +++ src/platforms/hosted/wchlink_protocol.h | 321 ++++++++++++++++++++++++ 7 files changed, 620 insertions(+), 1 deletion(-) create mode 100644 src/platforms/hosted/wchlink.c create mode 100644 src/platforms/hosted/wchlink.h create mode 100644 src/platforms/hosted/wchlink_protocol.h 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..321e040439a 100644 --- a/src/platforms/hosted/meson.build +++ b/src/platforms/hosted/meson.build @@ -54,6 +54,7 @@ bmda_sources = files( 'jlink.c', 'jlink_jtag.c', 'jlink_swd.c', + 'wchlink.c', ) subdir('remote') diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index f3258110f58..d4d5682e69c 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 @@ -411,6 +417,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..78a8544a78b 100644 --- a/src/platforms/hosted/platform.h +++ b/src/platforms/hosted/platform.h @@ -77,6 +77,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 +89,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..7decf28fb76 --- /dev/null +++ b/src/platforms/hosted/wchlink.c @@ -0,0 +1,241 @@ +/* + * 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" + +typedef struct wchlink { + struct wchlink_fw_version { + uint8_t major; + uint8_t minor; + } fw_version; /* Firmware version */ + + uint8_t hw_type; /* Hardware type */ +} 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; +} + +/* + * 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 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; +} + +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..c653801d2ef --- /dev/null +++ b/src/platforms/hosted/wchlink.h @@ -0,0 +1,40 @@ +/* + * 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); + +#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..0fa46e27736 --- /dev/null +++ b/src/platforms/hosted/wchlink_protocol.h @@ -0,0 +1,321 @@ +/* + * 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); + +#endif /* PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H */ From b5dfd7d8a05719b471c66851434e258a8dc5c540 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 16:54:44 +0100 Subject: [PATCH 02/24] hosted/wchlink: implement DMI transfer --- src/platforms/hosted/wchlink.c | 73 +++++++++++++++++++++++++ src/platforms/hosted/wchlink_protocol.h | 1 + 2 files changed, 74 insertions(+) diff --git a/src/platforms/hosted/wchlink.c b/src/platforms/hosted/wchlink.c index 7decf28fb76..f342ec62c66 100644 --- a/src/platforms/hosted/wchlink.c +++ b/src/platforms/hosted/wchlink.c @@ -34,6 +34,7 @@ #include "wchlink.h" #include "wchlink_protocol.h" +#include "buffer_utils.h" typedef struct wchlink { struct wchlink_fw_version { @@ -119,6 +120,78 @@ bool wchlink_command_send_recv(const uint8_t command, const uint8_t subcommand, 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 diff --git a/src/platforms/hosted/wchlink_protocol.h b/src/platforms/hosted/wchlink_protocol.h index 0fa46e27736..7c106dcc166 100644 --- a/src/platforms/hosted/wchlink_protocol.h +++ b/src/platforms/hosted/wchlink_protocol.h @@ -317,5 +317,6 @@ 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); #endif /* PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H */ From 3556898cb17f5c3aebba301e43082bf50d6b389c Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 16:49:08 +0100 Subject: [PATCH 03/24] riscv_debug: expose DMI operation and status defines --- src/target/riscv_debug.h | 7 +++++++ src/target/riscv_jtag_dtm.c | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index aa4a305aa02..7885f3eeb0a 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -156,6 +156,13 @@ typedef struct riscv_hart { #define RV_STATUS_VERSION_MASK 0x0000000fU +#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 + #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..edbf8e82b32 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); From 4fb98cf39ab040515cb09d432fd66a01260ded40 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 31 Jul 2023 23:21:09 +0100 Subject: [PATCH 04/24] jep106: add BMD internal flag Used to mark codes that are not part of the JEP106 or any other standard but are used internally by BMD to identify targets that don't provide a usable identification codes --- src/target/jep106.h | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/target/jep106.h b/src/target/jep106.h index f9225067151..ac82232e9a3 100644 --- a/src/target/jep106.h +++ b/src/target/jep106.h @@ -44,14 +44,27 @@ * * |15 |11 |7|6 0| * | | | | | | | | |0| | | | | | | | - * |\____/ \______/|\_____________/ - * | V V | V - * | Unused Cont | code - * | Code | - * \_ Legacy flag \_ Parity bit (always 0) + * | |\__/ \______/|\_____________/ + * | | V V | V + * | | Unused Cont | code + * | | Code | + * | | \_ Parity bit (always 0) + * | \_ BMD internal flag + * \_ Legacy flag */ #define ASCII_CODE_FLAG (1U << 15U) /* flag the code as legacy ASCII */ +/* + * BMD internal flag + * + * This is used to mark codes that are not part of the JEP106 or any other standard but are used + * internally by BMD to identify targets that don't provide a usable identification code + * + * These codes may be changed or removed at any time, and should not be relied upon outside of + * the context of BMD + */ +#define BMD_INTERNAL_FLAG (1U << 14U) /* flag the code as internal use in BMD */ + #define JEP106_MANUFACTURER_ARM 0x43bU /* ARM Ltd. */ #define JEP106_MANUFACTURER_FREESCALE 0x00eU /* Freescale */ #define JEP106_MANUFACTURER_NXP 0x015U /* NXP */ @@ -69,6 +82,7 @@ #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, * so in the unlikely event we need to support chips by them, here be dragons. From 272ef44422cff06581c4e54db73d629b21753d25 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 23 Feb 2023 15:27:55 +0000 Subject: [PATCH 05/24] jep106: add internal WCH non-jep106 code --- src/target/jep106.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/target/jep106.h b/src/target/jep106.h index ac82232e9a3..a07f94d11a0 100644 --- a/src/target/jep106.h +++ b/src/target/jep106.h @@ -83,6 +83,10 @@ #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 */ +/* BMD internal codes */ +/* WCH AKA Nanjing Qinheng Microelectronics */ +#define NOT_JEP106_MANUFACTURER_WCH (BMD_INTERNAL_FLAG | 0x01U) /* WCH (WinChipHead) */ + /* * This JEP code should belong to "Andes Technology Corporation", but is used on RISC-V by GigaDevice, * so in the unlikely event we need to support chips by them, here be dragons. From 41bf9860e83c65b89380baea1a0939394964d29b Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 21:17:56 +0100 Subject: [PATCH 06/24] riscv_jtag_dtm: move RV_DMI_TOO_SOON handling one level up --- src/target/riscv_debug.c | 18 ++++++++++++++++-- src/target/riscv_jtag_dtm.c | 32 ++++++++++---------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 14d28b35926..aa3bd134055 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -504,16 +504,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) diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index edbf8e82b32..df3a09631f6 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -176,33 +176,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_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); 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_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); return result; } From a8feda2b02c2191e77e3226ae868d98370b3cc0d Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 18:31:26 +0100 Subject: [PATCH 07/24] hosted/wchlink: implement RISC-V DTM handler --- src/platforms/hosted/meson.build | 1 + src/platforms/hosted/wchlink.h | 1 + src/platforms/hosted/wchlink_riscv_dtm.c | 90 ++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 src/platforms/hosted/wchlink_riscv_dtm.c diff --git a/src/platforms/hosted/meson.build b/src/platforms/hosted/meson.build index 321e040439a..7acc54eb61b 100644 --- a/src/platforms/hosted/meson.build +++ b/src/platforms/hosted/meson.build @@ -55,6 +55,7 @@ bmda_sources = files( 'jlink_jtag.c', 'jlink_swd.c', 'wchlink.c', + 'wchlink_riscv_dtm.c', ) subdir('remote') diff --git a/src/platforms/hosted/wchlink.h b/src/platforms/hosted/wchlink.h index c653801d2ef..720d7f74760 100644 --- a/src/platforms/hosted/wchlink.h +++ b/src/platforms/hosted/wchlink.h @@ -36,5 +36,6 @@ #include "bmp_hosted.h" bool wchlink_init(void); +void wchlink_riscv_dtm_handler(void); #endif /* PLATFORMS_HOSTED_WCHLINK_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..35dfb30d458 --- /dev/null +++ b/src/platforms/hosted/wchlink_riscv_dtm.c @@ -0,0 +1,90 @@ +/* + * 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 = NOT_JEP106_MANUFACTURER_WCH; + + dmi->version = RISCV_DEBUG_0_13; /* Assumption, unverified */ + + /* WCH-Link has a fixed address width of 8 bits, limited by the USB protocol (is RVSWD also fixed?) */ + dmi->address_width = 8U; + + 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_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 == 1U ? 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_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 == 1U ? RV_DMI_FAILURE : status; + return dmi->fault == RV_DMI_SUCCESS; +} From 63a22bdf23ef12d22e7e3bd5ac21d5df71ec6631 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 15:43:13 +0100 Subject: [PATCH 08/24] command: add rvswd scan routine command --- src/command.c | 57 ++++++++++++++++++++++++++++++++- src/include/target.h | 1 + src/platforms/hosted/platform.c | 10 ++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index 52a60c6c721..94cbe8b1571 100644 --- a/src/command.c +++ b/src/command.c @@ -56,6 +56,7 @@ 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); +static bool cmd_rvswd_scan(target_s *target, int argc, const char **argv); 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 +95,7 @@ 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"}, + {"rvswd_scan", cmd_rvswd_scan, "Scan RVSWD for devices"}, {"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 +318,50 @@ bool cmd_swd_scan(target_s *target, int argc, const char **argv) return true; } +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; +} + bool cmd_auto_scan(target_s *target, int argc, const char **argv) { (void)target; @@ -342,8 +388,17 @@ 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 CONFIG_BMDA == 1 + scan_result = bmda_rvswd_scan(); +#else + scan_result = false; +#endif + if (!scan_result) + gdb_out("RVSWD scan found no devices.\n"); + } } } CATCH () { diff --git a/src/include/target.h b/src/include/target.h index 5b4c9a15346..80783b68fe0 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -45,6 +45,7 @@ typedef struct target_controller target_controller_s; #if CONFIG_BMDA == 1 bool bmda_swd_scan(uint32_t targetid); bool bmda_jtag_scan(void); +bool bmda_rvswd_scan(void); #endif bool adiv5_swd_scan(uint32_t targetid); bool jtag_scan(void); diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index d4d5682e69c..2c67cdd3686 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -315,6 +315,16 @@ bool bmda_jtag_init(void) } } +bool bmda_rvswd_scan() +{ + bmda_probe_info.is_jtag = false; + + switch (bmda_probe_info.type) { + default: + return false; + } +} + void bmda_adiv5_dp_init(adiv5_debug_port_s *const dp) { switch (bmda_probe_info.type) { From 85d9f8e08011205ca6970be0f7fbfa193f56fcec Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 18:32:17 +0100 Subject: [PATCH 09/24] hosted/wchlink: implement RVSWD scan --- src/platforms/hosted/meson.build | 1 + src/platforms/hosted/platform.c | 5 ++ src/platforms/hosted/wchlink.c | 73 +++++++++++++++++++++++++ src/platforms/hosted/wchlink.h | 1 + src/platforms/hosted/wchlink_protocol.h | 2 + src/platforms/hosted/wchlink_rvswd.c | 53 ++++++++++++++++++ 6 files changed, 135 insertions(+) create mode 100644 src/platforms/hosted/wchlink_rvswd.c diff --git a/src/platforms/hosted/meson.build b/src/platforms/hosted/meson.build index 7acc54eb61b..13c0d511113 100644 --- a/src/platforms/hosted/meson.build +++ b/src/platforms/hosted/meson.build @@ -55,6 +55,7 @@ bmda_sources = files( '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 2c67cdd3686..041fbd362a3 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -320,6 +320,11 @@ 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; } diff --git a/src/platforms/hosted/wchlink.c b/src/platforms/hosted/wchlink.c index f342ec62c66..78c58c9126d 100644 --- a/src/platforms/hosted/wchlink.c +++ b/src/platforms/hosted/wchlink.c @@ -43,6 +43,8 @@ typedef struct wchlink { } 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; @@ -261,6 +263,32 @@ static char *wchlink_hw_type_to_string(const uint8_t hardware_id) } } +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]; @@ -282,6 +310,51 @@ static bool wchlink_get_version(void) 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)); diff --git a/src/platforms/hosted/wchlink.h b/src/platforms/hosted/wchlink.h index 720d7f74760..8e6a283dca2 100644 --- a/src/platforms/hosted/wchlink.h +++ b/src/platforms/hosted/wchlink.h @@ -36,6 +36,7 @@ #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 index 7c106dcc166..4de7b70695d 100644 --- a/src/platforms/hosted/wchlink_protocol.h +++ b/src/platforms/hosted/wchlink_protocol.h @@ -319,4 +319,6 @@ bool wchlink_command_send_recv(uint8_t command, uint8_t subcommand, const void * 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_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; +} From ec2d85a0cffef4c309137182f8374eb77f928763 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 23 Feb 2023 15:30:35 +0000 Subject: [PATCH 10/24] riscv32: add ch32v3 target probe --- src/target/ch32vx.c | 79 +++++++++++++++++++++++++++++++++++++++ src/target/meson.build | 1 + src/target/riscv32.c | 3 ++ src/target/target_probe.c | 1 + src/target/target_probe.h | 1 + 5 files changed, 85 insertions(+) create mode 100644 src/target/ch32vx.c diff --git a/src/target/ch32vx.c b/src/target/ch32vx.c new file mode 100644 index 00000000000..f66f71a764c --- /dev/null +++ b/src/target/ch32vx.c @@ -0,0 +1,79 @@ +/* + * 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" + +/* 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 + +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: + 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; + } + + target->part_id = idcode; + + return true; +} diff --git a/src/target/meson.build b/src/target/meson.build index 4e11cb99ef0..fafb91d3a05 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -289,6 +289,7 @@ target_stm32f4 = declare_dependency( target_ch32 = declare_dependency( sources: files( 'ch32f1.c', + 'ch32vx.c', ), dependencies: target_stm32f1, ) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 57e9472c844..9ebb2fd8697 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -101,6 +101,9 @@ bool riscv32_probe(target_s *const target) break; default: break; + case NOT_JEP106_MANUFACTURER_WCH: + PROBE(ch32vx_probe); + break; } #if CONFIG_BMDA == 0 diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 2fee2a324e1..c2d869b13f2 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -104,6 +104,7 @@ 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(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..e579988b082 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -58,6 +58,7 @@ 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 ch32vx_probe(target_s *target); bool gd32f1_probe(target_s *target); bool gd32f4_probe(target_s *target); bool gd32vf1_probe(target_s *target); From 49e9466c18e13db54a4ab41ee2db63fb85fce8ae Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 22:38:00 +0100 Subject: [PATCH 11/24] ch32vx: implement electronic signature (ESIG) register handling --- src/target/ch32vx.c | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/target/ch32vx.c b/src/target/ch32vx.c index f66f71a764c..e5268acabd5 100644 --- a/src/target/ch32vx.c +++ b/src/target/ch32vx.c @@ -23,6 +23,7 @@ #include "general.h" #include "target.h" #include "target_internal.h" +#include "buffer_utils.h" /* IDCODE register */ #define CH32VX_IDCODE 0x1ffff704U @@ -36,6 +37,30 @@ #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 ch32vx_probe(target_s *const target) { const uint32_t idcode = target_mem32_read32(target, CH32VX_IDCODE); @@ -73,7 +98,30 @@ bool ch32vx_probe(target_s *const target) 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; } From 094201b0714ccfd665a0fb7b4eb7e9be8278d359 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 5 Oct 2023 16:28:01 +0100 Subject: [PATCH 12/24] riscv32: add ch32v003 target probe --- src/target/ch32vx.c | 37 +++++++++++++++++++++++++++++++++++++ src/target/riscv32.c | 1 + src/target/target_probe.c | 1 + src/target/target_probe.h | 1 + 4 files changed, 40 insertions(+) diff --git a/src/target/ch32vx.c b/src/target/ch32vx.c index e5268acabd5..5332164f2af 100644 --- a/src/target/ch32vx.c +++ b/src/target/ch32vx.c @@ -25,6 +25,13 @@ #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 @@ -61,6 +68,35 @@ static void ch32vx_read_uid(target_s *const t, uint8_t *const uid) 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%08x\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); @@ -78,6 +114,7 @@ bool ch32vx_probe(target_s *const target) case 0x30700508U: /* CH32V307VCT6 */ break; default: + DEBUG_INFO("Unrecognized CH32Vx IDCODE: 0x%08x\n", idcode); return false; break; } diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 9ebb2fd8697..c3d611a6be2 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -102,6 +102,7 @@ bool riscv32_probe(target_s *const target) default: break; case NOT_JEP106_MANUFACTURER_WCH: + PROBE(ch32v003x_probe); PROBE(ch32vx_probe); break; } diff --git a/src/target/target_probe.c b/src/target/target_probe.c index c2d869b13f2..5be60806147 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -104,6 +104,7 @@ 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) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index e579988b082..9976a23cd5a 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -58,6 +58,7 @@ 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); From 145de4c44f4907601af67ea13755344b9deabeec Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 15 Jan 2024 17:46:23 +0000 Subject: [PATCH 13/24] buffer_utils: add write_char util write_char writes a single char into a char buffer at the provided offset, returns the offset incremented by 1 Memory safety safeguards are in place, if no buffer is provided, a buffer size of 0 is given, or the offset is out of bounds of the buffer, the buffer is not accessed, but the offset is still incremented Target use case is using in special print functions behaving similar to snprintf --- src/include/buffer_utils.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/include/buffer_utils.h b/src/include/buffer_utils.h index 0f05a25e4f3..0340699a12e 100644 --- a/src/include/buffer_utils.h +++ b/src/include/buffer_utils.h @@ -84,4 +84,11 @@ static inline uint64_t read_be8(const uint8_t *const buffer, const size_t offset ((uint64_t)buffer[offset + 6] << 8U) | buffer[offset + 7]; } +static inline size_t write_char(char *const buffer, const size_t buffer_size, const size_t offset, const char c) +{ + if (buffer && offset < buffer_size) + buffer[offset] = c; + return offset + 1U; +} + #endif /*INCLUDE_BUFFER_UTILS_H*/ From 3e5c6469c1e33d78c0e4c9be3e4c900ca3a7fa7b Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 15 Jan 2024 17:50:22 +0000 Subject: [PATCH 14/24] riscv_debug: formally parse the ISA subset --- src/target/riscv_debug.c | 96 ++++++++++++++++++++++++++++++---------- src/target/riscv_debug.h | 41 ++++++++++++++--- 2 files changed, 109 insertions(+), 28 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index aa3bd134055..949607248de 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -36,6 +36,7 @@ #include "target_internal.h" #include "gdb_reg.h" #include "riscv_debug.h" +#include "buffer_utils.h" #include @@ -421,6 +422,58 @@ static void riscv_hart_read_ids(riscv_hart_s *const hart) /* rv128 is unimpl. */ } +static size_t riscv_snprint_isa_subset( + char *const string_buffer, const size_t buffer_size, const uint32_t access_width, const uint32_t extensions) +{ + size_t offset = snprintf(string_buffer, buffer_size, "rv%" PRIu8, access_width); + + const bool is_embedded = extensions & RV_ISA_EXT_EMBEDDED; + + offset = write_char(string_buffer, buffer_size, offset, is_embedded ? 'e' : 'i'); + + const bool is_general_purpose_isa = + !is_embedded && (extensions & RV_ISA_EXT_GENERAL_PURPOSE) == RV_ISA_EXT_GENERAL_PURPOSE; + + if (is_general_purpose_isa) { + offset = write_char(string_buffer, buffer_size, offset, 'g'); + if (extensions & RV_ISA_EXT_QUAD_FLOAT) + offset = write_char(string_buffer, buffer_size, offset, 'q'); + } else { + if (extensions & RV_ISA_EXT_MUL_DIV_INT) + offset = write_char(string_buffer, buffer_size, offset, 'm'); + if (extensions & RV_ISA_EXT_ATOMIC) + offset = write_char(string_buffer, buffer_size, offset, 'a'); + if (extensions & RV_ISA_EXT_QUAD_FLOAT) + offset = write_char(string_buffer, buffer_size, offset, 'q'); /* Implies d */ + else if (extensions & RV_ISA_EXT_DOUBLE_FLOAT) + offset = write_char(string_buffer, buffer_size, offset, 'd'); /* Implies f */ + else if (extensions & RV_ISA_EXT_SINGLE_FLOAT) + offset = write_char(string_buffer, buffer_size, offset, 'f'); + } + if (extensions & RV_ISA_EXT_DECIMAL_FLOAT) + offset = write_char(string_buffer, buffer_size, offset, 'l'); + if (extensions & RV_ISA_EXT_COMPRESSED) + offset = write_char(string_buffer, buffer_size, offset, 'c'); + if (extensions & RV_ISA_EXT_BIT_MANIP) + offset = write_char(string_buffer, buffer_size, offset, 'b'); + if (extensions & RV_ISA_EXT_DYNAMIC_LANG) + offset = write_char(string_buffer, buffer_size, offset, 'j'); + if (extensions & RV_ISA_EXT_TRANSACT_MEM) + offset = write_char(string_buffer, buffer_size, offset, 't'); + if (extensions & RV_ISA_EXT_PACKED_SIMD) + offset = write_char(string_buffer, buffer_size, offset, 'p'); + if (extensions & RV_ISA_EXT_VECTOR) + offset = write_char(string_buffer, buffer_size, offset, 'v'); + if (extensions & RV_ISA_EXT_USER_INTERRUPTS) + offset = write_char(string_buffer, buffer_size, offset, 'n'); + + /* null-terminate the string */ + if (string_buffer && buffer_size > 0) + string_buffer[offset < buffer_size ? offset : buffer_size - 1U] = '\0'; + + return offset; +} + static bool riscv_hart_init(riscv_hart_s *const hart) { /* Allocate a new target */ @@ -444,9 +497,12 @@ static bool riscv_hart_init(riscv_hart_s *const hart) /* Then read out the ID registers */ riscv_hart_read_ids(hart); - DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 - ", exts = %08" PRIx32 "\n", - hart->hartid, hart->access_width, hart->archid, hart->vendorid, hart->implid, hart->extensions); + /* Build the ISA subset string from the Hart */ + riscv_snprint_isa_subset(hart->isa_name, sizeof(hart->isa_name), hart->access_width, hart->extensions); + + DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), %s ISA (exts = %08" PRIx32 + "), vendor = %" PRIx32 ", impl = %" PRIx32 "\n", + hart->hartid, hart->access_width, hart->archid, hart->isa_name, hart->extensions, hart->vendorid, hart->implid); /* We don't support rv128, so tell the user and fast-quit on this target. */ if (hart->access_width == 128U) { @@ -1105,17 +1161,6 @@ static void riscv_reset(target_s *const target) target_check_error(target); } -static const char *riscv_fpu_ext_string(const uint32_t extensions) -{ - if (extensions & RV_ISA_EXT_QUAD_FLOAT) - return "q"; - if (extensions & RV_ISA_EXT_DOUBLE_FLOAT) - return "d"; - if (extensions & RV_ISA_EXT_SINGLE_FLOAT) - return "f"; - return ""; -} - /* * Generate the FPU section of the description. * fpu_size = 32 -> single precision float @@ -1234,19 +1279,24 @@ static size_t riscv_build_target_fpu_description(char *const buffer, size_t max_ * */ static size_t riscv_build_target_description( - char *const buffer, size_t max_length, const uint8_t address_width, const uint32_t extensions) + char *const buffer, const size_t max_length, const uint8_t address_width, const uint32_t extensions) { - const bool embedded = extensions & RV_ISA_EXT_EMBEDDED; - const uint32_t fpu = extensions & RV_ISA_EXT_ANY_FLOAT; - size_t print_size = max_length; /* Start with the "preamble" chunks, which are mostly common across targets save for 2 words. */ - size_t offset = - (size_t)snprintf(buffer, print_size, "%s target %sriscv:rv%u%c%s%s ", - gdb_xml_preamble_first, gdb_xml_preamble_second, address_width, embedded ? 'e' : 'i', - riscv_fpu_ext_string(fpu), gdb_xml_preamble_third); + int offset = snprintf(buffer, print_size, "%s target %sriscv:", gdb_xml_preamble_first, gdb_xml_preamble_second); + if (max_length != 0) + print_size = max_length - (size_t)offset; + /* Write the architecture string, which is the ISA subset */ + offset += riscv_snprint_isa_subset(buffer + offset, print_size, address_width, extensions); + if (max_length != 0) + print_size = max_length - (size_t)offset; + /* Finally finish the rest of the preamble */ + offset += + snprintf(buffer + offset, print_size, "%s ", gdb_xml_preamble_third); + if (max_length != 0) + print_size = max_length - (size_t)offset; - const uint8_t gprs = embedded ? 16U : 32U; + const uint8_t gprs = extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; /* Then build the general purpose register descriptions using the arrays at top of file */ /* Note that in a device using the embedded (E) extension, we only generate the first 16. */ for (uint8_t i = 0; i < gprs; ++i) { diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 7885f3eeb0a..3edec4bb4bb 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -150,6 +150,8 @@ typedef struct riscv_hart { uint32_t implid; uint32_t hartid; + char isa_name[32U]; + uint32_t triggers; uint32_t trigger_uses[RV_TRIGGERS_MAX]; } riscv_hart_s; @@ -208,11 +210,40 @@ typedef struct riscv_hart { /* The FP base defines the starting register space address for the floating point registers */ #define RV_FP_BASE 0x1020U -#define RV_ISA_EXT_EMBEDDED 0x00000010U -#define RV_ISA_EXT_ANY_FLOAT 0x00010028U -#define RV_ISA_EXT_SINGLE_FLOAT 0x00000020U -#define RV_ISA_EXT_DOUBLE_FLOAT 0x00000008U -#define RV_ISA_EXT_QUAD_FLOAT 0x00010000U +/* + * The Extensions field encodes the presence of standard extensions, with a single bit per alphabet letter + * (bit 0 encodes presence of extension “A” through to bit 25 which encodes “Z”) + * + * This list is taken from the RISC-V Instruction Set Manual v2.2 + * + * The list order is the canonical representation order in the ISA subset string + */ + +/* Base ISA */ +#define RV_ISA_EXT_INTEGER (1U << 8U) /* 'I': RV32I/64I/128I integer base ISA */ +#define RV_ISA_EXT_EMBEDDED (1U << 4U) /* 'E': RV32E reduced integer base ISA (Embedded) */ + +/* Standard general-purpose ISA */ +#define RV_ISA_EXT_MUL_DIV_INT (1U << 12U) /* 'M': Integer multiplication and division */ +#define RV_ISA_EXT_ATOMIC (1U << 0U) /* 'A': Atomic instructions */ +#define RV_ISA_EXT_SINGLE_FLOAT (1U << 5U) /* 'F': Single-precision floating-point */ +#define RV_ISA_EXT_DOUBLE_FLOAT (1U << 3U) /* 'D': Double-precision floating-point */ + +/* 'G' standard general-purpose ISA abbreviation, representing 'IMAFD' */ +#define RV_ISA_EXT_GENERAL_PURPOSE \ + (RV_ISA_EXT_INTEGER | RV_ISA_EXT_MUL_DIV_INT | RV_ISA_EXT_ATOMIC | RV_ISA_EXT_SINGLE_FLOAT | \ + RV_ISA_EXT_DOUBLE_FLOAT) + +/* Standard Unprivileged Extensions */ +#define RV_ISA_EXT_QUAD_FLOAT (1U << 16U) /* 'Q': Quad-precision floating-point */ +#define RV_ISA_EXT_DECIMAL_FLOAT (1U << 11U) /* 'L': Decimal floating-point */ +#define RV_ISA_EXT_COMPRESSED (1U << 2U) /* 'C': 16-bit compressed instructions */ +#define RV_ISA_EXT_BIT_MANIP (1U << 1U) /* 'B': Bit manipulation */ +#define RV_ISA_EXT_DYNAMIC_LANG (1U << 9U) /* 'J': Dynamic languages */ +#define RV_ISA_EXT_TRANSACT_MEM (1U << 19U) /* 'T': Transactional memory */ +#define RV_ISA_EXT_PACKED_SIMD (1U << 15U) /* 'P': Packed-SIMD */ +#define RV_ISA_EXT_VECTOR (1U << 21U) /* 'V': Vector extensions */ +#define RV_ISA_EXT_USER_INTERRUPTS (1U << 13U) /* 'N': User-level interrupts */ #define RV_TRIGGER_SUPPORT_MASK 0x0000fffeU #define RV_TRIGGER_MODE_MASK 0xffff0000U From 40206d0fbb060126d28c8335fb6157b8f8ad6d6f Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 15 Jan 2024 17:52:15 +0000 Subject: [PATCH 15/24] riscv_debug: use the ISA subset as the target core name --- src/target/riscv32.c | 4 +--- src/target/riscv64.c | 4 +--- src/target/riscv_debug.c | 2 +- src/target/target_internal.h | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index c3d611a6be2..5d272b486f8 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -79,9 +79,7 @@ static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); bool riscv32_probe(target_s *const target) { /* Finish setting up the target structure with generic rv32 functions */ - target->core = "rv32"; - /* Provide the length of a suitable registers structure */ - target->regs_size = sizeof(riscv32_regs_s); + target->regs_size = sizeof(riscv32_regs_s); /* Provide the length of a suitable registers structure */ target->regs_read = riscv32_regs_read; target->regs_write = riscv32_regs_write; target->reg_write = riscv32_reg_write; diff --git a/src/target/riscv64.c b/src/target/riscv64.c index b2fe72051e2..37a8aae774c 100644 --- a/src/target/riscv64.c +++ b/src/target/riscv64.c @@ -49,9 +49,7 @@ static void riscv64_mem_read(target_s *target, void *dest, target_addr64_t src, bool riscv64_probe(target_s *const target) { /* Finish setting up the target structure with generic rv64 functions */ - target->core = "rv64"; - /* Provide the length of a suitable registers structure */ - target->regs_size = sizeof(riscv64_regs_s); + target->regs_size = sizeof(riscv64_regs_s); /* Provide the length of a suitable registers structure */ target->regs_read = riscv64_regs_read; target->regs_write = riscv64_regs_write; target->mem_read = riscv64_mem_read; diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 949607248de..01fd685599e 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -499,6 +499,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) /* Build the ISA subset string from the Hart */ riscv_snprint_isa_subset(hart->isa_name, sizeof(hart->isa_name), hart->access_width, hart->extensions); + target->core = hart->isa_name; DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), %s ISA (exts = %08" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 "\n", @@ -506,7 +507,6 @@ static bool riscv_hart_init(riscv_hart_s *const hart) /* We don't support rv128, so tell the user and fast-quit on this target. */ if (hart->access_width == 128U) { - target->core = "(unsup) rv128"; DEBUG_WARN("rv128 is unsupported, ignoring this hart\n"); return true; } diff --git a/src/target/target_internal.h b/src/target/target_internal.h index f7755cc94ae..85e58c93ab8 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -171,7 +171,7 @@ struct target { /* Other stuff */ const char *driver; uint32_t cpuid; - char *core; + const char *core; char cmdline[MAX_CMDLINE]; target_addr_t heapinfo[4]; target_command_s *commands; From b71af18077b6b5f0fdb1a614834cadf0835fd8e6 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 15 Jan 2024 17:55:11 +0000 Subject: [PATCH 16/24] riscv_debug: fix typo --- src/target/riscv_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 01fd685599e..2a046382c35 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -481,7 +481,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) if (!target) return false; - /* Grab a reference to the DMI and DM structurues and do preliminary setup of the target structure */ + /* Grab a reference to the DMI and DM structures and do preliminary setup of the target structure */ riscv_dm_ref(hart->dbg_module); target->driver = "RISC-V"; target->priv = hart; From 628ffc55359e7c15f91e2a69084bb2abe52b2278 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 15 Jan 2024 17:55:25 +0000 Subject: [PATCH 17/24] riscv64: fix typo --- src/target/riscv64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/riscv64.c b/src/target/riscv64.c index 37a8aae774c..4dc4b6b11be 100644 --- a/src/target/riscv64.c +++ b/src/target/riscv64.c @@ -114,7 +114,7 @@ static void riscv64_mem_read(target_s *const target, void *const dest, const tar if (!len) return; riscv_hart_s *const hart = riscv_hart_struct(target); - /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + /* Figure out the maximal width of access to perform, up to the bitness of the target */ const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; /* Build the access command */ From 8124471e559607a394f166b098c2304a6f2768d4 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 15 Jan 2024 20:04:11 +0000 Subject: [PATCH 18/24] riscv_debug: update RV_CSR macros --- src/target/riscv_debug.h | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 3edec4bb4bb..23a6aee7ee3 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -249,20 +249,19 @@ typedef struct riscv_hart { #define RV_TRIGGER_MODE_MASK 0xffff0000U #define RV_TRIGGER_SUPPORT_BREAKWATCH 0x00000004U -/* - * The CSR number when requested by GDB is shifted by RV_CSR_GDB_OFFSET so they cannot collide with +/* The CSR number when requested by GDB is shifted by RV_CSR_GDB_OFFSET so they cannot collide with * the GPRs. As a result, we have to subtract RV_CSR_GDB_OFFSET from the value received from GDB. */ -#define RV_CSR_GDB_OFFSET 128 -#define RV_CSR_STATUS 0x300 -#define RV_CSR_MISA 0x301 -#define RV_CSR_MIE 0x304 -#define RV_CSR_MTVEC 0x305 -#define RV_CSR_MSCRATCH 0x340 -#define RV_CSR_MEPC 0x341 -#define RV_CSR_MCAUSE 0x342 -#define RV_CSR_MTVAL 0x343 -#define RV_CSR_MIP 0x344 +#define RV_CSR_GDB_OFFSET 128U +#define RV_CSR_STATUS 0x300U +#define RV_CSR_MISA 0x301U +#define RV_CSR_MIE 0x304U +#define RV_CSR_MTVEC 0x305U +#define RV_CSR_MSCRATCH 0x340U +#define RV_CSR_MEPC 0x341U +#define RV_CSR_MCAUSE 0x342U +#define RV_CSR_MTVAL 0x343U +#define RV_CSR_MIP 0x344U /* * These two lines are about allowing GDB to access FPU registers through fake registers offset by From 7679460b8b880af89993a98d79ee82dda603e811 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 15 Jan 2024 21:25:06 +0000 Subject: [PATCH 19/24] !experimental! riscv32: add hacky fallback memory access using the progbuf when sysbus and abstract memory access is not available --- src/target/riscv32.c | 157 ++++++++++++++++++++++++++++++++++++++- src/target/riscv_debug.c | 24 +++--- src/target/riscv_debug.h | 7 +- 3 files changed, 172 insertions(+), 16 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 5d272b486f8..51c1a2e3b2e 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -72,6 +72,8 @@ static size_t riscv32_reg_read(target_s *target, uint32_t reg, void *data, size_ static size_t riscv32_reg_write(target_s *target, uint32_t reg, const void *data, size_t max); static void riscv32_regs_read(target_s *target, void *data); static void riscv32_regs_write(target_s *target, const void *data); +static void riscv32_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); +static void riscv32_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len); static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); @@ -566,7 +568,139 @@ static void riscv32_sysbus_mem_write( riscv32_sysbus_mem_adjusted_write(hart, address, data, remainder, native_access_width, native_access_length); } -void riscv32_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) +static void riscv32_abstract_progbuf_mem_read( + riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) +{ + if (!(hart->extensions & RV_ISA_EXT_COMPRESSED)) { + DEBUG_ERROR("This target does not implement the compressed ISA extension\n"); + return; + } + + /* Figure out the maximal width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + // const uint8_t access_length = 1U << access_width; + // /* Build the access command */ + // const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_ABST_MEM_ACCESS_SHIFT) | + // (access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U); + // /* Write the address to read to arg1 */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) + // return; + // uint8_t *const data = (uint8_t *)dest; + // for (size_t offset = 0; offset < len; offset += access_length) { + // /* Execute the read */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + // return; + // /* Extract back the data from arg0 */ + // uint32_t value = 0; + // if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value)) + // return; + // riscv32_unpack_data(data + offset, value, access_width); + // } + + /* Disable auto-exec */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_AUTO, 0)) + // return; + + /* + * progbuf 0 + * c.lw x8,0(x11) // Pull the address from DATA1 + * c.lw x9,0(x8) // Read the data at that location + */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF0, 0x40044180U)) + return; + + /* + * progbuf 1 + * c.nop // alternately, `c.addi x8, 4` , for auto-increment (0xc1040411) + * c.sw x9, 0(x10) // Write back to DATA0 + */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF1, 0xc1040001U)) + return; + + /* + * progbuf 2 + * c.sw x8, 0(x11) // Write addy to DATA1 + * c.ebreak + */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF2, 0x9002c180U)) + return; + + /* StaticUpdatePROGBUFRegs */ + uint32_t rr; + if (!riscv_dm_read(hart->dbg_module, 0x12U, &rr)) { + DEBUG_ERROR("Could not get hart info\n"); + return; + } + DEBUG_INFO("rr: %08" PRIx32 "\n", rr); + const uint32_t data0_offset = 0xe0000000U | (rr & 0x7ffU); + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, data0_offset)) // DATA0's location in memory. + return; + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100aU)) // Copy data to x10 + return; + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, data0_offset + 4U)) // DATA1's location in memory. + return; + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100bU)) // Copy data to x11 + return; + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, 0x40022010U)) // FLASH->CTLR + // return; + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100cU)) // Copy data to x12 + // return; + // #define CR_PAGE_PG ((uint32_t)0x00010000) + // #define CR_BUF_LOAD ((uint32_t)0x00040000) + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, CR_PAGE_PG | CR_BUF_LOAD)) + // return; + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100dU)) // Copy data to x13 + // return; + + /* Enable auto-exec */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_AUTO, 1U)) + // return; + + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) + return; + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x00241000U) || !riscv_command_wait_complete(hart)) + return; + + /* Extract back the data from arg0 */ + uint32_t value = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value)) + return; + + riscv32_unpack_data(dest, value, access_width); +} + +static void riscv32_abstract_progbuf_mem_write( + riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte write of %08" PRIx32 " using PROGBUF\n", len, dest); + + (void)hart; + (void)dest; + (void)src; + (void)len; + + // /* Figure out the maximal width of access to perform, up to the bitness of the target */ + // const uint8_t access_width = riscv_mem_access_width(hart, dest, len); + // const uint8_t access_length = 1U << access_width; + // /* Build the access command */ + // const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_WRITE | (access_width << RV_ABST_MEM_ACCESS_SHIFT) | + // (access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U); + // /* Write the address to write to arg1 */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, dest)) + // return; + // const uint8_t *const data = (const uint8_t *)src; + // for (size_t offset = 0; offset < len; offset += access_length) { + // /* Pack the data to write into arg0 */ + // uint32_t value = riscv32_pack_data(data + offset, access_width); + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value)) + // return; + // /* Execute the write */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + // return; + // } +} + +static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) { /* If we're asked to do a 0-byte read, do nothing */ if (!len) { @@ -577,8 +711,15 @@ void riscv32_mem_read(target_s *const target, void *const dest, const target_add riscv_hart_s *const hart = riscv_hart_struct(target); if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) riscv32_sysbus_mem_read(hart, dest, src, len); - else + else if (hart->flags & RV_HART_FLAG_MEMORY_ABSTRACT) { riscv32_abstract_mem_read(hart, dest, src, len); + if (hart->status == RISCV_HART_NOT_SUPP) { + DEBUG_WARN("Abstract memory access not supported, falling back to prog buffer\n"); + hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_ABSTRACT; + riscv32_abstract_progbuf_mem_read(hart, dest, src, len); + } + } else + riscv32_abstract_progbuf_mem_read(hart, dest, src, len); #if ENABLE_DEBUG DEBUG_PROTO("%s: @ %08" PRIx32 " len %zu:", __func__, (uint32_t)src, len); @@ -596,7 +737,8 @@ void riscv32_mem_read(target_s *const target, void *const dest, const target_add #endif } -void riscv32_mem_write(target_s *const target, const target_addr64_t dest, const void *const src, const size_t len) +static void riscv32_mem_write( + target_s *const target, const target_addr64_t dest, const void *const src, const size_t len) { #if ENABLE_DEBUG DEBUG_PROTO("%s: @ %" PRIx32 " len %zu:", __func__, (uint32_t)dest, len); @@ -619,8 +761,15 @@ void riscv32_mem_write(target_s *const target, const target_addr64_t dest, const riscv_hart_s *const hart = riscv_hart_struct(target); if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) riscv32_sysbus_mem_write(hart, dest, src, len); - else + else if (hart->flags & RV_HART_FLAG_MEMORY_ABSTRACT) { riscv32_abstract_mem_write(hart, dest, src, len); + if (hart->status == RISCV_HART_NOT_SUPP) { + DEBUG_WARN("Abstract memory access not supported, falling back to prog buffer\n"); + hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_ABSTRACT; + riscv32_abstract_progbuf_mem_write(hart, dest, src, len); + } + } else + riscv32_abstract_progbuf_mem_write(hart, dest, src, len); } /* diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 2a046382c35..6ae1573e027 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -961,19 +961,25 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) static void riscv_hart_memory_access_type(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); - hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_SYSBUS; - uint32_t sysbus_status; + hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_MASK; /* * Try reading the system bus access control and status register. * Check if the value read back is non-zero for the sbasize field */ - if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &sysbus_status) || - !(sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK)) - return; - /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ - hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); - /* System Bus also means the target can have memory read without halting */ - target->target_options |= TOPT_NON_HALTING_MEM_IO; + uint32_t sysbus_status; + if (riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &sysbus_status) && + sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK) { + /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ + hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); + /* System Bus also means the target can have memory read without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; + } else { + /* + * If the system bus is not valid, we need to fall back to using abstract commands + * Later, if the memory access fails, we'll clear the flag and fall back to use the prog buffer + */ + hart->flags = RV_HART_FLAG_MEMORY_ABSTRACT; + } /* Make sure the system bus is not in any kind of error state */ (void)riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, 0x00407000U); } diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 23a6aee7ee3..2e097b390f0 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -171,6 +171,10 @@ typedef struct riscv_hart { #define RV_DM_DATA3 0x07U #define RV_DM_ABST_CTRLSTATUS 0x16U #define RV_DM_ABST_COMMAND 0x17U +#define RV_DM_ABST_AUTO 0x18U +#define RV_DM_PROGBUF0 0x20U +#define RV_DM_PROGBUF1 0x21U +#define RV_DM_PROGBUF2 0x22U #define RV_DM_SYSBUS_CTRLSTATUS 0x38U #define RV_DM_SYSBUS_ADDR0 0x39U #define RV_DM_SYSBUS_ADDR1 0x3aU @@ -302,7 +306,4 @@ uint8_t riscv_mem_access_width(const riscv_hart_s *hart, target_addr_t address, void riscv32_unpack_data(void *dest, uint32_t data, uint8_t access_width); uint32_t riscv32_pack_data(const void *src, uint8_t access_width); -void riscv32_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); -void riscv32_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len); - #endif /*TARGET_RISCV_DEBUG_H*/ From c643d8139194a1ba8b603d8289675893bd696753 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Wed, 17 Jan 2024 14:51:05 +0000 Subject: [PATCH 20/24] rvswd: gate rvswd functionality behind platform define --- src/command.c | 9 ++++++++- src/platforms/hosted/platform.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index 94cbe8b1571..7cea3ded04c 100644 --- a/src/command.c +++ b/src/command.c @@ -56,7 +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); +#ifdef 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); @@ -95,7 +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"}, +#ifdef 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"}, @@ -318,6 +322,7 @@ bool cmd_swd_scan(target_s *target, int argc, const char **argv) return true; } +#ifdef PLATFORM_HAS_RVSWD bool cmd_rvswd_scan(target_s *target, int argc, const char **argv) { (void)target; @@ -361,6 +366,7 @@ bool cmd_rvswd_scan(target_s *target, int argc, const char **argv) morse(NULL, false); return true; } +#endif bool cmd_auto_scan(target_s *target, int argc, const char **argv) { @@ -390,7 +396,7 @@ bool cmd_auto_scan(target_s *target, int argc, const char **argv) #endif if (!scan_result) { gdb_out("SWD scan found no devices.\n"); - +#ifdef PLATFORM_HAS_RVSWD #if CONFIG_BMDA == 1 scan_result = bmda_rvswd_scan(); #else @@ -398,6 +404,7 @@ bool cmd_auto_scan(target_s *target, int argc, const char **argv) #endif if (!scan_result) gdb_out("RVSWD scan found no devices.\n"); +#endif } } } diff --git a/src/platforms/hosted/platform.h b/src/platforms/hosted/platform.h index 78a8544a78b..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 From 223a1ad5299a74974db78eda1c522481ebb2089c Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Wed, 17 Jan 2024 14:51:39 +0000 Subject: [PATCH 21/24] experimental: start working towards a native implementation --- src/command.c | 4 +- src/include/rvswd.h | 44 +++++ src/include/target.h | 3 + src/platforms/common/meson.build | 1 + src/platforms/common/rvswd.c | 197 ++++++++++++++++++++++ src/platforms/hosted/platform.c | 2 + src/platforms/native/platform.h | 1 + src/target/meson.build | 1 + src/target/rvswd_scan.c | 269 +++++++++++++++++++++++++++++++ src/target/rvswd_scan.h | 64 ++++++++ 10 files changed, 584 insertions(+), 2 deletions(-) create mode 100644 src/include/rvswd.h create mode 100644 src/platforms/common/rvswd.c create mode 100644 src/target/rvswd_scan.c create mode 100644 src/target/rvswd_scan.h diff --git a/src/command.c b/src/command.c index 7cea3ded04c..c1b84191cf1 100644 --- a/src/command.c +++ b/src/command.c @@ -340,7 +340,7 @@ bool cmd_rvswd_scan(target_s *target, int argc, const char **argv) #if CONFIG_BMDA == 1 scan_result = bmda_rvswd_scan(); #else - scan_result = false; + scan_result = rvswd_scan(); #endif } CATCH () { @@ -400,7 +400,7 @@ bool cmd_auto_scan(target_s *target, int argc, const char **argv) #if CONFIG_BMDA == 1 scan_result = bmda_rvswd_scan(); #else - scan_result = false; + scan_result = rvswd_scan(); #endif if (!scan_result) gdb_out("RVSWD scan found no devices.\n"); diff --git a/src/include/rvswd.h b/src/include/rvswd.h new file mode 100644 index 00000000000..79235ec4106 --- /dev/null +++ b/src/include/rvswd.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * 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 INCLUDE_RVSWD_H +#define INCLUDE_RVSWD_H + +#include +#include +#include + +/* Functions interface talking RVSWD */ +typedef struct rvswd_proc { + /* Perform a clock_cycles read */ + uint32_t (*seq_in)(size_t clock_cycles); + /* Perform a clock_cycles read + parity */ + bool (*seq_in_parity)(uint32_t *ret, size_t clock_cycles); + /* Perform a clock_cycles write with the provided data */ + void (*seq_out)(uint32_t tms_states, size_t clock_cycles); + /* Perform a clock_cycles write + parity with the provided data */ + void (*seq_out_parity)(uint32_t tms_states, size_t clock_cycles); +} rvswd_proc_s; + +extern rvswd_proc_s rvswd_proc; + +void rvswd_init(void); + +#endif /* INCLUDE_RVSWD_H */ diff --git a/src/include/target.h b/src/include/target.h index 80783b68fe0..2ca65276791 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -49,6 +49,9 @@ bool bmda_rvswd_scan(void); #endif bool adiv5_swd_scan(uint32_t targetid); bool jtag_scan(void); +// #ifdef PLATFORM_HAS_RVSWD +bool rvswd_scan(void); +// #endif size_t target_foreach(void (*callback)(size_t index, target_s *target, void *context), void *context); void target_list_free(void); diff --git a/src/platforms/common/meson.build b/src/platforms/common/meson.build index 4c2ce75a98b..ced755b32ea 100644 --- a/src/platforms/common/meson.build +++ b/src/platforms/common/meson.build @@ -34,6 +34,7 @@ platform_common_sources = files( 'aux_serial.c', 'jtagtap.c', 'swdptap.c', + 'rvswd.c', 'usb.c', 'usb_dfu_stub.c', 'usb_serial.c', diff --git a/src/platforms/common/rvswd.c b/src/platforms/common/rvswd.c new file mode 100644 index 00000000000..ae7e931238d --- /dev/null +++ b/src/platforms/common/rvswd.c @@ -0,0 +1,197 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * 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 the SW-DP interface. */ + +#include "general.h" +#include "platform.h" +#include "timing.h" +#include "rvswd.h" +#include "maths_utils.h" + +// FIXME: reusing the SWD macros for now +#if !defined(SWDIO_IN_PORT) +#define SWDIO_IN_PORT SWDIO_PORT +#endif +#if !defined(SWDIO_IN_PIN) +#define SWDIO_IN_PIN SWDIO_PIN +#endif + +// typedef enum swdio_status_e { +// SWDIO_STATUS_FLOAT = 0, +// SWDIO_STATUS_DRIVE +// } swdio_status_t; + +rvswd_proc_s rvswd_proc; + +// static void swdptap_turnaround(swdio_status_t dir) __attribute__((optimize(3))); +// static uint32_t swdptap_seq_in(size_t clock_cycles) __attribute__((optimize(3))); +// static bool swdptap_seq_in_parity(uint32_t *ret, size_t clock_cycles) __attribute__((optimize(3))); +// static void swdptap_seq_out(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); +// static void swdptap_seq_out_parity(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); + +void rvswd_init(void) +{ + // rvswd_proc.seq_in = swdptap_seq_in; + // rvswd_proc.seq_in_parity = swdptap_seq_in_parity; + // rvswd_proc.seq_out = swdptap_seq_out; + // rvswd_proc.seq_out_parity = swdptap_seq_out_parity; +} + +// static void swdptap_turnaround(const swdio_status_t dir) +// { +// static swdio_status_t olddir = SWDIO_STATUS_FLOAT; +// /* Don't turnaround if direction not changing */ +// if (dir == olddir) +// return; +// olddir = dir; + +// #ifdef DEBUG_SWD_BITS +// DEBUG_INFO("%s", dir ? "\n-> " : "\n<- "); +// #endif + +// if (dir == SWDIO_STATUS_FLOAT) { +// SWDIO_MODE_FLOAT(); +// } else +// gpio_clear(SWCLK_PORT, SWCLK_PIN); + +// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) +// continue; + +// gpio_set(SWCLK_PORT, SWCLK_PIN); +// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) +// continue; + +// if (dir == SWDIO_STATUS_DRIVE) { +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// SWDIO_MODE_DRIVE(); +// } +// } + +// static uint32_t swdptap_seq_in_clk_delay(size_t clock_cycles) __attribute__((optimize(3))); + +// static uint32_t swdptap_seq_in_clk_delay(const size_t clock_cycles) +// { +// uint32_t value = 0; +// for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// value |= gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN) ? 1U << cycle : 0U; +// for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) +// continue; +// gpio_set(SWCLK_PORT, SWCLK_PIN); +// for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) +// continue; +// } +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// return value; +// } + +// static uint32_t swdptap_seq_in_no_delay(size_t clock_cycles) __attribute__((optimize(3))); + +// static uint32_t swdptap_seq_in_no_delay(const size_t clock_cycles) +// { +// uint32_t value = 0; +// for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// value |= gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN) ? 1U << cycle : 0U; +// gpio_set(SWCLK_PORT, SWCLK_PIN); +// __asm__("nop"); +// } +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// return value; +// } + +// static uint32_t swdptap_seq_in(size_t clock_cycles) +// { +// swdptap_turnaround(SWDIO_STATUS_FLOAT); +// if (target_clk_divider != UINT32_MAX) +// return swdptap_seq_in_clk_delay(clock_cycles); +// else // NOLINT(readability-else-after-return) +// return swdptap_seq_in_no_delay(clock_cycles); +// } + +// static bool swdptap_seq_in_parity(uint32_t *ret, size_t clock_cycles) +// { +// const uint32_t result = swdptap_seq_in(clock_cycles); +// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) +// continue; + +// const bool parity = calculate_odd_parity(result); +// const bool bit = gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN); + +// gpio_set(SWCLK_PORT, SWCLK_PIN); +// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) +// continue; + +// *ret = result; +// /* Terminate the read cycle now */ +// swdptap_turnaround(SWDIO_STATUS_DRIVE); +// return parity != bit; +// } + +// static void swdptap_seq_out_clk_delay(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); + +// static void swdptap_seq_out_clk_delay(const uint32_t tms_states, const size_t clock_cycles) +// { +// for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// gpio_set_val(SWDIO_PORT, SWDIO_PIN, tms_states & (1 << cycle)); +// for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) +// continue; +// gpio_set(SWCLK_PORT, SWCLK_PIN); +// for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) +// continue; +// } +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// } + +// static void swdptap_seq_out_no_delay(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); + +// static void swdptap_seq_out_no_delay(const uint32_t tms_states, const size_t clock_cycles) +// { +// for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// gpio_set_val(SWDIO_PORT, SWDIO_PIN, tms_states & (1 << cycle)); +// gpio_set(SWCLK_PORT, SWCLK_PIN); +// } +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// } + +// static void swdptap_seq_out(const uint32_t tms_states, const size_t clock_cycles) +// { +// swdptap_turnaround(SWDIO_STATUS_DRIVE); +// if (target_clk_divider != UINT32_MAX) +// swdptap_seq_out_clk_delay(tms_states, clock_cycles); +// else +// swdptap_seq_out_no_delay(tms_states, clock_cycles); +// } + +// static void swdptap_seq_out_parity(const uint32_t tms_states, const size_t clock_cycles) +// { +// const bool parity = calculate_odd_parity(tms_states); +// swdptap_seq_out(tms_states, clock_cycles); +// gpio_set_val(SWDIO_PORT, SWDIO_PIN, parity); +// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) +// continue; +// gpio_set(SWCLK_PORT, SWCLK_PIN); +// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) +// continue; +// gpio_clear(SWCLK_PORT, SWCLK_PIN); +// } diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index 041fbd362a3..d6111fe3451 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -31,6 +31,7 @@ #include "platform.h" #include "jtagtap.h" #include "swd.h" +#include "rvswd.h" #include "target.h" #include "target_internal.h" #include "adiv5.h" @@ -64,6 +65,7 @@ bmda_probe_s bmda_probe_info; #ifndef ENABLE_GPIOD jtag_proc_s jtag_proc; swd_proc_s swd_proc; +rvswd_proc_s rvswd_proc; #endif static uint32_t max_frequency = 4000000U; diff --git a/src/platforms/native/platform.h b/src/platforms/native/platform.h index e2c393c7a33..abc7e99cc14 100644 --- a/src/platforms/native/platform.h +++ b/src/platforms/native/platform.h @@ -29,6 +29,7 @@ #define PLATFORM_HAS_TRACESWO #define PLATFORM_HAS_POWER_SWITCH +#define PLATFORM_HAS_RVSWD #if ENABLE_DEBUG == 1 #define PLATFORM_HAS_DEBUG diff --git a/src/target/meson.build b/src/target/meson.build index fafb91d3a05..22073372aa8 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -42,6 +42,7 @@ target_common_sources = files( 'gdb_reg.c', 'jtag_devs.c', 'jtag_scan.c', + 'rvswd_scan.c', 'semihosting.c', 'sfdp.c', 'spi.c', diff --git a/src/target/rvswd_scan.c b/src/target/rvswd_scan.c new file mode 100644 index 00000000000..6fe49a13fe6 --- /dev/null +++ b/src/target/rvswd_scan.c @@ -0,0 +1,269 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * All rights reserved. + * + * 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. + */ + +/* This file implements RVSWD protocol support. TODO */ + +#include "general.h" +// #include "jtagtap.h" +#include "rvswd_scan.h" + +// #include "target.h" +// #include "adiv5.h" +// #include "jtag_devs.h" +// #include "gdb_packet.h" + +/* + * This scans the RVSWD interface for any possible device chain attached. + * It acomplishes this by performing these basic steps: + * + * 1. Perform a SWD -> JTAG transition just in case any ARM devices were in SWD mode + * 2. Reset the TAPs of any attached device (this ensures they're all in ID code mode) + * 3. Read out the ID code register chain, shifting in all 1's, until we read an + * all-1's ID back (indicating the end of the chain) + * 4. Read out the active instruction register chain, shifting in all 1's, + * and applying quirks as required to calculate how long each IR is + * 5. Switch back to the DR chain and read out all the devices again now they are in + * BYPASS mode as a way to validate we have the chain length right + * + * Once this process is complete, all devices should be accounted for, the + * device structures all set up with suitable pre- and post-scan values for both the + * IR and DR chains, and all devices should be in BYPASS ready for additional probing + * and inspection. Finally, we loop through seeing if we understand any of the + * ID codes seen and dispatching to suitable handlers if we do. + */ +bool rvswd_scan(void) +{ + /* Free the device list if any, and clean state ready */ + target_list_free(); + +#if CONFIG_BMDA == 0 + rvswd_init(); +#endif + + // platform_target_clk_output_enable(false); + + return false; +} + +// static bool jtag_read_idcodes(void) +// { +// /* Reset the chain ready and transition to Shift-DR */ +// jtag_proc.jtagtap_reset(); +// DEBUG_INFO("Change state to Shift-DR\n"); +// jtagtap_shift_dr(); + +// DEBUG_INFO("Scanning out ID codes\n"); +// size_t device = 0; +// /* Try scanning to one ID past the end of the chain */ +// for (; device <= JTAG_MAX_DEVS; ++device) { +// /* Try to read out 32 bits, while shifting in 1's */ +// uint32_t idcode = 0; +// jtag_proc.jtagtap_tdi_tdo_seq((uint8_t *)&idcode, false, ones, 32); +// /* If the IDCode read is all 1's, we've reached the end */ +// if (idcode == 0xffffffffU) +// break; +// /* Check if the max supported chain length is exceeded */ +// if (device == JTAG_MAX_DEVS) { +// DEBUG_ERROR("jtag_scan: Maximum chain length exceeded\n"); +// jtag_dev_count = 0; +// return false; +// } +// /* We got a valid device, add it to the set */ +// jtag_devs[device].jd_idcode = idcode; +// } + +// /* Well, it worked, so clean up and do housekeeping */ +// DEBUG_INFO("Return to Run-Test/Idle\n"); +// jtag_proc.jtagtap_next(true, true); +// jtagtap_return_idle(1); +// jtag_dev_count = device; +// return true; +// } + +// static void jtag_display_idcodes(void) +// { +// #if ENABLE_DEBUG == 1 +// for (size_t device = 0; device < jtag_dev_count; ++device) { +// const char *description = "Unknown"; +// for (size_t idx = 0; dev_descr[idx].idcode; ++idx) { +// if ((jtag_devs[device].jd_idcode & dev_descr[idx].idmask) == dev_descr[idx].idcode) { +// if (dev_descr[idx].descr) +// description = dev_descr[idx].descr; +// break; +// } +// } +// DEBUG_INFO("ID code 0x%08" PRIx32 ": %s\n", jtag_devs[device].jd_idcode, description); +// } +// #endif +// } + +// static jtag_ir_quirks_s jtag_device_get_quirks(const uint32_t idcode) +// { +// for (size_t idx = 0; dev_descr[idx].idcode; ++idx) { +// if ((idcode & dev_descr[idx].idmask) == dev_descr[idx].idcode) +// return dev_descr[idx].ir_quirks; +// } +// return (jtag_ir_quirks_s){0}; +// } + +// static bool jtag_read_irs(void) +// { +// /* Transition to Shift-IR */ +// DEBUG_INFO("Change state to Shift-IR\n"); +// jtagtap_shift_ir(); + +// DEBUG_INFO("Scanning out IRs\n"); +// /* Start with no prescan and the first device */ +// size_t prescan = 0U; +// size_t device = 0U; +// uint8_t ir_len = 0U; +// /* Grab the first device's quirks, if any */ +// jtag_ir_quirks_s ir_quirks = jtag_device_get_quirks(jtag_devs[0].jd_idcode); + +// /* Try scanning out the IR for the device */ +// while (ir_len <= JTAG_MAX_IR_LEN) { +// /* Read the next IR bit */ +// const bool next_bit = jtag_proc.jtagtap_next(false, true); +// /* If we have quirks, validate the bit against the expected IR */ +// if (ir_quirks.ir_length && ((ir_quirks.ir_value >> ir_len) & 1U) != next_bit) { +// DEBUG_ERROR("jtag_scan: IR does not match the expected value, bailing out\n"); +// jtag_dev_count = 0; +// return false; +// } +// /* IEEE 1149.1 requires the first bit to be a 1, but not all devices conform (see #1130 on GH) */ +// if (ir_len == 0 && !next_bit) +// DEBUG_WARN("jtag_scan: Sanity check failed: IR[0] shifted out as 0\n"); + +// /* The bit validated ok, so increment the counter */ +// ++ir_len; + +// /* +// * If we do not have quirks in play, this was a 1 bit and we're not reading the first bit of the +// * current IR, or if we've now read sufficient bits for the quirk, we've begun the next device +// */ +// if ((!ir_quirks.ir_length && next_bit && ir_len > 1U) || ir_len == ir_quirks.ir_length) { +// /* If we're not in quirks mode and the IR length is now 2 (2 1-bits in a row read), we're actually done */ +// if (!ir_quirks.ir_length && ir_len == 2U) +// break; + +// /* +// * If we're reading using quirks, we'll read exactly the right number of bits, +// * if not then we overrun by 1 for the device. Calculate the adjustment. +// */ +// const uint8_t overrun = ir_quirks.ir_length ? 0U : 1U; +// const uint8_t device_ir = ir_len - overrun; + +// /* Set up the IR fields for the device and set up for the next */ +// jtag_devs[device].ir_len = device_ir; +// jtag_devs[device].ir_prescan = prescan; +// jtag_devs[device].current_ir = UINT32_MAX; +// prescan += device_ir; +// ++device; +// ir_len = overrun; +// /* Grab the device quirks for this new device, if any */ +// ir_quirks = jtag_device_get_quirks(jtag_devs[device].jd_idcode); +// } +// } + +// /* Sanity check that we didn't get an over-long IR */ +// if (ir_len > JTAG_MAX_IR_LEN) { +// DEBUG_ERROR("jtag_scan: Maximum IR length exceeded\n"); +// jtag_dev_count = 0; +// return 0; +// } +// return true; +// } + +// static bool jtag_sanity_check(void) +// { +// /* Transition to Shift-DR */ +// DEBUG_INFO("Change state to Shift-DR\n"); +// jtagtap_shift_dr(); +// /* Count devices on chain */ +// size_t device = 0; +// for (; device <= jtag_dev_count; ++device) { +// if (jtag_proc.jtagtap_next(false, true)) +// break; +// /* Configure the DR pre/post scan values */ +// jtag_devs[device].dr_prescan = device; +// jtag_devs[device].dr_postscan = jtag_dev_count - device - 1U; +// } + +// /* If the device count gleaned above does not match the device count, error out */ +// if (device != jtag_dev_count) { +// DEBUG_ERROR("jtag_scan: Sanity check failed: BYPASS dev count doesn't match IR scan\n"); +// jtag_dev_count = 0; +// return false; +// } + +// /* Everything's accounted for, so clean up */ +// DEBUG_INFO("Return to Run-Test/Idle\n"); +// jtag_proc.jtagtap_next(true, true); +// jtagtap_return_idle(1); +// /* Return if there are any devices on the scan chain */ +// return jtag_dev_count; +// } + +// void jtag_dev_write_ir(const uint8_t dev_index, const uint32_t ir) +// { +// jtag_dev_s *device = &jtag_devs[dev_index]; +// /* If the request would duplicate work already done, do nothing */ +// if (ir == device->current_ir) +// return; + +// /* Set all the other devices IR's to being in bypass */ +// for (size_t device_index = 0; device_index < jtag_dev_count; device_index++) +// jtag_devs[device_index].current_ir = UINT32_MAX; +// device->current_ir = ir; + +// /* Do the work to make the scanchain match the jtag_devs state */ +// jtagtap_shift_ir(); +// jtag_proc.jtagtap_tdi_seq(false, ones, device->ir_prescan); +// jtag_proc.jtagtap_tdi_seq(!device->ir_postscan, (const uint8_t *)&ir, device->ir_len); +// jtag_proc.jtagtap_tdi_seq(true, ones, device->ir_postscan); +// jtagtap_return_idle(1); +// } + +// void jtag_dev_shift_dr(const uint8_t dev_index, uint8_t *data_out, const uint8_t *data_in, const size_t clock_cycles) +// { +// jtag_dev_s *device = &jtag_devs[dev_index]; +// jtagtap_shift_dr(); +// jtag_proc.jtagtap_tdi_seq(false, ones, device->dr_prescan); +// if (data_out) +// jtag_proc.jtagtap_tdi_tdo_seq( +// (uint8_t *)data_out, !device->dr_postscan, (const uint8_t *)data_in, clock_cycles); +// else +// jtag_proc.jtagtap_tdi_seq(!device->dr_postscan, (const uint8_t *)data_in, clock_cycles); +// jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); +// jtagtap_return_idle(1); +// } diff --git a/src/target/rvswd_scan.h b/src/target/rvswd_scan.h new file mode 100644 index 00000000000..81ca84ee09a --- /dev/null +++ b/src/target/rvswd_scan.h @@ -0,0 +1,64 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * All rights reserved. + * + * 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 TARGET_RVSWD_SCAN_H +#define TARGET_RVSWD_SCAN_H + +// #include +#include "rvswd.h" + +// #define JTAG_MAX_DEVS 32U +// #define JTAG_MAX_IR_LEN 32U /* NOTE: This is not long enough for all Xilinx devices */ + +// typedef struct jtag_dev { +// uint32_t jd_idcode; +// uint32_t current_ir; + +// /* The DR prescan doubles as the device index */ +// uint8_t dr_prescan; +// uint8_t dr_postscan; + +// uint8_t ir_len; +// uint8_t ir_prescan; +// uint8_t ir_postscan; +// } jtag_dev_s; + +// extern jtag_dev_s jtag_devs[JTAG_MAX_DEVS]; +// extern uint32_t jtag_dev_count; +// extern const uint8_t ones[8]; + +// void jtag_dev_write_ir(uint8_t jd_index, uint32_t ir); +// void jtag_dev_shift_dr(uint8_t jd_index, uint8_t *dout, const uint8_t *din, size_t ticks); +// void jtag_add_device(uint32_t dev_index, const jtag_dev_s *jtag_dev); + +#endif /* TARGET_RVSWD_SCAN_H */ From 1f2d949091eb6671bea1002e43242b2d67abd2b5 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 19 Dec 2024 14:10:53 +0000 Subject: [PATCH 22/24] gdb_packet: add note on gdb remote protocol debugging This information is hard to find, so while not directly related to BMD it is useful while developing. Adding it for future developer reference --- src/gdb_packet.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gdb_packet.c b/src/gdb_packet.c index acc0051be3a..89994ee63ef 100644 --- a/src/gdb_packet.c +++ b/src/gdb_packet.c @@ -81,6 +81,10 @@ void gdb_set_noackmode(bool enable) } #ifndef DEBUG_GDB_IS_NOOP +/* + * To see what packets GDB is seeing you can enable remote protocol debugging with: + * `(gdb) set debug remote 1` + */ static void gdb_packet_debug(const char *const func, const gdb_packet_s *const packet) { /* Log packet for debugging */ From d1b2b5aaf81de6d82fc5b4db4ddc0d1361e6320e Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 19 Dec 2024 14:12:57 +0000 Subject: [PATCH 23/24] riscv32: fix RV32E register count and respective size RV32E base ISA devices have 16 GPRs while RV32I base ISA devices have 32, this is correctly reported to gdb in the target description xml, but when reading or writing all registers 32 registers were considered to be present --- src/target/riscv32.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 51c1a2e3b2e..a39ff5ce5b1 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -40,11 +40,6 @@ #include "gdb_packet.h" #include "adiv5.h" -typedef struct riscv32_regs { - uint32_t gprs[32]; - uint32_t pc; -} riscv32_regs_s; - /* This defines a match trigger that's for an address or data location */ #define RV32_MATCH_ADDR_DATA_TRIGGER 0x20000000U /* A dmode of 1 restricts the writability of the trigger to debug mode only */ @@ -80,8 +75,11 @@ static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); bool riscv32_probe(target_s *const target) { + /* 'E' base ISA has 16 GPRs + PC, 'I' base ISA has 32 GPRs + PC */ + riscv_hart_s *const hart = riscv_hart_struct(target); + target->regs_size = ((hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U) + 1U) * sizeof(uint32_t); + /* Finish setting up the target structure with generic rv32 functions */ - target->regs_size = sizeof(riscv32_regs_s); /* Provide the length of a suitable registers structure */ target->regs_read = riscv32_regs_read; target->regs_write = riscv32_regs_write; target->reg_write = riscv32_reg_write; @@ -119,30 +117,30 @@ static void riscv32_regs_read(target_s *const target, void *const data) { /* Grab the hart structure and figure out how many registers need reading out */ riscv_hart_s *const hart = riscv_hart_struct(target); - riscv32_regs_s *const regs = (riscv32_regs_s *)data; + uint32_t *const regs = (uint32_t *)data; const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; /* Loop through reading out the GPRs */ for (size_t gpr = 0; gpr < gprs_count; ++gpr) { // TODO: handle when this fails.. - riscv_csr_read(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + riscv_csr_read(hart, RV_GPR_BASE + gpr, ®s[gpr]); } /* Special access to grab the program counter that would be executed on resuming the hart */ - riscv_csr_read(hart, RV_DPC, ®s->pc); + riscv_csr_read(hart, RV_DPC, ®s[gprs_count]); } static void riscv32_regs_write(target_s *const target, const void *const data) { /* Grab the hart structure and figure out how many registers need reading out */ riscv_hart_s *const hart = riscv_hart_struct(target); - const riscv32_regs_s *const regs = (const riscv32_regs_s *)data; + const uint32_t *const regs = (const uint32_t *)data; const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; /* Loop through writing out the GPRs, except for the first which is always 0 */ for (size_t gpr = 1; gpr < gprs_count; ++gpr) { // TODO: handle when this fails.. - riscv_csr_write(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + riscv_csr_write(hart, RV_GPR_BASE + gpr, ®s[gpr]); } /* Special access to poke in the program counter that will be executed on resuming the hart */ - riscv_csr_write(hart, RV_DPC, ®s->pc); + riscv_csr_write(hart, RV_DPC, ®s[gprs_count]); } static inline size_t riscv32_bool_to_4(const bool ret) From 80725c5ffc73bbc2d2d8075cbf52d8e88ba98856 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 19 Dec 2024 20:16:59 +0000 Subject: [PATCH 24/24] rvswd: experimental native implementation (does not work yet!) --- src/include/rvswd.h | 10 +- src/platforms/common/rvswd.c | 378 +++++++++++++++++++---------------- src/target/meson.build | 2 +- src/target/riscv_debug.c | 2 +- src/target/rvswd_dtm.c | 198 ++++++++++++++++++ src/target/rvswd_scan.c | 269 ------------------------- src/target/rvswd_scan.h | 64 ------ 7 files changed, 410 insertions(+), 513 deletions(-) create mode 100644 src/target/rvswd_dtm.c delete mode 100644 src/target/rvswd_scan.c delete mode 100644 src/target/rvswd_scan.h diff --git a/src/include/rvswd.h b/src/include/rvswd.h index 79235ec4106..125377053d6 100644 --- a/src/include/rvswd.h +++ b/src/include/rvswd.h @@ -27,14 +27,14 @@ /* Functions interface talking RVSWD */ typedef struct rvswd_proc { + /* Perform a start condition */ + void (*start)(void); + /* Perform a start condition */ + void (*stop)(void); /* Perform a clock_cycles read */ uint32_t (*seq_in)(size_t clock_cycles); - /* Perform a clock_cycles read + parity */ - bool (*seq_in_parity)(uint32_t *ret, size_t clock_cycles); /* Perform a clock_cycles write with the provided data */ - void (*seq_out)(uint32_t tms_states, size_t clock_cycles); - /* Perform a clock_cycles write + parity with the provided data */ - void (*seq_out_parity)(uint32_t tms_states, size_t clock_cycles); + void (*seq_out)(uint32_t dio_states, size_t clock_cycles); } rvswd_proc_s; extern rvswd_proc_s rvswd_proc; diff --git a/src/platforms/common/rvswd.c b/src/platforms/common/rvswd.c index ae7e931238d..b734924e4ea 100644 --- a/src/platforms/common/rvswd.c +++ b/src/platforms/common/rvswd.c @@ -1,24 +1,41 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2011 Black Sphere Technologies Ltd. - * Written by Gareth McMullin + * Copyright (C) 2025 1BitSquared + * Written by Rafael Silva + * All rights reserved. * - * 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * 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. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * 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. */ -/* This file implements the SW-DP interface. */ +/* + * This file implements the RVSWD interface + * + * It's mostly the same routines as the SWD interface, with some RVSWD specifics + */ #include "general.h" #include "platform.h" @@ -26,172 +43,187 @@ #include "rvswd.h" #include "maths_utils.h" -// FIXME: reusing the SWD macros for now -#if !defined(SWDIO_IN_PORT) -#define SWDIO_IN_PORT SWDIO_PORT -#endif -#if !defined(SWDIO_IN_PIN) -#define SWDIO_IN_PIN SWDIO_PIN -#endif +/** + * RVSWD I/O is shared with SWD + */ +#define RVSWD_DIO_DIR_PORT SWDIO_DIR_PORT +#define RVSWD_DIO_PORT SWDIO_PORT +#define RVSWD_CLK_PORT SWCLK_PORT +#define RVSWD_DIO_DIR_PIN SWDIO_DIR_PIN +#define RVSWD_DIO_PIN SWDIO_PIN +#define RVSWD_CLK_PIN SWCLK_PIN -// typedef enum swdio_status_e { -// SWDIO_STATUS_FLOAT = 0, -// SWDIO_STATUS_DRIVE -// } swdio_status_t; +#define RVSWD_DIO_MODE_FLOAT SWDIO_MODE_FLOAT +#define RVSWD_DIO_MODE_DRIVE SWDIO_MODE_DRIVE rvswd_proc_s rvswd_proc; -// static void swdptap_turnaround(swdio_status_t dir) __attribute__((optimize(3))); -// static uint32_t swdptap_seq_in(size_t clock_cycles) __attribute__((optimize(3))); -// static bool swdptap_seq_in_parity(uint32_t *ret, size_t clock_cycles) __attribute__((optimize(3))); -// static void swdptap_seq_out(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); -// static void swdptap_seq_out_parity(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); +static void rvswd_start(void) __attribute__((optimize(3))); +static void rvswd_stop(void) __attribute__((optimize(3))); + +static uint32_t rvswd_seq_in(size_t clock_cycles) __attribute__((optimize(3))); +static uint32_t rvswd_seq_in_clk_delay(size_t clock_cycles) __attribute__((optimize(3))); +static uint32_t rvswd_seq_in_no_delay(size_t clock_cycles) __attribute__((optimize(3))); + +static void rvswd_seq_out(uint32_t dio_states, size_t clock_cycles) __attribute__((optimize(3))); +static void rvswd_seq_out_clk_delay(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); +static void rvswd_seq_out_no_delay(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); + +static inline void __attribute__((always_inline)) rvswd_hold_period(void) +{ + /* Hold for a period */ + for (volatile uint32_t counter = target_clk_divider + 1U; counter > 0U; --counter) + continue; +} void rvswd_init(void) { - // rvswd_proc.seq_in = swdptap_seq_in; - // rvswd_proc.seq_in_parity = swdptap_seq_in_parity; - // rvswd_proc.seq_out = swdptap_seq_out; - // rvswd_proc.seq_out_parity = swdptap_seq_out_parity; + rvswd_proc.start = rvswd_start; + rvswd_proc.stop = rvswd_stop; + rvswd_proc.seq_in = rvswd_seq_in; + rvswd_proc.seq_out = rvswd_seq_out; } -// static void swdptap_turnaround(const swdio_status_t dir) -// { -// static swdio_status_t olddir = SWDIO_STATUS_FLOAT; -// /* Don't turnaround if direction not changing */ -// if (dir == olddir) -// return; -// olddir = dir; - -// #ifdef DEBUG_SWD_BITS -// DEBUG_INFO("%s", dir ? "\n-> " : "\n<- "); -// #endif - -// if (dir == SWDIO_STATUS_FLOAT) { -// SWDIO_MODE_FLOAT(); -// } else -// gpio_clear(SWCLK_PORT, SWCLK_PIN); - -// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) -// continue; - -// gpio_set(SWCLK_PORT, SWCLK_PIN); -// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) -// continue; - -// if (dir == SWDIO_STATUS_DRIVE) { -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// SWDIO_MODE_DRIVE(); -// } -// } - -// static uint32_t swdptap_seq_in_clk_delay(size_t clock_cycles) __attribute__((optimize(3))); - -// static uint32_t swdptap_seq_in_clk_delay(const size_t clock_cycles) -// { -// uint32_t value = 0; -// for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// value |= gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN) ? 1U << cycle : 0U; -// for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) -// continue; -// gpio_set(SWCLK_PORT, SWCLK_PIN); -// for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) -// continue; -// } -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// return value; -// } - -// static uint32_t swdptap_seq_in_no_delay(size_t clock_cycles) __attribute__((optimize(3))); - -// static uint32_t swdptap_seq_in_no_delay(const size_t clock_cycles) -// { -// uint32_t value = 0; -// for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// value |= gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN) ? 1U << cycle : 0U; -// gpio_set(SWCLK_PORT, SWCLK_PIN); -// __asm__("nop"); -// } -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// return value; -// } - -// static uint32_t swdptap_seq_in(size_t clock_cycles) -// { -// swdptap_turnaround(SWDIO_STATUS_FLOAT); -// if (target_clk_divider != UINT32_MAX) -// return swdptap_seq_in_clk_delay(clock_cycles); -// else // NOLINT(readability-else-after-return) -// return swdptap_seq_in_no_delay(clock_cycles); -// } - -// static bool swdptap_seq_in_parity(uint32_t *ret, size_t clock_cycles) -// { -// const uint32_t result = swdptap_seq_in(clock_cycles); -// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) -// continue; - -// const bool parity = calculate_odd_parity(result); -// const bool bit = gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN); - -// gpio_set(SWCLK_PORT, SWCLK_PIN); -// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) -// continue; - -// *ret = result; -// /* Terminate the read cycle now */ -// swdptap_turnaround(SWDIO_STATUS_DRIVE); -// return parity != bit; -// } - -// static void swdptap_seq_out_clk_delay(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); - -// static void swdptap_seq_out_clk_delay(const uint32_t tms_states, const size_t clock_cycles) -// { -// for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// gpio_set_val(SWDIO_PORT, SWDIO_PIN, tms_states & (1 << cycle)); -// for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) -// continue; -// gpio_set(SWCLK_PORT, SWCLK_PIN); -// for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) -// continue; -// } -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// } - -// static void swdptap_seq_out_no_delay(uint32_t tms_states, size_t clock_cycles) __attribute__((optimize(3))); - -// static void swdptap_seq_out_no_delay(const uint32_t tms_states, const size_t clock_cycles) -// { -// for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// gpio_set_val(SWDIO_PORT, SWDIO_PIN, tms_states & (1 << cycle)); -// gpio_set(SWCLK_PORT, SWCLK_PIN); -// } -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// } - -// static void swdptap_seq_out(const uint32_t tms_states, const size_t clock_cycles) -// { -// swdptap_turnaround(SWDIO_STATUS_DRIVE); -// if (target_clk_divider != UINT32_MAX) -// swdptap_seq_out_clk_delay(tms_states, clock_cycles); -// else -// swdptap_seq_out_no_delay(tms_states, clock_cycles); -// } - -// static void swdptap_seq_out_parity(const uint32_t tms_states, const size_t clock_cycles) -// { -// const bool parity = calculate_odd_parity(tms_states); -// swdptap_seq_out(tms_states, clock_cycles); -// gpio_set_val(SWDIO_PORT, SWDIO_PIN, parity); -// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) -// continue; -// gpio_set(SWCLK_PORT, SWCLK_PIN); -// for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) -// continue; -// gpio_clear(SWCLK_PORT, SWCLK_PIN); -// } +static void rvswd_start(void) +{ + /* + * DIO falling edge while CLK is idle high marks a START condition + */ + + /* Setup for the start sequence by setting the bus to the idle state */ + RVSWD_DIO_MODE_DRIVE(); + gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + gpio_set(RVSWD_DIO_DIR_PORT, RVSWD_DIO_PIN); + + /* Ensure the bus is idle for a period */ + rvswd_hold_period(); + + /* Generate the start condition */ + gpio_clear(RVSWD_DIO_PORT, RVSWD_DIO_PIN); + rvswd_hold_period(); +} + +static void rvswd_stop(void) +{ + /* + * DIO rising edge while CLK is idle high marks a STOP condition + */ + + /* + * Setup for the stop condition by driving the CLK and DIO low + * + * It is likely that the previous sequence left the CLK low already + * but a redundant low CLK set ensures we don't issue a start condition by mistake + */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + RVSWD_DIO_MODE_DRIVE(); + gpio_clear(RVSWD_DIO_PORT, RVSWD_DIO_PIN); + + /* Ensure setup for a period */ + rvswd_hold_period(); + + /* Generate the stop condition */ + gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + rvswd_hold_period(); + gpio_set(RVSWD_DIO_PORT, RVSWD_DIO_PIN); +} + +static uint32_t rvswd_seq_in_clk_delay(const size_t clock_cycles) +{ + uint32_t value = 0; /* Return value */ + + /* Shift clock_cycles bits in */ + for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { + /* Drive the CLK low and hold for a period */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + rvswd_hold_period(); + + /* Sample the DIO line and raise the CLK, then hold for a period */ + value |= gpio_get(RVSWD_DIO_PORT, RVSWD_DIO_PIN) ? 1U << cycle : 0U; + gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + rvswd_hold_period(); + } + + /* Leave the CLK low and return the value */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + return value; +} + +static uint32_t rvswd_seq_in_no_delay(const size_t clock_cycles) +{ + uint32_t value = 0U; /* Return value */ + + /* Shift clock_cycles bits in */ + for (size_t cycle = 0U; cycle < clock_cycles; ++cycle) { + /* Drive the CLK low */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + + /* Sample the DIO line and raise the CLK */ + value |= gpio_get(RVSWD_DIO_PORT, RVSWD_DIO_PIN) ? 1U << cycle : 0U; + gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + + __asm__("nop"); /* Ensure there's time for the CLK to settle */ + } + + /* Leave the CLK low and return the value */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + return value; +} + +static uint32_t rvswd_seq_in(size_t clock_cycles) +{ + /* Set the DIO line to float to give control to the target */ + RVSWD_DIO_MODE_FLOAT(); + + /* Delegate to the appropriate sequence in routine depending on the clock divider */ + if (target_clk_divider != UINT32_MAX) + return rvswd_seq_in_clk_delay(clock_cycles); + else + return rvswd_seq_in_no_delay(clock_cycles); +} + +static void rvswd_seq_out_clk_delay(const uint32_t dio_states, const size_t clock_cycles) +{ + /* Shift clock_cycles bits out */ + for (size_t cycle = 0U; cycle < clock_cycles; ++cycle) { + /* Drive the CLK low and setup the DIO line, then hold for a period */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + gpio_set_val(SWDIO_PORT, SWDIO_PIN, dio_states & (1U << cycle)); + rvswd_hold_period(); + + /* Raise the CLK and hold for a period */ + gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + rvswd_hold_period(); + } + + /* Leave the CLK low */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); +} + +static void rvswd_seq_out_no_delay(const uint32_t dio_states, const size_t clock_cycles) +{ + /* Shift clock_cycles bits out */ + for (size_t cycle = 0; cycle < clock_cycles; ++cycle) { + /* Drive the CLK low and setup the DIO line */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + gpio_set_val(SWDIO_PORT, SWDIO_PIN, dio_states & (1U << cycle)); + + /* Raise the CLK */ + gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN); + } + + /* Leave the CLK low */ + gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN); +} + +static void rvswd_seq_out(const uint32_t dio_states, const size_t clock_cycles) +{ + /* Set the DIO line to drive to give us control */ + RVSWD_DIO_MODE_DRIVE(); + + /* Delegate to the appropriate sequence in routine depending on the clock divider */ + if (target_clk_divider != UINT32_MAX) + rvswd_seq_out_clk_delay(dio_states, clock_cycles); + else + rvswd_seq_out_no_delay(dio_states, clock_cycles); +} diff --git a/src/target/meson.build b/src/target/meson.build index 22073372aa8..c137bd69883 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -42,7 +42,7 @@ target_common_sources = files( 'gdb_reg.c', 'jtag_devs.c', 'jtag_scan.c', - 'rvswd_scan.c', + 'rvswd_dtm.c', 'semihosting.c', 'sfdp.c', 'spi.c', diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 6ae1573e027..052023d0687 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -423,7 +423,7 @@ static void riscv_hart_read_ids(riscv_hart_s *const hart) } static size_t riscv_snprint_isa_subset( - char *const string_buffer, const size_t buffer_size, const uint32_t access_width, const uint32_t extensions) + char *const string_buffer, const size_t buffer_size, const uint8_t access_width, const uint32_t extensions) { size_t offset = snprintf(string_buffer, buffer_size, "rv%" PRIu8, access_width); diff --git a/src/target/rvswd_dtm.c b/src/target/rvswd_dtm.c new file mode 100644 index 00000000000..995e1fe0012 --- /dev/null +++ b/src/target/rvswd_dtm.c @@ -0,0 +1,198 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2025 1BitSquared + * Written by Rafael Silva + * All rights reserved. + * + * 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. + */ + +/* This file implements RVSWD protocol support. TODO */ + +#include "general.h" +#include "rvswd.h" +#include "riscv_debug.h" +#include "maths_utils.h" +#include "jep106.h" + +static void rvswd_wakeup_sequence(void) +{ + /* + * A wakeup sequence consist of 100 CLK cycles with DIO high followed by a STOP condition + */ + DEBUG_INFO("Performing RVSWD wakeup sequence\n"); + + /* 100 CLK cycles with DIO high */ + for (size_t i = 0U; i < 100U; i += 32U) { + const size_t cycles = 100U - i; /* Remaining cycles */ + rvswd_proc.seq_out(0xffffffffU, MAX(cycles, 32U)); /* Up to 32 cycles HIGH */ + } + rvswd_proc.stop(); /* STOP condition */ +} + +static bool rvswd_transfer_dmi(const uint8_t operation, const uint32_t address, const uint32_t value, + uint32_t *const result, uint8_t *const status) +{ + /* + * RVSWD "Long" packet format: + * + * + */ + DEBUG_INFO("Performing RVSWD DMI transfer: operation %d, address 0x%08" PRIx32 ", value 0x%08" PRIx32 "\n", + operation, address, value); + + /* Address limited to 7 bits for now, TODO: check if RVSWD supports more somehow */ + if (address & ~0x7fU) { + DEBUG_ERROR("Address 0x%08" PRIx32 " is too large for RVSWD\n", address); + return false; + } + + /* TODO: This is just experimental code, it should packed into 32-bit words for efficiency */ + + const uint8_t host_parity = + calculate_odd_parity(address & 0x7fU) ^ calculate_odd_parity(value) ^ calculate_odd_parity(operation & 0x3U); + + /* Start condition */ + rvswd_proc.start(); + + /* Host address */ + rvswd_proc.seq_out(address & 0x7fU, 7U); + + /* Host data */ + rvswd_proc.seq_out(value, 32U); + + /* Operation */ + rvswd_proc.seq_out(operation & 0x3U, 2U); + + /* Host parity */ + rvswd_proc.seq_out(host_parity, 1U); + + /* Target address */ + const uint32_t target_address = rvswd_proc.seq_in(7U); + + /* Target data */ + const uint32_t target_data = rvswd_proc.seq_in(32U); + + /* Status */ + const uint32_t target_status = rvswd_proc.seq_in(2U); + + /* Target parity */ + const uint32_t target_parity = rvswd_proc.seq_in(1U); + + /* Stop condition */ + rvswd_proc.stop(); + + /* Check parity */ + const uint8_t calculated_target_parity = calculate_odd_parity(target_address & 0x7fU) ^ + calculate_odd_parity(target_data) ^ calculate_odd_parity(target_status & 0x3U); + + if (target_parity != calculated_target_parity) { + DEBUG_ERROR("Parity error in RVSWD transfer: expected %u, got %u\n", (uint8_t)target_parity, + (uint8_t)calculated_target_parity); + // return false; + } + + DEBUG_INFO("RVSWD DMI transfer result: status %u, address 0x%08" PRIx32 ", value 0x%08" PRIx32 "\n", + (uint8_t)target_status, target_address, target_data); + + if (result) + *result = target_data; + if (status) + *status = target_status; + + return true; +} + +static bool rvswd_riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +{ + uint8_t status = 0; + const bool result = rvswd_transfer_dmi(RV_DMI_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 == 1U ? RV_DMI_FAILURE : status; + return dmi->fault == RV_DMI_SUCCESS; +} + +static bool rvswd_riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) +{ + uint8_t status = 0; + const bool result = rvswd_transfer_dmi(RV_DMI_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 == 1U ? RV_DMI_FAILURE : status; + return dmi->fault == RV_DMI_SUCCESS; +} + +static void rvswd_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 = NOT_JEP106_MANUFACTURER_WCH; + + dmi->version = RISCV_DEBUG_0_13; /* Assumption, unverified */ + + /* WCH-Link has a fixed address width of 8 bits, limited by the USB protocol (is RVSWD also fixed?) */ + dmi->address_width = 8U; + + dmi->read = rvswd_riscv_dmi_read; + dmi->write = rvswd_riscv_dmi_write; + + riscv_dmi_init(dmi); +} + +static void riscv_rvswd_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; + } + + rvswd_riscv_dtm_init(dmi); + /* If we failed to find any DMs or Harts, free the structure */ + if (!dmi->ref_count) + free(dmi); +} + +bool rvswd_scan(void) +{ + /* Free the device list if any, and clean state ready */ + target_list_free(); + +#if CONFIG_BMDA == 0 + rvswd_init(); +#endif + + platform_target_clk_output_enable(true); + + /* Run the wakeup sequence */ + rvswd_wakeup_sequence(); + + /* Delegate to the RISC-V DTM handler */ + riscv_rvswd_dtm_handler(); + + return false; +} diff --git a/src/target/rvswd_scan.c b/src/target/rvswd_scan.c deleted file mode 100644 index 6fe49a13fe6..00000000000 --- a/src/target/rvswd_scan.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * This file is part of the Black Magic Debug project. - * - * Copyright (C) 2023 1BitSquared - * Written by Rafael Silva - * All rights reserved. - * - * 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. - */ - -/* This file implements RVSWD protocol support. TODO */ - -#include "general.h" -// #include "jtagtap.h" -#include "rvswd_scan.h" - -// #include "target.h" -// #include "adiv5.h" -// #include "jtag_devs.h" -// #include "gdb_packet.h" - -/* - * This scans the RVSWD interface for any possible device chain attached. - * It acomplishes this by performing these basic steps: - * - * 1. Perform a SWD -> JTAG transition just in case any ARM devices were in SWD mode - * 2. Reset the TAPs of any attached device (this ensures they're all in ID code mode) - * 3. Read out the ID code register chain, shifting in all 1's, until we read an - * all-1's ID back (indicating the end of the chain) - * 4. Read out the active instruction register chain, shifting in all 1's, - * and applying quirks as required to calculate how long each IR is - * 5. Switch back to the DR chain and read out all the devices again now they are in - * BYPASS mode as a way to validate we have the chain length right - * - * Once this process is complete, all devices should be accounted for, the - * device structures all set up with suitable pre- and post-scan values for both the - * IR and DR chains, and all devices should be in BYPASS ready for additional probing - * and inspection. Finally, we loop through seeing if we understand any of the - * ID codes seen and dispatching to suitable handlers if we do. - */ -bool rvswd_scan(void) -{ - /* Free the device list if any, and clean state ready */ - target_list_free(); - -#if CONFIG_BMDA == 0 - rvswd_init(); -#endif - - // platform_target_clk_output_enable(false); - - return false; -} - -// static bool jtag_read_idcodes(void) -// { -// /* Reset the chain ready and transition to Shift-DR */ -// jtag_proc.jtagtap_reset(); -// DEBUG_INFO("Change state to Shift-DR\n"); -// jtagtap_shift_dr(); - -// DEBUG_INFO("Scanning out ID codes\n"); -// size_t device = 0; -// /* Try scanning to one ID past the end of the chain */ -// for (; device <= JTAG_MAX_DEVS; ++device) { -// /* Try to read out 32 bits, while shifting in 1's */ -// uint32_t idcode = 0; -// jtag_proc.jtagtap_tdi_tdo_seq((uint8_t *)&idcode, false, ones, 32); -// /* If the IDCode read is all 1's, we've reached the end */ -// if (idcode == 0xffffffffU) -// break; -// /* Check if the max supported chain length is exceeded */ -// if (device == JTAG_MAX_DEVS) { -// DEBUG_ERROR("jtag_scan: Maximum chain length exceeded\n"); -// jtag_dev_count = 0; -// return false; -// } -// /* We got a valid device, add it to the set */ -// jtag_devs[device].jd_idcode = idcode; -// } - -// /* Well, it worked, so clean up and do housekeeping */ -// DEBUG_INFO("Return to Run-Test/Idle\n"); -// jtag_proc.jtagtap_next(true, true); -// jtagtap_return_idle(1); -// jtag_dev_count = device; -// return true; -// } - -// static void jtag_display_idcodes(void) -// { -// #if ENABLE_DEBUG == 1 -// for (size_t device = 0; device < jtag_dev_count; ++device) { -// const char *description = "Unknown"; -// for (size_t idx = 0; dev_descr[idx].idcode; ++idx) { -// if ((jtag_devs[device].jd_idcode & dev_descr[idx].idmask) == dev_descr[idx].idcode) { -// if (dev_descr[idx].descr) -// description = dev_descr[idx].descr; -// break; -// } -// } -// DEBUG_INFO("ID code 0x%08" PRIx32 ": %s\n", jtag_devs[device].jd_idcode, description); -// } -// #endif -// } - -// static jtag_ir_quirks_s jtag_device_get_quirks(const uint32_t idcode) -// { -// for (size_t idx = 0; dev_descr[idx].idcode; ++idx) { -// if ((idcode & dev_descr[idx].idmask) == dev_descr[idx].idcode) -// return dev_descr[idx].ir_quirks; -// } -// return (jtag_ir_quirks_s){0}; -// } - -// static bool jtag_read_irs(void) -// { -// /* Transition to Shift-IR */ -// DEBUG_INFO("Change state to Shift-IR\n"); -// jtagtap_shift_ir(); - -// DEBUG_INFO("Scanning out IRs\n"); -// /* Start with no prescan and the first device */ -// size_t prescan = 0U; -// size_t device = 0U; -// uint8_t ir_len = 0U; -// /* Grab the first device's quirks, if any */ -// jtag_ir_quirks_s ir_quirks = jtag_device_get_quirks(jtag_devs[0].jd_idcode); - -// /* Try scanning out the IR for the device */ -// while (ir_len <= JTAG_MAX_IR_LEN) { -// /* Read the next IR bit */ -// const bool next_bit = jtag_proc.jtagtap_next(false, true); -// /* If we have quirks, validate the bit against the expected IR */ -// if (ir_quirks.ir_length && ((ir_quirks.ir_value >> ir_len) & 1U) != next_bit) { -// DEBUG_ERROR("jtag_scan: IR does not match the expected value, bailing out\n"); -// jtag_dev_count = 0; -// return false; -// } -// /* IEEE 1149.1 requires the first bit to be a 1, but not all devices conform (see #1130 on GH) */ -// if (ir_len == 0 && !next_bit) -// DEBUG_WARN("jtag_scan: Sanity check failed: IR[0] shifted out as 0\n"); - -// /* The bit validated ok, so increment the counter */ -// ++ir_len; - -// /* -// * If we do not have quirks in play, this was a 1 bit and we're not reading the first bit of the -// * current IR, or if we've now read sufficient bits for the quirk, we've begun the next device -// */ -// if ((!ir_quirks.ir_length && next_bit && ir_len > 1U) || ir_len == ir_quirks.ir_length) { -// /* If we're not in quirks mode and the IR length is now 2 (2 1-bits in a row read), we're actually done */ -// if (!ir_quirks.ir_length && ir_len == 2U) -// break; - -// /* -// * If we're reading using quirks, we'll read exactly the right number of bits, -// * if not then we overrun by 1 for the device. Calculate the adjustment. -// */ -// const uint8_t overrun = ir_quirks.ir_length ? 0U : 1U; -// const uint8_t device_ir = ir_len - overrun; - -// /* Set up the IR fields for the device and set up for the next */ -// jtag_devs[device].ir_len = device_ir; -// jtag_devs[device].ir_prescan = prescan; -// jtag_devs[device].current_ir = UINT32_MAX; -// prescan += device_ir; -// ++device; -// ir_len = overrun; -// /* Grab the device quirks for this new device, if any */ -// ir_quirks = jtag_device_get_quirks(jtag_devs[device].jd_idcode); -// } -// } - -// /* Sanity check that we didn't get an over-long IR */ -// if (ir_len > JTAG_MAX_IR_LEN) { -// DEBUG_ERROR("jtag_scan: Maximum IR length exceeded\n"); -// jtag_dev_count = 0; -// return 0; -// } -// return true; -// } - -// static bool jtag_sanity_check(void) -// { -// /* Transition to Shift-DR */ -// DEBUG_INFO("Change state to Shift-DR\n"); -// jtagtap_shift_dr(); -// /* Count devices on chain */ -// size_t device = 0; -// for (; device <= jtag_dev_count; ++device) { -// if (jtag_proc.jtagtap_next(false, true)) -// break; -// /* Configure the DR pre/post scan values */ -// jtag_devs[device].dr_prescan = device; -// jtag_devs[device].dr_postscan = jtag_dev_count - device - 1U; -// } - -// /* If the device count gleaned above does not match the device count, error out */ -// if (device != jtag_dev_count) { -// DEBUG_ERROR("jtag_scan: Sanity check failed: BYPASS dev count doesn't match IR scan\n"); -// jtag_dev_count = 0; -// return false; -// } - -// /* Everything's accounted for, so clean up */ -// DEBUG_INFO("Return to Run-Test/Idle\n"); -// jtag_proc.jtagtap_next(true, true); -// jtagtap_return_idle(1); -// /* Return if there are any devices on the scan chain */ -// return jtag_dev_count; -// } - -// void jtag_dev_write_ir(const uint8_t dev_index, const uint32_t ir) -// { -// jtag_dev_s *device = &jtag_devs[dev_index]; -// /* If the request would duplicate work already done, do nothing */ -// if (ir == device->current_ir) -// return; - -// /* Set all the other devices IR's to being in bypass */ -// for (size_t device_index = 0; device_index < jtag_dev_count; device_index++) -// jtag_devs[device_index].current_ir = UINT32_MAX; -// device->current_ir = ir; - -// /* Do the work to make the scanchain match the jtag_devs state */ -// jtagtap_shift_ir(); -// jtag_proc.jtagtap_tdi_seq(false, ones, device->ir_prescan); -// jtag_proc.jtagtap_tdi_seq(!device->ir_postscan, (const uint8_t *)&ir, device->ir_len); -// jtag_proc.jtagtap_tdi_seq(true, ones, device->ir_postscan); -// jtagtap_return_idle(1); -// } - -// void jtag_dev_shift_dr(const uint8_t dev_index, uint8_t *data_out, const uint8_t *data_in, const size_t clock_cycles) -// { -// jtag_dev_s *device = &jtag_devs[dev_index]; -// jtagtap_shift_dr(); -// jtag_proc.jtagtap_tdi_seq(false, ones, device->dr_prescan); -// if (data_out) -// jtag_proc.jtagtap_tdi_tdo_seq( -// (uint8_t *)data_out, !device->dr_postscan, (const uint8_t *)data_in, clock_cycles); -// else -// jtag_proc.jtagtap_tdi_seq(!device->dr_postscan, (const uint8_t *)data_in, clock_cycles); -// jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); -// jtagtap_return_idle(1); -// } diff --git a/src/target/rvswd_scan.h b/src/target/rvswd_scan.h deleted file mode 100644 index 81ca84ee09a..00000000000 --- a/src/target/rvswd_scan.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of the Black Magic Debug project. - * - * Copyright (C) 2023 1BitSquared - * Written by Rafael Silva - * All rights reserved. - * - * 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 TARGET_RVSWD_SCAN_H -#define TARGET_RVSWD_SCAN_H - -// #include -#include "rvswd.h" - -// #define JTAG_MAX_DEVS 32U -// #define JTAG_MAX_IR_LEN 32U /* NOTE: This is not long enough for all Xilinx devices */ - -// typedef struct jtag_dev { -// uint32_t jd_idcode; -// uint32_t current_ir; - -// /* The DR prescan doubles as the device index */ -// uint8_t dr_prescan; -// uint8_t dr_postscan; - -// uint8_t ir_len; -// uint8_t ir_prescan; -// uint8_t ir_postscan; -// } jtag_dev_s; - -// extern jtag_dev_s jtag_devs[JTAG_MAX_DEVS]; -// extern uint32_t jtag_dev_count; -// extern const uint8_t ones[8]; - -// void jtag_dev_write_ir(uint8_t jd_index, uint32_t ir); -// void jtag_dev_shift_dr(uint8_t jd_index, uint8_t *dout, const uint8_t *din, size_t ticks); -// void jtag_add_device(uint32_t dev_index, const jtag_dev_s *jtag_dev); - -#endif /* TARGET_RVSWD_SCAN_H */