diff --git a/cross-file/native-riscv.ini b/cross-file/native-riscv.ini index 17ebf4c6482..5627870e86d 100644 --- a/cross-file/native-riscv.ini +++ b/cross-file/native-riscv.ini @@ -23,3 +23,4 @@ probe = 'native' targets = 'riscv32,riscv64,gd32,rp' rtt_support = false bmd_bootloader = true +rvswd_support = true diff --git a/cross-file/swlink-riscv.ini b/cross-file/swlink-riscv.ini new file mode 100644 index 00000000000..ca99d997beb --- /dev/null +++ b/cross-file/swlink-riscv.ini @@ -0,0 +1,26 @@ +# This a cross-file for the native probe. It defines a default configuration profile that +# provides only support for both RISC-V architectures and support for RISC-V targets. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'swlink' +targets = 'riscv32,riscv64,gd32,ch32v' +rtt_support = false +bmd_bootloader = false +rvswd_support = true diff --git a/src/command.c b/src/command.c index c86dedb34f7..d55fc669eee 100644 --- a/src/command.c +++ b/src/command.c @@ -63,6 +63,9 @@ static bool cmd_jtag_scan(target_s *target, int argc, const char **argv); static bool cmd_swd_scan(target_s *target, int argc, const char **argv); #if defined(CONFIG_RVSWD) && defined(PLATFORM_HAS_RVSWD) static bool cmd_rvswd_scan(target_s *target, int argc, const char **argv); +#if CONFIG_BMDA == 0 +bool bmp_rvswd_scan(void); +#endif #endif static bool cmd_onboard_flash_scan(target_s *target, int argc, const char **argv); static bool cmd_auto_scan(target_s *target, int argc, const char **argv); @@ -370,7 +373,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 = bmp_rvswd_scan(); #endif } CATCH () { @@ -423,6 +426,9 @@ bool cmd_auto_scan(target_s *target, int argc, const char **argv) { {jtag_scan, "JTAG"}, {adiv5_swd_scan, "SWD"}, +#if defined(CONFIG_RVSWD) && defined(PLATFORM_HAS_RVSWD) + {bmp_rvswd_scan, "RVSWD"}, + #endif }; /* clang-format on */ #endif diff --git a/src/platforms/common/meson.build b/src/platforms/common/meson.build index 4c2ce75a98b..35fae36a8b3 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', + 'rvswdptap.c', 'usb.c', 'usb_dfu_stub.c', 'usb_serial.c', diff --git a/src/platforms/common/rvswdptap.c b/src/platforms/common/rvswdptap.c new file mode 100644 index 00000000000..312b6c60ba4 --- /dev/null +++ b/src/platforms/common/rvswdptap.c @@ -0,0 +1,255 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by mean00 + * + * 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 RVSW interface used by WCH chips */ + +#include "general.h" +#include "platform.h" +#include "timing.h" +#include "swd.h" +#include "maths_utils.h" +#include "swdptap_common.h" +#include "jep106.h" +#include "riscv_debug.h" + +bool bmp_rvswd_scan(void); +// -- protocol part -- +static bool rv_dm_reset(void); +static bool rv_start_frame(uint32_t adr, bool wr); +static bool rv_end_frame(uint32_t *status); + +#define CLK_OFF() \ + { \ + gpio_clear(SWCLK_PORT, SWCLK_PIN); \ + for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) \ + __asm__("nop"); \ + } +#define CLK_ON() \ + { \ + gpio_set(SWCLK_PORT, SWCLK_PIN); \ + for (volatile uint32_t counter = target_clk_divider + 1; counter > 0; --counter) \ + __asm__("nop"); \ + } + +#define IO_OFF() \ + { \ + gpio_clear(SWDIO_PORT, SWDIO_PIN); \ + } +#define IO_ON() \ + { \ + gpio_set(SWDIO_PORT, SWDIO_PIN); \ + } + +static void rv_write_nbits(int n, uint32_t value) +{ + value <<= (uint32_t)(32 - n); + const uint32_t mask = 0x80000000UL; + for (int i = 0; i < n; i++) { + CLK_OFF(); + if (value & mask) + IO_ON() + else + IO_OFF(); + CLK_ON(); + value <<= 1; + } +} + +static void rv_start_bit(void) +{ + SWDIO_MODE_DRIVE(); + IO_OFF(); +} + +static void rv_stop_bit(void) +{ + CLK_OFF(); + SWDIO_MODE_DRIVE(); + IO_OFF(); + CLK_ON(); + IO_ON(); +} + +static uint32_t rv_read_nbits(int n) +{ + SWDIO_MODE_FLOAT(); + uint32_t out = 0; + for (int i = 0; i < n; i++) { + CLK_OFF(); + CLK_ON(); + out = (out << 1) + (!!gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN)); // read bit on rising edge + } + return out; +} + +static bool rv_dm_reset(void) +{ + // toggle the clock 100 times + SWDIO_MODE_DRIVE(); + IO_ON(); + for (int i = 0; i < 5; i++) // 100 bits to 1 + { + rv_write_nbits(20, 0xfffff); + } + IO_OFF(); + IO_ON(); + platform_delay(10); + return true; +} + +static bool rv_start_frame(uint32_t adr, bool wr) +{ + rv_start_bit(); + adr = (adr << 1) + wr; + uint8_t parity = calculate_odd_parity(adr); + adr = adr << 2 | (parity + parity + parity); + rv_write_nbits(10, adr); + return true; +} + +static bool rv_end_frame(uint32_t *status) +{ + uint32_t out = 0; + + // now get the reply - 4 bits + out = rv_read_nbits(4); + rv_stop_bit(); + + *status = out; + if (out != 3 && out != 7) { + DEBUG_ERROR("Status error : 0x%x\n", out); + return false; + } + return out; +} + +bool rv_dm_write(uint32_t adr, uint32_t val) +{ + rv_start_frame(adr, true); + + rv_write_nbits(4, 0); + // Now data + uint8_t parity2 = calculate_odd_parity(val); + rv_write_nbits(32, val); + + // data parity (twice) + rv_write_nbits(2, parity2 + parity2 + parity2); + + uint32_t st = 0; + if (!rv_end_frame(&st)) { + DEBUG_ERROR("Write failed Adr=0x%x Value=0x%x status=0x%x\n", adr, val, st); + return false; + } + return true; +} + +bool rv_dm_read(uint32_t adr, uint32_t *output) +{ + rv_start_frame(adr, false); + rv_write_nbits(4, 1); // 000 1 + *output = rv_read_nbits(32); + rv_read_nbits(2); // parity + + uint32_t st = 0; + if (!rv_end_frame(&st)) { + DEBUG_ERROR("Read failed Adr=0x%x Value=0x%x status=0x%x\n", adr, *output, st); + return false; + } + return true; +} + +static bool rv_dm_probe(uint32_t *chip_id) +{ + *chip_id = 0; + uint32_t out = 0; // scratch + // This follows exactly what the wchlink does + rv_dm_write(0x10, 0x80000001UL); // write DM CTRL = 0x800000001 + platform_delay(1); + rv_dm_write(0x10, 0x80000001UL); // write DM CTRL = 0x800000001 + platform_delay(1); + rv_dm_read(0x11, &out); // read DM_STATUS + platform_delay(1); + rv_dm_read(0x7f, chip_id); // read 0x7f + return ((*chip_id) & 0x7fff) != 0x7fff; +} + +//---------------------- RVSWD DMI ----------------------- +static bool rvswdp_riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +static bool rvswdp_riscv_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); + +static bool rvswdp_riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +{ + int retries = 1; + while (1) { + if (!retries) { + dmi->fault = RV_DMI_FAILURE; + return false; + } + const bool result = rv_dm_read(address, value); + if (result) { + dmi->fault = RV_DMI_SUCCESS; + return true; + } + retries--; + } +} + +static bool rvswdp_riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) +{ + const bool result = rv_dm_write(address, value); + if (result) { + dmi->fault = RV_DMI_SUCCESS; + return true; + } + dmi->fault = RV_DMI_FAILURE; + return false; +} + +bool bmp_rvswd_scan(void) +{ + uint32_t id = 0; + rv_dm_reset(); + target_list_free(); + if (!rv_dm_probe(&id)) { + return false; + } + DEBUG_INFO("WCH : found 0x%x device\n", id); + riscv_dmi_s *dmi = (riscv_dmi_s *)calloc(1, sizeof(riscv_dmi_s)); + if (!dmi) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return false; + } + dmi->designer_code = JEP106_MANUFACTURER_WCH; + dmi->version = RISCV_DEBUG_0_13; /* Assumption, unverified */ + /*dmi->version = RISCV_DEBUG_NONSTANDARD;*/ + dmi->address_width = 8U; + dmi->read = rvswdp_riscv_dmi_read; + dmi->write = rvswdp_riscv_dmi_write; + + riscv_dmi_init(dmi); + /* If we failed to find any DMs or Harts, free the structure */ + if (!dmi->ref_count) { + free(dmi); + return false; + } + return true; +} + +// EOF diff --git a/src/platforms/common/swdptap.c b/src/platforms/common/swdptap.c index 1f171f83dd0..e176914b011 100644 --- a/src/platforms/common/swdptap.c +++ b/src/platforms/common/swdptap.c @@ -26,17 +26,7 @@ #include "swd.h" #include "maths_utils.h" -#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; +#include "swdptap_common.h" swd_proc_s swd_proc; diff --git a/src/platforms/common/swdptap_common.h b/src/platforms/common/swdptap_common.h new file mode 100644 index 00000000000..0f796f3b30b --- /dev/null +++ b/src/platforms/common/swdptap_common.h @@ -0,0 +1,13 @@ +#pragma once + +#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; diff --git a/src/platforms/native/platform.h b/src/platforms/native/platform.h index 45ada7551a9..c08fd5207ef 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/platforms/swlink/platform.c b/src/platforms/swlink/platform.c index bff34040943..6262c3c452d 100644 --- a/src/platforms/swlink/platform.c +++ b/src/platforms/swlink/platform.c @@ -63,7 +63,7 @@ void platform_init(void) /* Unmap JTAG Pins so we can reuse as GPIO */ data = AFIO_MAPR; data &= ~AFIO_MAPR_SWJ_MASK; - data |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF; + data |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON; AFIO_MAPR = data; /* Setup JTAG GPIO ports */ gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_INPUT_FLOAT, TMS_PIN); diff --git a/src/platforms/swlink/platform.h b/src/platforms/swlink/platform.h index 65d774b74a6..0e1b67bd669 100644 --- a/src/platforms/swlink/platform.h +++ b/src/platforms/swlink/platform.h @@ -29,6 +29,7 @@ #include "timing_stm32.h" #define PLATFORM_HAS_TRACESWO +#define PLATFORM_HAS_RVSWD #if ENABLE_DEBUG == 1 #define PLATFORM_HAS_DEBUG @@ -43,11 +44,11 @@ extern bool debug_bmp; #define TDI_PORT GPIOA #define TDO_PORT GPIOB #define TRST_PORT GPIOB -#define TMS_PIN GPIO13 -#define TCK_PIN GPIO14 +#define TMS_PIN GPIO6 +#define TCK_PIN GPIO5 #define TDI_PIN GPIO15 #define TDO_PIN GPIO3 -#define TRST_PIN GPIO4 +#define TRST_PIN GPIO0 #define SWDIO_PORT TMS_PORT #define SWCLK_PORT TCK_PORT @@ -61,8 +62,8 @@ extern bool debug_bmp; #define LED_PORT_UART GPIOC #define LED_UART GPIO14 -#define SWD_CR GPIO_CRH(SWDIO_PORT) -#define SWD_CR_MULT (1U << ((13U - 8U) << 2U)) +#define SWD_CR GPIO_CRL(SWDIO_PORT) +#define SWD_CR_MULT (1U << ((6U) << 2U)) #define TMS_SET_MODE() gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN); #define SWDIO_MODE_FLOAT() \