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:
committed by
Tomas Vanek
parent
ab22b0bf8f
commit
5754aebc49
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
3902
src/target/riscv/debug_defines.c
Normal file
3902
src/target/riscv/debug_defines.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
109
src/target/riscv/debug_reg_printer.c
Normal file
109
src/target/riscv/debug_reg_printer.c
Normal 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;
|
||||
}
|
||||
40
src/target/riscv/debug_reg_printer.h
Normal file
40
src/target/riscv/debug_reg_printer.h
Normal 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 */
|
||||
@@ -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)
|
||||
|
||||
47
src/target/riscv/field_helpers.h
Normal file
47
src/target/riscv/field_helpers.h
Normal 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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
15
src/target/riscv/riscv-011.h
Normal file
15
src/target/riscv/riscv-011.h
Normal 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 */
|
||||
86
src/target/riscv/riscv-011_reg.c
Normal file
86
src/target/riscv/riscv-011_reg.c
Normal 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;
|
||||
}
|
||||
19
src/target/riscv/riscv-011_reg.h
Normal file
19
src/target/riscv/riscv-011_reg.h
Normal 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
27
src/target/riscv/riscv-013.h
Normal file
27
src/target/riscv/riscv-013.h
Normal 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 */
|
||||
389
src/target/riscv/riscv-013_reg.c
Normal file
389
src/target/riscv/riscv-013_reg.c
Normal 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;
|
||||
}
|
||||
32
src/target/riscv/riscv-013_reg.h
Normal file
32
src/target/riscv/riscv-013_reg.h
Normal 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
@@ -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 */
|
||||
|
||||
990
src/target/riscv/riscv_reg.c
Normal file
990
src/target/riscv/riscv_reg.c
Normal 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;
|
||||
}
|
||||
49
src/target/riscv/riscv_reg.h
Normal file
49
src/target/riscv/riscv_reg.h
Normal 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 */
|
||||
222
src/target/riscv/riscv_reg_impl.h
Normal file
222
src/target/riscv/riscv_reg_impl.h
Normal 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 */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
55
src/target/riscv/startup.tcl
Normal file
55
src/target/riscv/startup.tcl
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user