target: riscv: Sync with the RISC-V fork

Regenerate autogenerated debug_defines.{c,h} files from current
riscv-debug-spec, sync remaining RISC-V target files with the
RISC-V fork.

This is based on the work of (in alphabetic order):

Aleksey Lotosh <lotosh@gmail.com>
Alexander Rumyantsev <cetygamer@gmail.com>
Anastasiya Chernikova <anastasiya.chernikova@syntacore.com>
Anatoly Parshintsev <114445139+aap-sc@users.noreply.github.com>
Bernhard Rosenkränzer <bero@baylibre.com>
bluew <bluewww@users.noreply.github.com>
Carsten Gosvig <40368726+cgsfv@users.noreply.github.com>
cgsfv <cgsfv@users.noreply.github.com>
Craig Blackmore <craig.blackmore@embecosm.com>
Dan Robertson <danlrobertson89@gmail.com>
Darius Rad <darius@bluespec.com>
dave-estes-syzexion <53795406+dave-estes-syzexion@users.noreply.github.com>
Dmitry Ryzhov <dmitry.ryzhov@cloudbear.ru>
Dolu1990 <charles.papon.90@gmail.com>
Emmanuel Blot <emmanuel.blot@free.fr>
Ernie Edgar <43148441+ernie-sifive@users.noreply.github.com>
Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
Farid Khaydari <f.khaydari@syntacore.com>
Gleb Gagarin <gleb@sifive.com>
Greg Savin <43152568+SiFiveGregS@users.noreply.github.com>
Hang Xu <xuhang@eswincomputing.com>
Hsiangkai <Hsiangkai@gmail.com>
Jan Matyas <jan.matyas@codasip.com>
jhjung81 <48940114+jhjung81@users.noreply.github.com>
Jiuyang Liu <liu@jiuyang.me>
Kaspar Schleiser <kaspar@schleiser.de>
Khem Raj <raj.khem@gmail.com>
Kirill Radkin <kirill.radkin@syntacore.com>
liangzhen <zhen.liang@spacemit.com>
Liviu Ionescu <ilg@livius.net>
Marc Schink <openocd-dev@marcschink.de>
Megan Wachs <megan@sifive.com>
Nils Wistoff <git@wistoff.net>
Palmer Dabbelt <palmer@dabbelt.com>
panciyan <panciyan@eswincomputing.com>
Parshintsev Anatoly <anatoly.parshintsev@syntacore.com>
Paul George <command.paul@gmail.com>
Pavel S. Smirnov <Paul.Smirnov.aka.sps@gmail.com>
Philipp Wagner <mail@philipp-wagner.com>
Ryan Macdonald <rmac@sifive.com>
Samuel Obuch <samuel.obuch17@gmail.com>
Tarek BOCHKATI <tarek.bouchkati@gmail.com>
Tim Newsome <tim@casualhacker.net>
Tobias Kaiser <mail@tb-kaiser.de>
Tom Hebb <tommyhebb@gmail.com>
Tommy Murphy <tommy_murphy@hotmail.com>
wxjstz <wxjstz@126.com>
wzgpeter <wzgpeter@outlook.com>
Xiang W <wxjstz@126.com>
zhusonghe <zhusonghe@eswincomputing.com>

Checkpatch-ignore MULTISTATEMENT_MACRO_USE_DO_WHILE is added to allow a
macro in riscv-013.c that can't use do/while because it expands to a
"case ...:" statement.

Checkpatch-ignore TRAILING_SEMICOLON is added to allow a construct in
riscv-013.c where a macro expands to either code (where it needs the
semicolon) or a member of an enum (where it needs a comma).

Checkpatch-ignore LONG_LINE_COMMENT and NEW_TYPEDEFS lines are added for
the sake of the autogenerated files from riscv-debug-spec.
All non-autogenerated files have been updated for checkpatch compliance.

Checkpatch-ignore: LONG_LINE_COMMENT
Checkpatch-ignore: NEW_TYPEDEFS
Checkpatch-ignore: MULTISTATEMENT_MACRO_USE_DO_WHILE
Checkpatch-ignore: TRAILING_SEMICOLON
Change-Id: Ie594915a4d6e6f9d9dad6016b176ab76409a099a
Signed-off-by: Bernhard Rosenkränzer <bero@baylibre.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8893
Tested-by: jenkins
Reviewed-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
This commit is contained in:
Bernhard Rosenkränzer
2025-05-06 01:20:22 +02:00
committed by Tomas Vanek
parent ab22b0bf8f
commit 5754aebc49
29 changed files with 16775 additions and 6857 deletions

View File

@@ -2,17 +2,31 @@
noinst_LTLIBRARIES += %D%/libriscv.la
%C%_libriscv_la_SOURCES = \
%D%/asm.h \
%D%/batch.h \
%D%/debug_defines.h \
%D%/debug_reg_printer.h \
%D%/encoding.h \
%D%/field_helpers.h \
%D%/gdb_regs.h \
%D%/opcodes.h \
%D%/program.h \
%D%/riscv.h \
%D%/riscv_reg_impl.h \
%D%/riscv_reg.h \
%D%/riscv-011.h \
%D%/riscv-011_reg.h \
%D%/riscv-013.h \
%D%/riscv-013_reg.h \
%D%/batch.c \
%D%/program.c \
%D%/riscv-011.c \
%D%/riscv-011_reg.c \
%D%/riscv-013.c \
%D%/riscv-013_reg.c \
%D%/riscv.c \
%D%/riscv_semihosting.c
%D%/riscv_reg.c \
%D%/riscv_semihosting.c \
%D%/debug_defines.c \
%D%/debug_reg_printer.c
STARTUP_TCL_SRCS += %D%/startup.tcl

View File

@@ -1,40 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef TARGET__RISCV__ASM_H
#define TARGET__RISCV__ASM_H
#include "riscv.h"
/*** Version-independent functions that we don't want in the main address space. ***/
static uint32_t load(const struct target *target, unsigned int rd,
unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t load(const struct target *target, unsigned int rd,
unsigned int base, uint16_t offset)
{
switch (riscv_xlen(target)) {
case 32:
return lw(rd, base, offset);
case 64:
return ld(rd, base, offset);
}
assert(0);
return 0; /* Silence -Werror=return-type */
}
static uint32_t store(const struct target *target, unsigned int src,
unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t store(const struct target *target, unsigned int src,
unsigned int base, uint16_t offset)
{
switch (riscv_xlen(target)) {
case 32:
return sw(src, base, offset);
case 64:
return sd(src, base, offset);
}
assert(0);
return 0; /* Silence -Werror=return-type */
}
#endif

View File

@@ -6,67 +6,90 @@
#include "batch.h"
#include "debug_defines.h"
#include "debug_reg_printer.h"
#include "riscv.h"
#include "field_helpers.h"
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
// TODO: DTM_DMI_MAX_ADDRESS_LENGTH should be reduced to 32 (per the debug spec)
#define DTM_DMI_MAX_ADDRESS_LENGTH ((1<<DTM_DTMCS_ABITS_LENGTH)-1)
#define DMI_SCAN_MAX_BIT_LENGTH (DTM_DMI_MAX_ADDRESS_LENGTH + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH)
#define DMI_SCAN_BUF_SIZE (DIV_ROUND_UP(DMI_SCAN_MAX_BIT_LENGTH, 8))
static void dump_field(int idle, const struct scan_field *field);
/* Reserve extra room in the batch (needed for the last NOP operation) */
#define BATCH_RESERVED_SCANS 1
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle)
static unsigned int get_dmi_scan_length(const struct target *target)
{
scans += 4;
const unsigned int abits = riscv_get_dmi_address_bits(target);
assert(abits > 0);
assert(abits <= DTM_DMI_MAX_ADDRESS_LENGTH);
return abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH;
}
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans)
{
scans += BATCH_RESERVED_SCANS;
struct riscv_batch *out = calloc(1, sizeof(*out));
if (!out)
goto error0;
if (!out) {
LOG_ERROR("Failed to allocate struct riscv_batch");
return NULL;
}
out->target = target;
out->allocated_scans = scans;
out->idle_count = idle;
out->data_out = malloc(sizeof(*out->data_out) * (scans) * DMI_SCAN_BUF_SIZE);
out->last_scan = RISCV_SCAN_TYPE_INVALID;
out->was_run = false;
out->last_scan_delay = 0;
out->data_out = NULL;
out->data_in = NULL;
out->fields = NULL;
out->delay_classes = NULL;
out->bscan_ctxt = NULL;
out->read_keys = NULL;
/* FIXME: There is potential for memory usage reduction. We could allocate
* smaller buffers than DMI_SCAN_BUF_SIZE (that is, buffers that correspond to
* the real DR scan length on the given target) */
out->data_out = malloc(sizeof(*out->data_out) * scans * DMI_SCAN_BUF_SIZE);
if (!out->data_out) {
LOG_ERROR("Failed to allocate data_out in RISC-V batch.");
goto error1;
goto alloc_error;
};
out->data_in = malloc(sizeof(*out->data_in) * (scans) * DMI_SCAN_BUF_SIZE);
out->data_in = malloc(sizeof(*out->data_in) * scans * DMI_SCAN_BUF_SIZE);
if (!out->data_in) {
LOG_ERROR("Failed to allocate data_in in RISC-V batch.");
goto error2;
goto alloc_error;
}
out->fields = malloc(sizeof(*out->fields) * (scans));
out->fields = malloc(sizeof(*out->fields) * scans);
if (!out->fields) {
LOG_ERROR("Failed to allocate fields in RISC-V batch.");
goto error3;
goto alloc_error;
}
out->delay_classes = malloc(sizeof(*out->delay_classes) * scans);
if (!out->delay_classes) {
LOG_ERROR("Failed to allocate delay_classes in RISC-V batch.");
goto alloc_error;
}
if (bscan_tunnel_ir_width != 0) {
out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * (scans));
out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * scans);
if (!out->bscan_ctxt) {
LOG_ERROR("Failed to allocate bscan_ctxt in RISC-V batch.");
goto error4;
goto alloc_error;
}
}
out->last_scan = RISCV_SCAN_TYPE_INVALID;
out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
out->read_keys = malloc(sizeof(*out->read_keys) * scans);
if (!out->read_keys) {
LOG_ERROR("Failed to allocate read_keys in RISC-V batch.");
goto error5;
goto alloc_error;
}
return out;
error5:
free(out->bscan_ctxt);
error4:
free(out->fields);
error3:
free(out->data_in);
error2:
free(out->data_out);
error1:
free(out);
error0:
alloc_error:
riscv_batch_free(out);
return NULL;
}
@@ -75,6 +98,7 @@ void riscv_batch_free(struct riscv_batch *batch)
free(batch->data_in);
free(batch->data_out);
free(batch->fields);
free(batch->delay_classes);
free(batch->bscan_ctxt);
free(batch->read_keys);
free(batch);
@@ -82,32 +106,209 @@ void riscv_batch_free(struct riscv_batch *batch)
bool riscv_batch_full(struct riscv_batch *batch)
{
return batch->used_scans > (batch->allocated_scans - 4);
return riscv_batch_available_scans(batch) == 0;
}
int riscv_batch_run(struct riscv_batch *batch)
static bool riscv_batch_was_scan_busy(const struct riscv_batch *batch,
size_t scan_idx)
{
if (batch->used_scans == 0) {
LOG_DEBUG("Ignoring empty batch.");
return ERROR_OK;
assert(batch->was_run);
assert(scan_idx < batch->used_scans);
const struct scan_field *field = batch->fields + scan_idx;
assert(field->in_value);
const uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
return get_field(in, DTM_DMI_OP) == DTM_DMI_OP_BUSY;
}
static void add_idle_before_batch(const struct riscv_batch *batch, size_t start_idx,
const struct riscv_scan_delays *delays)
{
if (!batch->was_run)
return;
/* Get the delay type of the scan that resulted in the busy response.
* Since DMI interactions always end with a NOP, if "start_idx" is zero
* the base delay value is used.
*/
const enum riscv_scan_delay_class delay_class = start_idx > 0
? batch->delay_classes[start_idx - 1]
: RISCV_DELAY_BASE;
const unsigned int new_delay = riscv_scan_get_delay(delays, delay_class);
if (new_delay <= batch->last_scan_delay)
return;
const unsigned int idle_change = new_delay - batch->last_scan_delay;
LOG_TARGET_DEBUG(batch->target, "Adding %u idle cycles before the batch.",
idle_change);
jtag_add_runtest(idle_change, TAP_IDLE);
}
static unsigned int get_delay(const struct riscv_batch *batch, size_t scan_idx,
const struct riscv_scan_delays *delays, bool resets_delays,
size_t reset_delays_after)
{
assert(batch);
assert(scan_idx < batch->used_scans);
const bool delays_were_reset = resets_delays
&& (scan_idx >= reset_delays_after);
const enum riscv_scan_delay_class delay_class =
batch->delay_classes[scan_idx];
const unsigned int delay = riscv_scan_get_delay(delays, delay_class);
return delays_were_reset ? 0 : delay;
}
static unsigned int decode_dmi(const struct riscv_batch *batch, char *text,
uint32_t address, uint32_t data)
{
static const struct {
uint32_t address;
enum riscv_debug_reg_ordinal ordinal;
} description[] = {
{DM_DMCONTROL, DM_DMCONTROL_ORDINAL},
{DM_DMSTATUS, DM_DMSTATUS_ORDINAL},
{DM_ABSTRACTCS, DM_ABSTRACTCS_ORDINAL},
{DM_COMMAND, DM_COMMAND_ORDINAL},
{DM_SBCS, DM_SBCS_ORDINAL}
};
for (unsigned int i = 0; i < ARRAY_SIZE(description); i++) {
if (riscv_get_dmi_address(batch->target, description[i].address)
== address) {
const riscv_debug_reg_ctx_t context = {
.XLEN = { .value = 0, .is_set = false },
.DXLEN = { .value = 0, .is_set = false },
.abits = { .value = 0, .is_set = false },
};
return riscv_debug_reg_to_s(text, description[i].ordinal,
context, data, RISCV_DEBUG_REG_HIDE_ALL_0);
}
}
if (text)
text[0] = '\0';
return 0;
}
static void log_dmi_decoded(const struct riscv_batch *batch, bool write,
uint32_t address, uint32_t data)
{
const size_t size = decode_dmi(batch, /* text */ NULL, address, data) + 1;
char * const decoded = malloc(size);
if (!decoded) {
LOG_ERROR("Not enough memory to allocate %zu bytes.", size);
return;
}
decode_dmi(batch, decoded, address, data);
LOG_DEBUG("%s: %s", write ? "write" : "read", decoded);
free(decoded);
}
static void log_batch(const struct riscv_batch *batch, size_t start_idx,
const struct riscv_scan_delays *delays, bool resets_delays,
size_t reset_delays_after)
{
if (debug_level < LOG_LVL_DEBUG)
return;
const unsigned int abits = riscv_get_dmi_address_bits(batch->target);
/* Determine the "op" and "address" of the scan that preceded the first
* executed scan.
* FIXME: The code here assumes that there were no DMI operations between
* the last execution of the batch and the current one.
* Caching the info about the last executed DMI scan in "dm013_info_t"
* would be a more robust solution.
*/
bool last_scan_was_read = false;
uint32_t last_scan_address = (uint32_t)(-1) /* to silence maybe-uninitialized */;
if (start_idx > 0) {
const struct scan_field * const field = &batch->fields[start_idx - 1];
assert(field->out_value);
last_scan_was_read = buf_get_u32(field->out_value, DTM_DMI_OP_OFFSET,
DTM_DMI_OP_LENGTH) == DTM_DMI_OP_READ;
last_scan_address = buf_get_u32(field->out_value,
DTM_DMI_ADDRESS_OFFSET, abits);
}
riscv_batch_add_nop(batch);
/* Decode and log every executed scan */
for (size_t i = start_idx; i < batch->used_scans; ++i) {
static const char * const op_string[] = {"-", "r", "w", "?"};
const unsigned int delay = get_delay(batch, i, delays, resets_delays,
reset_delays_after);
const struct scan_field * const field = &batch->fields[i];
for (size_t i = 0; i < batch->used_scans; ++i) {
assert(field->out_value);
const unsigned int out_op = buf_get_u32(field->out_value,
DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
const uint32_t out_data = buf_get_u32(field->out_value,
DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
const uint32_t out_address = buf_get_u32(field->out_value,
DTM_DMI_ADDRESS_OFFSET, abits);
if (field->in_value) {
static const char * const status_string[] = {
"+", "?", "F", "b"
};
const unsigned int in_op = buf_get_u32(field->in_value,
DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
const uint32_t in_data = buf_get_u32(field->in_value,
DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
const uint32_t in_address = buf_get_u32(field->in_value,
DTM_DMI_ADDRESS_OFFSET, abits);
LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32
" -> %s %08" PRIx32 " @%02" PRIx32 "; %ui",
field->num_bits, op_string[out_op], out_data, out_address,
status_string[in_op], in_data, in_address, delay);
if (last_scan_was_read && in_op == DTM_DMI_OP_SUCCESS)
log_dmi_decoded(batch, /*write*/ false,
last_scan_address, in_data);
} else {
LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 " -> ?; %ui",
field->num_bits, op_string[out_op], out_data, out_address,
delay);
}
if (out_op == DTM_DMI_OP_WRITE)
log_dmi_decoded(batch, /*write*/ true, out_address,
out_data);
last_scan_was_read = out_op == DTM_DMI_OP_READ;
last_scan_address = out_address;
}
}
int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
const struct riscv_scan_delays *delays, bool resets_delays,
size_t reset_delays_after)
{
assert(batch->used_scans);
assert(start_idx < batch->used_scans);
assert(batch->last_scan == RISCV_SCAN_TYPE_NOP);
assert(!batch->was_run || riscv_batch_was_scan_busy(batch, start_idx));
assert(start_idx == 0 || !riscv_batch_was_scan_busy(batch, start_idx - 1));
if (batch->was_run)
add_idle_before_batch(batch, start_idx, delays);
LOG_TARGET_DEBUG(batch->target, "Running batch of scans [%zu, %zu)",
start_idx, batch->used_scans);
unsigned int delay = 0 /* to silence maybe-uninitialized */;
for (size_t i = start_idx; i < batch->used_scans; ++i) {
if (bscan_tunnel_ir_width != 0)
riscv_add_bscan_tunneled_scan(batch->target, batch->fields+i, batch->bscan_ctxt+i);
riscv_add_bscan_tunneled_scan(batch->target->tap, batch->fields + i,
batch->bscan_ctxt + i);
else
jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE);
if (batch->idle_count > 0)
jtag_add_runtest(batch->idle_count, TAP_IDLE);
delay = get_delay(batch, i, delays, resets_delays,
reset_delays_after);
if (delay > 0)
jtag_add_runtest(delay, TAP_IDLE);
}
keep_alive();
if (jtag_execute_queue() != ERROR_OK) {
LOG_ERROR("Unable to execute JTAG queue");
LOG_TARGET_ERROR(batch->target, "Unable to execute JTAG queue");
return ERROR_FAIL;
}
@@ -115,40 +316,69 @@ int riscv_batch_run(struct riscv_batch *batch)
if (bscan_tunnel_ir_width != 0) {
/* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */
for (size_t i = 0; i < batch->used_scans; ++i) {
for (size_t i = start_idx; i < batch->used_scans; ++i) {
if ((batch->fields + i)->in_value)
buffer_shr((batch->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1);
}
}
for (size_t i = 0; i < batch->used_scans; ++i)
dump_field(batch->idle_count, batch->fields + i);
log_batch(batch, start_idx, delays, resets_delays, reset_delays_after);
batch->was_run = true;
batch->last_scan_delay = delay;
return ERROR_OK;
}
void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned int address, uint64_t data)
void riscv_batch_add_dmi_write(struct riscv_batch *batch, uint32_t address, uint32_t data,
bool read_back, enum riscv_scan_delay_class delay_class)
{
// TODO: Check that the bit width of "address" is no more than dtmcs.abits,
// otherwise return an error (during batch creation or when the batch is executed).
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
field->num_bits = get_dmi_scan_length(batch->target);
assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH);
uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE;
uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE;
field->out_value = out_value;
riscv_fill_dmi_write(batch->target, out_value, address, data);
if (read_back) {
field->in_value = in_value;
riscv_fill_dm_nop(batch->target, in_value);
} else {
field->in_value = NULL;
}
batch->delay_classes[batch->used_scans] = delay_class;
batch->last_scan = RISCV_SCAN_TYPE_WRITE;
batch->used_scans++;
}
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned int address)
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, uint32_t address,
enum riscv_scan_delay_class delay_class)
{
// TODO: Check that the bit width of "address" is no more than dtmcs.abits,
// otherwise return an error (during batch creation or when the batch is executed).
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dmi_read_u64(batch->target, (char *)field->out_value, address);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
field->num_bits = get_dmi_scan_length(batch->target);
assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH);
uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE;
uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE;
field->out_value = out_value;
field->in_value = in_value;
riscv_fill_dmi_read(batch->target, out_value, address);
riscv_fill_dm_nop(batch->target, in_value);
batch->delay_classes[batch->used_scans] = delay_class;
batch->last_scan = RISCV_SCAN_TYPE_READ;
batch->used_scans++;
@@ -156,21 +386,21 @@ size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned int address)
return batch->read_keys_used++;
}
uint32_t riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key)
uint32_t riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key)
{
assert(key < batch->read_keys_used);
size_t index = batch->read_keys[key];
assert(index <= batch->used_scans);
assert(index < batch->used_scans);
uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index;
/* extract "op" field from the DMI read result */
return buf_get_u32(base, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
}
uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key)
uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key)
{
assert(key < batch->read_keys_used);
size_t index = batch->read_keys[key];
assert(index <= batch->used_scans);
assert(index < batch->used_scans);
uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index;
/* extract "data" field from the DMI read result */
return buf_get_u32(base, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
@@ -180,48 +410,48 @@ void riscv_batch_add_nop(struct riscv_batch *batch)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->out_value);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
field->num_bits = get_dmi_scan_length(batch->target);
assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH);
uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE;
uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE;
field->out_value = out_value;
field->in_value = in_value;
riscv_fill_dm_nop(batch->target, out_value);
riscv_fill_dm_nop(batch->target, in_value);
/* DMI NOP never triggers any debug module operation,
* so the shortest (base) delay can be used. */
batch->delay_classes[batch->used_scans] = RISCV_DELAY_BASE;
batch->last_scan = RISCV_SCAN_TYPE_NOP;
batch->used_scans++;
}
void dump_field(int idle, const struct scan_field *field)
{
static const char * const op_string[] = {"-", "r", "w", "?"};
static const char * const status_string[] = {"+", "?", "F", "b"};
if (debug_level < LOG_LVL_DEBUG)
return;
assert(field->out_value);
uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
unsigned int out_op = get_field(out, DTM_DMI_OP);
unsigned int out_data = get_field(out, DTM_DMI_DATA);
unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET;
if (field->in_value) {
uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
unsigned int in_op = get_field(in, DTM_DMI_OP);
unsigned int in_data = get_field(in, DTM_DMI_DATA);
unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET;
log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__,
"%ub %s %08x @%02x -> %s %08x @%02x; %di",
field->num_bits, op_string[out_op], out_data, out_address,
status_string[in_op], in_data, in_address, idle);
} else {
log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%ub %s %08x @%02x -> ?; %di",
field->num_bits, op_string[out_op], out_data, out_address, idle);
}
}
size_t riscv_batch_available_scans(struct riscv_batch *batch)
{
return batch->allocated_scans - batch->used_scans - 4;
assert(batch->allocated_scans >= (batch->used_scans + BATCH_RESERVED_SCANS));
return batch->allocated_scans - batch->used_scans - BATCH_RESERVED_SCANS;
}
bool riscv_batch_was_batch_busy(const struct riscv_batch *batch)
{
assert(batch->was_run);
assert(batch->used_scans);
assert(batch->last_scan == RISCV_SCAN_TYPE_NOP);
return riscv_batch_was_scan_busy(batch, batch->used_scans - 1);
}
size_t riscv_batch_finished_scans(const struct riscv_batch *batch)
{
if (!riscv_batch_was_batch_busy(batch)) {
/* Whole batch succeeded. */
return batch->used_scans;
}
assert(batch->used_scans);
size_t first_busy = 0;
while (!riscv_batch_was_scan_busy(batch, first_busy))
++first_busy;
return first_busy;
}

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef TARGET__RISCV__SCANS_H
#define TARGET__RISCV__SCANS_H
#ifndef OPENOCD_TARGET_RISCV_BATCH_H
#define OPENOCD_TARGET_RISCV_BATCH_H
#include "target/target.h"
#include "jtag/jtag.h"
@@ -14,6 +14,112 @@ enum riscv_scan_type {
RISCV_SCAN_TYPE_WRITE,
};
/* These types are used to specify how many JTAG RTI cycles to add after a
* scan.
*/
enum riscv_scan_delay_class {
/* Delay needed for accessing debug module registers: */
RISCV_DELAY_BASE,
/* Delay for execution of an abstract command: */
RISCV_DELAY_ABSTRACT_COMMAND,
/* Delay for System Bus read operation: */
RISCV_DELAY_SYSBUS_READ,
/* Delay for System Bus write operation: */
RISCV_DELAY_SYSBUS_WRITE
};
static inline const char *
riscv_scan_delay_class_name(enum riscv_scan_delay_class delay_class)
{
switch (delay_class) {
case RISCV_DELAY_BASE:
return "DM access";
case RISCV_DELAY_ABSTRACT_COMMAND:
return "Abstract Command";
case RISCV_DELAY_SYSBUS_READ:
return "System Bus read";
case RISCV_DELAY_SYSBUS_WRITE:
return "System Bus write";
}
assert(0);
return NULL;
}
/* The scan delay values are passed to "jtag_add_runtest()", which accepts an
* "int". Therefore, the passed value should be no greater than "INT_MAX".
*
* Since the resulting delay value can be a sum of two individual delays,
* individual delays are limited to "INT_MAX / 2" to prevent overflow of the
* final sum.
*/
#define RISCV_SCAN_DELAY_MAX (INT_MAX / 2)
struct riscv_scan_delays {
unsigned int base_delay;
unsigned int ac_delay;
unsigned int sb_read_delay;
unsigned int sb_write_delay;
};
static inline unsigned int
riscv_scan_get_delay(const struct riscv_scan_delays *delays,
enum riscv_scan_delay_class delay_class)
{
switch (delay_class) {
case RISCV_DELAY_BASE:
return delays->base_delay;
case RISCV_DELAY_ABSTRACT_COMMAND:
return delays->base_delay + delays->ac_delay;
case RISCV_DELAY_SYSBUS_READ:
return delays->base_delay + delays->sb_read_delay;
case RISCV_DELAY_SYSBUS_WRITE:
return delays->base_delay + delays->sb_write_delay;
}
assert(0);
return 0;
}
static inline void riscv_scan_set_delay(struct riscv_scan_delays *delays,
enum riscv_scan_delay_class delay_class, unsigned int delay)
{
assert(delay <= RISCV_SCAN_DELAY_MAX);
LOG_DEBUG("%s delay is set to %u.",
riscv_scan_delay_class_name(delay_class), delay);
switch (delay_class) {
case RISCV_DELAY_BASE:
delays->base_delay = delay;
return;
case RISCV_DELAY_ABSTRACT_COMMAND:
delays->ac_delay = delay;
return;
case RISCV_DELAY_SYSBUS_READ:
delays->sb_read_delay = delay;
return;
case RISCV_DELAY_SYSBUS_WRITE:
delays->sb_write_delay = delay;
return;
}
assert(0);
}
static inline int riscv_scan_increase_delay(struct riscv_scan_delays *delays,
enum riscv_scan_delay_class delay_class)
{
const unsigned int delay = riscv_scan_get_delay(delays, delay_class);
const unsigned int delay_step = delay / 10 + 1;
if (delay > RISCV_SCAN_DELAY_MAX - delay_step) {
/* It's not clear if this issue actually occurs in real
* use-cases, so stick with a simple solution until the
* first bug report.
*/
LOG_ERROR("Delay for %s (%d) is not increased anymore (maximum was reached).",
riscv_scan_delay_class_name(delay_class), delay);
return ERROR_FAIL;
}
riscv_scan_set_delay(delays, delay_class, delay + delay_step);
return ERROR_OK;
}
/* A batch of multiple JTAG scans, which are grouped together to avoid the
* overhead of some JTAG adapters when sending single commands. This is
* designed to support block copies, as that's what we actually need to go
@@ -24,11 +130,10 @@ struct riscv_batch {
size_t allocated_scans;
size_t used_scans;
size_t idle_count;
uint8_t *data_out;
uint8_t *data_in;
struct scan_field *fields;
enum riscv_scan_delay_class *delay_classes;
/* If in BSCAN mode, this field will be allocated (one per scan),
and utilized to tunnel all the scans in the batch. If not in
@@ -44,29 +149,75 @@ struct riscv_batch {
/* The read keys. */
size_t *read_keys;
size_t read_keys_used;
/* Flag indicating that the last run of the batch finished without an error
* from the underlying JTAG layer of OpenOCD - all scans were performed.
* However, RISC-V DMI "busy" condition could still have occurred.
*/
bool was_run;
/* Number of RTI cycles used by the last scan on the last run.
* Only valid when `was_run` is set.
*/
unsigned int last_scan_delay;
};
/* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG
* scans that can be issued to this object, and idle is the number of JTAG idle
* cycles between every real scan. */
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle);
* scans that can be issued to this object. */
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans);
void riscv_batch_free(struct riscv_batch *batch);
/* Checks to see if this batch is full. */
bool riscv_batch_full(struct riscv_batch *batch);
/* Executes this scan batch. */
int riscv_batch_run(struct riscv_batch *batch);
/* Executes this batch of JTAG DTM DMI scans, starting form "start" scan.
*
* If batch is run for the first time, it is expected that "start" is zero.
* It is expected that the batch ends with a DMI NOP operation.
*
* "idle_counts" specifies the number of JTAG Run-Test-Idle cycles to add
* after each scan depending on the delay class of the scan.
*
* If "resets_delays" is true, the algorithm will stop inserting idle cycles
* (JTAG Run-Test-Idle) after "reset_delays_after" number of scans is
* performed. This is useful for stress-testing of RISC-V algorithms in
* OpenOCD that are based on batches.
*/
int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
const struct riscv_scan_delays *delays, bool resets_delays,
size_t reset_delays_after);
/* Adds a DMI write to this batch. */
void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned int address, uint64_t data);
/* Get the number of scans successfully executed form this batch. */
size_t riscv_batch_finished_scans(const struct riscv_batch *batch);
/* DMI reads must be handled in two parts: the first one schedules a read and
/* Adds a DM register write to this batch. */
void riscv_batch_add_dmi_write(struct riscv_batch *batch, uint32_t address, uint32_t data,
bool read_back, enum riscv_scan_delay_class delay_class);
static inline void
riscv_batch_add_dm_write(struct riscv_batch *batch, uint32_t address, uint32_t data,
bool read_back, enum riscv_scan_delay_class delay_type)
{
return riscv_batch_add_dmi_write(batch,
riscv_get_dmi_address(batch->target, address), data,
read_back, delay_type);
}
/* DM register reads must be handled in two parts: the first one schedules a read and
* provides a key, the second one actually obtains the result of the read -
* status (op) and the actual data. */
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned int address);
uint32_t riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key);
uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key);
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, uint32_t address,
enum riscv_scan_delay_class delay_class);
static inline size_t
riscv_batch_add_dm_read(struct riscv_batch *batch, uint32_t address,
enum riscv_scan_delay_class delay_type)
{
return riscv_batch_add_dmi_read(batch,
riscv_get_dmi_address(batch->target, address), delay_type);
}
uint32_t riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key);
uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key);
/* Scans in a NOP. */
void riscv_batch_add_nop(struct riscv_batch *batch);
@@ -74,4 +225,7 @@ void riscv_batch_add_nop(struct riscv_batch *batch);
/* Returns the number of available scans. */
size_t riscv_batch_available_scans(struct riscv_batch *batch);
#endif
/* Return true iff the last scan in the batch returned DMI_OP_BUSY. */
bool riscv_batch_was_batch_busy(const struct riscv_batch *batch);
#endif /* OPENOCD_TARGET_RISCV_BATCH_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stdio.h>
#include <inttypes.h>
#include <assert.h>
#include <stdarg.h>
#include "debug_reg_printer.h"
static unsigned int get_len_or_sprintf(char *buf, unsigned int curr, const char *format, ...)
{
assert(format);
va_list args;
int length;
va_start(args, format);
if (buf)
length = vsprintf(buf + curr, format, args);
else
length = vsnprintf(NULL, 0, format, args);
va_end(args);
assert(length >= 0);
return (unsigned int)length;
}
static unsigned int print_number(char *buf, unsigned int offset, uint64_t value)
{
const char * const format = value > 9 ? "0x%" PRIx64 : "%" PRIx64;
return get_len_or_sprintf(buf, offset, format, value);
}
static unsigned int riscv_debug_reg_field_value_to_s(char *buf, unsigned int offset,
const char * const *field_value_names, uint64_t field_value)
{
const char * const field_value_name = field_value_names ?
field_value_names[field_value] :
NULL;
if (!field_value_name)
return print_number(buf, offset, field_value);
return get_len_or_sprintf(buf, offset, "%s", field_value_name);
}
static unsigned int riscv_debug_reg_field_to_s(char *buf, unsigned int offset,
riscv_debug_reg_field_info_t field, riscv_debug_reg_ctx_t context,
uint64_t field_value)
{
const unsigned int name_len = get_len_or_sprintf(buf, offset, "%s=", field.name);
return name_len + riscv_debug_reg_field_value_to_s(buf, offset + name_len,
field.values, field_value);
}
static uint64_t riscv_debug_reg_field_value(riscv_debug_reg_field_info_t field, uint64_t value)
{
assert(field.msb < 64);
assert(field.msb >= field.lsb);
const uint64_t trailing_ones_mask = (uint64_t)(-1) >> (63 - field.msb);
return (value & trailing_ones_mask) >> field.lsb;
}
static unsigned int riscv_debug_reg_fields_to_s(char *buf, unsigned int offset,
struct riscv_debug_reg_field_list_t (*get_next)(riscv_debug_reg_ctx_t contex),
riscv_debug_reg_ctx_t context, uint64_t value,
enum riscv_debug_reg_show show)
{
unsigned int curr = offset;
curr += get_len_or_sprintf(buf, curr, " {");
char *separator = "";
for (struct riscv_debug_reg_field_list_t list; get_next; get_next = list.get_next) {
list = get_next(context);
uint64_t field_value = riscv_debug_reg_field_value(list.field, value);
if (show == RISCV_DEBUG_REG_SHOW_ALL ||
(show == RISCV_DEBUG_REG_HIDE_UNNAMED_0 &&
(field_value != 0 ||
(list.field.values && list.field.values[0]))) ||
(show == RISCV_DEBUG_REG_HIDE_ALL_0 && field_value != 0)) {
curr += get_len_or_sprintf(buf, curr, separator);
curr += riscv_debug_reg_field_to_s(buf, curr, list.field, context,
field_value);
separator = " ";
}
}
curr += get_len_or_sprintf(buf, curr, "}");
return curr - offset;
}
unsigned int riscv_debug_reg_to_s(char *buf, enum riscv_debug_reg_ordinal reg_ordinal,
riscv_debug_reg_ctx_t context, uint64_t value,
enum riscv_debug_reg_show show)
{
unsigned int length = 0;
riscv_debug_reg_info_t reg = get_riscv_debug_reg_info(reg_ordinal);
length += get_len_or_sprintf(buf, length, "%s=", reg.name);
length += print_number(buf, length, value);
if (reg.get_fields_head)
length += riscv_debug_reg_fields_to_s(buf, length,
reg.get_fields_head, context, value, show);
if (buf)
buf[length] = '\0';
return length;
}

View File

@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_DEBUG_REG_PRINTER_H
#define OPENOCD_TARGET_RISCV_DEBUG_REG_PRINTER_H
#include "debug_defines.h"
enum riscv_debug_reg_show {
RISCV_DEBUG_REG_SHOW_ALL,
RISCV_DEBUG_REG_HIDE_ALL_0,
RISCV_DEBUG_REG_HIDE_UNNAMED_0,
};
/**
* This function is used to fill a buffer with a decoded string representation
* of register's value.
* @param buf If non-NULL, the buffer to write the string into.
* Otherwise, the function does not perform any writes,
* it just calculates the number of characters used.
* @param reg_ordinal
* The ordinal of the register.
* @param context The structure, containing the information about the target,
* necessary to decode the register.
* @param value The value to be decoded.
*
* Returns the number of characters used by the string representation
* (excluding '\0').
*
* Example:
* const struct riscv_debug_reg_ctx_t context = {
* .abits = { .value = <abits value>, .is_set = true }
* };
* char buf[riscv_debug_reg_to_s(NULL, DTM_DMI_ORDINAL, context, <dmi value>) + 1]
* riscv_debug_reg_to_s(buf, DTM_DMI_ORDINAL, context, <dmi value>);
*/
unsigned int riscv_debug_reg_to_s(char *buf, enum riscv_debug_reg_ordinal reg_ordinal,
riscv_debug_reg_ctx_t context, uint64_t value,
enum riscv_debug_reg_show show);
#endif /* OPENOCD_TARGET_RISCV_DEBUG_REG_PRINTER_H */

View File

@@ -1,10 +1,10 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright (c) 2022 RISC-V International */
/* Copyright (c) 2023 RISC-V International */
/*
* This file is auto-generated by running 'make' in
* https://github.com/riscv/riscv-opcodes (dcdf8d3)
* https://github.com/riscv/riscv-opcodes (ed68c21)
*/
#ifndef RISCV_CSR_ENCODING_H
@@ -156,14 +156,17 @@
#define MENVCFG_CBIE 0x00000030
#define MENVCFG_CBCFE 0x00000040
#define MENVCFG_CBZE 0x00000080
#define MENVCFG_HADE 0x2000000000000000
#define MENVCFG_PBMTE 0x4000000000000000
#define MENVCFG_STCE 0x8000000000000000
#define MENVCFGH_HADE 0x20000000
#define MENVCFGH_PBMTE 0x40000000
#define MENVCFGH_STCE 0x80000000
#define MSTATEEN0_CS 0x00000001
#define MSTATEEN0_FCSR 0x00000002
#define MSTATEEN0_JVT 0x00000004
#define MSTATEEN0_HCONTEXT 0x0200000000000000
#define MSTATEEN0_HENVCFG 0x4000000000000000
#define MSTATEEN_HSTATEEN 0x8000000000000000
@@ -190,14 +193,17 @@
#define HENVCFG_CBIE 0x00000030
#define HENVCFG_CBCFE 0x00000040
#define HENVCFG_CBZE 0x00000080
#define HENVCFG_HADE 0x2000000000000000
#define HENVCFG_PBMTE 0x4000000000000000
#define HENVCFG_STCE 0x8000000000000000
#define HENVCFGH_HADE 0x20000000
#define HENVCFGH_PBMTE 0x40000000
#define HENVCFGH_STCE 0x80000000
#define HSTATEEN0_CS 0x00000001
#define HSTATEEN0_FCSR 0x00000002
#define HSTATEEN0_JVT 0x00000004
#define HSTATEEN0_SCONTEXT 0x0200000000000000
#define HSTATEEN0_SENVCFG 0x4000000000000000
#define HSTATEEN_SSTATEEN 0x8000000000000000
@@ -213,6 +219,7 @@
#define SSTATEEN0_CS 0x00000001
#define SSTATEEN0_FCSR 0x00000002
#define SSTATEEN0_JVT 0x00000004
#define MSECCFG_MML 0x00000001
#define MSECCFG_MMWP 0x00000002
@@ -220,6 +227,10 @@
#define MSECCFG_USEED 0x00000100
#define MSECCFG_SSEED 0x00000200
/* jvt fields */
#define JVT_MODE 0x3F
#define JVT_BASE (~0x3F)
#define PRV_U 0
#define PRV_S 1
#define PRV_M 3
@@ -366,12 +377,8 @@
#define MASK_ADD8 0xfe00707f
#define MATCH_ADD_UW 0x800003b
#define MASK_ADD_UW 0xfe00707f
#define MATCH_ADDD 0x7b
#define MASK_ADDD 0xfe00707f
#define MATCH_ADDI 0x13
#define MASK_ADDI 0x707f
#define MATCH_ADDID 0x5b
#define MASK_ADDID 0x707f
#define MATCH_ADDIW 0x1b
#define MASK_ADDIW 0x707f
#define MATCH_ADDW 0x3b
@@ -474,10 +481,6 @@
#define MASK_BINV 0xfe00707f
#define MATCH_BINVI 0x68001013
#define MASK_BINVI 0xfc00707f
#define MATCH_BITREV 0xe6000077
#define MASK_BITREV 0xfe00707f
#define MATCH_BITREVI 0xe8000077
#define MASK_BITREVI 0xfc00707f
#define MATCH_BLT 0x4063
#define MASK_BLT 0x707f
#define MATCH_BLTU 0x6063
@@ -490,8 +493,6 @@
#define MASK_BMATXOR 0xfe00707f
#define MATCH_BNE 0x1063
#define MASK_BNE 0x707f
#define MATCH_BPICK 0x3077
#define MASK_BPICK 0x600707f
#define MATCH_BSET 0x28001033
#define MASK_BSET 0xfe00707f
#define MATCH_BSETI 0x28001013
@@ -542,38 +543,48 @@
#define MASK_C_JALR 0xf07f
#define MATCH_C_JR 0x8002
#define MASK_C_JR 0xf07f
#define MATCH_C_LBU 0x8000
#define MASK_C_LBU 0xfc03
#define MATCH_C_LD 0x6000
#define MASK_C_LD 0xe003
#define MATCH_C_LDSP 0x6002
#define MASK_C_LDSP 0xe003
#define MATCH_C_LH 0x8440
#define MASK_C_LH 0xfc43
#define MATCH_C_LHU 0x8400
#define MASK_C_LHU 0xfc43
#define MATCH_C_LI 0x4001
#define MASK_C_LI 0xe003
#define MATCH_C_LQ 0x2000
#define MASK_C_LQ 0xe003
#define MATCH_C_LQSP 0x2002
#define MASK_C_LQSP 0xe003
#define MATCH_C_LUI 0x6001
#define MASK_C_LUI 0xe003
#define MATCH_C_LW 0x4000
#define MASK_C_LW 0xe003
#define MATCH_C_LWSP 0x4002
#define MASK_C_LWSP 0xe003
#define MATCH_C_MUL 0x9c41
#define MASK_C_MUL 0xfc63
#define MATCH_C_MV 0x8002
#define MASK_C_MV 0xf003
#define MATCH_C_NOP 0x1
#define MASK_C_NOP 0xef83
#define MATCH_C_NOT 0x9c75
#define MASK_C_NOT 0xfc7f
#define MATCH_C_OR 0x8c41
#define MASK_C_OR 0xfc63
#define MATCH_C_SB 0x8800
#define MASK_C_SB 0xfc03
#define MATCH_C_SD 0xe000
#define MASK_C_SD 0xe003
#define MATCH_C_SDSP 0xe002
#define MASK_C_SDSP 0xe003
#define MATCH_C_SEXT_B 0x9c65
#define MASK_C_SEXT_B 0xfc7f
#define MATCH_C_SEXT_H 0x9c6d
#define MASK_C_SEXT_H 0xfc7f
#define MATCH_C_SH 0x8c00
#define MASK_C_SH 0xfc43
#define MATCH_C_SLLI 0x2
#define MASK_C_SLLI 0xe003
#define MATCH_C_SQ 0xa000
#define MASK_C_SQ 0xe003
#define MATCH_C_SQSP 0xa002
#define MASK_C_SQSP 0xe003
#define MATCH_C_SRAI 0x8401
#define MASK_C_SRAI 0xec03
#define MATCH_C_SRLI 0x8001
@@ -588,6 +599,12 @@
#define MASK_C_SWSP 0xe003
#define MATCH_C_XOR 0x8c21
#define MASK_C_XOR 0xfc63
#define MATCH_C_ZEXT_B 0x9c61
#define MASK_C_ZEXT_B 0xfc7f
#define MATCH_C_ZEXT_H 0x9c69
#define MASK_C_ZEXT_H 0xfc7f
#define MATCH_C_ZEXT_W 0x9c71
#define MASK_C_ZEXT_W 0xfc7f
#define MATCH_CBO_CLEAN 0x10200f
#define MASK_CBO_CLEAN 0xfff07fff
#define MATCH_CBO_FLUSH 0x20200f
@@ -602,12 +619,6 @@
#define MASK_CLMULH 0xfe00707f
#define MATCH_CLMULR 0xa002033
#define MASK_CLMULR 0xfe00707f
#define MATCH_CLO16 0xaeb00077
#define MASK_CLO16 0xfff0707f
#define MATCH_CLO32 0xafb00077
#define MASK_CLO32 0xfff0707f
#define MATCH_CLO8 0xae300077
#define MASK_CLO8 0xfff0707f
#define MATCH_CLRS16 0xae800077
#define MASK_CLRS16 0xfff0707f
#define MATCH_CLRS32 0xaf800077
@@ -624,6 +635,20 @@
#define MASK_CLZ8 0xfff0707f
#define MATCH_CLZW 0x6000101b
#define MASK_CLZW 0xfff0707f
#define MATCH_CM_JALT 0xa002
#define MASK_CM_JALT 0xfc03
#define MATCH_CM_MVA01S 0xac62
#define MASK_CM_MVA01S 0xfc63
#define MATCH_CM_MVSA01 0xac22
#define MASK_CM_MVSA01 0xfc63
#define MATCH_CM_POP 0xba02
#define MASK_CM_POP 0xff03
#define MATCH_CM_POPRET 0xbe02
#define MASK_CM_POPRET 0xff03
#define MATCH_CM_POPRETZ 0xbc02
#define MASK_CM_POPRETZ 0xff03
#define MATCH_CM_PUSH 0xb802
#define MASK_CM_PUSH 0xff03
#define MATCH_CMIX 0x6001033
#define MASK_CMIX 0x600707f
#define MATCH_CMOV 0x6005033
@@ -676,6 +701,10 @@
#define MASK_CTZ 0xfff0707f
#define MATCH_CTZW 0x6010101b
#define MASK_CTZW 0xfff0707f
#define MATCH_CZERO_EQZ 0xe005033
#define MASK_CZERO_EQZ 0xfe00707f
#define MATCH_CZERO_NEZ 0xe007033
#define MASK_CZERO_NEZ 0xfe00707f
#define MATCH_DIV 0x2004033
#define MASK_DIV 0xfe00707f
#define MATCH_DIVU 0x2005033
@@ -1238,14 +1267,10 @@
#define MASK_LBU 0x707f
#define MATCH_LD 0x3003
#define MASK_LD 0x707f
#define MATCH_LDU 0x7003
#define MASK_LDU 0x707f
#define MATCH_LH 0x1003
#define MASK_LH 0x707f
#define MATCH_LHU 0x5003
#define MASK_LHU 0x707f
#define MATCH_LQ 0x300f
#define MASK_LQ 0x707f
#define MATCH_LR_D 0x1000302f
#define MASK_LR_D 0xf9f0707f
#define MATCH_LR_W 0x1000202f
@@ -1262,14 +1287,10 @@
#define MASK_MAX 0xfe00707f
#define MATCH_MAXU 0xa007033
#define MASK_MAXU 0xfe00707f
#define MATCH_MAXW 0xf2000077
#define MASK_MAXW 0xfe00707f
#define MATCH_MIN 0xa004033
#define MASK_MIN 0xfe00707f
#define MATCH_MINU 0xa005033
#define MASK_MINU 0xfe00707f
#define MATCH_MINW 0xf0000077
#define MASK_MINW 0xfe00707f
#define MATCH_MRET 0x30200073
#define MASK_MRET 0xffffffff
#define MATCH_MSUBR32 0xc6001077
@@ -1312,8 +1333,6 @@
#define MASK_PBSADA 0xfe00707f
#define MATCH_PKBB16 0xe001077
#define MASK_PKBB16 0xfe00707f
#define MATCH_PKBB32 0xe002077
#define MASK_PKBB32 0xfe00707f
#define MATCH_PKBT16 0x1e001077
#define MASK_PKBT16 0xfe00707f
#define MATCH_PKBT32 0x1e002077
@@ -1324,8 +1343,6 @@
#define MASK_PKTB32 0xfe00707f
#define MATCH_PKTT16 0x2e001077
#define MASK_PKTT16 0xfe00707f
#define MATCH_PKTT32 0x2e002077
#define MASK_PKTT32 0xfe00707f
#define MATCH_PREFETCH_I 0x6013
#define MASK_PREFETCH_I 0x1f07fff
#define MATCH_PREFETCH_R 0x106013
@@ -1478,20 +1495,18 @@
#define MASK_SLL32 0xfe00707f
#define MATCH_SLL8 0x5c000077
#define MASK_SLL8 0xfe00707f
#define MATCH_SLLD 0x107b
#define MASK_SLLD 0xfe00707f
#define MATCH_SLLI 0x1013
#define MASK_SLLI 0xf800707f
#define MASK_SLLI 0xfc00707f
#define MATCH_SLLI16 0x74000077
#define MASK_SLLI16 0xff00707f
#define MATCH_SLLI32 0x74002077
#define MASK_SLLI32 0xfe00707f
#define MATCH_SLLI8 0x7c000077
#define MASK_SLLI8 0xff80707f
#define MATCH_SLLI_RV32 0x1013
#define MASK_SLLI_RV32 0xfe00707f
#define MATCH_SLLI_UW 0x800101b
#define MASK_SLLI_UW 0xfc00707f
#define MATCH_SLLID 0x105b
#define MASK_SLLID 0xfc00707f
#define MATCH_SLLIW 0x101b
#define MASK_SLLIW 0xfe00707f
#define MATCH_SLLW 0x103b
@@ -1604,8 +1619,6 @@
#define MASK_SMXDS 0xfe00707f
#define MATCH_SMXDS32 0x78002077
#define MASK_SMXDS32 0xfe00707f
#define MATCH_SQ 0x4023
#define MASK_SQ 0x707f
#define MATCH_SRA 0x40005033
#define MASK_SRA 0xfe00707f
#define MATCH_SRA16 0x50000077
@@ -1622,10 +1635,8 @@
#define MASK_SRA8_U 0xfe00707f
#define MATCH_SRA_U 0x24001077
#define MASK_SRA_U 0xfe00707f
#define MATCH_SRAD 0x4000507b
#define MASK_SRAD 0xfe00707f
#define MATCH_SRAI 0x40005013
#define MASK_SRAI 0xf800707f
#define MASK_SRAI 0xfc00707f
#define MATCH_SRAI16 0x70000077
#define MASK_SRAI16 0xff00707f
#define MATCH_SRAI16_U 0x71000077
@@ -1638,10 +1649,10 @@
#define MASK_SRAI8 0xff80707f
#define MATCH_SRAI8_U 0x78800077
#define MASK_SRAI8_U 0xff80707f
#define MATCH_SRAI_RV32 0x40005013
#define MASK_SRAI_RV32 0xfe00707f
#define MATCH_SRAI_U 0xd4001077
#define MASK_SRAI_U 0xfc00707f
#define MATCH_SRAID 0x4000505b
#define MASK_SRAID 0xfc00707f
#define MATCH_SRAIW 0x4000501b
#define MASK_SRAIW 0xfe00707f
#define MATCH_SRAIW_U 0x34001077
@@ -1664,10 +1675,8 @@
#define MASK_SRL8 0xfe00707f
#define MATCH_SRL8_U 0x6a000077
#define MASK_SRL8_U 0xfe00707f
#define MATCH_SRLD 0x507b
#define MASK_SRLD 0xfe00707f
#define MATCH_SRLI 0x5013
#define MASK_SRLI 0xf800707f
#define MASK_SRLI 0xfc00707f
#define MATCH_SRLI16 0x72000077
#define MASK_SRLI16 0xff00707f
#define MATCH_SRLI16_U 0x73000077
@@ -1680,8 +1689,8 @@
#define MASK_SRLI8 0xff80707f
#define MATCH_SRLI8_U 0x7a800077
#define MASK_SRLI8_U 0xff80707f
#define MATCH_SRLID 0x505b
#define MASK_SRLID 0xfc00707f
#define MATCH_SRLI_RV32 0x5013
#define MASK_SRLI_RV32 0xfe00707f
#define MATCH_SRLIW 0x501b
#define MASK_SRLIW 0xfe00707f
#define MATCH_SRLW 0x503b
@@ -1712,8 +1721,6 @@
#define MASK_SUB64 0xfe00707f
#define MATCH_SUB8 0x4a000077
#define MASK_SUB8 0xfe00707f
#define MATCH_SUBD 0x4000007b
#define MASK_SUBD 0xfe00707f
#define MATCH_SUBW 0x4000003b
#define MASK_SUBW 0xfe00707f
#define MATCH_SUNPKD810 0xac800077
@@ -1728,8 +1735,6 @@
#define MASK_SUNPKD832 0xfff0707f
#define MATCH_SW 0x2023
#define MASK_SW 0x707f
#define MATCH_SWAP8 0xad800077
#define MASK_SWAP8 0xfff0707f
#define MATCH_UCLIP16 0x85000077
#define MASK_UCLIP16 0xff00707f
#define MATCH_UCLIP32 0xf4000077
@@ -2750,10 +2755,6 @@
#define MASK_VZEXT_VF4 0xfc0ff07f
#define MATCH_VZEXT_VF8 0x48012057
#define MASK_VZEXT_VF8 0xfc0ff07f
#define MATCH_WEXT 0xce000077
#define MASK_WEXT 0xfe00707f
#define MATCH_WEXTI 0xde000077
#define MASK_WEXTI 0xfe00707f
#define MATCH_WFI 0x10500073
#define MASK_WFI 0xffffffff
#define MATCH_WRS_NTO 0xd00073
@@ -2793,6 +2794,7 @@
#define CSR_VXRM 0xa
#define CSR_VCSR 0xf
#define CSR_SEED 0x15
#define CSR_JVT 0x17
#define CSR_CYCLE 0xc00
#define CSR_TIME 0xc01
#define CSR_INSTRET 0xc02
@@ -2845,6 +2847,9 @@
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
#define CSR_STIMECMP 0x14d
#define CSR_SISELECT 0x150
#define CSR_SIREG 0x151
#define CSR_STOPEI 0x15c
#define CSR_SATP 0x180
#define CSR_SCONTEXT 0x5a8
#define CSR_VSSTATUS 0x200
@@ -2856,6 +2861,9 @@
#define CSR_VSTVAL 0x243
#define CSR_VSIP 0x244
#define CSR_VSTIMECMP 0x24d
#define CSR_VSISELECT 0x250
#define CSR_VSIREG 0x251
#define CSR_VSTOPEI 0x25c
#define CSR_VSATP 0x280
#define CSR_HSTATUS 0x600
#define CSR_HEDELEG 0x602
@@ -2864,6 +2872,8 @@
#define CSR_HTIMEDELTA 0x605
#define CSR_HCOUNTEREN 0x606
#define CSR_HGEIE 0x607
#define CSR_HVIEN 0x608
#define CSR_HVICTL 0x609
#define CSR_HENVCFG 0x60a
#define CSR_HSTATEEN0 0x60c
#define CSR_HSTATEEN1 0x60d
@@ -2872,11 +2882,15 @@
#define CSR_HTVAL 0x643
#define CSR_HIP 0x644
#define CSR_HVIP 0x645
#define CSR_HVIPRIO1 0x646
#define CSR_HVIPRIO2 0x647
#define CSR_HTINST 0x64a
#define CSR_HGATP 0x680
#define CSR_HCONTEXT 0x6a8
#define CSR_HGEIP 0xe12
#define CSR_VSTOPI 0xeb0
#define CSR_SCOUNTOVF 0xda0
#define CSR_STOPI 0xdb0
#define CSR_UTVT 0x7
#define CSR_UNXTI 0x45
#define CSR_UINTSTATUS 0x46
@@ -2899,6 +2913,8 @@
#define CSR_MIE 0x304
#define CSR_MTVEC 0x305
#define CSR_MCOUNTEREN 0x306
#define CSR_MVIEN 0x308
#define CSR_MVIP 0x309
#define CSR_MENVCFG 0x30a
#define CSR_MSTATEEN0 0x30c
#define CSR_MSTATEEN1 0x30d
@@ -2912,6 +2928,9 @@
#define CSR_MIP 0x344
#define CSR_MTINST 0x34a
#define CSR_MTVAL2 0x34b
#define CSR_MISELECT 0x350
#define CSR_MIREG 0x351
#define CSR_MTOPEI 0x35c
#define CSR_PMPCFG0 0x3a0
#define CSR_PMPCFG1 0x3a1
#define CSR_PMPCFG2 0x3a2
@@ -3070,10 +3089,20 @@
#define CSR_MIMPID 0xf13
#define CSR_MHARTID 0xf14
#define CSR_MCONFIGPTR 0xf15
#define CSR_MTOPI 0xfb0
#define CSR_SIEH 0x114
#define CSR_SIPH 0x154
#define CSR_STIMECMPH 0x15d
#define CSR_VSIEH 0x214
#define CSR_VSIPH 0x254
#define CSR_VSTIMECMPH 0x25d
#define CSR_HTIMEDELTAH 0x615
#define CSR_HIDELEGH 0x613
#define CSR_HVIENH 0x618
#define CSR_HENVCFGH 0x61a
#define CSR_HVIPH 0x655
#define CSR_HVIPRIO1H 0x656
#define CSR_HVIPRIO2H 0x657
#define CSR_HSTATEEN0H 0x61c
#define CSR_HSTATEEN1H 0x61d
#define CSR_HSTATEEN2H 0x61e
@@ -3111,11 +3140,16 @@
#define CSR_HPMCOUNTER30H 0xc9e
#define CSR_HPMCOUNTER31H 0xc9f
#define CSR_MSTATUSH 0x310
#define CSR_MIDELEGH 0x313
#define CSR_MIEH 0x314
#define CSR_MVIENH 0x318
#define CSR_MVIPH 0x319
#define CSR_MENVCFGH 0x31a
#define CSR_MSTATEEN0H 0x31c
#define CSR_MSTATEEN1H 0x31d
#define CSR_MSTATEEN2H 0x31e
#define CSR_MSTATEEN3H 0x31f
#define CSR_MIPH 0x354
#define CSR_MHPMEVENT3H 0x723
#define CSR_MHPMEVENT4H 0x724
#define CSR_MHPMEVENT5H 0x725
@@ -3221,7 +3255,7 @@
#define INSN_FIELD_IMM12LO 0xf80
#define INSN_FIELD_BIMM12LO 0xf80
#define INSN_FIELD_ZIMM 0xf8000
#define INSN_FIELD_SHAMT 0x7f00000
#define INSN_FIELD_SHAMTQ 0x7f00000
#define INSN_FIELD_SHAMTW 0x1f00000
#define INSN_FIELD_SHAMTW4 0xf00000
#define INSN_FIELD_SHAMTD 0x3f00000
@@ -3276,6 +3310,11 @@
#define INSN_FIELD_C_UIMM9SPHI 0x1000
#define INSN_FIELD_C_UIMM10SP_S 0x1f80
#define INSN_FIELD_C_UIMM9SP_S 0x1f80
#define INSN_FIELD_C_UIMM2 0x60
#define INSN_FIELD_C_UIMM1 0x20
#define INSN_FIELD_C_RLIST 0xf0
#define INSN_FIELD_C_SPIMM 0xc
#define INSN_FIELD_C_INDEX 0x3fc
#define INSN_FIELD_RS1_P 0x380
#define INSN_FIELD_RS2_P 0x1c
#define INSN_FIELD_RD_P 0x1c
@@ -3288,6 +3327,8 @@
#define INSN_FIELD_C_RS2_N0 0x7c
#define INSN_FIELD_C_RS1_N0 0xf80
#define INSN_FIELD_C_RS2 0x7c
#define INSN_FIELD_C_SREG1 0x380
#define INSN_FIELD_C_SREG2 0x1c
#endif
#ifdef DECLARE_INSN
DECLARE_INSN(add, MATCH_ADD, MASK_ADD)
@@ -3296,9 +3337,7 @@ DECLARE_INSN(add32, MATCH_ADD32, MASK_ADD32)
DECLARE_INSN(add64, MATCH_ADD64, MASK_ADD64)
DECLARE_INSN(add8, MATCH_ADD8, MASK_ADD8)
DECLARE_INSN(add_uw, MATCH_ADD_UW, MASK_ADD_UW)
DECLARE_INSN(addd, MATCH_ADDD, MASK_ADDD)
DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI)
DECLARE_INSN(addid, MATCH_ADDID, MASK_ADDID)
DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW)
DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW)
DECLARE_INSN(aes32dsi, MATCH_AES32DSI, MASK_AES32DSI)
@@ -3350,15 +3389,12 @@ DECLARE_INSN(bge, MATCH_BGE, MASK_BGE)
DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU)
DECLARE_INSN(binv, MATCH_BINV, MASK_BINV)
DECLARE_INSN(binvi, MATCH_BINVI, MASK_BINVI)
DECLARE_INSN(bitrev, MATCH_BITREV, MASK_BITREV)
DECLARE_INSN(bitrevi, MATCH_BITREVI, MASK_BITREVI)
DECLARE_INSN(blt, MATCH_BLT, MASK_BLT)
DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU)
DECLARE_INSN(bmatflip, MATCH_BMATFLIP, MASK_BMATFLIP)
DECLARE_INSN(bmator, MATCH_BMATOR, MASK_BMATOR)
DECLARE_INSN(bmatxor, MATCH_BMATXOR, MASK_BMATXOR)
DECLARE_INSN(bne, MATCH_BNE, MASK_BNE)
DECLARE_INSN(bpick, MATCH_BPICK, MASK_BPICK)
DECLARE_INSN(bset, MATCH_BSET, MASK_BSET)
DECLARE_INSN(bseti, MATCH_BSETI, MASK_BSETI)
DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD)
@@ -3384,22 +3420,27 @@ DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J)
DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL)
DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR)
DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR)
DECLARE_INSN(c_lbu, MATCH_C_LBU, MASK_C_LBU)
DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD)
DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP)
DECLARE_INSN(c_lh, MATCH_C_LH, MASK_C_LH)
DECLARE_INSN(c_lhu, MATCH_C_LHU, MASK_C_LHU)
DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI)
DECLARE_INSN(c_lq, MATCH_C_LQ, MASK_C_LQ)
DECLARE_INSN(c_lqsp, MATCH_C_LQSP, MASK_C_LQSP)
DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI)
DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW)
DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP)
DECLARE_INSN(c_mul, MATCH_C_MUL, MASK_C_MUL)
DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV)
DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP)
DECLARE_INSN(c_not, MATCH_C_NOT, MASK_C_NOT)
DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR)
DECLARE_INSN(c_sb, MATCH_C_SB, MASK_C_SB)
DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD)
DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP)
DECLARE_INSN(c_sext_b, MATCH_C_SEXT_B, MASK_C_SEXT_B)
DECLARE_INSN(c_sext_h, MATCH_C_SEXT_H, MASK_C_SEXT_H)
DECLARE_INSN(c_sh, MATCH_C_SH, MASK_C_SH)
DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI)
DECLARE_INSN(c_sq, MATCH_C_SQ, MASK_C_SQ)
DECLARE_INSN(c_sqsp, MATCH_C_SQSP, MASK_C_SQSP)
DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI)
DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI)
DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB)
@@ -3407,6 +3448,9 @@ DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW)
DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW)
DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP)
DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR)
DECLARE_INSN(c_zext_b, MATCH_C_ZEXT_B, MASK_C_ZEXT_B)
DECLARE_INSN(c_zext_h, MATCH_C_ZEXT_H, MASK_C_ZEXT_H)
DECLARE_INSN(c_zext_w, MATCH_C_ZEXT_W, MASK_C_ZEXT_W)
DECLARE_INSN(cbo_clean, MATCH_CBO_CLEAN, MASK_CBO_CLEAN)
DECLARE_INSN(cbo_flush, MATCH_CBO_FLUSH, MASK_CBO_FLUSH)
DECLARE_INSN(cbo_inval, MATCH_CBO_INVAL, MASK_CBO_INVAL)
@@ -3414,9 +3458,6 @@ DECLARE_INSN(cbo_zero, MATCH_CBO_ZERO, MASK_CBO_ZERO)
DECLARE_INSN(clmul, MATCH_CLMUL, MASK_CLMUL)
DECLARE_INSN(clmulh, MATCH_CLMULH, MASK_CLMULH)
DECLARE_INSN(clmulr, MATCH_CLMULR, MASK_CLMULR)
DECLARE_INSN(clo16, MATCH_CLO16, MASK_CLO16)
DECLARE_INSN(clo32, MATCH_CLO32, MASK_CLO32)
DECLARE_INSN(clo8, MATCH_CLO8, MASK_CLO8)
DECLARE_INSN(clrs16, MATCH_CLRS16, MASK_CLRS16)
DECLARE_INSN(clrs32, MATCH_CLRS32, MASK_CLRS32)
DECLARE_INSN(clrs8, MATCH_CLRS8, MASK_CLRS8)
@@ -3425,6 +3466,13 @@ DECLARE_INSN(clz16, MATCH_CLZ16, MASK_CLZ16)
DECLARE_INSN(clz32, MATCH_CLZ32, MASK_CLZ32)
DECLARE_INSN(clz8, MATCH_CLZ8, MASK_CLZ8)
DECLARE_INSN(clzw, MATCH_CLZW, MASK_CLZW)
DECLARE_INSN(cm_jalt, MATCH_CM_JALT, MASK_CM_JALT)
DECLARE_INSN(cm_mva01s, MATCH_CM_MVA01S, MASK_CM_MVA01S)
DECLARE_INSN(cm_mvsa01, MATCH_CM_MVSA01, MASK_CM_MVSA01)
DECLARE_INSN(cm_pop, MATCH_CM_POP, MASK_CM_POP)
DECLARE_INSN(cm_popret, MATCH_CM_POPRET, MASK_CM_POPRET)
DECLARE_INSN(cm_popretz, MATCH_CM_POPRETZ, MASK_CM_POPRETZ)
DECLARE_INSN(cm_push, MATCH_CM_PUSH, MASK_CM_PUSH)
DECLARE_INSN(cmix, MATCH_CMIX, MASK_CMIX)
DECLARE_INSN(cmov, MATCH_CMOV, MASK_CMOV)
DECLARE_INSN(cmpeq16, MATCH_CMPEQ16, MASK_CMPEQ16)
@@ -3451,6 +3499,8 @@ DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW)
DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI)
DECLARE_INSN(ctz, MATCH_CTZ, MASK_CTZ)
DECLARE_INSN(ctzw, MATCH_CTZW, MASK_CTZW)
DECLARE_INSN(czero_eqz, MATCH_CZERO_EQZ, MASK_CZERO_EQZ)
DECLARE_INSN(czero_nez, MATCH_CZERO_NEZ, MASK_CZERO_NEZ)
DECLARE_INSN(div, MATCH_DIV, MASK_DIV)
DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU)
DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW)
@@ -3732,10 +3782,8 @@ DECLARE_INSN(kwmmul_u, MATCH_KWMMUL_U, MASK_KWMMUL_U)
DECLARE_INSN(lb, MATCH_LB, MASK_LB)
DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU)
DECLARE_INSN(ld, MATCH_LD, MASK_LD)
DECLARE_INSN(ldu, MATCH_LDU, MASK_LDU)
DECLARE_INSN(lh, MATCH_LH, MASK_LH)
DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU)
DECLARE_INSN(lq, MATCH_LQ, MASK_LQ)
DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D)
DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W)
DECLARE_INSN(lui, MATCH_LUI, MASK_LUI)
@@ -3744,10 +3792,8 @@ DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU)
DECLARE_INSN(maddr32, MATCH_MADDR32, MASK_MADDR32)
DECLARE_INSN(max, MATCH_MAX, MASK_MAX)
DECLARE_INSN(maxu, MATCH_MAXU, MASK_MAXU)
DECLARE_INSN(maxw, MATCH_MAXW, MASK_MAXW)
DECLARE_INSN(min, MATCH_MIN, MASK_MIN)
DECLARE_INSN(minu, MATCH_MINU, MASK_MINU)
DECLARE_INSN(minw, MATCH_MINW, MASK_MINW)
DECLARE_INSN(mret, MATCH_MRET, MASK_MRET)
DECLARE_INSN(msubr32, MATCH_MSUBR32, MASK_MSUBR32)
DECLARE_INSN(mul, MATCH_MUL, MASK_MUL)
@@ -3769,13 +3815,11 @@ DECLARE_INSN(pause, MATCH_PAUSE, MASK_PAUSE)
DECLARE_INSN(pbsad, MATCH_PBSAD, MASK_PBSAD)
DECLARE_INSN(pbsada, MATCH_PBSADA, MASK_PBSADA)
DECLARE_INSN(pkbb16, MATCH_PKBB16, MASK_PKBB16)
DECLARE_INSN(pkbb32, MATCH_PKBB32, MASK_PKBB32)
DECLARE_INSN(pkbt16, MATCH_PKBT16, MASK_PKBT16)
DECLARE_INSN(pkbt32, MATCH_PKBT32, MASK_PKBT32)
DECLARE_INSN(pktb16, MATCH_PKTB16, MASK_PKTB16)
DECLARE_INSN(pktb32, MATCH_PKTB32, MASK_PKTB32)
DECLARE_INSN(pktt16, MATCH_PKTT16, MASK_PKTT16)
DECLARE_INSN(pktt32, MATCH_PKTT32, MASK_PKTT32)
DECLARE_INSN(prefetch_i, MATCH_PREFETCH_I, MASK_PREFETCH_I)
DECLARE_INSN(prefetch_r, MATCH_PREFETCH_R, MASK_PREFETCH_R)
DECLARE_INSN(prefetch_w, MATCH_PREFETCH_W, MASK_PREFETCH_W)
@@ -3852,13 +3896,12 @@ DECLARE_INSN(sll, MATCH_SLL, MASK_SLL)
DECLARE_INSN(sll16, MATCH_SLL16, MASK_SLL16)
DECLARE_INSN(sll32, MATCH_SLL32, MASK_SLL32)
DECLARE_INSN(sll8, MATCH_SLL8, MASK_SLL8)
DECLARE_INSN(slld, MATCH_SLLD, MASK_SLLD)
DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI)
DECLARE_INSN(slli16, MATCH_SLLI16, MASK_SLLI16)
DECLARE_INSN(slli32, MATCH_SLLI32, MASK_SLLI32)
DECLARE_INSN(slli8, MATCH_SLLI8, MASK_SLLI8)
DECLARE_INSN(slli_rv32, MATCH_SLLI_RV32, MASK_SLLI_RV32)
DECLARE_INSN(slli_uw, MATCH_SLLI_UW, MASK_SLLI_UW)
DECLARE_INSN(sllid, MATCH_SLLID, MASK_SLLID)
DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW)
DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW)
DECLARE_INSN(slo, MATCH_SLO, MASK_SLO)
@@ -3915,7 +3958,6 @@ DECLARE_INSN(smulx16, MATCH_SMULX16, MASK_SMULX16)
DECLARE_INSN(smulx8, MATCH_SMULX8, MASK_SMULX8)
DECLARE_INSN(smxds, MATCH_SMXDS, MASK_SMXDS)
DECLARE_INSN(smxds32, MATCH_SMXDS32, MASK_SMXDS32)
DECLARE_INSN(sq, MATCH_SQ, MASK_SQ)
DECLARE_INSN(sra, MATCH_SRA, MASK_SRA)
DECLARE_INSN(sra16, MATCH_SRA16, MASK_SRA16)
DECLARE_INSN(sra16_u, MATCH_SRA16_U, MASK_SRA16_U)
@@ -3924,7 +3966,6 @@ DECLARE_INSN(sra32_u, MATCH_SRA32_U, MASK_SRA32_U)
DECLARE_INSN(sra8, MATCH_SRA8, MASK_SRA8)
DECLARE_INSN(sra8_u, MATCH_SRA8_U, MASK_SRA8_U)
DECLARE_INSN(sra_u, MATCH_SRA_U, MASK_SRA_U)
DECLARE_INSN(srad, MATCH_SRAD, MASK_SRAD)
DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI)
DECLARE_INSN(srai16, MATCH_SRAI16, MASK_SRAI16)
DECLARE_INSN(srai16_u, MATCH_SRAI16_U, MASK_SRAI16_U)
@@ -3932,8 +3973,8 @@ DECLARE_INSN(srai32, MATCH_SRAI32, MASK_SRAI32)
DECLARE_INSN(srai32_u, MATCH_SRAI32_U, MASK_SRAI32_U)
DECLARE_INSN(srai8, MATCH_SRAI8, MASK_SRAI8)
DECLARE_INSN(srai8_u, MATCH_SRAI8_U, MASK_SRAI8_U)
DECLARE_INSN(srai_rv32, MATCH_SRAI_RV32, MASK_SRAI_RV32)
DECLARE_INSN(srai_u, MATCH_SRAI_U, MASK_SRAI_U)
DECLARE_INSN(sraid, MATCH_SRAID, MASK_SRAID)
DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW)
DECLARE_INSN(sraiw_u, MATCH_SRAIW_U, MASK_SRAIW_U)
DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW)
@@ -3945,7 +3986,6 @@ DECLARE_INSN(srl32, MATCH_SRL32, MASK_SRL32)
DECLARE_INSN(srl32_u, MATCH_SRL32_U, MASK_SRL32_U)
DECLARE_INSN(srl8, MATCH_SRL8, MASK_SRL8)
DECLARE_INSN(srl8_u, MATCH_SRL8_U, MASK_SRL8_U)
DECLARE_INSN(srld, MATCH_SRLD, MASK_SRLD)
DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI)
DECLARE_INSN(srli16, MATCH_SRLI16, MASK_SRLI16)
DECLARE_INSN(srli16_u, MATCH_SRLI16_U, MASK_SRLI16_U)
@@ -3953,7 +3993,7 @@ DECLARE_INSN(srli32, MATCH_SRLI32, MASK_SRLI32)
DECLARE_INSN(srli32_u, MATCH_SRLI32_U, MASK_SRLI32_U)
DECLARE_INSN(srli8, MATCH_SRLI8, MASK_SRLI8)
DECLARE_INSN(srli8_u, MATCH_SRLI8_U, MASK_SRLI8_U)
DECLARE_INSN(srlid, MATCH_SRLID, MASK_SRLID)
DECLARE_INSN(srli_rv32, MATCH_SRLI_RV32, MASK_SRLI_RV32)
DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW)
DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW)
DECLARE_INSN(sro, MATCH_SRO, MASK_SRO)
@@ -3969,7 +4009,6 @@ DECLARE_INSN(sub16, MATCH_SUB16, MASK_SUB16)
DECLARE_INSN(sub32, MATCH_SUB32, MASK_SUB32)
DECLARE_INSN(sub64, MATCH_SUB64, MASK_SUB64)
DECLARE_INSN(sub8, MATCH_SUB8, MASK_SUB8)
DECLARE_INSN(subd, MATCH_SUBD, MASK_SUBD)
DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW)
DECLARE_INSN(sunpkd810, MATCH_SUNPKD810, MASK_SUNPKD810)
DECLARE_INSN(sunpkd820, MATCH_SUNPKD820, MASK_SUNPKD820)
@@ -3977,7 +4016,6 @@ DECLARE_INSN(sunpkd830, MATCH_SUNPKD830, MASK_SUNPKD830)
DECLARE_INSN(sunpkd831, MATCH_SUNPKD831, MASK_SUNPKD831)
DECLARE_INSN(sunpkd832, MATCH_SUNPKD832, MASK_SUNPKD832)
DECLARE_INSN(sw, MATCH_SW, MASK_SW)
DECLARE_INSN(swap8, MATCH_SWAP8, MASK_SWAP8)
DECLARE_INSN(uclip16, MATCH_UCLIP16, MASK_UCLIP16)
DECLARE_INSN(uclip32, MATCH_UCLIP32, MASK_UCLIP32)
DECLARE_INSN(uclip8, MATCH_UCLIP8, MASK_UCLIP8)
@@ -4488,8 +4526,6 @@ DECLARE_INSN(vxor_vx, MATCH_VXOR_VX, MASK_VXOR_VX)
DECLARE_INSN(vzext_vf2, MATCH_VZEXT_VF2, MASK_VZEXT_VF2)
DECLARE_INSN(vzext_vf4, MATCH_VZEXT_VF4, MASK_VZEXT_VF4)
DECLARE_INSN(vzext_vf8, MATCH_VZEXT_VF8, MASK_VZEXT_VF8)
DECLARE_INSN(wext, MATCH_WEXT, MASK_WEXT)
DECLARE_INSN(wexti, MATCH_WEXTI, MASK_WEXTI)
DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI)
DECLARE_INSN(wrs_nto, MATCH_WRS_NTO, MASK_WRS_NTO)
DECLARE_INSN(wrs_sto, MATCH_WRS_STO, MASK_WRS_STO)
@@ -4515,6 +4551,7 @@ DECLARE_CSR(vxsat, CSR_VXSAT)
DECLARE_CSR(vxrm, CSR_VXRM)
DECLARE_CSR(vcsr, CSR_VCSR)
DECLARE_CSR(seed, CSR_SEED)
DECLARE_CSR(jvt, CSR_JVT)
DECLARE_CSR(cycle, CSR_CYCLE)
DECLARE_CSR(time, CSR_TIME)
DECLARE_CSR(instret, CSR_INSTRET)
@@ -4567,6 +4604,9 @@ DECLARE_CSR(scause, CSR_SCAUSE)
DECLARE_CSR(stval, CSR_STVAL)
DECLARE_CSR(sip, CSR_SIP)
DECLARE_CSR(stimecmp, CSR_STIMECMP)
DECLARE_CSR(siselect, CSR_SISELECT)
DECLARE_CSR(sireg, CSR_SIREG)
DECLARE_CSR(stopei, CSR_STOPEI)
DECLARE_CSR(satp, CSR_SATP)
DECLARE_CSR(scontext, CSR_SCONTEXT)
DECLARE_CSR(vsstatus, CSR_VSSTATUS)
@@ -4578,6 +4618,9 @@ DECLARE_CSR(vscause, CSR_VSCAUSE)
DECLARE_CSR(vstval, CSR_VSTVAL)
DECLARE_CSR(vsip, CSR_VSIP)
DECLARE_CSR(vstimecmp, CSR_VSTIMECMP)
DECLARE_CSR(vsiselect, CSR_VSISELECT)
DECLARE_CSR(vsireg, CSR_VSIREG)
DECLARE_CSR(vstopei, CSR_VSTOPEI)
DECLARE_CSR(vsatp, CSR_VSATP)
DECLARE_CSR(hstatus, CSR_HSTATUS)
DECLARE_CSR(hedeleg, CSR_HEDELEG)
@@ -4586,6 +4629,8 @@ DECLARE_CSR(hie, CSR_HIE)
DECLARE_CSR(htimedelta, CSR_HTIMEDELTA)
DECLARE_CSR(hcounteren, CSR_HCOUNTEREN)
DECLARE_CSR(hgeie, CSR_HGEIE)
DECLARE_CSR(hvien, CSR_HVIEN)
DECLARE_CSR(hvictl, CSR_HVICTL)
DECLARE_CSR(henvcfg, CSR_HENVCFG)
DECLARE_CSR(hstateen0, CSR_HSTATEEN0)
DECLARE_CSR(hstateen1, CSR_HSTATEEN1)
@@ -4594,11 +4639,15 @@ DECLARE_CSR(hstateen3, CSR_HSTATEEN3)
DECLARE_CSR(htval, CSR_HTVAL)
DECLARE_CSR(hip, CSR_HIP)
DECLARE_CSR(hvip, CSR_HVIP)
DECLARE_CSR(hviprio1, CSR_HVIPRIO1)
DECLARE_CSR(hviprio2, CSR_HVIPRIO2)
DECLARE_CSR(htinst, CSR_HTINST)
DECLARE_CSR(hgatp, CSR_HGATP)
DECLARE_CSR(hcontext, CSR_HCONTEXT)
DECLARE_CSR(hgeip, CSR_HGEIP)
DECLARE_CSR(vstopi, CSR_VSTOPI)
DECLARE_CSR(scountovf, CSR_SCOUNTOVF)
DECLARE_CSR(stopi, CSR_STOPI)
DECLARE_CSR(utvt, CSR_UTVT)
DECLARE_CSR(unxti, CSR_UNXTI)
DECLARE_CSR(uintstatus, CSR_UINTSTATUS)
@@ -4621,6 +4670,8 @@ DECLARE_CSR(mideleg, CSR_MIDELEG)
DECLARE_CSR(mie, CSR_MIE)
DECLARE_CSR(mtvec, CSR_MTVEC)
DECLARE_CSR(mcounteren, CSR_MCOUNTEREN)
DECLARE_CSR(mvien, CSR_MVIEN)
DECLARE_CSR(mvip, CSR_MVIP)
DECLARE_CSR(menvcfg, CSR_MENVCFG)
DECLARE_CSR(mstateen0, CSR_MSTATEEN0)
DECLARE_CSR(mstateen1, CSR_MSTATEEN1)
@@ -4634,6 +4685,9 @@ DECLARE_CSR(mtval, CSR_MTVAL)
DECLARE_CSR(mip, CSR_MIP)
DECLARE_CSR(mtinst, CSR_MTINST)
DECLARE_CSR(mtval2, CSR_MTVAL2)
DECLARE_CSR(miselect, CSR_MISELECT)
DECLARE_CSR(mireg, CSR_MIREG)
DECLARE_CSR(mtopei, CSR_MTOPEI)
DECLARE_CSR(pmpcfg0, CSR_PMPCFG0)
DECLARE_CSR(pmpcfg1, CSR_PMPCFG1)
DECLARE_CSR(pmpcfg2, CSR_PMPCFG2)
@@ -4792,10 +4846,20 @@ DECLARE_CSR(marchid, CSR_MARCHID)
DECLARE_CSR(mimpid, CSR_MIMPID)
DECLARE_CSR(mhartid, CSR_MHARTID)
DECLARE_CSR(mconfigptr, CSR_MCONFIGPTR)
DECLARE_CSR(mtopi, CSR_MTOPI)
DECLARE_CSR(sieh, CSR_SIEH)
DECLARE_CSR(siph, CSR_SIPH)
DECLARE_CSR(stimecmph, CSR_STIMECMPH)
DECLARE_CSR(vsieh, CSR_VSIEH)
DECLARE_CSR(vsiph, CSR_VSIPH)
DECLARE_CSR(vstimecmph, CSR_VSTIMECMPH)
DECLARE_CSR(htimedeltah, CSR_HTIMEDELTAH)
DECLARE_CSR(hidelegh, CSR_HIDELEGH)
DECLARE_CSR(hvienh, CSR_HVIENH)
DECLARE_CSR(henvcfgh, CSR_HENVCFGH)
DECLARE_CSR(hviph, CSR_HVIPH)
DECLARE_CSR(hviprio1h, CSR_HVIPRIO1H)
DECLARE_CSR(hviprio2h, CSR_HVIPRIO2H)
DECLARE_CSR(hstateen0h, CSR_HSTATEEN0H)
DECLARE_CSR(hstateen1h, CSR_HSTATEEN1H)
DECLARE_CSR(hstateen2h, CSR_HSTATEEN2H)
@@ -4833,11 +4897,16 @@ DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H)
DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H)
DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H)
DECLARE_CSR(mstatush, CSR_MSTATUSH)
DECLARE_CSR(midelegh, CSR_MIDELEGH)
DECLARE_CSR(mieh, CSR_MIEH)
DECLARE_CSR(mvienh, CSR_MVIENH)
DECLARE_CSR(mviph, CSR_MVIPH)
DECLARE_CSR(menvcfgh, CSR_MENVCFGH)
DECLARE_CSR(mstateen0h, CSR_MSTATEEN0H)
DECLARE_CSR(mstateen1h, CSR_MSTATEEN1H)
DECLARE_CSR(mstateen2h, CSR_MSTATEEN2H)
DECLARE_CSR(mstateen3h, CSR_MSTATEEN3H)
DECLARE_CSR(miph, CSR_MIPH)
DECLARE_CSR(mhpmevent3h, CSR_MHPMEVENT3H)
DECLARE_CSR(mhpmevent4h, CSR_MHPMEVENT4H)
DECLARE_CSR(mhpmevent5h, CSR_MHPMEVENT5H)

View File

@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_FIELD_HELPERS_H
#define OPENOCD_TARGET_RISCV_FIELD_HELPERS_H
#include <stdint.h>
#include <assert.h>
static inline uint64_t get_field(uint64_t reg, uint64_t mask)
{
return (reg & mask) / (mask & ~(mask << 1));
}
static inline uint32_t get_field32(uint64_t reg, uint64_t mask)
{
uint64_t value = get_field(reg, mask);
assert(value <= UINT32_MAX);
return value;
}
static inline uint64_t set_field(uint64_t reg, uint64_t mask, uint64_t val)
{
/* Clear current value from field. */
reg &= ~mask;
uint64_t low_field_bit = mask & ~(mask << 1);
/* Assert if the value doesn't fit in the field. */
assert(((val * low_field_bit) & ~mask) == 0);
reg |= (val * low_field_bit) & mask;
return reg;
}
static inline uint32_t set_field32(uint32_t reg, uint32_t mask, uint32_t val)
{
return (uint32_t)set_field(reg, mask, val);
}
static inline uint64_t field_value(uint64_t mask, uint64_t val)
{
return set_field(0, mask, val);
}
static inline uint32_t field_value32(uint32_t mask, uint32_t val)
{
return set_field32(0, mask, val);
}
#endif /* OPENOCD_TARGET_RISCV_FIELD_HELPERS_H */

View File

@@ -1,7 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef TARGET__RISCV__GDB_REGS_H
#define TARGET__RISCV__GDB_REGS_H
#ifndef OPENOCD_TARGET_RISCV_GDB_REGS_H
#define OPENOCD_TARGET_RISCV_GDB_REGS_H
#include "encoding.h"
/* gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in
* its source tree. We must interpret the numbers the same here. */
@@ -78,15 +80,21 @@ enum gdb_regno {
GDB_REGNO_FT11,
GDB_REGNO_FPR31 = GDB_REGNO_FT11,
GDB_REGNO_CSR0 = 65,
GDB_REGNO_FCSR = CSR_FCSR + GDB_REGNO_CSR0,
GDB_REGNO_FFLAGS = CSR_FFLAGS + GDB_REGNO_CSR0,
GDB_REGNO_FRM = CSR_FRM + GDB_REGNO_CSR0,
GDB_REGNO_VSTART = CSR_VSTART + GDB_REGNO_CSR0,
GDB_REGNO_VXSAT = CSR_VXSAT + GDB_REGNO_CSR0,
GDB_REGNO_VXRM = CSR_VXRM + GDB_REGNO_CSR0,
GDB_REGNO_VCSR = CSR_VCSR + GDB_REGNO_CSR0,
GDB_REGNO_VLENB = CSR_VLENB + GDB_REGNO_CSR0,
GDB_REGNO_VL = CSR_VL + GDB_REGNO_CSR0,
GDB_REGNO_VTYPE = CSR_VTYPE + GDB_REGNO_CSR0,
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0,
GDB_REGNO_TDATA3 = CSR_TDATA3 + GDB_REGNO_CSR0,
GDB_REGNO_TINFO = CSR_TINFO + GDB_REGNO_CSR0,
GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0,
GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0,
GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0,
@@ -95,6 +103,11 @@ enum gdb_regno {
GDB_REGNO_MEPC = CSR_MEPC + GDB_REGNO_CSR0,
GDB_REGNO_MCAUSE = CSR_MCAUSE + GDB_REGNO_CSR0,
GDB_REGNO_SATP = CSR_SATP + GDB_REGNO_CSR0,
GDB_REGNO_VSATP = CSR_VSATP + GDB_REGNO_CSR0,
GDB_REGNO_HGATP = CSR_HGATP + GDB_REGNO_CSR0,
GDB_REGNO_HSTATUS = CSR_HSTATUS + GDB_REGNO_CSR0,
GDB_REGNO_MTOPI = CSR_MTOPI + GDB_REGNO_CSR0,
GDB_REGNO_MTOPEI = CSR_MTOPEI + GDB_REGNO_CSR0,
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
GDB_REGNO_PRIV = 4161,
/* It's still undecided what register numbers GDB will actually use for
@@ -112,6 +125,4 @@ enum gdb_regno {
GDB_REGNO_COUNT
};
const char *gdb_regno_name(enum gdb_regno regno);
#endif
#endif /* OPENOCD_TARGET_RISCV_GDB_REGS_H */

View File

@@ -1,15 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_OPCODES_H
#define OPENOCD_TARGET_RISCV_OPCODES_H
#include "encoding.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#define ZERO 0
#define T0 5
#define S0 8
#define S1 9
#define MAX_GPR_NUM 31
#define MAX_FPR_NUM 31
#define MAX_VREG_NUM 31
#define MAX_CSR_NUM 4095
#define MIN_INT12 (-0x800)
#define MAX_INT12 0x7ff
#define MIN_INT13 (-0x1000)
#define MAX_INT13 0xfff
#define MIN_INT21 (-0x100000)
#define MAX_INT21 0xfffff
#define MAX_UINT5 0x1f
#define MAX_UINT11 0x7ff
#define MAX_UINT12 0xfff
static uint32_t bits(uint32_t value, unsigned int hi, unsigned int lo)
{
return (value >> lo) & ((1 << (hi+1-lo)) - 1);
return (value >> lo) & (((uint32_t)1 << (hi + 1 - lo)) - 1);
}
static uint32_t bit(uint32_t value, unsigned int b)
@@ -65,153 +89,246 @@ static uint32_t imm_j(uint32_t imm)
return (bits(imm, 19, 12) << 12) | (bit(imm, 11) << 20) | (bits(imm, 10, 1) << 21) | (bit(imm, 20) << 31);
}
static uint32_t jal(unsigned int rd, uint32_t imm) __attribute__ ((unused));
static uint32_t jal(unsigned int rd, uint32_t imm)
static uint32_t jal(unsigned int rd, int32_t imm) __attribute__ ((unused));
static uint32_t jal(unsigned int rd, int32_t imm)
{
return imm_j(imm) | inst_rd(rd) | MATCH_JAL;
assert(rd <= MAX_GPR_NUM);
assert((imm >= MIN_INT21) && (imm <= MAX_INT21));
assert((imm & 1) == 0);
return imm_j((uint32_t)imm) | inst_rd(rd) | MATCH_JAL;
}
static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused));
static uint32_t csrsi(unsigned int csr, uint16_t imm)
static uint32_t csrsi(unsigned int csr, uint8_t imm) __attribute__ ((unused));
static uint32_t csrsi(unsigned int csr, uint8_t imm)
{
assert(csr <= MAX_CSR_NUM);
assert(imm <= MAX_UINT5);
return imm_i(csr) | inst_rs1(imm) | MATCH_CSRRSI;
}
static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset)
static uint32_t sw(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t sw(unsigned int src, unsigned int base, int16_t offset)
{
return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SW;
assert(src <= MAX_GPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SW;
}
static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset)
static uint32_t sd(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t sd(unsigned int src, unsigned int base, int16_t offset)
{
return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SD;
assert(src <= MAX_GPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SD;
}
static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset)
static uint32_t sh(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t sh(unsigned int src, unsigned int base, int16_t offset)
{
return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SH;
assert(src <= MAX_GPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SH;
}
static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset)
static uint32_t sb(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t sb(unsigned int src, unsigned int base, int16_t offset)
{
return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SB;
assert(src <= MAX_GPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SB;
}
static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset)
static uint32_t ld(unsigned int rd, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t ld(unsigned int rd, unsigned int base, int16_t offset)
{
return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LD;
assert(rd <= MAX_GPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LD;
}
static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset)
static uint32_t lw(unsigned int rd, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t lw(unsigned int rd, unsigned int base, int16_t offset)
{
return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LW;
assert(rd <= MAX_GPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LW;
}
static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset)
static uint32_t lh(unsigned int rd, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t lh(unsigned int rd, unsigned int base, int16_t offset)
{
return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LH;
assert(rd <= MAX_GPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LH;
}
static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset)
static uint32_t lb(unsigned int rd, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t lb(unsigned int rd, unsigned int base, int16_t offset)
{
return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LB;
assert(rd <= MAX_GPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LB;
}
static uint32_t csrw(unsigned int source, unsigned int csr) __attribute__ ((unused));
static uint32_t csrw(unsigned int source, unsigned int csr)
{
assert(source <= MAX_GPR_NUM);
assert(csr <= MAX_CSR_NUM);
return imm_i(csr) | inst_rs1(source) | MATCH_CSRRW;
}
static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm)
static uint32_t addi(unsigned int dest, unsigned int src, int16_t imm) __attribute__ ((unused));
static uint32_t addi(unsigned int dest, unsigned int src, int16_t imm)
{
return imm_i(imm) | inst_rs1(src) | inst_rd(dest) | MATCH_ADDI;
assert(dest <= MAX_GPR_NUM);
assert(src <= MAX_GPR_NUM);
assert((imm >= MIN_INT12) && (imm <= MAX_INT12));
return imm_i((uint16_t)imm) | inst_rs1(src) | inst_rd(dest) | MATCH_ADDI;
}
static uint32_t csrr(unsigned int rd, unsigned int csr) __attribute__ ((unused));
static uint32_t csrr(unsigned int rd, unsigned int csr)
{
assert(rd <= MAX_GPR_NUM);
assert(csr <= MAX_CSR_NUM);
return imm_i(csr) | inst_rd(rd) | MATCH_CSRRS;
}
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr)
{
assert(rd <= MAX_GPR_NUM);
assert(rs <= MAX_GPR_NUM);
assert(csr <= MAX_CSR_NUM);
return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRS;
}
static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr)
{
assert(rd <= MAX_GPR_NUM);
assert(rs <= MAX_GPR_NUM);
assert(csr <= MAX_CSR_NUM);
return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRW;
}
static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr)
static uint32_t csrrci(unsigned int rd, uint8_t zimm, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrci(unsigned int rd, uint8_t zimm, unsigned int csr)
{
assert(rd <= MAX_GPR_NUM);
assert(zimm <= MAX_UINT5);
assert(csr <= MAX_CSR_NUM);
return imm_i(csr) | inst_rs1(zimm) | inst_rd(rd) | MATCH_CSRRCI;
}
static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr)
static uint32_t csrrsi(unsigned int rd, uint8_t zimm, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrsi(unsigned int rd, uint8_t zimm, unsigned int csr)
{
assert(rd <= MAX_GPR_NUM);
assert(zimm <= MAX_UINT5);
assert(csr <= MAX_CSR_NUM);
return imm_i(csr) | inst_rs1(zimm) | inst_rd(rd) | MATCH_CSRRSI;
}
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset)
static uint32_t fsw(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t fsw(unsigned int src, unsigned int base, int16_t offset)
{
return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSW;
assert(src <= MAX_FPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSW;
}
static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset)
static uint32_t fsd(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t fsd(unsigned int src, unsigned int base, int16_t offset)
{
return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSD;
assert(src <= MAX_FPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSD;
}
static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset)
static uint32_t flw(unsigned int dest, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t flw(unsigned int dest, unsigned int base, int16_t offset)
{
return imm_i(offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLW;
assert(dest <= MAX_FPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLW;
}
static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset)
static uint32_t fld(unsigned int dest, unsigned int base, int16_t offset) __attribute__ ((unused));
static uint32_t fld(unsigned int dest, unsigned int base, int16_t offset)
{
return imm_i(offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLD;
assert(dest <= MAX_FPR_NUM);
assert(base <= MAX_GPR_NUM);
assert((offset >= MIN_INT12) && (offset <= MAX_INT12));
return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLD;
}
static uint32_t fmv_x_w(unsigned int dest, unsigned int src) __attribute__ ((unused));
static uint32_t fmv_x_w(unsigned int dest, unsigned int src)
{
assert(dest <= MAX_GPR_NUM);
assert(src <= MAX_FPR_NUM);
return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_X_W;
}
static uint32_t fmv_x_d(unsigned int dest, unsigned int src) __attribute__ ((unused));
static uint32_t fmv_x_d(unsigned int dest, unsigned int src)
{
assert(dest <= MAX_GPR_NUM);
assert(src <= MAX_FPR_NUM);
return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_X_D;
}
static uint32_t fmv_w_x(unsigned int dest, unsigned int src) __attribute__ ((unused));
static uint32_t fmv_w_x(unsigned int dest, unsigned int src)
{
assert(dest <= MAX_FPR_NUM);
assert(src <= MAX_GPR_NUM);
return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_W_X;
}
static uint32_t fmv_d_x(unsigned int dest, unsigned int src) __attribute__ ((unused));
static uint32_t fmv_d_x(unsigned int dest, unsigned int src)
{
assert(dest <= MAX_FPR_NUM);
assert(src <= MAX_GPR_NUM);
return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_D_X;
}
@@ -238,97 +355,95 @@ static uint32_t fence_i(void)
static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused));
static uint32_t lui(unsigned int dest, uint32_t imm)
{
assert(dest <= MAX_GPR_NUM);
assert(bits(imm, 11, 0) == 0);
return imm_u(imm) | inst_rd(dest) | MATCH_LUI;
}
/*
static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused));
static uint32_t csrci(unsigned int csr, uint16_t imm)
static uint32_t xori(unsigned int dest, unsigned int src, int16_t imm) __attribute__ ((unused));
static uint32_t xori(unsigned int dest, unsigned int src, int16_t imm)
{
return (csr << 20) |
(bits(imm, 4, 0) << 15) |
MATCH_CSRRCI;
}
assert(dest <= MAX_GPR_NUM);
assert(src <= MAX_GPR_NUM);
assert((imm >= MIN_INT12) && (imm <= MAX_INT12));
static uint32_t li(unsigned int dest, uint16_t imm) __attribute__ ((unused));
static uint32_t li(unsigned int dest, uint16_t imm)
{
return addi(dest, 0, imm);
}
static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset)
{
return (bits(offset, 11, 5) << 25) |
(bits(src, 4, 0) << 20) |
(base << 15) |
(bits(offset, 4, 0) << 7) |
MATCH_FSD;
}
static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm)
{
return (bits(imm, 11, 0) << 20) |
(src << 15) |
(dest << 7) |
MATCH_ORI;
}
static uint32_t nop(void) __attribute__ ((unused));
static uint32_t nop(void)
{
return addi(0, 0, 0);
}
*/
static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm)
{
return imm_i(imm) | inst_rs1(src) | inst_rd(dest) | MATCH_XORI;
return imm_i((uint16_t)imm) | inst_rs1(src) | inst_rd(dest) | MATCH_XORI;
}
static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) __attribute__ ((unused));
static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt)
{
assert(dest <= MAX_GPR_NUM);
assert(src <= MAX_GPR_NUM);
assert(shamt <= MAX_UINT5);
return inst_rs2(shamt) | inst_rs1(src) | inst_rd(dest) | MATCH_SRLI;
}
static uint32_t fence(void) __attribute__((unused));
static uint32_t fence(void)
static uint32_t fence_rw_rw(void) __attribute__((unused));
static uint32_t fence_rw_rw(void)
{
return MATCH_FENCE;
/* fence rw,rw */
return MATCH_FENCE | 0x3300000;
}
static uint32_t auipc(unsigned int dest) __attribute__((unused));
static uint32_t auipc(unsigned int dest)
{
assert(dest <= MAX_GPR_NUM);
return MATCH_AUIPC | inst_rd(dest);
}
static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm) __attribute__((unused));
static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm)
static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t vtypei) __attribute__((unused));
static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t vtypei)
{
return (bits(imm, 10, 0) << 20) | inst_rs1(src) | inst_rd(dest) | MATCH_VSETVLI;
assert(dest <= MAX_GPR_NUM);
assert(src <= MAX_GPR_NUM);
assert(vtypei <= MAX_UINT11);
return (bits(vtypei, 10, 0) << 20) | inst_rs1(src) | inst_rd(dest) | MATCH_VSETVLI;
}
static uint32_t vsetvl(unsigned int rd, unsigned int rs1, unsigned int rs2) __attribute__((unused));
static uint32_t vsetvl(unsigned int rd, unsigned int rs1, unsigned int rs2)
{
assert(rd <= MAX_GPR_NUM);
assert(rs1 <= MAX_GPR_NUM);
assert(rs2 <= MAX_GPR_NUM);
return inst_rd(rd) | inst_rs1(rs1) | inst_rs2(rs2) | MATCH_VSETVL;
}
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) __attribute__((unused));
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2)
{
assert(rd <= MAX_GPR_NUM);
assert(vs2 <= MAX_VREG_NUM);
return inst_rs2(vs2) | inst_rd(rd) | MATCH_VMV_X_S;
}
static uint32_t vmv_s_x(unsigned int vd, unsigned int vs2) __attribute__((unused));
static uint32_t vmv_s_x(unsigned int vd, unsigned int rs1)
static uint32_t vmv_s_x(unsigned int vd, unsigned int rs2) __attribute__((unused));
static uint32_t vmv_s_x(unsigned int vd, unsigned int rs2)
{
return inst_rs1(rs1) | inst_rd(vd) | MATCH_VMV_S_X;
assert(vd <= MAX_VREG_NUM);
assert(rs2 <= MAX_GPR_NUM);
return inst_rs1(rs2) | inst_rd(vd) | MATCH_VMV_S_X;
}
static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
unsigned int rs1, unsigned int vm) __attribute__((unused));
unsigned int rs1, bool vm) __attribute__((unused));
static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
unsigned int rs1, unsigned int vm)
unsigned int rs1, bool vm)
{
return ((vm & 1) << 25) | inst_rs2(vs2) | inst_rs1(rs1) | inst_rd(vd) | MATCH_VSLIDE1DOWN_VX;
assert(vd <= MAX_VREG_NUM);
assert(vs2 <= MAX_VREG_NUM);
assert(rs1 <= MAX_GPR_NUM);
return (vm ? (1u << 25) : 0u) | inst_rs2(vs2) | inst_rs1(rs1) | inst_rd(vd) | MATCH_VSLIDE1DOWN_VX;
}
#endif /* OPENOCD_TARGET_RISCV_OPCODES_H */

View File

@@ -10,7 +10,7 @@
#include "program.h"
#include "helper/log.h"
#include "asm.h"
#include "debug_defines.h"
#include "encoding.h"
/* Program interface. */
@@ -19,22 +19,20 @@ int riscv_program_init(struct riscv_program *p, struct target *target)
memset(p, 0, sizeof(*p));
p->target = target;
p->instruction_count = 0;
p->target_xlen = riscv_xlen(target);
for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i)
p->writes_xreg[i] = 0;
for (size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i)
p->debug_buffer[i] = -1;
for (unsigned int i = 0; i < RISCV013_MAX_PROGBUF_SIZE; ++i)
p->progbuf[i] = (uint32_t)(-1);
p->execution_result = RISCV_PROGBUF_EXEC_RESULT_NOT_EXECUTED;
return ERROR_OK;
}
int riscv_program_write(struct riscv_program *program)
{
for (unsigned int i = 0; i < program->instruction_count; ++i) {
LOG_DEBUG("debug_buffer[%02x] = DASM(0x%08x)", i, program->debug_buffer[i]);
if (riscv_write_debug_buffer(program->target, i,
program->debug_buffer[i]) != ERROR_OK)
LOG_TARGET_DEBUG(program->target, "progbuf[%02x] = DASM(0x%08x)",
i, program->progbuf[i]);
if (riscv_write_progbuf(program->target, i, program->progbuf[i]) != ERROR_OK)
return ERROR_FAIL;
}
return ERROR_OK;
@@ -45,90 +43,114 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
{
keep_alive();
riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1];
for (size_t i = GDB_REGNO_ZERO + 1; i <= GDB_REGNO_XPR31; ++i) {
if (p->writes_xreg[i]) {
LOG_DEBUG("Saving register %d as used by program", (int)i);
int result = riscv_get_register(t, &saved_registers[i], i);
if (result != ERROR_OK)
return result;
}
}
p->execution_result = RISCV_PROGBUF_EXEC_RESULT_UNKNOWN;
if (riscv_program_ebreak(p) != ERROR_OK) {
LOG_ERROR("Unable to write ebreak");
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
LOG_ERROR("ram[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]",
(int)i, p->debug_buffer[i], p->debug_buffer[i]);
LOG_TARGET_ERROR(t, "Unable to insert ebreak into program buffer");
for (unsigned int i = 0; i < riscv_progbuf_size(p->target); ++i)
LOG_TARGET_ERROR(t, "progbuf[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]",
i, p->progbuf[i], p->progbuf[i]);
return ERROR_FAIL;
}
if (riscv_program_write(p) != ERROR_OK)
return ERROR_FAIL;
if (riscv_execute_debug_buffer(t) != ERROR_OK) {
LOG_DEBUG("Unable to execute program %p", p);
uint32_t cmderr;
if (riscv_execute_progbuf(t, &cmderr) != ERROR_OK) {
p->execution_result = (cmderr == DM_ABSTRACTCS_CMDERR_EXCEPTION)
? RISCV_PROGBUF_EXEC_RESULT_EXCEPTION
: RISCV_PROGBUF_EXEC_RESULT_UNKNOWN_ERROR;
/* TODO: what happens if we fail here, but need to restore registers? */
LOG_TARGET_DEBUG(t, "Unable to execute program %p", p);
return ERROR_FAIL;
}
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
if (i >= riscv_debug_buffer_size(p->target))
p->debug_buffer[i] = riscv_read_debug_buffer(t, i);
for (size_t i = GDB_REGNO_ZERO; i <= GDB_REGNO_XPR31; ++i)
if (p->writes_xreg[i])
riscv_set_register(t, i, saved_registers[i]);
p->execution_result = RISCV_PROGBUF_EXEC_RESULT_SUCCESS;
return ERROR_OK;
}
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset)
{
return riscv_program_insert(p, sd(d, b, offset));
}
int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset)
{
return riscv_program_insert(p, sw(d, b, offset));
}
int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset)
{
return riscv_program_insert(p, sh(d, b, offset));
}
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset)
{
return riscv_program_insert(p, sb(d, b, offset));
}
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
int riscv_program_store(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset,
unsigned int size)
{
switch (size) {
case 1:
return riscv_program_sbr(p, d, b, offset);
case 2:
return riscv_program_shr(p, d, b, offset);
case 4:
return riscv_program_swr(p, d, b, offset);
case 8:
return riscv_program_sdr(p, d, b, offset);
}
assert(false && "Unsupported size");
return ERROR_FAIL;
}
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset)
{
return riscv_program_insert(p, ld(d, b, offset));
}
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset)
{
return riscv_program_insert(p, lw(d, b, offset));
}
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset)
{
return riscv_program_insert(p, lh(d, b, offset));
}
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset)
{
return riscv_program_insert(p, lb(d, b, offset));
}
int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr)
int riscv_program_load(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset,
unsigned int size)
{
switch (size) {
case 1:
return riscv_program_lbr(p, d, b, offset);
case 2:
return riscv_program_lhr(p, d, b, offset);
case 4:
return riscv_program_lwr(p, d, b, offset);
case 8:
return riscv_program_ldr(p, d, b, offset);
}
assert(false && "Unsupported size");
return ERROR_FAIL;
}
int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, uint8_t z, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095);
return riscv_program_insert(p, csrrsi(d, z, csr - GDB_REGNO_CSR0));
}
int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr)
int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, uint8_t z, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095);
return riscv_program_insert(p, csrrci(d, z, csr - GDB_REGNO_CSR0));
@@ -142,7 +164,7 @@ int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0);
assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095);
return riscv_program_insert(p, csrrw(GDB_REGNO_ZERO, s, csr - GDB_REGNO_CSR0));
}
@@ -151,17 +173,17 @@ int riscv_program_fence_i(struct riscv_program *p)
return riscv_program_insert(p, fence_i());
}
int riscv_program_fence(struct riscv_program *p)
int riscv_program_fence_rw_rw(struct riscv_program *p)
{
return riscv_program_insert(p, fence());
return riscv_program_insert(p, fence_rw_rw());
}
int riscv_program_ebreak(struct riscv_program *p)
{
struct target *target = p->target;
RISCV_INFO(r);
if (p->instruction_count == riscv_debug_buffer_size(p->target) &&
r->impebreak) {
if (p->instruction_count == riscv_progbuf_size(target) &&
r->get_impebreak(target)) {
return ERROR_OK;
}
return riscv_program_insert(p, ebreak());
@@ -174,14 +196,14 @@ int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i)
{
if (p->instruction_count >= riscv_debug_buffer_size(p->target)) {
LOG_ERROR("Unable to insert instruction:");
LOG_ERROR(" instruction_count=%d", (int)p->instruction_count);
LOG_ERROR(" buffer size =%d", (int)riscv_debug_buffer_size(p->target));
if (p->instruction_count >= riscv_progbuf_size(p->target)) {
LOG_TARGET_ERROR(p->target, "Unable to insert program into progbuf, "
"capacity would be exceeded (progbufsize=%u).",
riscv_progbuf_size(p->target));
return ERROR_FAIL;
}
p->debug_buffer[p->instruction_count] = i;
p->progbuf[p->instruction_count] = i;
p->instruction_count++;
return ERROR_OK;
}

View File

@@ -1,13 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef TARGET__RISCV__PROGRAM_H
#define TARGET__RISCV__PROGRAM_H
#ifndef OPENOCD_TARGET_RISCV_PROGRAM_H
#define OPENOCD_TARGET_RISCV_PROGRAM_H
#include "riscv.h"
#define RISCV_MAX_DEBUG_BUFFER_SIZE 32
#define RISCV_REGISTER_COUNT 32
#define RISCV_DSCRATCH_COUNT 2
#define RISCV013_MAX_PROGBUF_SIZE 16
enum riscv_progbuf_exec_result {
RISCV_PROGBUF_EXEC_RESULT_NOT_EXECUTED,
RISCV_PROGBUF_EXEC_RESULT_UNKNOWN,
RISCV_PROGBUF_EXEC_RESULT_EXCEPTION,
RISCV_PROGBUF_EXEC_RESULT_UNKNOWN_ERROR,
RISCV_PROGBUF_EXEC_RESULT_SUCCESS
};
/* The various RISC-V debug specifications all revolve around setting up
* program buffers and executing them on the target. This structure contains a
@@ -15,17 +21,14 @@
struct riscv_program {
struct target *target;
uint32_t debug_buffer[RISCV_MAX_DEBUG_BUFFER_SIZE];
uint32_t progbuf[RISCV013_MAX_PROGBUF_SIZE];
/* Number of 32-bit instructions in the program. */
size_t instruction_count;
unsigned int instruction_count;
/* Side effects of executing this program. These must be accounted for
* in order to maintain correct executing of the target system. */
bool writes_xreg[RISCV_REGISTER_COUNT];
/* XLEN on the target. */
int target_xlen;
/* execution result of the program */
/* TODO: remove this field. We should make it a parameter to riscv_program_exec */
enum riscv_progbuf_exec_result execution_result;
};
/* Initializes a program with the header. */
@@ -36,7 +39,7 @@ int riscv_program_write(struct riscv_program *program);
/* Executes a program, returning 0 if the program successfully executed. Note
* that this may cause registers to be saved or restored, which could result to
* calls to things like riscv_save_register which itself could require a
* calls to things like riscv013_reg_save which itself could require a
* program to execute. That's OK, just make sure this eventually terminates.
* */
int riscv_program_exec(struct riscv_program *p, struct target *t);
@@ -47,25 +50,29 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
/* Helpers to assemble various instructions. Return 0 on success. These might
* assemble into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int16_t o);
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int16_t o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int16_t o);
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int16_t o);
int riscv_program_load(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t o,
unsigned int s);
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int16_t o);
int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int16_t o);
int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int16_t o);
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int16_t o);
int riscv_program_store(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t o,
unsigned int s);
int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr);
int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr);
int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, uint8_t z, enum gdb_regno csr);
int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, uint8_t z, enum gdb_regno csr);
int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr);
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr);
int riscv_program_fence_i(struct riscv_program *p);
int riscv_program_fence(struct riscv_program *p);
int riscv_program_fence_rw_rw(struct riscv_program *p);
int riscv_program_ebreak(struct riscv_program *p);
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i);
#endif
#endif /* OPENOCD_TARGET_RISCV_PROGRAM_H */

View File

@@ -13,6 +13,8 @@
#include "config.h"
#endif
#include "riscv-011.h"
#include "target/target.h"
#include "target/algorithm.h"
#include "target/target_type.h"
@@ -22,8 +24,10 @@
#include "target/breakpoints.h"
#include "helper/time_support.h"
#include "riscv.h"
#include "asm.h"
#include "riscv_reg.h"
#include "riscv-011_reg.h"
#include "gdb_regs.h"
#include "field_helpers.h"
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@@ -67,8 +71,7 @@
* to the target. Afterwards use cache_get... to read results.
*/
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
static int handle_halt(struct target *target, bool announce);
/* Constants for legacy SiFive hardware breakpoints. */
#define CSR_BPCONTROL_X (1<<0)
@@ -161,15 +164,6 @@ typedef enum slot {
#define DRAM_CACHE_SIZE 16
struct trigger {
uint64_t address;
uint32_t length;
uint64_t mask;
uint64_t value;
bool read, write, execute;
int unique_id;
};
struct memory_cache_line {
uint32_t data;
bool valid;
@@ -219,7 +213,6 @@ typedef struct {
static int poll_target(struct target *target, bool announce);
static int riscv011_poll(struct target *target);
static int get_register(struct target *target, riscv_reg_t *value, int regid);
/*** Utility functions. ***/
@@ -257,18 +250,46 @@ static unsigned int slot_offset(const struct target *target, slot_t slot)
return 0; /* Silence -Werror=return-type */
}
static uint32_t load(const struct target *target, unsigned int rd,
unsigned int base, int16_t offset)
{
switch (riscv_xlen(target)) {
case 32:
return lw(rd, base, offset);
case 64:
return ld(rd, base, offset);
}
assert(0);
return 0; /* Silence -Werror=return-type */
}
static uint32_t store(const struct target *target, unsigned int src,
unsigned int base, int16_t offset)
{
switch (riscv_xlen(target)) {
case 32:
return sw(src, base, offset);
case 64:
return sd(src, base, offset);
}
assert(0);
return 0; /* Silence -Werror=return-type */
}
static uint32_t load_slot(const struct target *target, unsigned int dest,
slot_t slot)
{
unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
return load(target, dest, ZERO, offset);
assert(offset <= MAX_INT12);
return load(target, dest, ZERO, (int16_t)offset);
}
static uint32_t store_slot(const struct target *target, unsigned int src,
slot_t slot)
{
unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
return store(target, src, ZERO, offset);
assert(offset <= MAX_INT12);
return store(target, src, ZERO, (int16_t)offset);
}
static uint16_t dram_address(unsigned int index)
@@ -279,36 +300,6 @@ static uint16_t dram_address(unsigned int index)
return 0x40 + index - 0x10;
}
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{
struct scan_field field;
uint8_t in_value[4];
uint8_t out_value[4] = { 0 };
buf_set_u32(out_value, 0, 32, out);
jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
field.num_bits = 32;
field.out_value = out_value;
field.in_value = in_value;
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
/* Always return to dbus. */
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
LOG_ERROR("failed jtag scan: %d", retval);
return retval;
}
uint32_t in = buf_get_u32(field.in_value, 0, 32);
LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in);
return in;
}
static uint32_t idcode_scan(struct target *target)
{
struct scan_field field;
@@ -344,7 +335,7 @@ static void increase_dbus_busy_delay(struct target *target)
info->dtmcontrol_idle, info->dbus_busy_delay,
info->interrupt_high_delay);
dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET);
dtmcs_scan(target->tap, DTMCONTROL_DBUS_RESET, NULL /* discard value */);
}
static void increase_interrupt_high_delay(struct target *target)
@@ -431,8 +422,13 @@ static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in,
.out_value = out,
.in_value = in
};
if (address_in)
*address_in = 0;
assert(info->addrbits != 0);
if (info->addrbits == 0) {
LOG_TARGET_ERROR(target, "Can't access DMI because addrbits=0.");
return DBUS_STATUS_FAILED;
}
buf_set_u64(out, DBUS_OP_START, DBUS_OP_SIZE, op);
buf_set_u64(out, DBUS_DATA_START, DBUS_DATA_SIZE, data_out);
@@ -605,9 +601,9 @@ static void scans_add_write32(scans_t *scans, uint16_t address, uint32_t data,
static void scans_add_write_jump(scans_t *scans, uint16_t address,
bool set_interrupt)
{
scans_add_write32(scans, address,
jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*address))),
set_interrupt);
unsigned int jump_offset = DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4 * address);
assert(jump_offset <= MAX_INT21);
scans_add_write32(scans, address, jal(0, (int32_t)jump_offset), set_interrupt);
}
/** Add a 32-bit dbus write for an instruction that loads from the indicated
@@ -686,18 +682,13 @@ static void dram_write32(struct target *target, unsigned int index, uint32_t val
}
/** Read the haltnot and interrupt bits. */
static bits_t read_bits(struct target *target)
static int read_bits(struct target *target, bits_t *result)
{
uint64_t value;
dbus_status_t status;
uint16_t address_in;
riscv011_info_t *info = get_info(target);
bits_t err_result = {
.haltnot = 0,
.interrupt = 0
};
do {
unsigned int i = 0;
do {
@@ -706,26 +697,23 @@ static bits_t read_bits(struct target *target)
if (address_in == (1<<info->addrbits) - 1 &&
value == (1ULL<<DBUS_DATA_SIZE) - 1) {
LOG_ERROR("TDO seems to be stuck high.");
return err_result;
return ERROR_FAIL;
}
increase_dbus_busy_delay(target);
} else if (status == DBUS_STATUS_FAILED) {
/* TODO: return an actual error */
return err_result;
}
} while (status == DBUS_STATUS_BUSY && i++ < 256);
if (i >= 256) {
if (status != DBUS_STATUS_SUCCESS) {
LOG_ERROR("Failed to read from 0x%x; status=%d", address_in, status);
return err_result;
return ERROR_FAIL;
}
} while (address_in > 0x10 && address_in != DMCONTROL);
bits_t result = {
.haltnot = get_field(value, DMCONTROL_HALTNOT),
.interrupt = get_field(value, DMCONTROL_INTERRUPT)
};
return result;
if (result) {
result->haltnot = get_field(value, DMCONTROL_HALTNOT);
result->interrupt = get_field(value, DMCONTROL_INTERRUPT);
}
return ERROR_OK;
}
static int wait_for_debugint_clear(struct target *target, bool ignore_first)
@@ -736,13 +724,19 @@ static int wait_for_debugint_clear(struct target *target, bool ignore_first)
* result of the read that happened just before debugint was set.
* (Assuming the last scan before calling this function was one that
* sets debugint.) */
read_bits(target);
read_bits(target, NULL);
}
while (1) {
bits_t bits = read_bits(target);
bits_t bits = {
.haltnot = 0,
.interrupt = 0
};
if (read_bits(target, &bits) != ERROR_OK)
return ERROR_FAIL;
if (!bits.interrupt)
return ERROR_OK;
if (time(NULL) - start > riscv_command_timeout_sec) {
if (time(NULL) - start > riscv_get_command_timeout_sec()) {
LOG_ERROR("Timed out waiting for debug int to clear."
"Increase timeout with riscv set_command_timeout_sec.");
return ERROR_FAIL;
@@ -788,22 +782,25 @@ static void cache_set(struct target *target, slot_t slot, uint64_t data)
static void cache_set_jump(struct target *target, unsigned int index)
{
cache_set32(target, index,
jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index))));
unsigned int jump_offset = DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4 * index);
assert(jump_offset <= MAX_INT21);
cache_set32(target, index, jal(0, (int32_t)jump_offset));
}
static void cache_set_load(struct target *target, unsigned int index,
unsigned int reg, slot_t slot)
{
uint16_t offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
cache_set32(target, index, load(target, reg, ZERO, offset));
unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
assert(offset <= MAX_INT12);
cache_set32(target, index, load(target, reg, ZERO, (int16_t)offset));
}
static void cache_set_store(struct target *target, unsigned int index,
unsigned int reg, slot_t slot)
{
uint16_t offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
cache_set32(target, index, store(target, reg, ZERO, offset));
unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
assert(offset <= MAX_INT12);
cache_set32(target, index, store(target, reg, ZERO, (int16_t)offset));
}
static void dump_debug_ram(struct target *target)
@@ -1012,9 +1009,9 @@ static uint64_t cache_get(struct target *target, slot_t slot)
static void dram_write_jump(struct target *target, unsigned int index,
bool set_interrupt)
{
dram_write32(target, index,
jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index))),
set_interrupt);
unsigned int jump_offset = DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4 * index);
assert(jump_offset <= MAX_INT21);
dram_write32(target, index, jal(0, (int32_t)jump_offset), set_interrupt);
}
static int wait_for_state(struct target *target, enum target_state state)
@@ -1026,7 +1023,7 @@ static int wait_for_state(struct target *target, enum target_state state)
return result;
if (target->state == state)
return ERROR_OK;
if (time(NULL) - start > riscv_command_timeout_sec) {
if (time(NULL) - start > riscv_get_command_timeout_sec()) {
LOG_ERROR("Timed out waiting for state %d. "
"Increase timeout with riscv set_command_timeout_sec.", state);
return ERROR_FAIL;
@@ -1048,7 +1045,7 @@ static int read_remote_csr(struct target *target, uint64_t *value, uint32_t csr)
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
LOG_WARNING("Got exception 0x%x when reading %s", exception,
gdb_regno_name(GDB_REGNO_CSR0 + csr));
riscv_reg_gdb_regno_name(target, GDB_REGNO_CSR0 + csr));
*value = ~0;
return ERROR_FAIL;
}
@@ -1107,12 +1104,25 @@ static int maybe_write_tselect(struct target *target)
return ERROR_OK;
}
static uint64_t set_ebreakx_fields(uint64_t dcsr, const struct target *target)
{
const struct riscv_private_config * const config = riscv_private_config(target);
dcsr = set_field(dcsr, DCSR_EBREAKM, config->dcsr_ebreak_fields[RISCV_MODE_M]);
dcsr = set_field(dcsr, DCSR_EBREAKS, config->dcsr_ebreak_fields[RISCV_MODE_S]);
dcsr = set_field(dcsr, DCSR_EBREAKU, config->dcsr_ebreak_fields[RISCV_MODE_U]);
dcsr = set_field(dcsr, DCSR_EBREAKH, 1);
return dcsr;
}
static int execute_resume(struct target *target, bool step)
{
riscv011_info_t *info = get_info(target);
LOG_DEBUG("step=%d", step);
if (riscv_reg_flush_all(target) != ERROR_OK)
return ERROR_FAIL;
maybe_write_tselect(target);
/* TODO: check if dpc is dirty (which also is true if an exception was hit
@@ -1137,10 +1147,7 @@ static int execute_resume(struct target *target, bool step)
}
}
info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1);
info->dcsr = set_ebreakx_fields(info->dcsr, target);
info->dcsr &= ~DCSR_HALT;
if (step)
@@ -1165,7 +1172,7 @@ static int execute_resume(struct target *target, bool step)
}
target->state = TARGET_RUNNING;
register_cache_invalidate(target->reg_cache);
riscv_reg_cache_invalidate_all(target);
return ERROR_OK;
}
@@ -1183,23 +1190,13 @@ static int full_step(struct target *target, bool announce)
return result;
if (target->state != TARGET_DEBUG_RUNNING)
break;
if (time(NULL) - start > riscv_command_timeout_sec) {
if (time(NULL) - start > riscv_get_command_timeout_sec()) {
LOG_ERROR("Timed out waiting for step to complete."
"Increase timeout with riscv set_command_timeout_sec");
return ERROR_FAIL;
}
}
return ERROR_OK;
}
static int resume(struct target *target, bool debug_execution, bool step)
{
if (debug_execution) {
LOG_ERROR("TODO: debug_execution is true");
return ERROR_FAIL;
}
return execute_resume(target, step);
return handle_halt(target, announce);
}
static uint64_t reg_cache_get(struct target *target, unsigned int number)
@@ -1234,7 +1231,7 @@ static int update_mstatus_actual(struct target *target)
/* Force reading the register. In that process mstatus_actual will be
* updated. */
riscv_reg_t mstatus;
return get_register(target, &mstatus, GDB_REGNO_MSTATUS);
return riscv011_get_register(target, &mstatus, GDB_REGNO_MSTATUS);
}
/*** OpenOCD target functions. ***/
@@ -1256,7 +1253,7 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum)
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(regnum));
LOG_WARNING("Got exception 0x%x when reading %s", exception, riscv_reg_gdb_regno_name(target, regnum));
*value = ~0;
return ERROR_FAIL;
}
@@ -1270,7 +1267,7 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum)
return ERROR_OK;
}
/* Write the register. No caching or games. */
/* Write the register. */
static int register_write(struct target *target, unsigned int number,
uint64_t value)
{
@@ -1289,7 +1286,7 @@ static int register_write(struct target *target, unsigned int number,
} else if (number <= GDB_REGNO_XPR31) {
cache_set_load(target, 0, number - GDB_REGNO_ZERO, SLOT0);
cache_set_jump(target, 1);
} else if (number == GDB_REGNO_PC) {
} else if (number == GDB_REGNO_PC || number == GDB_REGNO_DPC) {
info->dpc = value;
return ERROR_OK;
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
@@ -1331,22 +1328,27 @@ static int register_write(struct target *target, unsigned int number,
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
LOG_WARNING("Got exception 0x%x when writing %s", exception,
gdb_regno_name(number));
riscv_reg_gdb_regno_name(target, number));
return ERROR_FAIL;
}
return ERROR_OK;
}
static int get_register(struct target *target, riscv_reg_t *value, int regid)
int riscv011_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno regid)
{
riscv011_info_t *info = get_info(target);
maybe_write_tselect(target);
if (regid <= GDB_REGNO_XPR31) {
/* FIXME: Here the implementation assumes that the value
* written to GPR will be the same as the value read back. This
* is not true for a write of a non-zero value to x0.
*/
*value = reg_cache_get(target, regid);
} else if (regid == GDB_REGNO_PC) {
} else if (regid == GDB_REGNO_PC || regid == GDB_REGNO_DPC) {
*value = info->dpc;
} else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) {
int result = update_mstatus_actual(target);
@@ -1376,15 +1378,32 @@ static int get_register(struct target *target, riscv_reg_t *value, int regid)
return result;
}
if (regid == GDB_REGNO_MSTATUS)
target->reg_cache->reg_list[regid].valid = true;
return ERROR_OK;
}
static int set_register(struct target *target, int regid, uint64_t value)
/* This function is intended to handle accesses to registers through register
* cache. */
int riscv011_set_register(struct target *target, enum gdb_regno regid,
riscv_reg_t value)
{
return register_write(target, regid, value);
assert(target->reg_cache);
assert(target->reg_cache->reg_list);
struct reg * const reg = &target->reg_cache->reg_list[regid];
assert(reg);
/* On RISC-V 0.11 targets valid value of some registers (e.g. `dcsr`)
* is stored in `riscv011_info_t` itself, not in register cache. This
* complicates register cache implementation.
* Therefore, for now, caching registers in register cache is disabled
* for all registers, except for reads of GPRs.
*/
assert(!reg->dirty);
int result = register_write(target, regid, value);
if (result != ERROR_OK)
return result;
reg_cache_set(target, regid, value);
/* FIXME: x0 (zero) should not be cached on writes. */
reg->valid = regid <= GDB_REGNO_XPR31;
return ERROR_OK;
}
static int halt(struct target *target)
@@ -1468,19 +1487,20 @@ static int step(struct target *target, bool current, target_addr_t address,
static int examine(struct target *target)
{
/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
LOG_DEBUG(" addrbits=%d", get_field(dtmcontrol, DTMCONTROL_ADDRBITS));
LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTMCONTROL_VERSION));
LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTMCONTROL_IDLE));
if (dtmcontrol == 0) {
LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
uint32_t dtmcontrol;
if (dtmcs_scan(target->tap, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) {
LOG_ERROR("Could not scan dtmcontrol. Check JTAG connectivity/board power.");
return ERROR_FAIL;
}
LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
LOG_DEBUG(" addrbits=%d", get_field32(dtmcontrol, DTMCONTROL_ADDRBITS));
LOG_DEBUG(" version=%d", get_field32(dtmcontrol, DTMCONTROL_VERSION));
LOG_DEBUG(" idle=%d", get_field32(dtmcontrol, DTMCONTROL_IDLE));
if (get_field(dtmcontrol, DTMCONTROL_VERSION) != 0) {
LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
get_field(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol);
get_field32(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol);
return ERROR_FAIL;
}
@@ -1498,22 +1518,22 @@ static int examine(struct target *target)
uint32_t dminfo = dbus_read(target, DMINFO);
LOG_DEBUG("dminfo: 0x%08x", dminfo);
LOG_DEBUG(" abussize=0x%x", get_field(dminfo, DMINFO_ABUSSIZE));
LOG_DEBUG(" serialcount=0x%x", get_field(dminfo, DMINFO_SERIALCOUNT));
LOG_DEBUG(" access128=%d", get_field(dminfo, DMINFO_ACCESS128));
LOG_DEBUG(" access64=%d", get_field(dminfo, DMINFO_ACCESS64));
LOG_DEBUG(" access32=%d", get_field(dminfo, DMINFO_ACCESS32));
LOG_DEBUG(" access16=%d", get_field(dminfo, DMINFO_ACCESS16));
LOG_DEBUG(" access8=%d", get_field(dminfo, DMINFO_ACCESS8));
LOG_DEBUG(" dramsize=0x%x", get_field(dminfo, DMINFO_DRAMSIZE));
LOG_DEBUG(" authenticated=0x%x", get_field(dminfo, DMINFO_AUTHENTICATED));
LOG_DEBUG(" authbusy=0x%x", get_field(dminfo, DMINFO_AUTHBUSY));
LOG_DEBUG(" authtype=0x%x", get_field(dminfo, DMINFO_AUTHTYPE));
LOG_DEBUG(" version=0x%x", get_field(dminfo, DMINFO_VERSION));
LOG_DEBUG(" abussize=0x%x", get_field32(dminfo, DMINFO_ABUSSIZE));
LOG_DEBUG(" serialcount=0x%x", get_field32(dminfo, DMINFO_SERIALCOUNT));
LOG_DEBUG(" access128=%d", get_field32(dminfo, DMINFO_ACCESS128));
LOG_DEBUG(" access64=%d", get_field32(dminfo, DMINFO_ACCESS64));
LOG_DEBUG(" access32=%d", get_field32(dminfo, DMINFO_ACCESS32));
LOG_DEBUG(" access16=%d", get_field32(dminfo, DMINFO_ACCESS16));
LOG_DEBUG(" access8=%d", get_field32(dminfo, DMINFO_ACCESS8));
LOG_DEBUG(" dramsize=0x%x", get_field32(dminfo, DMINFO_DRAMSIZE));
LOG_DEBUG(" authenticated=0x%x", get_field32(dminfo, DMINFO_AUTHENTICATED));
LOG_DEBUG(" authbusy=0x%x", get_field32(dminfo, DMINFO_AUTHBUSY));
LOG_DEBUG(" authtype=0x%x", get_field32(dminfo, DMINFO_AUTHTYPE));
LOG_DEBUG(" version=0x%x", get_field32(dminfo, DMINFO_VERSION));
if (get_field(dminfo, DMINFO_VERSION) != 1) {
LOG_ERROR("OpenOCD only supports Debug Module version 1, not %d "
"(dminfo=0x%x)", get_field(dminfo, DMINFO_VERSION), dminfo);
"(dminfo=0x%x)", get_field32(dminfo, DMINFO_VERSION), dminfo);
return ERROR_FAIL;
}
@@ -1581,7 +1601,7 @@ static int examine(struct target *target)
}
/* Update register list to match discovered XLEN/supported extensions. */
riscv_init_registers(target);
riscv011_reg_init_all(target);
info->never_halted = true;
@@ -1590,9 +1610,6 @@ static int examine(struct target *target)
return result;
target_set_examined(target);
riscv_set_current_hartid(target, 0);
for (size_t i = 0; i < 32; ++i)
reg_cache_set(target, i, -1);
LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64,
riscv_xlen(target), r->misa);
@@ -1778,10 +1795,10 @@ static riscv_error_t handle_halt_routine(struct target *target)
reg = S0;
break;
case 31:
reg = CSR_DPC;
reg = GDB_REGNO_DPC;
break;
case 32:
reg = CSR_DCSR;
reg = GDB_REGNO_DCSR;
break;
default:
assert(0);
@@ -1815,8 +1832,8 @@ static riscv_error_t handle_halt_routine(struct target *target)
}
/* TODO: get rid of those 2 variables and talk to the cache directly. */
info->dpc = reg_cache_get(target, CSR_DPC);
info->dcsr = reg_cache_get(target, CSR_DCSR);
info->dpc = reg_cache_get(target, GDB_REGNO_DPC);
info->dcsr = reg_cache_get(target, GDB_REGNO_DCSR);
cache_invalidate(target);
@@ -1872,8 +1889,16 @@ static int handle_halt(struct target *target, bool announce)
if (target->debug_reason == DBG_REASON_BREAKPOINT) {
int retval;
if (riscv_semihosting(target, &retval) != 0)
return retval;
/* Hotfix: Don't try to handle semihosting before the target is marked as examined. */
/* TODO: The code should be rearranged so that:
* - Semihosting is not attempted before the target is examined.
* - When the target is already halted on a semihosting magic sequence
* at the time when OpenOCD connects to it, this semihosting attempt
* gets handled right after the examination.
*/
if (target_was_examined(target))
if (riscv_semihosting(target, &retval) != SEMIHOSTING_NONE)
return retval;
}
if (announce)
@@ -1905,7 +1930,13 @@ static int poll_target(struct target *target, bool announce)
int old_debug_level = debug_level;
if (debug_level >= LOG_LVL_DEBUG)
debug_level = LOG_LVL_INFO;
bits_t bits = read_bits(target);
bits_t bits = {
.haltnot = 0,
.interrupt = 0
};
if (read_bits(target, &bits) != ERROR_OK)
return ERROR_FAIL;
debug_level = old_debug_level;
if (bits.haltnot && bits.interrupt) {
@@ -1937,7 +1968,7 @@ static int riscv011_resume(struct target *target, bool current,
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
r->prepped = false;
return resume(target, debug_execution, false);
return execute_resume(target, false);
}
static int assert_reset(struct target *target)
@@ -1955,10 +1986,7 @@ static int assert_reset(struct target *target)
/* Not sure what we should do when there are multiple cores.
* Here just reset the single hart we're talking to. */
info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1);
info->dcsr = set_ebreakx_fields(info->dcsr, target);
info->dcsr |= DCSR_HALT;
if (target->reset_halt)
info->dcsr |= DCSR_NDRESET;
@@ -1985,9 +2013,16 @@ static int deassert_reset(struct target *target)
return wait_for_state(target, TARGET_RUNNING);
}
static int read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
static int read_memory(struct target *target, const struct riscv_mem_access_args args)
{
assert(riscv_mem_access_is_read(args));
const target_addr_t address = args.address;
const uint32_t size = args.size;
const uint32_t count = args.count;
const uint32_t increment = args.increment;
uint8_t * const buffer = args.read_buffer;
if (increment != size) {
LOG_ERROR("read_memory with custom increment not implemented");
return ERROR_NOT_IMPLEMENTED;
@@ -2155,9 +2190,20 @@ static int setup_write_memory(struct target *target, uint32_t size)
return ERROR_OK;
}
static int write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
static int write_memory(struct target *target, const struct riscv_mem_access_args args)
{
assert(riscv_mem_access_is_write(args));
if (args.increment != args.size) {
LOG_TARGET_ERROR(target, "Write increment size has to be equal to element size");
return ERROR_NOT_IMPLEMENTED;
}
const target_addr_t address = args.address;
const uint32_t size = args.size;
const uint32_t count = args.count;
const uint8_t * const buffer = args.write_buffer;
riscv011_info_t *info = get_info(target);
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
@@ -2293,6 +2339,15 @@ error:
return ERROR_FAIL;
}
static int access_memory(struct target *target, const struct riscv_mem_access_args args)
{
assert(riscv_mem_access_is_valid(args));
const bool is_write = riscv_mem_access_is_write(args);
if (is_write)
return write_memory(target, args);
return read_memory(target, args);
}
static int arch_state(struct target *target)
{
return ERROR_OK;
@@ -2325,10 +2380,10 @@ static int wait_for_authbusy(struct target *target)
uint32_t dminfo = dbus_read(target, DMINFO);
if (!get_field(dminfo, DMINFO_AUTHBUSY))
break;
if (time(NULL) - start > riscv_command_timeout_sec) {
if (time(NULL) - start > riscv_get_command_timeout_sec()) {
LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dminfo=0x%x). "
"Increase the timeout with riscv set_command_timeout_sec.",
riscv_command_timeout_sec,
riscv_get_command_timeout_sec(),
dminfo);
return ERROR_FAIL;
}
@@ -2369,17 +2424,27 @@ static int riscv011_authdata_write(struct target *target, uint32_t value, unsign
return ERROR_OK;
}
static bool riscv011_get_impebreak(const struct target *target)
{
return false;
}
static unsigned int riscv011_get_progbufsize(const struct target *target)
{
return 0;
}
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
LOG_DEBUG("init");
RISCV_INFO(generic_info);
generic_info->get_register = get_register;
generic_info->set_register = set_register;
generic_info->read_memory = read_memory;
generic_info->access_memory = access_memory;
generic_info->authdata_read = &riscv011_authdata_read;
generic_info->authdata_write = &riscv011_authdata_write;
generic_info->print_info = &riscv011_print_info;
generic_info->get_impebreak = &riscv011_get_impebreak;
generic_info->get_progbufsize = &riscv011_get_progbufsize;
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
if (!generic_info->version_specific)
@@ -2387,7 +2452,7 @@ static int init_target(struct command_context *cmd_ctx,
/* Assume 32-bit until we discover the real value in examine(). */
generic_info->xlen = 32;
riscv_init_registers(target);
riscv011_reg_init_all(target);
return ERROR_OK;
}
@@ -2409,7 +2474,5 @@ struct target_type riscv011_target = {
.assert_reset = assert_reset,
.deassert_reset = deassert_reset,
.write_memory = write_memory,
.arch_state = arch_state,
};

View File

@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_RISCV_011_H
#define OPENOCD_TARGET_RISCV_RISCV_011_H
#include "riscv.h"
#include "gdb_regs.h"
#include "target/target.h"
int riscv011_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno regid);
int riscv011_set_register(struct target *target, enum gdb_regno regid,
riscv_reg_t value);
#endif /* OPENOCD_TARGET_RISCV_RISCV_011_H */

View File

@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "riscv-011_reg.h"
#include "riscv_reg_impl.h"
#include "riscv-011.h"
static int riscv011_reg_get(struct reg *reg)
{
struct target * const target = riscv_reg_impl_get_target(reg);
riscv_reg_t value;
const int result = riscv011_get_register(target, &value, reg->number);
if (result != ERROR_OK)
return result;
buf_set_u64(reg->value, 0, reg->size, value);
return ERROR_OK;
}
static int riscv011_reg_set(struct reg *reg, uint8_t *buf)
{
const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
struct target * const target = riscv_reg_impl_get_target(reg);
return riscv011_set_register(target, reg->number, value);
}
static const struct reg_arch_type *riscv011_gdb_regno_reg_type(uint32_t regno)
{
static const struct reg_arch_type riscv011_reg_type = {
.get = riscv011_reg_get,
.set = riscv011_reg_set
};
return &riscv011_reg_type;
}
int riscv011_reg_init_all(struct target *target)
{
int res = riscv_reg_impl_init_cache(target);
if (res != ERROR_OK)
return res;
init_shared_reg_info(target);
RISCV_INFO(r);
assert(!r->vlenb
&& "VLENB discovery is not supported on RISC-V 0.11 targets");
/* Existence of some registers depends on others.
* E.g. the presence of "v0-31" registers is infered from "vlenb" being
* non-zero.
* Currently, discovery of the following registers is not supported on
* RISC-V 0.11 targets. */
uint32_t non_discoverable_regs[] = {
GDB_REGNO_VLENB,
GDB_REGNO_MTOPI,
GDB_REGNO_MTOPEI
};
for (unsigned int i = 0; i < ARRAY_SIZE(non_discoverable_regs); ++i) {
const uint32_t regno = non_discoverable_regs[i];
res = riscv_reg_impl_init_cache_entry(target, regno,
/*exist*/ false, riscv011_gdb_regno_reg_type(regno));
if (res != ERROR_OK)
return res;
}
for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno) {
const struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
if (riscv_reg_impl_is_initialized(reg))
continue;
res = riscv_reg_impl_init_cache_entry(target, regno,
riscv_reg_impl_gdb_regno_exist(target, regno),
riscv011_gdb_regno_reg_type(regno));
if (res != ERROR_OK)
return res;
}
if (riscv_reg_impl_expose_csrs(target) != ERROR_OK)
return ERROR_FAIL;
riscv_reg_impl_hide_csrs(target);
return ERROR_OK;
}

View File

@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_RISCV_011_REG_H
#define OPENOCD_TARGET_RISCV_RISCV_011_REG_H
#include "target/target.h"
/**
* This file describes additional register cache interface available to the
* RISC-V Debug Specification v0.11 targets.
*/
/**
* Initialize register cache. After this function all registers can be
* safely accessed via functions described here and in `riscv_reg.h`.
*/
int riscv011_reg_init_all(struct target *target);
#endif /* OPENOCD_TARGET_RISCV_RISCV_011_REG_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_RISCV_013_H
#define OPENOCD_TARGET_RISCV_RISCV_013_H
#include "riscv.h"
/* TODO: These functions should be replaced here by access methods that can be
* reused by other modules (e.g. a function writing an abstract commands, a
* function filling/executing program buffer, etc.), while the specifics on how
* to use these general-purpose version-specific methods to get a register's
* value will be in `riscv-013_reg.c`.
*/
int riscv013_get_register(struct target *target,
riscv_reg_t *value, enum gdb_regno rid);
int riscv013_get_register_buf(struct target *target, uint8_t *value,
enum gdb_regno regno);
int riscv013_set_register(struct target *target, enum gdb_regno rid,
riscv_reg_t value);
int riscv013_set_register_buf(struct target *target, enum gdb_regno regno,
const uint8_t *value);
uint32_t riscv013_access_register_command(struct target *target, uint32_t number,
unsigned int size, uint32_t flags);
int riscv013_execute_abstract_command(struct target *target, uint32_t command,
uint32_t *cmderr);
#endif /* OPENOCD_TARGET_RISCV_RISCV_013_H */

View File

@@ -0,0 +1,389 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "riscv-013_reg.h"
#include "field_helpers.h"
#include "riscv_reg.h"
#include "riscv_reg_impl.h"
#include "riscv-013.h"
#include "debug_defines.h"
#include <helper/time_support.h>
static int riscv013_reg_get(struct reg *reg)
{
struct target *target = riscv_reg_impl_get_target(reg);
/* TODO: Hack to deal with gdb that thinks these registers still exist. */
if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
riscv_supports_extension(target, 'E')) {
buf_set_u64(reg->value, 0, reg->size, 0);
return ERROR_OK;
}
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
if (riscv013_get_register_buf(target, reg->value, reg->number) != ERROR_OK)
return ERROR_FAIL;
reg->valid = riscv_reg_impl_gdb_regno_cacheable(reg->number, /* is write? */ false);
} else {
uint64_t value;
int result = riscv_reg_get(target, &value, reg->number);
if (result != ERROR_OK)
return result;
buf_set_u64(reg->value, 0, reg->size, value);
}
char *str = buf_to_hex_str(reg->value, reg->size);
LOG_TARGET_DEBUG(target, "Read 0x%s from %s (valid=%d).", str, reg->name,
reg->valid);
free(str);
return ERROR_OK;
}
static int riscv013_reg_set(struct reg *reg, uint8_t *buf)
{
struct target *target = riscv_reg_impl_get_target(reg);
char *str = buf_to_hex_str(buf, reg->size);
LOG_TARGET_DEBUG(target, "Write 0x%s to %s (valid=%d).", str, reg->name,
reg->valid);
free(str);
/* TODO: Hack to deal with gdb that thinks these registers still exist. */
if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
riscv_supports_extension(target, 'E') &&
buf_get_u64(buf, 0, reg->size) == 0)
return ERROR_OK;
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
if (riscv013_set_register_buf(target, reg->number, buf) != ERROR_OK)
return ERROR_FAIL;
memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
reg->valid = riscv_reg_impl_gdb_regno_cacheable(reg->number, /* is write? */ true);
} else {
const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
if (riscv_reg_set(target, reg->number, value) != ERROR_OK)
return ERROR_FAIL;
}
return ERROR_OK;
}
static const struct reg_arch_type *riscv013_gdb_regno_reg_type(uint32_t regno)
{
static const struct reg_arch_type riscv013_reg_type = {
.get = riscv013_reg_get,
.set = riscv013_reg_set
};
return &riscv013_reg_type;
}
static int init_cache_entry(struct target *target, uint32_t regno)
{
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
if (riscv_reg_impl_is_initialized(reg))
return ERROR_OK;
return riscv_reg_impl_init_cache_entry(target, regno,
riscv_reg_impl_gdb_regno_exist(target, regno),
riscv013_gdb_regno_reg_type(regno));
}
/**
* Some registers are optional (e.g. "misa"). For such registers it is first
* assumed they exist (via "assume_reg_exist()"), then the read is attempted
* (via the usual "riscv_reg_get()") and if the read fails, the register is
* marked as non-existing (via "riscv_reg_impl_set_exist()").
*/
static int assume_reg_exist(struct target *target, uint32_t regno)
{
return riscv_reg_impl_init_cache_entry(target, regno,
/* exist */ true, riscv013_gdb_regno_reg_type(regno));
}
static int examine_xlen(struct target *target)
{
RISCV_INFO(r);
unsigned int cmderr;
const uint32_t command = riscv013_access_register_command(target,
GDB_REGNO_S0, /* size */ 64, AC_ACCESS_REGISTER_TRANSFER);
int res = riscv013_execute_abstract_command(target, command, &cmderr);
if (res == ERROR_OK) {
r->xlen = 64;
return ERROR_OK;
}
if (res == ERROR_TIMEOUT_REACHED)
return ERROR_FAIL;
r->xlen = 32;
return ERROR_OK;
}
static int examine_vlenb(struct target *target)
{
RISCV_INFO(r);
/* Reading "vlenb" requires "mstatus.vs" to be set, so "mstatus" should
* be accessible.*/
int res = init_cache_entry(target, GDB_REGNO_MSTATUS);
if (res != ERROR_OK)
return res;
res = assume_reg_exist(target, GDB_REGNO_VLENB);
if (res != ERROR_OK)
return res;
riscv_reg_t vlenb_val;
if (riscv_reg_get(target, &vlenb_val, GDB_REGNO_VLENB) != ERROR_OK) {
if (riscv_supports_extension(target, 'V'))
LOG_TARGET_WARNING(target, "Couldn't read vlenb; vector register access won't work.");
r->vlenb = 0;
return riscv_reg_impl_set_exist(target, GDB_REGNO_VLENB, false);
}
/* As defined by RISC-V V extension specification:
* https://github.com/riscv/riscv-v-spec/blob/2f68ef7256d6ec53e4d2bd7cb12862f406d64e34/v-spec.adoc?plain=1#L67-L72 */
const unsigned int vlen_max = 65536;
const unsigned int vlenb_max = vlen_max / 8;
if (vlenb_val > vlenb_max) {
LOG_TARGET_WARNING(target, "'vlenb == %" PRIu64
"' is greater than maximum allowed by specification (%u); vector register access won't work.",
vlenb_val, vlenb_max);
r->vlenb = 0;
return ERROR_OK;
}
assert(vlenb_max <= UINT_MAX);
r->vlenb = (unsigned int)vlenb_val;
LOG_TARGET_INFO(target, "Vector support with vlenb=%u", r->vlenb);
return ERROR_OK;
}
enum misa_mxl {
MISA_MXL_INVALID = 0,
MISA_MXL_32 = 1,
MISA_MXL_64 = 2,
MISA_MXL_128 = 3
};
unsigned int mxl_to_xlen(enum misa_mxl mxl)
{
switch (mxl) {
case MISA_MXL_32:
return 32;
case MISA_MXL_64:
return 64;
case MISA_MXL_128:
return 128;
case MISA_MXL_INVALID:
assert(0);
}
return 0;
}
static int check_misa_mxl(const struct target *target)
{
RISCV_INFO(r);
if (r->misa == 0) {
LOG_TARGET_WARNING(target, "'misa' register is read as zero."
"OpenOCD will not be able to determine some hart's capabilities.");
return ERROR_OK;
}
const unsigned int dxlen = riscv_xlen(target);
assert(dxlen <= sizeof(riscv_reg_t) * CHAR_BIT);
assert(dxlen >= 2);
const riscv_reg_t misa_mxl_mask = (riscv_reg_t)0x3 << (dxlen - 2);
const unsigned int mxl = get_field(r->misa, misa_mxl_mask);
if (mxl == MISA_MXL_INVALID) {
/* This is not an error!
* Imagine the platform that:
* - Has no abstract access to CSRs, so that CSRs are read
* through Program Buffer via "csrr" instruction.
* - Complies to v1.10 of the Priveleged Spec, so that misa.mxl
* is WARL and MXLEN may be chainged.
* https://github.com/riscv/riscv-isa-manual/commit/9a7dd2fe29011587954560b5dcf1875477b27ad8
* - DXLEN == MXLEN on reset == 64.
* In a following scenario:
* - misa.mxl was written, so that MXLEN is 32.
* - Debugger connects to the target.
* - Debugger observes DXLEN == 64.
* - Debugger reads misa:
* - Abstract access fails with "cmderr == not supported".
* - Access via Program Buffer involves reading "misa" to an
* "xreg" via "csrr", so that the "xreg" is filled with
* zero-extended value of "misa" (since "misa" is
* MXLEN-wide).
* - Debugger derives "misa.mxl" assumig "misa" is DXLEN-bit
* wide (64) while MXLEN is 32 and therefore erroneously
* assumes "misa.mxl" to be zero (invalid).
*/
LOG_TARGET_WARNING(target, "Detected DXLEN (%u) does not match "
"MXLEN: misa.mxl == 0, misa == 0x%" PRIx64 ".",
dxlen, r->misa);
return ERROR_OK;
}
const unsigned int mxlen = mxl_to_xlen(mxl);
if (dxlen < mxlen) {
LOG_TARGET_ERROR(target,
"MXLEN (%u) reported in misa.mxl field exceeds "
"the detected DXLEN (%u)",
mxlen, dxlen);
return ERROR_FAIL;
}
/* NOTE:
* The value of "misa.mxl" may stil not coincide with "xlen".
* "misa[26:XLEN-3]" bits are marked as WIRI in at least version 1.10
* of RISC-V Priveleged Spec. Therefore, if "xlen" is erroneously
* assumed to be 32 when it actually is 64, "mxl" will be read from
* this WIRI field and may be equal to "MISA_MXL_32" by coincidence.
* This is not an issue though from the version 1.11 onward, since
* "misa[26:XLEN-3]" became WARL and equal to 0.
*/
/* Display this as early as possible to help people who are using
* really slow simulators. */
LOG_TARGET_DEBUG(target, " XLEN=%d, misa=0x%" PRIx64, riscv_xlen(target), r->misa);
return ERROR_OK;
}
static int examine_misa(struct target *target)
{
RISCV_INFO(r);
int res = init_cache_entry(target, GDB_REGNO_MISA);
if (res != ERROR_OK)
return res;
res = riscv_reg_get(target, &r->misa, GDB_REGNO_MISA);
if (res != ERROR_OK)
return res;
return check_misa_mxl(target);
}
static int examine_mtopi(struct target *target)
{
/* Assume the registers exist */
int res = assume_reg_exist(target, GDB_REGNO_MTOPI);
if (res != ERROR_OK)
return res;
res = assume_reg_exist(target, GDB_REGNO_MTOPEI);
if (res != ERROR_OK)
return res;
riscv_reg_t value;
if (riscv_reg_get(target, &value, GDB_REGNO_MTOPI) != ERROR_OK) {
res = riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPI, false);
if (res != ERROR_OK)
return res;
return riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPEI, false);
}
if (riscv_reg_get(target, &value, GDB_REGNO_MTOPEI) != ERROR_OK) {
LOG_TARGET_INFO(target, "S?aia detected without IMSIC");
return riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPEI, false);
}
LOG_TARGET_INFO(target, "S?aia detected with IMSIC");
return ERROR_OK;
}
/**
* This function assumes target's DM to be initialized (target is able to
* access DMs registers, execute program buffer, etc.)
*/
int riscv013_reg_examine_all(struct target *target)
{
int res = riscv_reg_impl_init_cache(target);
if (res != ERROR_OK)
return res;
init_shared_reg_info(target);
assert(target->state == TARGET_HALTED);
res = examine_xlen(target);
if (res != ERROR_OK)
return res;
/* Reading CSRs may clobber "s0", "s1", so it should be possible to
* save them in cache. */
res = init_cache_entry(target, GDB_REGNO_S0);
if (res != ERROR_OK)
return res;
res = init_cache_entry(target, GDB_REGNO_S1);
if (res != ERROR_OK)
return res;
res = examine_misa(target);
if (res != ERROR_OK)
return res;
res = examine_vlenb(target);
if (res != ERROR_OK)
return res;
riscv_reg_impl_init_vector_reg_type(target);
res = examine_mtopi(target);
if (res != ERROR_OK)
return res;
for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno) {
res = init_cache_entry(target, regno);
if (res != ERROR_OK)
return res;
}
res = riscv_reg_impl_expose_csrs(target);
if (res != ERROR_OK)
return res;
riscv_reg_impl_hide_csrs(target);
return ERROR_OK;
}
/**
* This function is used to save the value of a register in cache. The register
* is marked as dirty, and writeback is delayed for as long as possible.
*/
int riscv013_reg_save(struct target *target, enum gdb_regno regid)
{
if (target->state != TARGET_HALTED) {
LOG_TARGET_ERROR(target, "Can't save register %s on a hart that is not halted.",
riscv_reg_gdb_regno_name(target, regid));
return ERROR_FAIL;
}
assert(riscv_reg_impl_gdb_regno_cacheable(regid, /* is write? */ false) &&
"Only cacheable registers can be saved.");
RISCV_INFO(r);
riscv_reg_t value;
if (!target->reg_cache) {
assert(!target_was_examined(target));
/* To create register cache it is needed to examine the target first,
* therefore during examine, any changed register needs to be saved
* and restored manually.
*/
return ERROR_OK;
}
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
LOG_TARGET_DEBUG(target, "Saving %s", reg->name);
if (riscv_reg_get(target, &value, regid) != ERROR_OK)
return ERROR_FAIL;
assert(reg->valid &&
"The register is cacheable, so the cache entry must be valid now.");
/* Mark the register dirty. We assume that this function is called
* because the caller is about to mess with the underlying value of the
* register. */
reg->dirty = true;
r->last_activity = timeval_ms();
return ERROR_OK;
}

View File

@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_RISCV_013_REG_H
#define OPENOCD_TARGET_RISCV_RISCV_013_REG_H
#include "target/target.h"
#include "gdb_regs.h"
#include "riscv.h"
/**
* This file describes additional register cache interface available to the
* RISC-V Debug Specification v0.13+ targets.
*/
/**
* This function assumes target is halted.
* After this function all registers can be safely accessed via functions
* described here and in `riscv_reg.h`.
*/
int riscv013_reg_examine_all(struct target *target);
/**
* This function is used to save the value of a register in cache. The register
* is marked as dirty, and writeback is delayed for as long as possible.
* Generally used to save registers before program buffer execution.
*
* TODO: The interface should be restricted in such a way that only GPRs can be
* saved.
*/
int riscv013_reg_save(struct target *target, enum gdb_regno regid);
#endif /* OPENOCD_TARGET_RISCV_RISCV_013_REG_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef RISCV_H
#define RISCV_H
#ifndef OPENOCD_TARGET_RISCV_RISCV_H
#define OPENOCD_TARGET_RISCV_RISCV_H
struct riscv_program;
@@ -9,28 +9,33 @@ struct riscv_program;
#include "opcodes.h"
#include "gdb_regs.h"
#include "jtag/jtag.h"
#include "target/register.h"
#include "target/semihosting_common.h"
#include "target/target.h"
#include "target/register.h"
#include <helper/command.h>
#include <helper/bits.h>
#define RISCV_COMMON_MAGIC 0x52495356U
/* The register cache is statically allocated. */
#define RISCV_MAX_HARTS 1024
#define RISCV_MAX_REGISTERS 5000
#define RISCV_MAX_HARTS ((int)BIT(20))
#define RISCV_MAX_TRIGGERS 32
#define RISCV_MAX_HWBPS 16
#define RISCV_MAX_DMS 100
#define DEFAULT_COMMAND_TIMEOUT_SEC 2
#define DEFAULT_RESET_TIMEOUT_SEC 30
#define DEFAULT_COMMAND_TIMEOUT_SEC 5
#define RISCV_SATP_MODE(xlen) ((xlen) == 32 ? SATP32_MODE : SATP64_MODE)
#define RISCV_SATP_PPN(xlen) ((xlen) == 32 ? SATP32_PPN : SATP64_PPN)
#define RISCV_HGATP_MODE(xlen) ((xlen) == 32 ? HGATP32_MODE : HGATP64_MODE)
#define RISCV_HGATP_PPN(xlen) ((xlen) == 32 ? HGATP32_PPN : HGATP64_PPN)
#define RISCV_PGSHIFT 12
#define RISCV_PGSIZE BIT(RISCV_PGSHIFT)
#define RISCV_PGBASE(addr) ((addr) & ~(RISCV_PGSIZE - 1))
#define RISCV_PGOFFSET(addr) ((addr) & (RISCV_PGSIZE - 1))
# define PG_MAX_LEVEL 4
#define PG_MAX_LEVEL 5
#define RISCV_NUM_MEM_ACCESS_METHODS 3
#define RISCV_BATCH_ALLOC_SIZE 128
extern struct target_type riscv011_target;
extern struct target_type riscv013_target;
@@ -42,16 +47,30 @@ typedef uint64_t riscv_reg_t;
typedef uint32_t riscv_insn_t;
typedef uint64_t riscv_addr_t;
enum yes_no_maybe {
YNM_MAYBE,
YNM_YES,
YNM_NO
};
enum riscv_mem_access_method {
RISCV_MEM_ACCESS_UNSPECIFIED,
RISCV_MEM_ACCESS_PROGBUF,
RISCV_MEM_ACCESS_SYSBUS,
RISCV_MEM_ACCESS_ABSTRACT
RISCV_MEM_ACCESS_ABSTRACT,
RISCV_MEM_ACCESS_MAX_METHODS_NUM
};
enum riscv_virt2phys_mode {
RISCV_VIRT2PHYS_MODE_HW,
RISCV_VIRT2PHYS_MODE_SW,
RISCV_VIRT2PHYS_MODE_OFF
};
const char *riscv_virt2phys_mode_to_str(enum riscv_virt2phys_mode mode);
enum riscv_halt_reason {
RISCV_HALT_INTERRUPT,
RISCV_HALT_BREAKPOINT,
RISCV_HALT_EBREAK,
RISCV_HALT_SINGLESTEP,
RISCV_HALT_TRIGGER,
RISCV_HALT_UNKNOWN,
@@ -59,8 +78,24 @@ enum riscv_halt_reason {
RISCV_HALT_ERROR
};
enum riscv_isrmasking_mode {
/* RISCV_ISRMASK_AUTO, */ /* not supported yet */
RISCV_ISRMASK_OFF,
/* RISCV_ISRMASK_ON, */ /* not supported yet */
RISCV_ISRMASK_STEPONLY,
};
enum riscv_hart_state {
RISCV_STATE_NON_EXISTENT,
RISCV_STATE_RUNNING,
RISCV_STATE_HALTED,
RISCV_STATE_UNAVAILABLE
};
/* RISC-V-specific data assigned to a register. */
typedef struct {
struct target *target;
/* Abstract command's regno for a custom register. */
unsigned int custom_number;
} riscv_reg_info_t;
@@ -87,6 +122,49 @@ typedef struct {
char *name;
} range_list_t;
#define DTM_DTMCS_VERSION_UNKNOWN ((unsigned int)-1)
#define RISCV_TINFO_VERSION_UNKNOWN (-1)
#define RISCV013_DTMCS_ABITS_MIN 7
#define RISCV013_DTMCS_ABITS_MAX 32
struct reg_name_table {
unsigned int num_entries;
char **reg_names;
};
struct riscv_mem_access_args {
target_addr_t address;
const uint8_t *write_buffer;
uint8_t *read_buffer;
uint32_t size;
uint32_t count;
uint32_t increment;
};
static inline bool
riscv_mem_access_is_valid(const struct riscv_mem_access_args args)
{
return !args.read_buffer != !args.write_buffer;
}
static inline bool
riscv_mem_access_is_read(const struct riscv_mem_access_args args)
{
assert(riscv_mem_access_is_valid(args));
return !args.write_buffer && args.read_buffer;
}
static inline bool
riscv_mem_access_is_write(const struct riscv_mem_access_args args)
{
assert(riscv_mem_access_is_valid(args));
return !args.read_buffer && args.write_buffer;
}
struct riscv_info {
unsigned int common_magic;
@@ -95,36 +173,57 @@ struct riscv_info {
struct command_context *cmd_ctx;
void *version_specific;
/* The hart that is currently being debugged. Note that this is
* different than the hartid that the RTOS is expected to use. This
* one will change all the time, it's more of a global argument to
* every function than an actual */
int current_hartid;
/* Single buffer that contains all register names, instead of calling
* malloc for each register. Needs to be freed when reg_list is freed. */
char *reg_names;
struct reg_name_table custom_register_names;
char **reg_names;
/* It's possible that each core has a different supported ISA set. */
int xlen;
/* TODO: use the value from the register cache instead. */
riscv_reg_t misa;
/* Cached value of vlenb. 0 if vlenb is not readable for some reason. */
/* TODO: use the value from the register cache instead.
* Cached value of vlenb. 0 indicates there is no vector support.
* Note that you can have vector support without misa.V set, because
* Zve* extensions implement vector registers without setting misa.V. */
unsigned int vlenb;
/* The number of triggers per hart. */
unsigned int trigger_count;
/* For each physical trigger, contains -1 if the hwbp is available, or the
* unique_id of the breakpoint/watchpoint that is using it.
/* Data structure to record known unsupported tdata1+tdata2 trigger CSR values.
* This is to avoid repetitive attempts to set trigger configurations that are already
* known to be unsupported in the HW.
* A separate data structure is created for each trigger. */
struct list_head *wp_triggers_negative_cache;
/* record the tinfo of each trigger */
unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];
/* Version of the implemented Sdtrig extension */
int tinfo_version;
/* Record if single-step is needed prior to resuming
* from a software breakpoint or trigger.
* Single-step is needed if the instruction that
* caused the halt was not retired. That is,
* when we halted "before" that instruction.
*/
bool need_single_step;
/* For each physical trigger contains:
* -1: the hwbp is available
* -4: The trigger is used by the itrigger command
* -5: The trigger is used by the etrigger command
* >= 0: unique_id of the breakpoint/watchpoint that is using it.
* Note that in RTOS mode the triggers are the same across all harts the
* target controls, while otherwise only a single hart is controlled. */
int trigger_unique_id[RISCV_MAX_HWBPS];
int64_t trigger_unique_id[RISCV_MAX_HWBPS];
/* The number of entries in the debug buffer. */
int debug_buffer_size;
/* The unique id of the trigger that caused the most recent halt. If the
* most recent halt was not caused by a trigger, then this is -1. */
int64_t trigger_hit;
/* This hart contains an implicit ebreak at the end of the program buffer. */
bool impebreak;
/* The configured approach to translate virtual addresses to physical */
enum riscv_virt2phys_mode virt2phys_mode;
bool triggers_enumerated;
@@ -137,21 +236,37 @@ struct riscv_info {
/* This target was selected using hasel. */
bool selected;
/* Used by riscv_openocd_poll(). */
bool halted_needs_event_callback;
enum target_event halted_callback_event;
unsigned int halt_group_repoll_count;
enum riscv_isrmasking_mode isrmask_mode;
/* Helper functions that target the various RISC-V debug spec
* implementations. */
int (*get_register)(struct target *target, riscv_reg_t *value, int regid);
int (*set_register)(struct target *target, int regid, uint64_t value);
int (*get_register_buf)(struct target *target, uint8_t *buf, int regno);
int (*set_register_buf)(struct target *target, int regno,
const uint8_t *buf);
int (*select_current_hart)(struct target *target);
bool (*is_halted)(struct target *target);
int (*select_target)(struct target *target);
int (*get_hart_state)(struct target *target, enum riscv_hart_state *state);
/* Resume this target, as well as every other prepped target that can be
* resumed near-simultaneously. Clear the prepped flag on any target that
* was resumed. */
int (*resume_go)(struct target *target);
int (*step_current_hart)(struct target *target);
int (*on_halt)(struct target *target);
/* These get called from riscv_poll_hart(), which is a house of cards
* together with openocd_poll(), so be careful not to upset things too
* much. */
int (*handle_became_halted)(struct target *target,
enum riscv_hart_state previous_riscv_state);
int (*handle_became_running)(struct target *target,
enum riscv_hart_state previous_riscv_state);
int (*handle_became_unavailable)(struct target *target,
enum riscv_hart_state previous_riscv_state);
/* Called periodically (no guarantees about frequency), while there's
* nothing else going on. */
int (*tick)(struct target *target);
/* Get this target as ready as possible to resume, without actually
* resuming. */
int (*resume_prep)(struct target *target);
@@ -159,14 +274,14 @@ struct riscv_info {
int (*halt_go)(struct target *target);
int (*on_step)(struct target *target);
enum riscv_halt_reason (*halt_reason)(struct target *target);
int (*write_debug_buffer)(struct target *target, unsigned int index,
riscv_insn_t d);
riscv_insn_t (*read_debug_buffer)(struct target *target, unsigned int index);
int (*execute_debug_buffer)(struct target *target);
int (*dmi_write_u64_bits)(struct target *target);
void (*fill_dmi_write_u64)(struct target *target, char *buf, int a, uint64_t d);
void (*fill_dmi_read_u64)(struct target *target, char *buf, int a);
void (*fill_dmi_nop_u64)(struct target *target, char *buf);
int (*write_progbuf)(struct target *target, unsigned int index, riscv_insn_t d);
riscv_insn_t (*read_progbuf)(struct target *target, unsigned int index);
int (*execute_progbuf)(struct target *target, uint32_t *cmderr);
int (*invalidate_cached_progbuf)(struct target *target);
unsigned int (*get_dmi_address_bits)(const struct target *target);
void (*fill_dmi_write)(const struct target *target, uint8_t *buf, uint32_t a, uint32_t d);
void (*fill_dmi_read)(const struct target *target, uint8_t *buf, uint32_t a);
void (*fill_dm_nop)(const struct target *target, uint8_t *buf);
int (*authdata_read)(struct target *target, uint32_t *value, unsigned int index);
int (*authdata_write)(struct target *target, uint32_t value, unsigned int index);
@@ -174,20 +289,29 @@ struct riscv_info {
int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address);
int (*dmi_write)(struct target *target, uint32_t address, uint32_t value);
bool (*get_impebreak)(const struct target *target);
unsigned int (*get_progbufsize)(const struct target *target);
/* Get the DMI address of target's DM's register.
* The function should return the passed address
* if the target is not assigned a DM yet.
*/
uint32_t (*get_dmi_address)(const struct target *target, uint32_t dm_address);
int (*sample_memory)(struct target *target,
struct riscv_sample_buf *buf,
riscv_sample_config_t *config,
int64_t until_ms);
int (*read_memory)(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment);
int (*access_memory)(struct target *target, const struct riscv_mem_access_args args);
/* How many harts are attached to the DM that this target is attached to? */
int (*hart_count)(struct target *target);
unsigned int (*data_bits)(struct target *target);
COMMAND_HELPER((*print_info), struct target *target);
/* Storage for arch_info of non-custom registers. */
riscv_reg_info_t shared_reg_info;
/* Storage for vector register types. */
struct reg_data_type_vector vector_uint8;
struct reg_data_type_vector vector_uint16;
@@ -203,18 +327,16 @@ struct riscv_info {
struct reg_data_type_union vector_union;
struct reg_data_type type_vector;
/* Set when trigger registers are changed by the user. This indicates we eed
* to beware that we may hit a trigger that we didn't realize had been set. */
bool manual_hwbp_set;
bool *reserved_triggers;
/* Memory access methods to use, ordered by priority, highest to lowest. */
int mem_access_methods[RISCV_NUM_MEM_ACCESS_METHODS];
enum riscv_mem_access_method mem_access_methods[RISCV_MEM_ACCESS_MAX_METHODS_NUM];
unsigned int num_enabled_mem_access_methods;
/* Different memory regions may need different methods but single configuration is applied
* for all. Following flags are used to warn only once about failing memory access method. */
bool mem_access_progbuf_warn;
bool mem_access_sysbus_warn;
bool mem_access_abstract_warn;
bool mem_access_warn[RISCV_MEM_ACCESS_MAX_METHODS_NUM];
/* In addition to the ones in the standard spec, we'll also expose additional
* CSRs in this list. */
@@ -224,10 +346,47 @@ struct riscv_info {
* from range 0xc000 ... 0xffff. */
struct list_head expose_custom;
/* The list of registers to mark as "hidden". Hidden registers are available
* but do not appear in gdb targets description or reg command output. */
struct list_head hide_csr;
riscv_sample_config_t sample_config;
struct riscv_sample_buf sample_buf;
/* Track when we were last asked to do something substantial. */
int64_t last_activity;
enum yes_no_maybe vsew64_supported;
bool range_trigger_fallback_encountered;
bool wp_allow_equality_match_trigger;
bool wp_allow_napot_trigger;
bool wp_allow_ge_lt_trigger;
bool autofence;
};
enum riscv_priv_mode {
RISCV_MODE_M,
RISCV_MODE_S,
RISCV_MODE_U,
RISCV_MODE_VS,
RISCV_MODE_VU,
N_RISCV_MODE
};
struct riscv_private_config {
bool dcsr_ebreak_fields[N_RISCV_MODE];
};
static inline struct riscv_private_config
*riscv_private_config(const struct target *target)
{
assert(target->private_config);
return target->private_config;
}
COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
unsigned int value);
@@ -240,6 +399,7 @@ typedef struct {
const char *name;
int level;
unsigned int va_bits;
/* log2(PTESIZE) */
unsigned int pte_shift;
unsigned int vpn_shift[PG_MAX_LEVEL];
unsigned int vpn_mask[PG_MAX_LEVEL];
@@ -249,16 +409,11 @@ typedef struct {
unsigned int pa_ppn_mask[PG_MAX_LEVEL];
} virt2phys_info_t;
bool riscv_virt2phys_mode_is_hw(const struct target *target);
bool riscv_virt2phys_mode_is_sw(const struct target *target);
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
extern int riscv_command_timeout_sec;
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
extern int riscv_reset_timeout_sec;
extern bool riscv_enable_virtual;
extern bool riscv_ebreakm;
extern bool riscv_ebreaks;
extern bool riscv_ebreaku;
int riscv_get_command_timeout_sec(void);
/* Everything needs the RISC-V specific info structure, so here's a nice macro
* that provides that. */
@@ -279,13 +434,14 @@ extern struct scan_field select_dtmcontrol;
extern struct scan_field select_dbus;
extern struct scan_field select_idcode;
int dtmcs_scan(struct jtag_tap *tap, uint32_t out, uint32_t *in_ptr);
extern struct scan_field *bscan_tunneled_select_dmi;
extern uint32_t bscan_tunneled_select_dmi_num_fields;
typedef enum { BSCAN_TUNNEL_NESTED_TAP, BSCAN_TUNNEL_DATA_REGISTER } bscan_tunnel_type_t;
extern int bscan_tunnel_ir_width;
extern uint8_t bscan_tunnel_ir_width;
uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out);
void select_dmi_via_bscan(struct target *target);
void select_dmi_via_bscan(struct jtag_tap *tap);
/*** OpenOCD Interface */
int riscv_openocd_poll(struct target *target);
@@ -299,52 +455,37 @@ int riscv_openocd_step(
bool handle_breakpoints
);
int riscv_openocd_assert_reset(struct target *target);
int riscv_openocd_deassert_reset(struct target *target);
/*** RISC-V Interface ***/
bool riscv_supports_extension(struct target *target, char letter);
bool riscv_supports_extension(const struct target *target, char letter);
/* Returns XLEN for the given (or current) hart. */
unsigned int riscv_xlen(const struct target *target);
int riscv_xlen_of_hart(const struct target *target);
/* Sets the current hart, which is the hart that will actually be used when
* issuing debug commands. */
int riscv_set_current_hartid(struct target *target, int hartid);
int riscv_select_current_hart(struct target *target);
int riscv_current_hartid(const struct target *target);
/* Returns VLENB for the given (or current) hart. */
unsigned int riscv_vlenb(const struct target *target);
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
* without requiring multiple targets. */
/* Lists the number of harts in the system, which are assumed to be
* consecutive and start with mhartid=0. */
int riscv_count_harts(struct target *target);
/** Set register, updating the cache. */
int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
/** Get register, from the cache if it's in there. */
int riscv_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno r);
/* Checks the state of the current hart -- "is_halted" checks the actual
* on-device register. */
bool riscv_is_halted(struct target *target);
int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state);
/* These helper functions let the generic program interface get target-specific
* information. */
size_t riscv_debug_buffer_size(struct target *target);
unsigned int riscv_progbuf_size(struct target *target);
riscv_insn_t riscv_read_debug_buffer(struct target *target, int index);
int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn);
int riscv_execute_debug_buffer(struct target *target);
riscv_insn_t riscv_read_progbuf(struct target *target, int index);
int riscv_write_progbuf(struct target *target, unsigned int index, riscv_insn_t insn);
int riscv_execute_progbuf(struct target *target, uint32_t *cmderr);
void riscv_fill_dmi_nop_u64(struct target *target, char *buf);
void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a);
int riscv_dmi_write_u64_bits(struct target *target);
void riscv_fill_dm_nop(const struct target *target, uint8_t *buf);
void riscv_fill_dmi_write(const struct target *target, uint8_t *buf, uint32_t a, uint32_t d);
void riscv_fill_dmi_read(const struct target *target, uint8_t *buf, uint32_t a);
unsigned int riscv_get_dmi_address_bits(const struct target *target);
uint32_t riscv_get_dmi_address(const struct target *target, uint32_t dm_address);
int riscv_enumerate_triggers(struct target *target);
@@ -352,16 +493,14 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint);
int riscv_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint);
int riscv_init_registers(struct target *target);
void riscv_semihosting_init(struct target *target);
enum semihosting_result riscv_semihosting(struct target *target, int *retval);
void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field,
void riscv_add_bscan_tunneled_scan(struct jtag_tap *tap, const struct scan_field *field,
riscv_bscan_tunneled_scan_context_t *ctxt);
int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer);
int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer);
#endif
#endif /* OPENOCD_TARGET_RISCV_RISCV_H */

View File

@@ -0,0 +1,990 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gdb_regs.h"
#include "riscv.h"
#include "riscv_reg.h"
#include "riscv_reg_impl.h"
/**
* TODO: Currently `reg->get/set` is implemented in terms of
* `riscv_get/set_register`. However, the intention behind
* `riscv_get/set_register` is to work with the cache, therefore it accesses
* and modifyes register cache directly. The idea is to implement
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
* `reg->get/set`.
* Once this is done, the following includes should be removed.
*/
#include "debug_defines.h"
#include "riscv-011.h"
#include "riscv-013.h"
#include "field_helpers.h"
static const char * const default_reg_names[GDB_REGNO_COUNT] = {
[GDB_REGNO_ZERO] = "zero",
[GDB_REGNO_RA] = "ra",
[GDB_REGNO_SP] = "sp",
[GDB_REGNO_GP] = "gp",
[GDB_REGNO_TP] = "tp",
[GDB_REGNO_T0] = "t0",
[GDB_REGNO_T1] = "t1",
[GDB_REGNO_T2] = "t2",
[GDB_REGNO_FP] = "fp",
[GDB_REGNO_S1] = "s1",
[GDB_REGNO_A0] = "a0",
[GDB_REGNO_A1] = "a1",
[GDB_REGNO_A2] = "a2",
[GDB_REGNO_A3] = "a3",
[GDB_REGNO_A4] = "a4",
[GDB_REGNO_A5] = "a5",
[GDB_REGNO_A6] = "a6",
[GDB_REGNO_A7] = "a7",
[GDB_REGNO_S2] = "s2",
[GDB_REGNO_S3] = "s3",
[GDB_REGNO_S4] = "s4",
[GDB_REGNO_S5] = "s5",
[GDB_REGNO_S6] = "s6",
[GDB_REGNO_S7] = "s7",
[GDB_REGNO_S8] = "s8",
[GDB_REGNO_S9] = "s9",
[GDB_REGNO_S10] = "s10",
[GDB_REGNO_S11] = "s11",
[GDB_REGNO_T3] = "t3",
[GDB_REGNO_T4] = "t4",
[GDB_REGNO_T5] = "t5",
[GDB_REGNO_T6] = "t6",
[GDB_REGNO_PC] = "pc",
[GDB_REGNO_PRIV] = "priv",
[GDB_REGNO_FT0] = "ft0",
[GDB_REGNO_FT1] = "ft1",
[GDB_REGNO_FT2] = "ft2",
[GDB_REGNO_FT3] = "ft3",
[GDB_REGNO_FT4] = "ft4",
[GDB_REGNO_FT5] = "ft5",
[GDB_REGNO_FT6] = "ft6",
[GDB_REGNO_FT7] = "ft7",
[GDB_REGNO_FS0] = "fs0",
[GDB_REGNO_FS1] = "fs1",
[GDB_REGNO_FA0] = "fa0",
[GDB_REGNO_FA1] = "fa1",
[GDB_REGNO_FA2] = "fa2",
[GDB_REGNO_FA3] = "fa3",
[GDB_REGNO_FA4] = "fa4",
[GDB_REGNO_FA5] = "fa5",
[GDB_REGNO_FA6] = "fa6",
[GDB_REGNO_FA7] = "fa7",
[GDB_REGNO_FS2] = "fs2",
[GDB_REGNO_FS3] = "fs3",
[GDB_REGNO_FS4] = "fs4",
[GDB_REGNO_FS5] = "fs5",
[GDB_REGNO_FS6] = "fs6",
[GDB_REGNO_FS7] = "fs7",
[GDB_REGNO_FS8] = "fs8",
[GDB_REGNO_FS9] = "fs9",
[GDB_REGNO_FS10] = "fs10",
[GDB_REGNO_FS11] = "fs11",
[GDB_REGNO_FT8] = "ft8",
[GDB_REGNO_FT9] = "ft9",
[GDB_REGNO_FT10] = "ft10",
[GDB_REGNO_FT11] = "ft11",
#define DECLARE_CSR(csr_name, number)[(number) + GDB_REGNO_CSR0] = #csr_name,
#include "encoding.h"
#undef DECLARE_CSR
};
static void free_custom_register_names(struct target *target)
{
RISCV_INFO(info);
if (!info->custom_register_names.reg_names)
return;
for (unsigned int i = 0; i < info->custom_register_names.num_entries; i++)
free(info->custom_register_names.reg_names[i]);
free(info->custom_register_names.reg_names);
info->custom_register_names.reg_names = NULL;
}
static void free_reg_names(struct target *target)
{
RISCV_INFO(info);
if (!info->reg_names)
return;
for (unsigned int i = 0; i < GDB_REGNO_COUNT; ++i)
free(info->reg_names[i]);
free(info->reg_names);
info->reg_names = NULL;
free_custom_register_names(target);
}
static char *init_reg_name(const char *name)
{
const int size_buf = strlen(name) + 1;
char * const buf = calloc(size_buf, sizeof(char));
if (!buf) {
LOG_ERROR("Failed to allocate memory for a register name.");
return NULL;
}
strcpy(buf, name);
return buf;
}
static void init_custom_csr_names(const struct target *target)
{
RISCV_INFO(info);
range_list_t *entry;
list_for_each_entry(entry, &info->expose_csr, list) {
if (!entry->name)
continue;
assert(entry->low == entry->high);
const unsigned int regno = entry->low + GDB_REGNO_CSR0;
assert(regno <= GDB_REGNO_CSR4095);
if (info->reg_names[regno])
return;
info->reg_names[regno] = init_reg_name(entry->name);
}
}
static char *init_reg_name_with_prefix(const char *name_prefix,
unsigned int num)
{
const int size_buf = snprintf(NULL, 0, "%s%d", name_prefix, num) + 1;
char * const buf = calloc(size_buf, sizeof(char));
if (!buf) {
LOG_ERROR("Failed to allocate memory for a register name.");
return NULL;
}
int result = snprintf(buf, size_buf, "%s%d", name_prefix, num);
assert(result > 0 && result <= (size_buf - 1));
return buf;
}
const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno regno)
{
RISCV_INFO(info);
if (regno >= GDB_REGNO_COUNT) {
assert(info->custom_register_names.reg_names);
assert(regno - GDB_REGNO_COUNT <= info->custom_register_names.num_entries);
return info->custom_register_names.reg_names[regno - GDB_REGNO_COUNT];
}
if (!info->reg_names)
info->reg_names = calloc(GDB_REGNO_COUNT, sizeof(char *));
if (info->reg_names[regno])
return info->reg_names[regno];
if (default_reg_names[regno])
return default_reg_names[regno];
if (regno <= GDB_REGNO_XPR31) {
info->reg_names[regno] = init_reg_name_with_prefix("x", regno - GDB_REGNO_ZERO);
return info->reg_names[regno];
}
if (regno <= GDB_REGNO_V31 && regno >= GDB_REGNO_V0) {
info->reg_names[regno] = init_reg_name_with_prefix("v", regno - GDB_REGNO_V0);
return info->reg_names[regno];
}
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
init_custom_csr_names(target);
if (!info->reg_names[regno])
info->reg_names[regno] = init_reg_name_with_prefix("csr", regno - GDB_REGNO_CSR0);
return info->reg_names[regno];
}
assert(!"Encountered uninitialized entry in reg_names table");
return NULL;
}
struct target *riscv_reg_impl_get_target(const struct reg *reg)
{
assert(riscv_reg_impl_is_initialized(reg));
return ((const riscv_reg_info_t *)reg->arch_info)->target;
}
static struct reg_feature *gdb_regno_feature(uint32_t regno)
{
if (regno <= GDB_REGNO_XPR31 || regno == GDB_REGNO_PC) {
static struct reg_feature feature_cpu = {
.name = "org.gnu.gdb.riscv.cpu"
};
return &feature_cpu;
}
if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
regno == GDB_REGNO_FFLAGS ||
regno == GDB_REGNO_FRM ||
regno == GDB_REGNO_FCSR) {
static struct reg_feature feature_fpu = {
.name = "org.gnu.gdb.riscv.fpu"
};
return &feature_fpu;
}
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
static struct reg_feature feature_vector = {
.name = "org.gnu.gdb.riscv.vector"
};
return &feature_vector;
}
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
static struct reg_feature feature_csr = {
.name = "org.gnu.gdb.riscv.csr"
};
return &feature_csr;
}
if (regno == GDB_REGNO_PRIV) {
static struct reg_feature feature_virtual = {
.name = "org.gnu.gdb.riscv.virtual"
};
return &feature_virtual;
}
assert(regno >= GDB_REGNO_COUNT);
static struct reg_feature feature_custom = {
.name = "org.gnu.gdb.riscv.custom"
};
return &feature_custom;
}
static bool gdb_regno_caller_save(uint32_t regno)
{
return regno <= GDB_REGNO_XPR31 ||
regno == GDB_REGNO_PC ||
(regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31);
}
static struct reg_data_type *gdb_regno_reg_data_type(const struct target *target,
uint32_t regno)
{
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) {
static struct reg_data_type type_ieee_single = {
.type = REG_TYPE_IEEE_SINGLE,
.id = "ieee_single"
};
static struct reg_data_type type_ieee_double = {
.type = REG_TYPE_IEEE_DOUBLE,
.id = "ieee_double"
};
static struct reg_data_type_union_field single_double_fields[] = {
{"float", &type_ieee_single, single_double_fields + 1},
{"double", &type_ieee_double, NULL},
};
static struct reg_data_type_union single_double_union = {
.fields = single_double_fields
};
static struct reg_data_type type_ieee_single_double = {
.type = REG_TYPE_ARCH_DEFINED,
.id = "FPU_FD",
.type_class = REG_TYPE_CLASS_UNION,
{.reg_type_union = &single_double_union}
};
return riscv_supports_extension(target, 'D') ?
&type_ieee_single_double :
&type_ieee_single;
}
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
RISCV_INFO(info);
return &info->type_vector;
}
return NULL;
}
static const char *gdb_regno_group(uint32_t regno)
{
if (regno <= GDB_REGNO_XPR31 ||
regno == GDB_REGNO_PC ||
regno == GDB_REGNO_PRIV)
return "general";
if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
regno == GDB_REGNO_FFLAGS ||
regno == GDB_REGNO_FRM ||
regno == GDB_REGNO_FCSR)
return "float";
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095)
return "csr";
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
return "vector";
assert(regno >= GDB_REGNO_COUNT);
return "custom";
}
uint32_t gdb_regno_size(const struct target *target, uint32_t regno)
{
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
return riscv_supports_extension(target, 'D') ? 64 : 32;
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
return riscv_vlenb(target) * 8;
if (regno == GDB_REGNO_PRIV)
return 8;
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
switch (csr_number) {
case CSR_DCSR:
case CSR_MVENDORID:
case CSR_MCOUNTINHIBIT:
case CSR_FFLAGS:
case CSR_FRM:
case CSR_FCSR:
case CSR_SCOUNTEREN:
case CSR_MCOUNTEREN:
return 32;
}
}
return riscv_xlen(target);
}
static bool vlenb_exists(const struct target *target)
{
return riscv_vlenb(target) != 0;
}
static bool reg_exists(const struct target *target, uint32_t regno)
{
const struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
assert(riscv_reg_impl_is_initialized(reg));
return reg->exist;
}
static bool is_known_standard_csr(unsigned int csr_num)
{
static const bool is_csr_in_buf[GDB_REGNO_CSR4095 - GDB_REGNO_CSR0 + 1] = {
#define DECLARE_CSR(csr_name, number)[number] = true,
#include "encoding.h"
#undef DECLARE_CSR
};
assert(csr_num < ARRAY_SIZE(is_csr_in_buf));
return is_csr_in_buf[csr_num];
}
bool riscv_reg_impl_gdb_regno_exist(const struct target *target, uint32_t regno)
{
switch (regno) {
case GDB_REGNO_VLENB:
case GDB_REGNO_MTOPI:
case GDB_REGNO_MTOPEI:
assert(false
&& "Existence of other registers is determined "
"depending on existence of these ones, so "
"whether these register exist or not should be "
"set explicitly.");
};
if (regno <= GDB_REGNO_XPR15 ||
regno == GDB_REGNO_PC ||
regno == GDB_REGNO_PRIV)
return true;
if (regno > GDB_REGNO_XPR15 && regno <= GDB_REGNO_XPR31)
return !riscv_supports_extension(target, 'E');
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
return riscv_supports_extension(target, 'F');
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
return vlenb_exists(target);
if (regno >= GDB_REGNO_COUNT)
return true;
assert(regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095);
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
switch (csr_number) {
case CSR_FFLAGS:
case CSR_FRM:
case CSR_FCSR:
return riscv_supports_extension(target, 'F');
case CSR_VSTART:
case CSR_VXSAT:
case CSR_VXRM:
case CSR_VL:
case CSR_VCSR:
case CSR_VTYPE:
return vlenb_exists(target);
case CSR_SCOUNTEREN:
case CSR_SSTATUS:
case CSR_STVEC:
case CSR_SIP:
case CSR_SIE:
case CSR_SSCRATCH:
case CSR_SEPC:
case CSR_SCAUSE:
case CSR_STVAL:
case CSR_SATP:
return riscv_supports_extension(target, 'S');
case CSR_MEDELEG:
case CSR_MIDELEG:
/* "In systems with only M-mode, or with both M-mode and
* U-mode but without U-mode trap support, the medeleg and
* mideleg registers should not exist." */
return riscv_supports_extension(target, 'S') ||
riscv_supports_extension(target, 'N');
case CSR_PMPCFG1:
case CSR_PMPCFG3:
case CSR_CYCLEH:
case CSR_TIMEH:
case CSR_INSTRETH:
case CSR_HPMCOUNTER3H:
case CSR_HPMCOUNTER4H:
case CSR_HPMCOUNTER5H:
case CSR_HPMCOUNTER6H:
case CSR_HPMCOUNTER7H:
case CSR_HPMCOUNTER8H:
case CSR_HPMCOUNTER9H:
case CSR_HPMCOUNTER10H:
case CSR_HPMCOUNTER11H:
case CSR_HPMCOUNTER12H:
case CSR_HPMCOUNTER13H:
case CSR_HPMCOUNTER14H:
case CSR_HPMCOUNTER15H:
case CSR_HPMCOUNTER16H:
case CSR_HPMCOUNTER17H:
case CSR_HPMCOUNTER18H:
case CSR_HPMCOUNTER19H:
case CSR_HPMCOUNTER20H:
case CSR_HPMCOUNTER21H:
case CSR_HPMCOUNTER22H:
case CSR_HPMCOUNTER23H:
case CSR_HPMCOUNTER24H:
case CSR_HPMCOUNTER25H:
case CSR_HPMCOUNTER26H:
case CSR_HPMCOUNTER27H:
case CSR_HPMCOUNTER28H:
case CSR_HPMCOUNTER29H:
case CSR_HPMCOUNTER30H:
case CSR_HPMCOUNTER31H:
case CSR_MCYCLEH:
case CSR_MINSTRETH:
case CSR_MHPMCOUNTER4H:
case CSR_MHPMCOUNTER5H:
case CSR_MHPMCOUNTER6H:
case CSR_MHPMCOUNTER7H:
case CSR_MHPMCOUNTER8H:
case CSR_MHPMCOUNTER9H:
case CSR_MHPMCOUNTER10H:
case CSR_MHPMCOUNTER11H:
case CSR_MHPMCOUNTER12H:
case CSR_MHPMCOUNTER13H:
case CSR_MHPMCOUNTER14H:
case CSR_MHPMCOUNTER15H:
case CSR_MHPMCOUNTER16H:
case CSR_MHPMCOUNTER17H:
case CSR_MHPMCOUNTER18H:
case CSR_MHPMCOUNTER19H:
case CSR_MHPMCOUNTER20H:
case CSR_MHPMCOUNTER21H:
case CSR_MHPMCOUNTER22H:
case CSR_MHPMCOUNTER23H:
case CSR_MHPMCOUNTER24H:
case CSR_MHPMCOUNTER25H:
case CSR_MHPMCOUNTER26H:
case CSR_MHPMCOUNTER27H:
case CSR_MHPMCOUNTER28H:
case CSR_MHPMCOUNTER29H:
case CSR_MHPMCOUNTER30H:
case CSR_MHPMCOUNTER31H:
return riscv_xlen(target) == 32;
case CSR_MCOUNTEREN:
return riscv_supports_extension(target, 'U');
/* Interrupts M-Mode CSRs. */
case CSR_MISELECT:
case CSR_MIREG:
case CSR_MVIEN:
case CSR_MVIP:
case CSR_MIEH:
case CSR_MIPH:
return reg_exists(target, GDB_REGNO_MTOPI);
case CSR_MIDELEGH:
case CSR_MVIENH:
case CSR_MVIPH:
return reg_exists(target, GDB_REGNO_MTOPI) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'S');
/* Interrupts S-Mode CSRs. */
case CSR_SISELECT:
case CSR_SIREG:
case CSR_STOPI:
return reg_exists(target, GDB_REGNO_MTOPI) &&
riscv_supports_extension(target, 'S');
case CSR_STOPEI:
return reg_exists(target, GDB_REGNO_MTOPEI) &&
riscv_supports_extension(target, 'S');
case CSR_SIEH:
case CSR_SIPH:
return reg_exists(target, GDB_REGNO_MTOPI) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'S');
/* Interrupts Hypervisor and VS CSRs. */
case CSR_HVIEN:
case CSR_HVICTL:
case CSR_HVIPRIO1:
case CSR_HVIPRIO2:
case CSR_VSISELECT:
case CSR_VSIREG:
case CSR_VSTOPI:
return reg_exists(target, GDB_REGNO_MTOPI) &&
riscv_supports_extension(target, 'H');
case CSR_VSTOPEI:
return reg_exists(target, GDB_REGNO_MTOPEI) &&
riscv_supports_extension(target, 'H');
case CSR_HIDELEGH:
case CSR_HVIENH:
case CSR_HVIPH:
case CSR_HVIPRIO1H:
case CSR_HVIPRIO2H:
case CSR_VSIEH:
case CSR_VSIPH:
return reg_exists(target, GDB_REGNO_MTOPI) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'H');
}
return is_known_standard_csr(csr_number);
}
static unsigned int gdb_regno_custom_number(const struct target *target, uint32_t regno)
{
if (regno < GDB_REGNO_COUNT)
return 0;
RISCV_INFO(info);
assert(!list_empty(&info->expose_custom));
range_list_t *range;
unsigned int regno_start = GDB_REGNO_COUNT;
unsigned int start = 0;
unsigned int offset = 0;
list_for_each_entry(range, &info->expose_custom, list) {
start = range->low;
assert(regno >= regno_start);
offset = regno - regno_start;
const unsigned int regs_in_range = range->high - range->low + 1;
if (offset < regs_in_range)
break;
regno_start += regs_in_range;
}
return start + offset;
}
struct reg *riscv_reg_impl_cache_entry(const struct target *target,
uint32_t number)
{
assert(target->reg_cache);
assert(target->reg_cache->reg_list);
assert(number < target->reg_cache->num_regs);
return &target->reg_cache->reg_list[number];
}
static int resize_reg(const struct target *target, uint32_t regno, bool exist,
uint32_t size)
{
struct reg *reg = riscv_reg_impl_cache_entry(target, regno);
assert(riscv_reg_impl_is_initialized(reg));
free(reg->value);
reg->size = size;
reg->exist = exist;
if (reg->exist) {
reg->value = malloc(DIV_ROUND_UP(reg->size, 8));
if (!reg->value) {
LOG_ERROR("Failed to allocate memory.");
return ERROR_FAIL;
}
} else {
reg->value = NULL;
}
assert(riscv_reg_impl_is_initialized(reg));
return ERROR_OK;
}
int riscv_reg_impl_set_exist(const struct target *target, uint32_t regno, bool exist)
{
const struct reg *reg = riscv_reg_impl_cache_entry(target, regno);
assert(riscv_reg_impl_is_initialized(reg));
return resize_reg(target, regno, exist, reg->size);
}
int riscv_reg_impl_init_cache_entry(struct target *target, uint32_t regno,
bool exist, const struct reg_arch_type *reg_type)
{
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
if (riscv_reg_impl_is_initialized(reg))
return ERROR_OK;
reg->number = regno;
reg->type = reg_type;
reg->dirty = false;
reg->valid = false;
reg->hidden = false;
reg->name = riscv_reg_gdb_regno_name(target, regno);
reg->feature = gdb_regno_feature(regno);
reg->caller_save = gdb_regno_caller_save(regno);
reg->reg_data_type = gdb_regno_reg_data_type(target, regno);
reg->group = gdb_regno_group(regno);
if (regno < GDB_REGNO_COUNT) {
RISCV_INFO(info);
reg->arch_info = &info->shared_reg_info;
} else {
reg->arch_info = calloc(1, sizeof(riscv_reg_info_t));
if (!reg->arch_info) {
LOG_ERROR("Out of memory.");
return ERROR_FAIL;
}
riscv_reg_info_t * const reg_arch_info = reg->arch_info;
reg_arch_info->target = target;
reg_arch_info->custom_number = gdb_regno_custom_number(target, regno);
}
return resize_reg(target, regno, exist, gdb_regno_size(target, regno));
}
static int init_custom_register_names(struct list_head *expose_custom,
struct reg_name_table *custom_register_names)
{
unsigned int custom_regs_num = 0;
if (!list_empty(expose_custom)) {
range_list_t *entry;
list_for_each_entry(entry, expose_custom, list)
custom_regs_num += entry->high - entry->low + 1;
}
if (!custom_regs_num)
return ERROR_OK;
custom_register_names->reg_names = calloc(custom_regs_num, sizeof(char *));
if (!custom_register_names->reg_names) {
LOG_ERROR("Failed to allocate memory for custom_register_names->reg_names");
return ERROR_FAIL;
}
custom_register_names->num_entries = custom_regs_num;
char **reg_names = custom_register_names->reg_names;
range_list_t *range;
unsigned int next_custom_reg_index = 0;
list_for_each_entry(range, expose_custom, list) {
for (unsigned int custom_number = range->low; custom_number <= range->high; ++custom_number) {
if (range->name)
reg_names[next_custom_reg_index] = init_reg_name(range->name);
else
reg_names[next_custom_reg_index] =
init_reg_name_with_prefix("custom", custom_number);
if (!reg_names[next_custom_reg_index])
return ERROR_FAIL;
++next_custom_reg_index;
}
}
return ERROR_OK;
}
int riscv_reg_impl_init_cache(struct target *target)
{
RISCV_INFO(info);
riscv_reg_free_all(target);
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
if (!target->reg_cache) {
LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache");
return ERROR_FAIL;
}
target->reg_cache->name = "RISC-V Registers";
if (init_custom_register_names(&info->expose_custom, &info->custom_register_names) != ERROR_OK) {
LOG_TARGET_ERROR(target, "init_custom_register_names failed");
return ERROR_FAIL;
}
target->reg_cache->num_regs = GDB_REGNO_COUNT + info->custom_register_names.num_entries;
LOG_TARGET_DEBUG(target, "create register cache for %d registers",
target->reg_cache->num_regs);
target->reg_cache->reg_list =
calloc(target->reg_cache->num_regs, sizeof(struct reg));
if (!target->reg_cache->reg_list) {
LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache->reg_list");
return ERROR_FAIL;
}
return ERROR_OK;
}
int riscv_reg_impl_expose_csrs(const struct target *target)
{
RISCV_INFO(info);
range_list_t *entry;
list_for_each_entry(entry, &info->expose_csr, list) {
assert(entry->low <= entry->high);
assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0);
const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high;
for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low;
regno <= last_regno; ++regno) {
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
if (reg->exist) {
LOG_TARGET_WARNING(target,
"Not exposing CSR %d: register already exists.",
csr_number);
continue;
}
if (riscv_reg_impl_set_exist(target, regno, /*exist*/ true) != ERROR_OK)
return ERROR_FAIL;
LOG_TARGET_DEBUG(target, "Exposing additional CSR %d (name=%s)",
csr_number, reg->name);
}
}
return ERROR_OK;
}
void riscv_reg_impl_hide_csrs(const struct target *target)
{
RISCV_INFO(info);
range_list_t *entry;
list_for_each_entry(entry, &info->hide_csr, list) {
assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0);
const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high;
for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low;
regno <= last_regno; ++regno) {
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
if (!reg->exist) {
LOG_TARGET_DEBUG(target,
"Not hiding CSR %d: register does not exist.",
csr_number);
continue;
}
LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s).", csr_number, reg->name);
reg->hidden = true;
}
}
}
void riscv_reg_free_all(struct target *target)
{
free_reg_names(target);
/* Free the shared structure use for most registers. */
if (!target->reg_cache)
return;
if (target->reg_cache->reg_list) {
for (unsigned int i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
free(target->reg_cache->reg_list[i].arch_info);
for (unsigned int i = 0; i < target->reg_cache->num_regs; i++)
free(target->reg_cache->reg_list[i].value);
free(target->reg_cache->reg_list);
}
free(target->reg_cache);
target->reg_cache = NULL;
}
int riscv_reg_flush_all(struct target *target)
{
if (!target->reg_cache)
return ERROR_OK;
LOG_TARGET_DEBUG(target, "Flushing register cache");
/* Writing non-GPR registers may require progbuf execution, and some GPRs
* may become dirty in the process (e.g. S0, S1). For that reason, flush
* registers in reverse order, so that GPRs are flushed last.
*/
for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) {
struct reg *reg = riscv_reg_impl_cache_entry(target, number);
if (reg->valid && reg->dirty) {
riscv_reg_t value = buf_get_u64(reg->value, 0, reg->size);
LOG_TARGET_DEBUG(target, "%s is dirty; write back 0x%" PRIx64,
reg->name, value);
if (riscv_reg_write(target, number, value) != ERROR_OK)
return ERROR_FAIL;
}
}
LOG_TARGET_DEBUG(target, "Flush of register cache completed");
return ERROR_OK;
}
/**
* This function is used internally by functions that change register values.
* If `write_through` is true, it is ensured that the value of the target's
* register is set to be equal to the `value` argument. The cached value is
* updated if the register is cacheable.
* TODO: Currently `reg->get/set` is implemented in terms of
* `riscv_get/set_register`. However, the intention behind
* `riscv_get/set_register` is to work with the cache, therefore it accesses
* and modifyes register cache directly. The idea is to implement
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
* `reg->get/set`.
*/
static int riscv_set_or_write_register(struct target *target,
enum gdb_regno regid, riscv_reg_t value, bool write_through)
{
RISCV_INFO(r);
assert(r);
if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
return riscv011_set_register(target, regid, value);
keep_alive();
if (regid == GDB_REGNO_PC) {
return riscv_set_or_write_register(target, GDB_REGNO_DPC, value, write_through);
} else if (regid == GDB_REGNO_PRIV) {
riscv_reg_t dcsr;
if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
return ERROR_FAIL;
dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
return riscv_set_or_write_register(target, GDB_REGNO_DCSR, dcsr, write_through);
}
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
assert(riscv_reg_impl_is_initialized(reg));
if (!reg->exist) {
LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
return ERROR_FAIL;
}
if (target->state != TARGET_HALTED) {
LOG_TARGET_DEBUG(target,
"Target not halted, writing to target: %s <- 0x%" PRIx64,
reg->name, value);
return riscv013_set_register(target, regid, value);
}
const bool need_to_write = !reg->valid || reg->dirty ||
value != buf_get_u64(reg->value, 0, reg->size);
const bool cacheable = riscv_reg_impl_gdb_regno_cacheable(regid, need_to_write);
if (!cacheable || (write_through && need_to_write)) {
LOG_TARGET_DEBUG(target,
"Writing to target: %s <- 0x%" PRIx64 " (cacheable=%s, valid=%s, dirty=%s)",
reg->name, value, cacheable ? "true" : "false",
reg->valid ? "true" : "false",
reg->dirty ? "true" : "false");
if (riscv013_set_register(target, regid, value) != ERROR_OK)
return ERROR_FAIL;
reg->dirty = false;
} else {
reg->dirty = need_to_write;
}
buf_set_u64(reg->value, 0, reg->size, value);
reg->valid = cacheable;
LOG_TARGET_DEBUG(target,
"Wrote 0x%" PRIx64 " to %s (cacheable=%s, valid=%s, dirty=%s)",
value, reg->name, cacheable ? "true" : "false",
reg->valid ? "true" : "false",
reg->dirty ? "true" : "false");
return ERROR_OK;
}
bool riscv_reg_cache_any_dirty(const struct target *target, int log_level)
{
bool any_dirty = false;
if (!target->reg_cache)
return any_dirty;
for (unsigned int number = 0; number < target->reg_cache->num_regs; ++number) {
const struct reg * const reg = riscv_reg_impl_cache_entry(target, number);
assert(riscv_reg_impl_is_initialized(reg));
if (reg->dirty) {
log_printf_lf(log_level, __FILE__, __LINE__, __func__,
"[%s] Register %s is dirty!", target_name(target), reg->name);
any_dirty = true;
}
}
return any_dirty;
}
void riscv_reg_cache_invalidate_all(struct target *target)
{
if (!target->reg_cache)
return;
LOG_TARGET_DEBUG(target, "Invalidating register cache.");
register_cache_invalidate(target->reg_cache);
}
/**
* This function is used to change the value of a register. The new value may
* be cached, and may not be written until the hart is resumed.
* TODO: Currently `reg->get/set` is implemented in terms of
* `riscv_get/set_register`. However, the intention behind
* `riscv_get/set_register` is to work with the cache, therefore it accesses
* and modifyes register cache directly. The idea is to implement
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
* `reg->get/set`.
*/
int riscv_reg_set(struct target *target, enum gdb_regno regid,
riscv_reg_t value)
{
return riscv_set_or_write_register(target, regid, value,
/* write_through */ false);
}
/**
* This function is used to change the value of a register. The new value may
* be cached, but it will be written to hart immediately.
* TODO: Currently `reg->get/set` is implemented in terms of
* `riscv_get/set_register`. However, the intention behind
* `riscv_get/set_register` is to work with the cache, therefore it accesses
* and modifyes register cache directly. The idea is to implement
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
* `reg->get/set`.
*/
int riscv_reg_write(struct target *target, enum gdb_regno regid,
riscv_reg_t value)
{
return riscv_set_or_write_register(target, regid, value,
/* write_through */ true);
}
/**
* This function is used to get the value of a register. If possible, the value
* in cache will be updated.
* TODO: Currently `reg->get/set` is implemented in terms of
* `riscv_get/set_register`. However, the intention behind
* `riscv_get/set_register` is to work with the cache, therefore it accesses
* and modifyes register cache directly. The idea is to implement
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
* `reg->get/set`.
*/
int riscv_reg_get(struct target *target, riscv_reg_t *value,
enum gdb_regno regid)
{
RISCV_INFO(r);
assert(r);
if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
return riscv011_get_register(target, value, regid);
keep_alive();
if (regid == GDB_REGNO_PC)
return riscv_reg_get(target, value, GDB_REGNO_DPC);
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
assert(riscv_reg_impl_is_initialized(reg));
if (!reg->exist) {
LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
return ERROR_FAIL;
}
if (reg->valid) {
*value = buf_get_u64(reg->value, 0, reg->size);
LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64 " (cached)", reg->name,
*value);
return ERROR_OK;
}
LOG_TARGET_DEBUG(target, "Reading %s from target", reg->name);
if (riscv013_get_register(target, value, regid) != ERROR_OK)
return ERROR_FAIL;
buf_set_u64(reg->value, 0, reg->size, *value);
reg->valid = riscv_reg_impl_gdb_regno_cacheable(regid, /* is write? */ false) &&
target->state == TARGET_HALTED;
reg->dirty = false;
LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64, reg->name, *value);
return ERROR_OK;
}

View File

@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_H
#define OPENOCD_TARGET_RISCV_RISCV_REG_H
#include "target/target.h"
#include "target/register.h"
/**
* This file describes the register cache interface available to the RISC-V
* target. Functions declared here should be safe to use once register cache is
* completely initialized and may be used with caution during register cache
* initialization.
*/
/** Return the name of the register by it's number in register cache. */
const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno regno);
/** Free register cache and associated structures. */
void riscv_reg_free_all(struct target *target);
/** Write all dirty registers to the target. */
int riscv_reg_flush_all(struct target *target);
/**
* Check whether there are any dirty registers in the OpenOCD's register cache.
* In addition, all dirty registers will be reported to the log using the
* supplied "log_level".
*/
bool riscv_reg_cache_any_dirty(const struct target *target, int log_level);
/**
* Invalidate all registers - forget their cached register values.
* WARNING: If a register was dirty, its walue will be silently lost!
*/
void riscv_reg_cache_invalidate_all(struct target *target);
/**
* Set the register value. For cacheable registers, only the cache is updated
* (write-back mode).
*/
int riscv_reg_set(struct target *target, enum gdb_regno i, riscv_reg_t v);
/**
* Set the register value and immediately write it to the target
* (write-through mode).
*/
int riscv_reg_write(struct target *target, enum gdb_regno i, riscv_reg_t v);
/** Get register, from the cache if it's in there. */
int riscv_reg_get(struct target *target, riscv_reg_t *value,
enum gdb_regno r);
#endif /* OPENOCD_TARGET_RISCV_RISCV_REG_H */

View File

@@ -0,0 +1,222 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H
#define OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H
#include "gdb_regs.h"
#include "riscv.h"
#include "target/target.h"
#include "target/register.h"
#include <assert.h>
/**
* This file describes the helpers to use during register cache initialization
* of a RISC-V target. Each cache entry proceedes through the following stages:
* - not allocated before `riscv_reg_impl_init_cache()`
* - not initialized before the call to `riscv_reg_impl_init_cache_entry()` with appropriate regno.
* - initialized until `riscv_reg_free_all()` is called.
*/
static inline bool riscv_reg_impl_is_initialized(const struct reg *reg)
{
assert(reg);
if (!reg->feature) {
const struct reg default_reg = {0};
assert(!memcmp(&default_reg, reg, sizeof(*reg)));
return false;
}
assert(reg->arch_info);
assert(((riscv_reg_info_t *)reg->arch_info)->target);
assert((!reg->exist && !reg->value) || (reg->exist && reg->value));
assert(reg->valid || !reg->dirty);
return true;
}
/**
* Initialize register cache. Note, that each specific register cache entry is
* not initialized by this function.
*/
int riscv_reg_impl_init_cache(struct target *target);
/** Initialize register. */
int riscv_reg_impl_init_cache_entry(struct target *target, uint32_t regno,
bool exist, const struct reg_arch_type *reg_type);
/**
* For most registers, returns whether they exist or not.
* For some registers the "exist" bit should be set explicitly.
*/
bool riscv_reg_impl_gdb_regno_exist(const struct target *target, uint32_t regno);
/** Mark register as existing or not. */
int riscv_reg_impl_set_exist(const struct target *target,
uint32_t regno, bool exist);
/** Return the entry in the register cache of the target. */
struct reg *riscv_reg_impl_cache_entry(const struct target *target,
uint32_t number);
/** Return the target that owns the cache entry. */
struct target *riscv_reg_impl_get_target(const struct reg *reg);
static inline void init_shared_reg_info(struct target *target)
{
RISCV_INFO(info);
info->shared_reg_info.target = target;
info->shared_reg_info.custom_number = 0;
}
/** TODO: vector register type description can be moved into `riscv013_info_t`,
* since 0.11 targets do not support access to vector registers. */
static inline void riscv_reg_impl_init_vector_reg_type(const struct target *target)
{
RISCV_INFO(info);
static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" };
static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" };
static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" };
static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" };
static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" };
/* This is roughly the XML we want:
* <vector id="bytes" type="uint8" count="16"/>
* <vector id="shorts" type="uint16" count="8"/>
* <vector id="words" type="uint32" count="4"/>
* <vector id="longs" type="uint64" count="2"/>
* <vector id="quads" type="uint128" count="1"/>
* <union id="riscv_vector_type">
* <field name="b" type="bytes"/>
* <field name="s" type="shorts"/>
* <field name="w" type="words"/>
* <field name="l" type="longs"/>
* <field name="q" type="quads"/>
* </union>
*/
info->vector_uint8.type = &type_uint8;
info->vector_uint8.count = riscv_vlenb(target);
info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint8_vector.id = "bytes";
info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
info->vector_uint16.type = &type_uint16;
info->vector_uint16.count = riscv_vlenb(target) / 2;
info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint16_vector.id = "shorts";
info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
info->vector_uint32.type = &type_uint32;
info->vector_uint32.count = riscv_vlenb(target) / 4;
info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint32_vector.id = "words";
info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
info->vector_uint64.type = &type_uint64;
info->vector_uint64.count = riscv_vlenb(target) / 8;
info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint64_vector.id = "longs";
info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
info->vector_uint128.type = &type_uint128;
info->vector_uint128.count = riscv_vlenb(target) / 16;
info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint128_vector.id = "quads";
info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint128_vector.reg_type_vector = &info->vector_uint128;
info->vector_fields[0].name = "b";
info->vector_fields[0].type = &info->type_uint8_vector;
if (riscv_vlenb(target) >= 2) {
info->vector_fields[0].next = info->vector_fields + 1;
info->vector_fields[1].name = "s";
info->vector_fields[1].type = &info->type_uint16_vector;
} else {
info->vector_fields[0].next = NULL;
}
if (riscv_vlenb(target) >= 4) {
info->vector_fields[1].next = info->vector_fields + 2;
info->vector_fields[2].name = "w";
info->vector_fields[2].type = &info->type_uint32_vector;
} else {
info->vector_fields[1].next = NULL;
}
if (riscv_vlenb(target) >= 8) {
info->vector_fields[2].next = info->vector_fields + 3;
info->vector_fields[3].name = "l";
info->vector_fields[3].type = &info->type_uint64_vector;
} else {
info->vector_fields[2].next = NULL;
}
if (riscv_vlenb(target) >= 16) {
info->vector_fields[3].next = info->vector_fields + 4;
info->vector_fields[4].name = "q";
info->vector_fields[4].type = &info->type_uint128_vector;
} else {
info->vector_fields[3].next = NULL;
}
info->vector_fields[4].next = NULL;
info->vector_union.fields = info->vector_fields;
info->type_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_vector.id = "riscv_vector";
info->type_vector.type_class = REG_TYPE_CLASS_UNION;
info->type_vector.reg_type_union = &info->vector_union;
}
/** Expose additional CSRs, as specified by `riscv_info_t::expose_csr` list. */
int riscv_reg_impl_expose_csrs(const struct target *target);
/** Hide additional CSRs, as specified by `riscv_info_t::hide_csr` list. */
void riscv_reg_impl_hide_csrs(const struct target *target);
/**
* If write is true:
* return true iff we are guaranteed that the register will contain exactly
* the value we just wrote when it's read.
* If write is false:
* return true iff we are guaranteed that the register will read the same
* value in the future as the value we just read.
*/
static inline bool riscv_reg_impl_gdb_regno_cacheable(enum gdb_regno regno,
bool is_write)
{
if (regno == GDB_REGNO_ZERO)
return !is_write;
/* GPRs, FPRs, vector registers are just normal data stores. */
if (regno <= GDB_REGNO_XPR31 ||
(regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31))
return true;
/* Most CSRs won't change value on us, but we can't assume it about arbitrary
* CSRs. */
switch (regno) {
case GDB_REGNO_DPC:
case GDB_REGNO_VSTART:
case GDB_REGNO_VXSAT:
case GDB_REGNO_VXRM:
case GDB_REGNO_VLENB:
case GDB_REGNO_VL:
case GDB_REGNO_VTYPE:
case GDB_REGNO_MISA:
case GDB_REGNO_DCSR:
case GDB_REGNO_DSCRATCH0:
case GDB_REGNO_MEPC:
case GDB_REGNO_SATP:
/*
* WARL registers might not contain the value we just wrote, but
* these ones won't spontaneously change their value either. *
*/
return !is_write;
case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */
case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */
case GDB_REGNO_TDATA2: /* Changes value when tselect is changed. */
default:
return false;
}
}
#endif /* OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H */

View File

@@ -32,10 +32,62 @@
#include "target/target.h"
#include "riscv.h"
#include "riscv_reg.h"
static int riscv_semihosting_setup(struct target *target, int enable);
static int riscv_semihosting_post_result(struct target *target);
static int riscv_semihosting_detect_magic_sequence(struct target *target,
const target_addr_t pc, bool *sequence_found)
{
assert(sequence_found);
/* The semihosting "magic" sequence must be the exact three instructions
* listed below. All these instructions, including the ebreak, must be
* uncompressed (4 bytes long). */
const uint32_t magic[] = {
0x01f01013, /* slli zero,zero,0x1f */
0x00100073, /* ebreak */
0x40705013 /* srai zero,zero,0x7 */
};
LOG_TARGET_DEBUG(target, "Checking for RISC-V semihosting sequence "
"at PC = 0x%" TARGET_PRIxADDR, pc);
/* Read three uncompressed instructions:
* The previous, the current one (pointed to by PC) and the next one. */
const target_addr_t sequence_start_address = pc - 4;
for (int i = 0; i < 3; i++) {
uint8_t buf[4];
/* Instruction memories may not support arbitrary read size.
* Use any size that will work. */
const target_addr_t address = sequence_start_address + (4 * i);
int result = riscv_read_by_any_size(target, address, 4, buf);
if (result != ERROR_OK) {
*sequence_found = false;
return result;
}
/* RISC-V instruction layout in memory is always little endian,
* regardless of the endianness of the whole system. */
const uint32_t value = le_to_h_u32(buf);
LOG_TARGET_DEBUG(target, "compare 0x%08" PRIx32 " from 0x%" PRIx64 " against 0x%08" PRIx32,
value, address, magic[i]);
if (value != magic[i]) {
LOG_TARGET_DEBUG(target, "Not a RISC-V semihosting sequence");
*sequence_found = false;
return ERROR_OK;
}
}
LOG_TARGET_DEBUG(target, "RISC-V semihosting sequence found "
"at PC = 0x%" TARGET_PRIxADDR, pc);
*sequence_found = true;
return ERROR_OK;
}
/**
* Initialize RISC-V semihosting. Use common ARM code.
*/
@@ -56,50 +108,47 @@ void riscv_semihosting_init(struct target *target)
enum semihosting_result riscv_semihosting(struct target *target, int *retval)
{
struct semihosting *semihosting = target->semihosting;
if (!semihosting) {
LOG_DEBUG(" -> NONE (!semihosting)");
return SEMIHOSTING_NONE;
assert(semihosting);
riscv_reg_t pc;
int result = riscv_reg_get(target, &pc, GDB_REGNO_PC);
if (result != ERROR_OK) {
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read PC)");
return SEMIHOSTING_ERROR;
}
bool sequence_found = false;
*retval = riscv_semihosting_detect_magic_sequence(target, pc, &sequence_found);
if (*retval != ERROR_OK) {
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (during magic seq. detection)");
return SEMIHOSTING_ERROR;
}
if (!semihosting->is_active) {
LOG_DEBUG(" -> NONE (!semihosting->is_active)");
if (sequence_found) {
// If semihositing is encountered but disabled, provide an additional hint to the user.
LOG_TARGET_WARNING(target, "RISC-V semihosting call encountered in the program "
"but semihosting is disabled!");
LOG_TARGET_WARNING(target, "The target will remain halted (PC = 0x%" TARGET_PRIxADDR ").", pc);
LOG_TARGET_WARNING(target, "Hint: Restart your debug session and enable semihosting "
"by command 'arm semihosting enable'.");
// TODO: This can be improved: The ebreak halt cause detection and riscv_semihosting() call
// can be added also to "arm semihosting enable", which would allow the user to continue
// without restart of the debug session.
}
LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (semihosting not enabled)");
return SEMIHOSTING_NONE;
}
riscv_reg_t pc;
int result = riscv_get_register(target, &pc, GDB_REGNO_PC);
if (result != ERROR_OK)
return SEMIHOSTING_ERROR;
uint8_t tmp_buf[12];
/* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */
for (int i = 0; i < 3; i++) {
/* Instruction memories may not support arbitrary read size. Use any size that will work. */
*retval = riscv_read_by_any_size(target, (pc - 4) + 4 * i, 4, tmp_buf + 4 * i);
if (*retval != ERROR_OK)
return SEMIHOSTING_ERROR;
}
/*
* The instructions that trigger a semihosting call,
* always uncompressed, should look like:
*
* 01f01013 slli zero,zero,0x1f
* 00100073 ebreak
* 40705013 srai zero,zero,0x7
*/
uint32_t pre = target_buffer_get_u32(target, tmp_buf);
uint32_t ebreak = target_buffer_get_u32(target, tmp_buf + 4);
uint32_t post = target_buffer_get_u32(target, tmp_buf + 8);
LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
/* Not the magic sequence defining semihosting. */
LOG_DEBUG(" -> NONE (no magic)");
if (!sequence_found) {
LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (no magic sequence)");
return SEMIHOSTING_NONE;
}
/* Otherwise we have a semihosting call (and semihosting is enabled).
* Proceed with the handling of semihosting. */
/*
* Perform semihosting call if we are not waiting on a fileio
* operation to complete.
@@ -109,15 +158,17 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
riscv_reg_t r0;
riscv_reg_t r1;
result = riscv_get_register(target, &r0, GDB_REGNO_A0);
result = riscv_reg_get(target, &r0, GDB_REGNO_A0);
if (result != ERROR_OK) {
LOG_DEBUG(" -> ERROR (couldn't read a0)");
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)");
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read a0)");
return SEMIHOSTING_ERROR;
}
result = riscv_get_register(target, &r1, GDB_REGNO_A1);
result = riscv_reg_get(target, &r1, GDB_REGNO_A1);
if (result != ERROR_OK) {
LOG_DEBUG(" -> ERROR (couldn't read a1)");
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)");
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read a1)");
return SEMIHOSTING_ERROR;
}
@@ -131,18 +182,20 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
*retval = semihosting_common(target);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
LOG_TARGET_ERROR(target, "Failed semihosting operation (0x%02X)", semihosting->op);
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (error during semihosting processing)");
return SEMIHOSTING_ERROR;
}
} else {
/* Unknown operation number, not a semihosting call. */
LOG_DEBUG(" -> NONE (unknown operation number)");
LOG_TARGET_ERROR(target, "Unknown semihosting operation requested (op = 0x%x)", semihosting->op);
LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (unknown semihosting opcode)");
return SEMIHOSTING_NONE;
}
}
/* Resume right after the EBREAK 4 bytes instruction. */
*retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
*retval = riscv_reg_set(target, GDB_REGNO_PC, pc + 4);
if (*retval != ERROR_OK)
return SEMIHOSTING_ERROR;
@@ -151,11 +204,11 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
* operation to complete.
*/
if (semihosting->is_resumable && !semihosting->hit_fileio) {
LOG_DEBUG(" -> HANDLED");
LOG_TARGET_DEBUG(target, "Semihosting outcome: HANDLED");
return SEMIHOSTING_HANDLED;
}
LOG_DEBUG(" -> WAITING");
LOG_TARGET_DEBUG(target, "Semihosting outcome: WAITING");
return SEMIHOSTING_WAITING;
}
@@ -168,24 +221,21 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
*/
static int riscv_semihosting_setup(struct target *target, int enable)
{
LOG_DEBUG("[%s] enable=%d", target_name(target), enable);
LOG_TARGET_DEBUG(target, "enable=%d", enable);
struct semihosting *semihosting = target->semihosting;
if (semihosting)
semihosting->setup_time = clock();
assert(semihosting);
semihosting->setup_time = clock();
return ERROR_OK;
}
static int riscv_semihosting_post_result(struct target *target)
{
struct semihosting *semihosting = target->semihosting;
if (!semihosting) {
/* If not enabled, silently ignored. */
return 0;
}
assert(semihosting);
LOG_DEBUG("0x%" PRIx64, semihosting->result);
riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
LOG_TARGET_DEBUG(target, "Result: 0x%" PRIx64, semihosting->result);
riscv_reg_set(target, GDB_REGNO_A0, semihosting->result);
return 0;
}

View File

@@ -0,0 +1,55 @@
# SPDX-License-Identifier: GPL-2.0-or-later
lappend _telnet_autocomplete_skip "riscv set_enable_virtual"
proc {riscv set_enable_virtual} on_off {
echo {DEPRECATED! use 'riscv virt2phys_mode' not 'riscv set_enable_virtual'}
foreach t [target names] {
if {[$t cget -type] ne "riscv"} { continue }
switch -- [$t riscv virt2phys_mode] {
off -
hw {
switch -- $on_off {
on {$t riscv virt2phys_mode hw}
off {$t riscv virt2phys_mode off}
}
}
sw {
if {$on_off eq "on"} {
error {Can't enable virtual while translation mode is SW}
}
}
}
}
return {}
}
lappend _telnet_autocomplete_skip "riscv set_enable_virt2phys"
proc {riscv set_enable_virt2phys} on_off {
echo {DEPRECATED! use 'riscv virt2phys_mode' not 'riscv set_enable_virt2phys'}
foreach t [target names] {
if {[$t cget -type] ne "riscv"} { continue }
switch -- [riscv virt2phys_mode] {
off -
sw {
switch -- $on_off {
on {riscv virt2phys_mode sw}
off {riscv virt2phys_mode off}
}
}
hw {
if {$on_off eq "on"} {
error {Can't enable virt2phys while translation mode is HW}
}
}
}
}
return {}
}
foreach mode {m s u} {
lappend _telnet_autocomplete_skip "riscv set_ebreak$mode"
}
proc riscv {cmd args} {
tailcall "riscv $cmd" {*}$args
}