Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.

Commit 28b7157

Browse files
perigosoRafael Silva
authored andcommitted
!experimental! rvswd: working implementation (change dump)
1 parent 19b844c commit 28b7157

12 files changed

Lines changed: 432 additions & 152 deletions

File tree

src/include/platform_support.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ void platform_timeout_set(platform_timeout_s *target, uint32_t ms);
5050
bool platform_timeout_is_expired(const platform_timeout_s *target);
5151
void platform_delay(uint32_t ms);
5252

53+
void platform_critical_enter(void);
54+
void platform_critical_exit(void);
55+
5356
#define POWER_CONFLICT_THRESHOLD 5U /* in 0.1V, so 5 stands for 0.5V */
5457

5558
extern bool connect_assert_nrst;

src/platforms/common/rvswd.c

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,20 @@
4646
/**
4747
* RVSWD I/O is shared with SWD
4848
*/
49-
#define RVSWD_DIO_DIR_PORT SWDIO_DIR_PORT
50-
#define RVSWD_DIO_PORT SWDIO_PORT
51-
#define RVSWD_CLK_PORT SWCLK_PORT
52-
#define RVSWD_DIO_DIR_PIN SWDIO_DIR_PIN
53-
#define RVSWD_DIO_PIN SWDIO_PIN
54-
#define RVSWD_CLK_PIN SWCLK_PIN
49+
#define RVSWD_DIO_PORT SWDIO_PORT
50+
#define RVSWD_DIO_PIN SWDIO_PIN
51+
52+
#define RVSWD_CLK_PORT SWCLK_PORT
53+
#define RVSWD_CLK_PIN SWCLK_PIN
5554

5655
#define RVSWD_DIO_MODE_FLOAT SWDIO_MODE_FLOAT
5756
#define RVSWD_DIO_MODE_DRIVE SWDIO_MODE_DRIVE
5857

58+
typedef enum rvswd_direction_t {
59+
RVSWD_DIRECTION_INPUT,
60+
RVSWD_DIRECTION_OUTPUT
61+
} rvswd_direction_t;
62+
5963
rvswd_proc_s rvswd_proc;
6064

6165
static void rvswd_start(void) __attribute__((optimize(3)));
@@ -84,19 +88,21 @@ void rvswd_init(void)
8488
rvswd_proc.seq_out = rvswd_seq_out;
8589
}
8690

87-
static void rvswd_set_dio_direction(bool output)
91+
static void rvswd_set_dio_direction(rvswd_direction_t direction)
8892
{
8993
/* Do nothing if the direction is already set */
90-
static bool current_direction = false;
91-
if (output == current_direction)
94+
/* FIXME: this internal state may become invalid if the IO is modified elsewhere (e.g. SWD) */
95+
static rvswd_direction_t current_direction = RVSWD_DIRECTION_INPUT;
96+
if (direction == current_direction)
9297
return;
9398

9499
/* Change the direction */
95-
if (output)
100+
if (direction == RVSWD_DIRECTION_OUTPUT) {
96101
RVSWD_DIO_MODE_DRIVE();
97-
else
102+
} else {
98103
RVSWD_DIO_MODE_FLOAT();
99-
current_direction = output;
104+
}
105+
current_direction = direction;
100106
}
101107

102108
static void rvswd_start(void)
@@ -106,9 +112,9 @@ static void rvswd_start(void)
106112
*/
107113

108114
/* Setup for the start sequence by setting the bus to the idle state */
109-
rvswd_set_dio_direction(true);
115+
rvswd_set_dio_direction(RVSWD_DIRECTION_OUTPUT);
116+
gpio_set(RVSWD_DIO_PORT, RVSWD_DIO_PIN);
110117
gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
111-
gpio_set(RVSWD_DIO_DIR_PORT, RVSWD_DIO_PIN);
112118

113119
/* Ensure the bus is idle for a period */
114120
rvswd_hold_period();
@@ -124,14 +130,9 @@ static void rvswd_stop(void)
124130
* DIO rising edge while CLK is idle high marks a STOP condition
125131
*/
126132

127-
/*
128-
* Setup for the stop condition by driving the CLK and DIO low
129-
*
130-
* It is likely that the previous sequence left the CLK low already
131-
* but a redundant low CLK set ensures we don't issue a start condition by mistake
132-
*/
133+
/* Setup for the stop condition by driving the CLK and DIO low */
133134
gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
134-
rvswd_set_dio_direction(true);
135+
rvswd_set_dio_direction(RVSWD_DIRECTION_OUTPUT);
135136
gpio_clear(RVSWD_DIO_PORT, RVSWD_DIO_PIN);
136137

137138
/* Ensure setup for a period */
@@ -153,14 +154,16 @@ static uint32_t rvswd_seq_in_clk_delay(const size_t clock_cycles)
153154
gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
154155
rvswd_hold_period();
155156

156-
/* Sample the DIO line and raise the CLK, then hold for a period */
157+
/* Sample the DIO line and Raise the CLK, then hold for a period */
157158
value |= gpio_get(RVSWD_DIO_PORT, RVSWD_DIO_PIN) ? (1U << (cycle - 1U)) : 0U;
158159
gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
159160
rvswd_hold_period();
160161
}
161162

162-
/* Leave the CLK low and return the value */
163-
gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
163+
// /* Leave the CLK low and return the value */
164+
// gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
165+
166+
/* Leave the CLK high and return the value */
164167
return value;
165168
}
166169

@@ -176,19 +179,19 @@ static uint32_t rvswd_seq_in_no_delay(const size_t clock_cycles)
176179
/* Sample the DIO line and raise the CLK */
177180
value |= gpio_get(RVSWD_DIO_PORT, RVSWD_DIO_PIN) ? (1U << (cycle - 1U)) : 0U;
178181
gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
179-
180-
__asm__("nop"); /* Ensure there's time for the CLK to settle */
181182
}
182183

183-
/* Leave the CLK low and return the value */
184-
gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
184+
// /* Leave the CLK low and return the value */
185+
// gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
186+
187+
/* Leave the CLK high and return the value */
185188
return value;
186189
}
187190

188191
static uint32_t rvswd_seq_in(size_t clock_cycles)
189192
{
190193
/* Set the DIO line to float to give control to the target */
191-
rvswd_set_dio_direction(false);
194+
rvswd_set_dio_direction(RVSWD_DIRECTION_INPUT);
192195

193196
/* Delegate to the appropriate sequence in routine depending on the clock divider */
194197
if (target_clk_divider != UINT32_MAX)
@@ -211,8 +214,10 @@ static void rvswd_seq_out_clk_delay(const uint32_t dio_states, const size_t cloc
211214
rvswd_hold_period();
212215
}
213216

214-
/* Leave the CLK low */
215-
gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
217+
// /* Leave the CLK low */
218+
// gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
219+
220+
/* Leave the CLK high and return */
216221
}
217222

218223
static void rvswd_seq_out_no_delay(const uint32_t dio_states, const size_t clock_cycles)
@@ -227,14 +232,16 @@ static void rvswd_seq_out_no_delay(const uint32_t dio_states, const size_t clock
227232
gpio_set(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
228233
}
229234

230-
/* Leave the CLK low */
231-
gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
235+
// /* Leave the CLK low */
236+
// gpio_clear(RVSWD_CLK_PORT, RVSWD_CLK_PIN);
237+
238+
/* Leave the CLK high and return */
232239
}
233240

234241
static void rvswd_seq_out(const uint32_t dio_states, const size_t clock_cycles)
235242
{
236243
/* Set the DIO line to drive to give us control */
237-
rvswd_set_dio_direction(true);
244+
rvswd_set_dio_direction(RVSWD_DIRECTION_OUTPUT);
238245

239246
/* Delegate to the appropriate sequence in routine depending on the clock divider */
240247
if (target_clk_divider != UINT32_MAX)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* This file is part of the Black Magic Debug project.
3+
*
4+
* Copyright (C) 2025 1BitSquared <info@1bitsquared.com>
5+
* Written by Rafael Silva <perigoso@riseup.net>
6+
* All rights reserved.
7+
*
8+
* Redistribution and use in source and binary forms, with or without
9+
* modification, are permitted provided that the following conditions are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright notice, this
12+
* list of conditions and the following disclaimer.
13+
*
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* 3. Neither the name of the copyright holder nor the names of its
19+
* contributors may be used to endorse or promote products derived from
20+
* this software without specific prior written permission.
21+
*
22+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32+
*/
33+
34+
/*
35+
* This file implements the RVSWD interface
36+
*
37+
* It's mostly the same routines as the SWD interface, with some RVSWD specifics
38+
*/
39+
40+
#include "general.h"
41+
#include "platform.h"
42+
43+
#include <libopencm3/cm3/cortex.h>
44+
45+
static uint32_t nested_critical = 0;
46+
static uint32_t primask = 0;
47+
48+
void platform_critical_enter(void)
49+
{
50+
if (nested_critical == 0) {
51+
primask = cm_mask_interrupts(1U);
52+
}
53+
nested_critical++;
54+
}
55+
56+
void platform_critical_exit(void)
57+
{
58+
nested_critical--;
59+
if (nested_critical == 0) {
60+
cm_mask_interrupts(primask);
61+
}
62+
}

src/platforms/common/stm32/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
platform_stm32_includes = include_directories('.')
3232

3333
platform_stm32_sources = files(
34+
'critical.c',
3435
'serialno.c',
3536
'timing_stm32.c',
3637
)

src/platforms/hosted/platform.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,16 @@ void platform_init(int argc, char **argv)
198198
}
199199
}
200200

201+
void platform_critical_enter(void)
202+
{
203+
/* Nothing to do */
204+
}
205+
206+
void platform_critical_exit(void)
207+
{
208+
/* Nothing to do */
209+
}
210+
201211
bool bmda_swd_scan(const uint32_t targetid)
202212
{
203213
bmda_probe_info.is_jtag = false;

src/platforms/hosted/wchlink_riscv_dtm.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,13 @@ static void wchlink_riscv_dtm_init(riscv_dmi_s *const dmi)
5858
/* WCH-Link doesn't have any mechanism to identify the DTM manufacturer, so we'll just assume it's WCH */
5959
dmi->designer_code = NOT_JEP106_MANUFACTURER_WCH;
6060

61-
dmi->version = RISCV_DEBUG_0_13; /* Assumption, unverified */
61+
dmi->version = RISCV_DEBUG_UNSPECIFIED; /* Not available */
6262

63-
/* WCH-Link has a fixed address width of 8 bits, limited by the USB protocol (is RVSWD also fixed?) */
64-
dmi->address_width = 8U;
63+
/*
64+
* WCH-Link has a fixed address width of 7 bits,
65+
* technically limited by the USB protocol to 8 bits but the underlying protocols are 7 bits
66+
*/
67+
dmi->address_width = 7U;
6568

6669
dmi->read = wchlink_riscv_dmi_read;
6770
dmi->write = wchlink_riscv_dmi_write;
@@ -72,19 +75,19 @@ static void wchlink_riscv_dtm_init(riscv_dmi_s *const dmi)
7275
static bool wchlink_riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value)
7376
{
7477
uint8_t status = 0;
75-
const bool result = wchlink_transfer_dmi(RV_DMI_READ, address, 0, value, &status);
78+
const bool result = wchlink_transfer_dmi(RV_DMI_OP_READ, address, 0, value, &status);
7679

7780
/* Translate error 1 into RV_DMI_FAILURE per the spec, also write RV_DMI_FAILURE if the transfer failed */
78-
dmi->fault = !result || status == 1U ? RV_DMI_FAILURE : status;
81+
dmi->fault = !result || status == RV_DMI_RESERVED ? RV_DMI_FAILURE : status;
7982
return dmi->fault == RV_DMI_SUCCESS;
8083
}
8184

8285
static bool wchlink_riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value)
8386
{
8487
uint8_t status = 0;
85-
const bool result = wchlink_transfer_dmi(RV_DMI_WRITE, address, value, NULL, &status);
88+
const bool result = wchlink_transfer_dmi(RV_DMI_OP_WRITE, address, value, NULL, &status);
8689

8790
/* Translate error 1 into RV_DMI_FAILURE per the spec, also write RV_DMI_FAILURE if the transfer failed */
88-
dmi->fault = !result || status == 1U ? RV_DMI_FAILURE : status;
91+
dmi->fault = !result || status == RV_DMI_RESERVED ? RV_DMI_FAILURE : status;
8992
return dmi->fault == RV_DMI_SUCCESS;
9093
}

src/target/riscv32.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ static void riscv32_abstract_mem_read(
240240
const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_ABST_MEM_ACCESS_SHIFT) |
241241
(access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U);
242242
/* Write the address to read to arg1 */
243-
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src))
243+
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA(1U), src))
244244
return;
245245
uint8_t *const data = (uint8_t *)dest;
246246
for (size_t offset = 0; offset < len; offset += access_length) {
@@ -249,7 +249,7 @@ static void riscv32_abstract_mem_read(
249249
return;
250250
/* Extract back the data from arg0 */
251251
uint32_t value = 0;
252-
if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value))
252+
if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA(0U), &value))
253253
return;
254254
riscv32_unpack_data(data + offset, value, access_width);
255255
}
@@ -265,13 +265,13 @@ static void riscv32_abstract_mem_write(
265265
const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_WRITE | (access_width << RV_ABST_MEM_ACCESS_SHIFT) |
266266
(access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U);
267267
/* Write the address to write to arg1 */
268-
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, dest))
268+
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA(1U), dest))
269269
return;
270270
const uint8_t *const data = (const uint8_t *)src;
271271
for (size_t offset = 0; offset < len; offset += access_length) {
272272
/* Pack the data to write into arg0 */
273273
uint32_t value = riscv32_pack_data(data + offset, access_width);
274-
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value))
274+
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA(0U), value))
275275
return;
276276
/* Execute the write */
277277
if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart))
@@ -604,23 +604,23 @@ static void riscv32_abstract_progbuf_mem_read(
604604
* c.lw x8,0(x11) // Pull the address from DATA1
605605
* c.lw x9,0(x8) // Read the data at that location
606606
*/
607-
if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF0, 0x40044180U))
607+
if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF(0U), 0x40044180U))
608608
return;
609609

610610
/*
611611
* progbuf 1
612612
* c.nop // alternately, `c.addi x8, 4` , for auto-increment (0xc1040411)
613613
* c.sw x9, 0(x10) // Write back to DATA0
614614
*/
615-
if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF1, 0xc1040001U))
615+
if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF(1U), 0xc1040001U))
616616
return;
617617

618618
/*
619619
* progbuf 2
620620
* c.sw x8, 0(x11) // Write addy to DATA1
621621
* c.ebreak
622622
*/
623-
if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF2, 0x9002c180U))
623+
if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF(2U), 0x9002c180U))
624624
return;
625625

626626
/* StaticUpdatePROGBUFRegs */
@@ -631,11 +631,11 @@ static void riscv32_abstract_progbuf_mem_read(
631631
}
632632
DEBUG_INFO("rr: %08" PRIx32 "\n", rr);
633633
const uint32_t data0_offset = 0xe0000000U | (rr & 0x7ffU);
634-
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, data0_offset)) // DATA0's location in memory.
634+
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA(0U), data0_offset)) // DATA0's location in memory.
635635
return;
636636
if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100aU)) // Copy data to x10
637637
return;
638-
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, data0_offset + 4U)) // DATA1's location in memory.
638+
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA(0U), data0_offset + 4U)) // DATA1's location in memory.
639639
return;
640640
if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100bU)) // Copy data to x11
641641
return;
@@ -654,14 +654,14 @@ static void riscv32_abstract_progbuf_mem_read(
654654
// if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_AUTO, 1U))
655655
// return;
656656

657-
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src))
657+
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA(1U), src))
658658
return;
659659
if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x00241000U) || !riscv_command_wait_complete(hart))
660660
return;
661661

662662
/* Extract back the data from arg0 */
663663
uint32_t value = 0;
664-
if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value))
664+
if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA(0U), &value))
665665
return;
666666

667667
riscv32_unpack_data(dest, value, access_width);

0 commit comments

Comments
 (0)