Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: WCH RISC-V support #1399

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
06af671
hosted: barebones wch-link implementation
perigoso Aug 10, 2023
466049b
hosted/wchlink: implement DMI transfer
perigoso Aug 11, 2023
de413af
riscv_debug: expose DMI operation and status defines
perigoso Aug 11, 2023
0e75dde
jep106: add BMD internal flag
perigoso Jul 31, 2023
50ffc29
jep106: add internal WCH non-jep106 code
perigoso Feb 23, 2023
117562f
riscv_jtag_dtm: move RV_DMI_TOO_SOON handling one level up
perigoso Aug 11, 2023
03441a8
hosted/wchlink: implement RISC-V DTM handler
perigoso Aug 11, 2023
ee4269f
command: add rvswd scan routine command
perigoso Aug 11, 2023
7c15658
hosted/wchlink: implement RVSWD scan
perigoso Aug 11, 2023
6fa3a98
riscv32: add ch32v3 target probe
perigoso Feb 23, 2023
488dfcc
ch32vx: implement electronic signature (ESIG) register handling
perigoso Aug 11, 2023
2c08951
riscv32: add ch32v003 target probe
perigoso Oct 5, 2023
d586014
buffer_utils: add write_char util
perigoso Jan 15, 2024
ab51224
riscv_debug: formally parse the ISA subset
perigoso Jan 15, 2024
5b65fa6
riscv_debug: use the ISA subset as the target core name
perigoso Jan 15, 2024
b332e50
riscv_debug: fix typo
perigoso Jan 15, 2024
abd5766
riscv64: fix typo
perigoso Jan 15, 2024
b81e1a5
riscv_debug: update RV_CSR macros
perigoso Jan 15, 2024
0d3768b
!experimental! riscv32: add hacky fallback memory access using the pr…
perigoso Jan 15, 2024
1ce1332
rvswd: gate rvswd functionality behind platform define
perigoso Jan 17, 2024
2a30df5
experimental: start working towards a native implementation
perigoso Jan 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion src/command.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ static bool cmd_help(target_s *target, int argc, const char **argv);

static bool cmd_jtag_scan(target_s *target, int argc, const char **argv);
static bool cmd_swd_scan(target_s *target, int argc, const char **argv);
#ifdef PLATFORM_HAS_RVSWD
static bool cmd_rvswd_scan(target_s *target, int argc, const char **argv);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be defined contingent on ENABLE_RISCV and we'd suggest that, at least for now, this be limited to BMDA. Iff we can figure out how to implement the protocol in the firmware we can then de-restrict it by removing the suggested check for PC_HOSTED.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I will consider how to gate this when it's more complete, there will still be a native implementation

#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);
Expand Down Expand Up @@ -94,6 +97,9 @@ const command_s cmd_list[] = {
{"jtag_scan", cmd_jtag_scan, "Scan JTAG chain for devices"},
{"swd_scan", cmd_swd_scan, "Scan SWD interface for devices: [TARGET_ID]"},
{"swdp_scan", cmd_swd_scan, "Deprecated: use swd_scan instead"},
#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"},
Expand Down Expand Up @@ -316,6 +322,52 @@ 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;
(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 = rvswd_scan();
#endif
}
CATCH () {
case EXCEPTION_TIMEOUT:
gdb_outf("Timeout during scan. Is target stuck in WFI?\n");
break;
case EXCEPTION_ERROR:
gdb_outf("Exception: %s\n", exception_frame.msg);
break;
default:
break;
}

if (!scan_result) {
platform_target_clk_output_enable(false);
platform_nrst_set_val(false);
gdb_out("RVSWD scan failed!\n");
return false;
}

cmd_targets(NULL, 0, NULL);
platform_target_clk_output_enable(false);
morse(NULL, false);
return true;
}
#endif

bool cmd_auto_scan(target_s *target, int argc, const char **argv)
{
(void)target;
Expand All @@ -342,8 +394,18 @@ bool cmd_auto_scan(target_s *target, int argc, const char **argv)
#else
scan_result = adiv5_swd_scan(0);
#endif
if (!scan_result)
if (!scan_result) {
gdb_out("SWD scan found no devices.\n");
#ifdef PLATFORM_HAS_RVSWD
#if CONFIG_BMDA == 1
scan_result = bmda_rvswd_scan();
#else
scan_result = rvswd_scan();
#endif
if (!scan_result)
gdb_out("RVSWD scan found no devices.\n");
#endif
}
}
}
CATCH () {
Expand Down
7 changes: 7 additions & 0 deletions src/include/buffer_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*/
44 changes: 44 additions & 0 deletions src/include/rvswd.h
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef INCLUDE_RVSWD_H
#define INCLUDE_RVSWD_H

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

/* 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 */
4 changes: 4 additions & 0 deletions src/include/target.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@ 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);
#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);
Expand Down
1 change: 1 addition & 0 deletions src/platforms/common/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
197 changes: 197 additions & 0 deletions src/platforms/common/rvswd.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*
* 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 <http://www.gnu.org/licenses/>.
*/

/* 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);
// }
3 changes: 2 additions & 1 deletion src/platforms/hosted/bmp_libusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/platforms/hosted/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ bmda_sources = files(
'jlink.c',
'jlink_jtag.c',
'jlink_swd.c',
'wchlink.c',
'wchlink_rvswd.c',
'wchlink_riscv_dtm.c',
)
subdir('remote')

Expand Down
Loading
Loading