From f998a2aaf19c14b46fd7f7dd50607a0904f40bd3 Mon Sep 17 00:00:00 2001 From: Jacek Wuwer Date: Tue, 6 Apr 2021 20:08:11 +0200 Subject: [PATCH] Cadence virtual debug interface (vdebug) integration Change-Id: I1bc105b3addc3f34161c2356c482ff3011e3f2cc Signed-off-by: Jacek Wuwer Reviewed-on: https://review.openocd.org/c/openocd/+/6097 Tested-by: jenkins Reviewed-by: Oleksij Rempel Reviewed-by: zapb Reviewed-by: Antonio Borneo --- configure.ac | 15 +- doc/openocd.texi | 41 ++ src/jtag/drivers/Makefile.am | 3 + src/jtag/drivers/vdebug.c | 1076 ++++++++++++++++++++++++++++++ src/jtag/interfaces.c | 6 + tcl/board/vd_a53x2_jtag.cfg | 31 + tcl/board/vd_m4_jtag.cfg | 30 + tcl/board/vd_pulpissimo_jtag.cfg | 32 + tcl/board/vd_swerv_jtag.cfg | 32 + tcl/interface/vdebug.cfg | 33 + tcl/target/vd_aarch64.cfg | 37 + tcl/target/vd_cortex_m.cfg | 12 + tcl/target/vd_riscv.cfg | 18 + 13 files changed, 1364 insertions(+), 2 deletions(-) create mode 100644 src/jtag/drivers/vdebug.c create mode 100644 tcl/board/vd_a53x2_jtag.cfg create mode 100644 tcl/board/vd_m4_jtag.cfg create mode 100644 tcl/board/vd_pulpissimo_jtag.cfg create mode 100644 tcl/board/vd_swerv_jtag.cfg create mode 100644 tcl/interface/vdebug.cfg create mode 100644 tcl/target/vd_aarch64.cfg create mode 100644 tcl/target/vd_cortex_m.cfg create mode 100644 tcl/target/vd_riscv.cfg diff --git a/configure.ac b/configure.ac index a178284ee..68fff45c1 100644 --- a/configure.ac +++ b/configure.ac @@ -274,6 +274,10 @@ AC_ARG_ENABLE([jtag_vpi], AS_HELP_STRING([--enable-jtag_vpi], [Enable building support for JTAG VPI]), [build_jtag_vpi=$enableval], [build_jtag_vpi=no]) +AC_ARG_ENABLE([vdebug], + AS_HELP_STRING([--enable-vdebug], [Enable building support for Cadence Virtual Debug Interface]), + [build_vdebug=$enableval], [build_vdebug=no]) + AC_ARG_ENABLE([jtag_dpi], AS_HELP_STRING([--enable-jtag_dpi], [Enable building support for JTAG DPI]), [build_jtag_dpi=$enableval], [build_jtag_dpi=no]) @@ -513,6 +517,12 @@ AS_IF([test "x$build_jtag_vpi" = "xyes"], [ AC_DEFINE([BUILD_JTAG_VPI], [0], [0 if you don't want JTAG VPI.]) ]) +AS_IF([test "x$build_vdebug" = "xyes"], [ + AC_DEFINE([BUILD_VDEBUG], [1], [1 if you want Cadence vdebug interface.]) +], [ + AC_DEFINE([BUILD_VDEBUG], [0], [0 if you don't want Cadence vdebug interface.]) +]) + AS_IF([test "x$build_jtag_dpi" = "xyes"], [ AC_DEFINE([BUILD_JTAG_DPI], [1], [1 if you want JTAG DPI.]) ], [ @@ -688,8 +698,9 @@ AM_CONDITIONAL([AT91RM9200], [test "x$build_at91rm9200" = "xyes"]) AM_CONDITIONAL([BCM2835GPIO], [test "x$build_bcm2835gpio" = "xyes"]) AM_CONDITIONAL([IMX_GPIO], [test "x$build_imx_gpio" = "xyes"]) AM_CONDITIONAL([BITBANG], [test "x$build_bitbang" = "xyes"]) -AM_CONDITIONAL([JTAG_VPI], [test "x$build_jtag_vpi" = "xyes" -o "x$build_jtag_vpi" = "xyes"]) -AM_CONDITIONAL([JTAG_DPI], [test "x$build_jtag_dpi" = "xyes" -o "x$build_jtag_dpi" = "xyes"]) +AM_CONDITIONAL([JTAG_VPI], [test "x$build_jtag_vpi" = "xyes"]) +AM_CONDITIONAL([VDEBUG], [test "x$build_vdebug" = "xyes"]) +AM_CONDITIONAL([JTAG_DPI], [test "x$build_jtag_dpi" = "xyes"]) AM_CONDITIONAL([USB_BLASTER_DRIVER], [test "x$enable_usb_blaster" != "xno" -o "x$enable_usb_blaster_2" != "xno"]) AM_CONDITIONAL([AMTJTAGACCEL], [test "x$build_amtjtagaccel" = "xyes"]) AM_CONDITIONAL([GW16012], [test "x$build_gw16012" = "xyes"]) diff --git a/doc/openocd.texi b/doc/openocd.texi index e2c495473..fd4a81da2 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -588,6 +588,12 @@ produced, PDF schematics are easily found and it is easy to make. @* A JTAG driver acting as a client for the JTAG VPI server interface. @* Link: @url{http://github.com/fjullien/jtag_vpi} +@item @b{vdebug} +@* A driver for Cadence virtual Debug Interface to emulated or simulated targets. +It implements a client connecting to the vdebug server, which in turn communicates +with the emulated or simulated RTL model through a transactor. The current version +supports only JTAG as a transport, but other virtual transports, like DAP are planned. + @item @b{jtag_dpi} @* A JTAG driver acting as a client for the SystemVerilog Direct Programming Interface (DPI) for JTAG devices. DPI allows OpenOCD to connect to the JTAG @@ -3345,6 +3351,41 @@ This value is only used with the standard variant. @end deffn +@deffn {Interface Driver} {vdebug} +Cadence Virtual Debug Interface driver. + +@deffn {Config Command} {vdebug server} host:port +Specifies the host and TCP port number where the vdebug server runs. +@end deffn + +@deffn {Config Command} {vdebug batching} value +Specifies the batching method for the vdebug request. Possible values are +0 for no batching +1 or wr to batch write transactions together (default) +2 or rw to batch both read and write transactions +@end deffn + +@deffn {Config Command} {vdebug polling} min max +Takes two values, representing the polling interval in ms. Lower values mean faster +debugger responsiveness, but lower emulation performance. The minimum should be +around 10, maximum should not exceed 1000, which is the default gdb and keepalive +timeout value. +@end deffn + +@deffn {Config Command} {vdebug bfm_path} path clk_period +Specifies the hierarchical path and input clk period of the vdebug BFM in the design. +The hierarchical path uses Verilog notation top.inst.inst +The clock period must include the unit, for instance 40ns. +@end deffn + +@deffn {Config Command} {vdebug mem_path} path base size +Specifies the hierarchical path to the design memory instance for backdoor access. +Up to 4 memories can be specified. The hierarchical path uses Verilog notation. +The base specifies start address in the design address space, size its size in bytes. +Both values can use hexadecimal notation with prefix 0x. +@end deffn +@end deffn + @deffn {Interface Driver} {jtag_dpi} SystemVerilog Direct Programming Interface (DPI) compatible driver for JTAG devices in emulation. The driver acts as a client for the SystemVerilog diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index c2161523d..887f99bcd 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -75,6 +75,9 @@ endif if JTAG_VPI DRIVERFILES += %D%/jtag_vpi.c endif +if VDEBUG +DRIVERFILES += %D%/vdebug.c +endif if JTAG_DPI DRIVERFILES += %D%/jtag_dpi.c endif diff --git a/src/jtag/drivers/vdebug.c b/src/jtag/drivers/vdebug.c new file mode 100644 index 000000000..a81740cb1 --- /dev/null +++ b/src/jtag/drivers/vdebug.c @@ -0,0 +1,1076 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +/*---------------------------------------------------------------------------- + * Copyright 2020-2021 Cadence Design Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + *---------------------------------------------------------------------------- + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------- +*/ + +/*! + * @file + * + * @brief the virtual debug interface provides a connection between a sw debugger + * and the simulated, emulated core over a soft connection, implemented by DPI + * The vdebug debug driver currently supports JTAG transport + * TODO: implement support and test big endian platforms + * +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#ifdef HAVE_UNISTD_H +#include /* close */ +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#endif +#include +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include + +#include "jtag/interface.h" +#include "jtag/commands.h" +#include "transport/transport.h" +#include "helper/time_support.h" +#include "helper/replacements.h" +#include "helper/log.h" + +#define VD_VERSION 43 +#define VD_BUFFER_LEN 4024 +#define VD_CHEADER_LEN 24 +#define VD_SHEADER_LEN 16 + +#define VD_MAX_MEMORIES 4 +#define VD_POLL_INTERVAL 500 +#define VD_SCALE_PSTOMS 1000000000 + +/** + * @brief List of transactor types + */ +enum { + VD_BFM_JTDP = 0x0001, /* transactor DAP JTAG DP */ + VD_BFM_SWDP = 0x0002, /* transactor DAP SWD DP */ + VD_BFM_AHB = 0x0003, /* transactor AMBA AHB */ + VD_BFM_APB = 0x0004, /* transactor AMBA APB */ + VD_BFM_AXI = 0x0005, /* transactor AMBA AXI */ + VD_BFM_JTAG = 0x0006, /* transactor serial JTAG */ + VD_BFM_SWD = 0x0007, /* transactor serial SWD */ +}; + +/** + * @brief List of signals that can be read or written by the debugger + */ +enum { + VD_SIG_TCK = 0x0001, /* JTAG clock; tclk */ + VD_SIG_TDI = 0x0002, /* JTAG TDI; tdi */ + VD_SIG_TMS = 0x0004, /* JTAG TMS; tms */ + VD_SIG_RESET = 0x0008, /* DUT reset; rst */ + VD_SIG_TRST = 0x0010, /* JTAG Reset; trstn */ + VD_SIG_TDO = 0x0020, /* JTAG TDO; tdo */ + VD_SIG_POWER = 0x0100, /* BFM power; bfm_up */ + VD_SIG_TCKDIV = 0x0200, /* JTAG clock divider; tclkdiv */ + VD_SIG_BUF = 0x1000, /* memory buffer; mem */ +}; + +/** + * @brief List of errors + */ +enum { + VD_ERR_NONE = 0x0000, /* no error */ + VD_ERR_NOT_IMPL = 0x0100, /* feature not implemented */ + VD_ERR_USAGE = 0x0101, /* incorrect usage */ + VD_ERR_PARAM = 0x0102, /* incorrect parameter */ + VD_ERR_CONFIG = 0x0107, /* incorrect configuration */ + VD_ERR_NO_MEMORY = 0x0104, /* out of memory */ + VD_ERR_SHM_OPEN = 0x010a, /* cannot open shared memory */ + VD_ERR_SHM_MAP = 0x010b, /* cannot map shared memory */ + VD_ERR_SOC_OPEN = 0x011a, /* cannot open socket */ + VD_ERR_SOC_OPT = 0x011b, /* cannot set socket option */ + VD_ERR_SOC_ADDR = 0x011c, /* cannot resolve host address */ + VD_ERR_SOC_CONN = 0x011d, /* cannot connect to host */ + VD_ERR_SOC_SEND = 0x011e, /* error sending data on socket */ + VD_ERR_SOC_RECV = 0x011f, /* error receiving data from socket */ + VD_ERR_LOCKED = 0x0202, /* device locked */ + VD_ERR_NOT_RUN = 0x0204, /* transactor not running */ + VD_ERR_NOT_OPEN = 0x0205, /* transactor not open/connected */ + VD_ERR_LICENSE = 0x0206, /* cannot check out the license */ + VD_ERR_VERSION = 0x0207, /* transactor version mismatch */ + VD_ERR_TIME_OUT = 0x0301, /* time out, waiting */ + VD_ERR_NO_POWER = 0x0302, /* power out error */ + VD_ERR_BUS_ERROR = 0x0304, /* bus protocol error, like pslverr */ + VD_ERR_NO_ACCESS = 0x0306, /* no access to an object */ + VD_ERR_INV_HANDLE = 0x0307, /* invalid object handle */ + VD_ERR_INV_SCOPE = 0x0308, /* invalid scope */ +}; + +enum { + VD_CMD_OPEN = 0x01, + VD_CMD_CLOSE = 0x02, + VD_CMD_CONNECT = 0x04, + VD_CMD_DISCONNECT = 0x05, + VD_CMD_WAIT = 0x09, + VD_CMD_SIGSET = 0x0a, + VD_CMD_SIGGET = 0x0b, + VD_CMD_JTAGCLOCK = 0x0f, + VD_CMD_JTAGSHTAP = 0x1a, + VD_CMD_MEMOPEN = 0x21, + VD_CMD_MEMCLOSE = 0x22, + VD_CMD_MEMWRITE = 0x23, +}; + +enum { + VD_BATCH_NO = 0, + VD_BATCH_WO = 1, + VD_BATCH_WR = 2, +}; + +struct vd_shm { + struct { /* VD_CHEADER_LEN written by client */ + uint8_t cmd; /* 000; command */ + uint8_t type; /* 001; interface type */ + uint16_t waddr; /* 002; write pointer */ + uint16_t wbytes; /* 004; data bytes */ + uint16_t rbytes; /* 006; data bytes to read */ + uint16_t wwords; /* 008; data words */ + uint16_t rwords; /* 00a; data words to read */ + uint32_t rwdata; /* 00c; read/write data */ + uint32_t offset; /* 010; address offset */ + uint16_t offseth; /* 014; address offset 47:32 */ + uint16_t wid; /* 016; request id*/ + }; + union { /* 018; */ + uint8_t wd8[VD_BUFFER_LEN]; + uint16_t wd16[VD_BUFFER_LEN / 2]; + uint32_t wd32[VD_BUFFER_LEN / 4]; + uint64_t wd64[VD_BUFFER_LEN / 8]; + }; + struct { /* VD_SHEADER_LEN written by server */ + uint16_t rid; /* fd0: request id read */ + uint16_t awords; /* fd2: actual data words read back */ + int32_t status; /* fd4; */ + uint64_t duttime; /* fd8; */ + }; + union { /* fe0: */ + uint8_t rd8[VD_BUFFER_LEN]; + uint16_t rd16[VD_BUFFER_LEN / 2]; + uint32_t rd32[VD_BUFFER_LEN / 4]; + uint64_t rd64[VD_BUFFER_LEN / 8]; + }; + uint32_t state; /* 1f98; connection state */ + uint32_t count; /* 1f9c; */ + uint8_t dummy[96]; /* 1fa0; 48+40B+8B; */ +}; + +struct vd_client { + uint8_t trans_batch; + bool trans_first; + bool trans_last; + uint8_t mem_ndx; + uint8_t buf_width; + uint8_t addr_bits; + uint8_t bfm_type; + uint16_t sig_read; + uint16_t sig_write; + uint32_t bfm_period; + uint32_t mem_base[VD_MAX_MEMORIES]; + uint32_t mem_size[VD_MAX_MEMORIES]; + uint32_t mem_width[VD_MAX_MEMORIES]; + uint32_t mem_depth[VD_MAX_MEMORIES]; + uint16_t server_port; + uint32_t poll_cycles; + uint32_t poll_min; + uint32_t poll_max; + uint32_t targ_time; + int hsocket; + char server_name[32]; + char bfm_path[128]; + char mem_path[VD_MAX_MEMORIES][128]; + uint8_t *tdo; +}; + +struct vd_jtag_hdr { + uint64_t tlen:24; + uint64_t post:3; + uint64_t pre:3; + uint64_t cmd:2; + uint64_t wlen:16; + uint64_t rlen:16; +}; + +static struct vd_shm *pbuf; +static struct vd_client vdc; + +static int vdebug_socket_error(void) +{ +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + +static int vdebug_socket_open(char *server_addr, uint32_t port) +{ + int hsock; + int rc = 0; + uint32_t buflen = sizeof(struct vd_shm); /* size of the send and rcv buffer */ + struct addrinfo *ainfo = NULL; + struct addrinfo ahint = { 0, AF_INET, SOCK_STREAM, 0, 0, NULL, NULL, NULL }; + +#ifdef _WIN32 + hsock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (hsock == INVALID_SOCKET) + rc = vdebug_socket_error(); +#else + uint32_t rcvwat = VD_SHEADER_LEN; /* size of the rcv header, as rcv min watermark */ + hsock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (hsock < 0) + rc = errno; + else if (setsockopt(hsock, SOL_SOCKET, SO_RCVLOWAT, &rcvwat, sizeof(rcvwat)) < 0) + rc = errno; +#endif + else if (setsockopt(hsock, SOL_SOCKET, SO_SNDBUF, (const char *)&buflen, sizeof(buflen)) < 0) + rc = vdebug_socket_error(); + else if (setsockopt(hsock, SOL_SOCKET, SO_RCVBUF, (const char *)&buflen, sizeof(buflen)) < 0) + rc = vdebug_socket_error(); + + if (rc) { + LOG_ERROR("socket_open: cannot set socket option, error %d", rc); + } else if (getaddrinfo(server_addr, NULL, &ahint, &ainfo) != 0) { + LOG_ERROR("socket_open: cannot resolve address %s, error %d", server_addr, vdebug_socket_error()); + rc = VD_ERR_SOC_ADDR; + } else { + ((struct sockaddr_in *)(ainfo->ai_addr))->sin_port = htons(port); + if (connect(hsock, ainfo->ai_addr, sizeof(struct sockaddr)) < 0) { + LOG_ERROR("socket_open: cannot connect to %s:%d, error %d", server_addr, port, vdebug_socket_error()); + rc = VD_ERR_SOC_CONN; + } + } + + if (rc) { + close_socket(hsock); + hsock = 0; + } + + if (ainfo) + freeaddrinfo(ainfo); + + return hsock; +} + +static int vdebug_socket_receive(int hsock, struct vd_shm *pmem) +{ + int rc; + int dreceived = 0; + int offset = (uint8_t *)&pmem->rid - &pmem->cmd; + int to_receive = VD_SHEADER_LEN + pmem->rbytes; + char *pb = (char *)pmem; + + do { + rc = recv(hsock, pb + offset, to_receive, 0); + if (rc <= 0) { + LOG_WARNING("socket_receive: recv failed, error %d", rc < 0 ? vdebug_socket_error() : 0); + return rc; + } + to_receive -= rc; + offset += rc; + LOG_DEBUG_IO("socket_receive: received %d, to receive %d", rc, to_receive); + dreceived += rc; + } while (to_receive); + + return dreceived; +} + +static int vdebug_socket_send(int hsock, struct vd_shm *pmem) +{ + int rc = send(hsock, (const char *)&pmem->cmd, VD_CHEADER_LEN + pmem->wbytes, 0); + if (rc <= 0) + LOG_WARNING("socket_send: send failed, error %d", vdebug_socket_error()); + else + LOG_DEBUG_IO("socket_send: sent %d, to send 0", rc); + + return rc; +} + +static uint32_t vdebug_wait_server(int hsock, struct vd_shm *pmem) +{ + if (!hsock) + return VD_ERR_SOC_OPEN; + int st = vdebug_socket_send(hsock, pmem); + if (st <= 0) + return VD_ERR_SOC_SEND; + + int rd = vdebug_socket_receive(hsock, pmem); + if (rd <= 0) + return VD_ERR_SOC_RECV; + + int rc = pmem->status; + LOG_DEBUG_IO("wait_server: cmd %02" PRIx8 " done, sent %d, rcvd %d, status %d", + pmem->cmd, st, rd, rc); + + return rc; +} + +int vdebug_run_jtag_queue(int hsock, struct vd_shm *pm, unsigned int count) +{ + uint8_t num_pre, num_post, tdi, tms; + unsigned int num, anum, bytes, hwords, words; + unsigned int req, waddr, rwords; + int64_t ts, te; + uint8_t *tdo; + int rc; + struct vd_jtag_hdr *hdr; + + req = 0; /* beginning of request */ + waddr = 0; + rwords = 0; + pm->wbytes = pm->wwords * vdc.buf_width; + pm->rbytes = pm->rwords * vdc.buf_width; + ts = timeval_ms(); + rc = vdebug_wait_server(hsock, pm); + while (!rc && (req < count)) { /* loop over requests to read data and print out */ + hdr = (struct vd_jtag_hdr *)&pm->wd8[waddr * 4]; + hwords = hdr->wlen; + words = hdr->rlen; + anum = hdr->tlen; + num_pre = hdr->pre; + num_post = hdr->post; + if (num_post) + num = anum - num_pre - num_post + 1; + else + num = anum - num_pre; + bytes = (num + 7) / 8; + vdc.trans_last = (req + 1) < count ? 0 : 1; + vdc.trans_first = waddr ? 0 : 1; + if (hdr->cmd == 3) { /* read */ + tdo = vdc.tdo; + for (unsigned int j = 0; j < bytes; j++) { + tdo[j] = (pm->rd8[rwords * 8 + j] >> num_pre) | (pm->rd8[rwords * 8 + j + 1] << (8 - num_pre)); + LOG_DEBUG_IO("%04x D0[%02x]:%02x", pm->wid - count + req, j, tdo[j]); + } + rwords += words; /* read data offset */ + } else { + tdo = NULL; + } + waddr += sizeof(struct vd_jtag_hdr) / 4; /* waddr past header */ + tdi = (pm->wd8[waddr * 4] >> num_pre) | (pm->wd8[waddr * 4 + 1] << (8 - num_pre)); + tms = (pm->wd8[waddr * 4 + 4] >> num_pre) | (pm->wd8[waddr * 4 + 4 + 1] << (8 - num_pre)); + LOG_DEBUG("%04x L:%02d O:%05x @%03x DI:%02x MS:%02x DO:%02x", + pm->wid - count + req, num, (vdc.trans_first << 14) | (vdc.trans_last << 15), + waddr - 2, tdi, tms, (tdo ? tdo[0] : 0xdd)); + waddr += hwords * 2; /* start of next request */ + req += 1; + } + + if (rc) { + LOG_ERROR("0x%x executing transaction", rc); + rc = ERROR_FAIL; + } + + te = timeval_ms(); + vdc.targ_time += (uint32_t)(te - ts); + pm->offseth = 0; /* reset buffer write address */ + pm->offset = 0; + pm->rwords = 0; + pm->waddr = 0; + + return rc; +} + +static int vdebug_open(int hsock, struct vd_shm *pm, const char *path, + uint8_t type, uint32_t period_ps, uint32_t sig_mask) +{ + int rc = VD_ERR_NOT_OPEN; + + pm->cmd = VD_CMD_OPEN; + pm->wid = VD_VERSION; /* client version */ + pm->wbytes = 0; + pm->rbytes = 0; + pm->wwords = 0; + pm->rwords = 0; + rc = vdebug_wait_server(hsock, pm); + if (rc != 0) { /* communication problem */ + LOG_ERROR("0x%x connecting to server", rc); + } else if (pm->rid < pm->wid) { + LOG_ERROR("server version %d too old for the client %d", pm->rid, pm->wid); + pm->cmd = VD_CMD_CLOSE; /* let server close the connection */ + vdebug_wait_server(hsock, pm); + rc = VD_ERR_VERSION; + } else { + pm->cmd = VD_CMD_CONNECT; + pm->type = type; /* BFM type to connect to, here JTAG */ + pm->rwdata = sig_mask | VD_SIG_BUF | (VD_SIG_BUF << 16); + pm->wbytes = strlen(path) + 1; + pm->rbytes = 12; + pm->wid = 0; /* reset wid for transaction ID */ + pm->wwords = 0; + pm->rwords = 0; + memcpy(pm->wd8, path, pm->wbytes + 1); + rc = vdebug_wait_server(hsock, pm); + vdc.sig_read = pm->rwdata >> 16; /* signal read mask */ + vdc.sig_write = pm->rwdata; /* signal write mask */ + vdc.bfm_period = period_ps; + vdc.buf_width = pm->rd32[0] / 8;/* access width in bytes */ + vdc.addr_bits = pm->rd32[2]; /* supported address bits */ + } + + if (rc) { + LOG_ERROR("0x%x connecting to BFM %s", rc, path); + return ERROR_FAIL; + } + + LOG_DEBUG("%s type %0x, period %dps, buffer %dx%dB signals r%04xw%04x", + path, type, vdc.bfm_period, VD_BUFFER_LEN / vdc.buf_width, + vdc.buf_width, vdc.sig_read, vdc.sig_write); + + return ERROR_OK; +} + +static int vdebug_close(int hsock, struct vd_shm *pm, uint8_t type) +{ + pm->cmd = VD_CMD_DISCONNECT; + pm->type = type; /* BFM type, here JTAG */ + pm->wbytes = 0; + pm->rbytes = 0; + pm->wwords = 0; + pm->rwords = 0; + vdebug_wait_server(hsock, pm); + pm->cmd = VD_CMD_CLOSE; + pm->wid = VD_VERSION; /* client version */ + pm->wbytes = 0; + pm->rbytes = 0; + pm->wwords = 0; + pm->rwords = 0; + vdebug_wait_server(hsock, pm); + LOG_DEBUG("type %0x", type); + + return ERROR_OK; +} + +static int vdebug_wait(int hsock, struct vd_shm *pm, uint32_t cycles) +{ + if (cycles) { + pm->cmd = VD_CMD_WAIT; + pm->wbytes = 0; + pm->rbytes = 0; + pm->rwdata = cycles; /* clock sycles to wait */ + int rc = vdebug_wait_server(hsock, pm); + if (rc) { + LOG_ERROR("0x%x waiting %" PRIx32 " cycles", rc, cycles); + return ERROR_FAIL; + } + LOG_DEBUG("%d cycles", cycles); + } + + return ERROR_OK; +} + +static int vdebug_sig_set(int hsock, struct vd_shm *pm, uint32_t write_mask, uint32_t value) +{ + pm->cmd = VD_CMD_SIGSET; + pm->wbytes = 0; + pm->rbytes = 0; + pm->rwdata = (write_mask << 16) | (value & 0xffff); /* mask and value of signals to set */ + int rc = vdebug_wait_server(hsock, pm); + if (rc) { + LOG_ERROR("0x%x setting signals %04" PRIx32, rc, write_mask); + return ERROR_FAIL; + } + + LOG_DEBUG("setting signals %04" PRIx32 " to %04" PRIx32, write_mask, value); + + return ERROR_OK; +} + +static int vdebug_jtag_clock(int hsock, struct vd_shm *pm, uint32_t value) +{ + pm->cmd = VD_CMD_JTAGCLOCK; + pm->wbytes = 0; + pm->rbytes = 0; + pm->rwdata = value; /* divider value */ + int rc = vdebug_wait_server(hsock, pm); + if (rc) { + LOG_ERROR("0x%x setting jtag_clock", rc); + return ERROR_FAIL; + } + + LOG_DEBUG("setting jtag clock divider to %" PRIx32, value); + + return ERROR_OK; +} + +static int vdebug_jtag_shift_tap(int hsock, struct vd_shm *pm, uint8_t num_pre, + const uint8_t tms_pre, uint32_t num, const uint8_t *tdi, + uint8_t num_post, const uint8_t tms_post, uint8_t *tdo, + uint8_t f_last) +{ + const uint32_t tobits = 8; + uint16_t bytes, hwords, anum, words, waddr; + int rc = 0; + + pm->cmd = VD_CMD_JTAGSHTAP; + vdc.trans_last = f_last || (vdc.trans_batch == VD_BATCH_NO) || tdo; + if (vdc.trans_first) + waddr = 0; /* reset buffer offset */ + else + waddr = pm->offseth; /* continue from the previous transaction */ + if (num_post) /* actual number of bits to shift */ + anum = num + num_pre + num_post - 1; + else + anum = num + num_pre; + hwords = (anum + 4 * vdc.buf_width - 1) / (4 * vdc.buf_width); /* in 4B TDI/TMS words */ + words = (hwords + 1) / 2; /* in 8B TDO words to read */ + bytes = (num + 7) / 8; /* data only portion in bytes */ + /* buffer overflow check and flush */ + if (4 * waddr + sizeof(struct vd_jtag_hdr) + 8 * hwords + 64 > VD_BUFFER_LEN) { + vdc.trans_last = 1; /* force flush within 64B of buffer end */ + } else if (4 * waddr + sizeof(struct vd_jtag_hdr) + 8 * hwords > VD_BUFFER_LEN) { + /* this req does not fit, discard it */ + LOG_ERROR("%04x L:%02d O:%05x @%04x too many bits to shift", + pm->wid, anum, (vdc.trans_first << 14) | (vdc.trans_last << 15), waddr); + rc = ERROR_FAIL; + } + + if (!rc && anum) { + uint16_t i, j; + struct vd_jtag_hdr *hdr = (struct vd_jtag_hdr *)&pm->wd8[4 * waddr]; /* 8 bytes header */ + hdr->cmd = (tdo ? 3 : 1); /* R and W bits */ + hdr->pre = num_pre; + hdr->post = num_post; + hdr->tlen = anum; + hdr->wlen = hwords; + hdr->rlen = words; + pm->wid++; /* transaction ID */ + waddr += 2; /* waddr past header */ + /* TDI/TMS data follows as 32 bit word pairs {TMS,TDI} */ + pm->wd8[4 * waddr] = (tdi ? (tdi[0] << num_pre) : 0); + pm->wd8[4 * waddr + 4] = tms_pre; /* init with tms_pre */ + if (num + num_pre <= 8) /* and tms_post for num <=4 */ + pm->wd8[4 * waddr + 4] |= (tms_post << (num + num_pre - 1)); + for (i = 1, j = 4 * waddr; i < bytes; i++) { + if (i == bytes - 1 && num + num_pre <= bytes * tobits) + pm->wd8[j + i + 4] = tms_post << ((num + num_pre - 1) % 8); + else + pm->wd8[j + i + 4] = 0x0;/* placing 4 bytes of TMS bits into high word */ + if (!tdi) /* placing 4 bytes of TDI bits into low word */ + pm->wd8[j + i] = 0x0; + else + pm->wd8[j + i] = (tdi[i] << num_pre) | (tdi[i - 1] >> (8 - num_pre)); + if (i % 4 == 3) + j += 4; + } + + if (tdi) + if (num + num_pre > bytes * tobits) /* in case 1 additional byte needed for TDI */ + pm->wd8[j + i] = (tdi[i - 1] >> (8 - num_pre)); /* put last TDI bits there */ + + if (num + num_pre <= bytes * tobits) { /* in case no or 1 additional byte needed */ + pm->wd8[j + i + 4] = tms_post >> (8 - (num + num_pre - 1) % 8); /* may need to add higher part */ + /* in case exactly 1 additional byte needed */ + } else if (num + num_pre > bytes * tobits && anum <= (bytes + 1) * tobits) { + pm->wd8[j + i + 4] = tms_post << ((num + num_pre - 1) % 8); /* add whole tms_post */ + } else { /* in case 2 additional bytes, tms_post split */ + pm->wd8[j + i + 4] = tms_post << ((num + num_pre - 1) % 8);/* add lower part of tms_post */ + if (i % 4 == 3) /* next byte is in the next 32b word */ + pm->wd8[j + i + 4 + 5] = tms_post >> (8 - (num + num_pre - 1) % 8); /* and higher part */ + else /* next byte is in the same 32b word */ + pm->wd8[j + i + 4 + 1] = tms_post >> (8 - (num + num_pre - 1) % 8); /* and higher part */ + } + + if (tdo) { + pm->rwords += words; /* keep track of the words to read */ + vdc.tdo = tdo; + } + pm->wwords = waddr / 2 + hwords; /* payload size *2 to include both TDI and TMS data */ + pm->waddr++; + } + + if (!waddr) /* flush issued, but buffer empty */ + ; + else if (!vdc.trans_last) /* buffered request */ + pm->offseth = waddr + hwords * 2; /* offset for next transaction, must be even */ + else /* execute batch of requests */ + rc = vdebug_run_jtag_queue(hsock, pm, pm->waddr); + vdc.trans_first = vdc.trans_last; /* flush forces trans_first flag */ + + return rc; +} + +static int vdebug_mem_open(int hsock, struct vd_shm *pm, const char *path, uint8_t ndx) +{ + int rc; + + if (!path) + return ERROR_OK; + + pm->cmd = VD_CMD_MEMOPEN; + pm->wbytes = strlen(path) + 1; /* includes terminating 0 */ + pm->rbytes = 8; + pm->wwords = 0; + pm->rwords = 0; + memcpy(pm->wd8, path, pm->wbytes); + rc = vdebug_wait_server(hsock, pm); + if (rc) { + LOG_ERROR("0x%x opening memory %s", rc, path); + } else if (ndx != pm->rd16[1]) { + LOG_WARNING("Invalid memory index %" PRIu16 " returned. Direct memory access disabled", pm->rd16[1]); + } else { + vdc.mem_width[ndx] = pm->rd16[0] / 8; /* memory width in bytes */ + vdc.mem_depth[ndx] = pm->rd32[1]; /* memory depth in words */ + LOG_DEBUG("%" PRIx8 ": %s memory %" PRIu32 "x%" PRIu32 "B, buffer %" PRIu32 "x%" PRIu32 "B", ndx, path, + vdc.mem_depth[ndx], vdc.mem_width[ndx], VD_BUFFER_LEN / vdc.mem_width[ndx], vdc.mem_width[ndx]); + } + + return ERROR_OK; +} + +static void vdebug_mem_close(int hsock, struct vd_shm *pm, uint8_t ndx) +{ + pm->cmd = VD_CMD_MEMCLOSE; + pm->rwdata = ndx; /* which memory */ + pm->wbytes = 0; + pm->rbytes = 0; + pm->wwords = 0; + pm->rwords = 0; + vdebug_wait_server(hsock, pm); + LOG_DEBUG("%" PRIx8 ": %s", ndx, vdc.mem_path[ndx]); +} + +static int vdebug_init(void) +{ + vdc.hsocket = vdebug_socket_open(vdc.server_name, vdc.server_port); + pbuf = calloc(1, sizeof(struct vd_shm)); + if (!pbuf) { + close_socket(vdc.hsocket); + vdc.hsocket = 0; + LOG_ERROR("cannot allocate %lu bytes", sizeof(struct vd_shm)); + return ERROR_FAIL; + } + if (vdc.hsocket <= 0) { + free(pbuf); + pbuf = NULL; + LOG_ERROR("cannot connect to vdebug server %s:%" PRIu16, + vdc.server_name, vdc.server_port); + return ERROR_FAIL; + } + vdc.trans_first = 1; + vdc.poll_cycles = vdc.poll_max; + uint32_t sig_mask = VD_SIG_RESET | VD_SIG_TRST | VD_SIG_TCKDIV; + int rc = vdebug_open(vdc.hsocket, pbuf, vdc.bfm_path, vdc.bfm_type, vdc.bfm_period, sig_mask); + if (rc != 0) { + LOG_ERROR("cannot connect to %s, rc 0x%x", vdc.bfm_path, rc); + close_socket(vdc.hsocket); + vdc.hsocket = 0; + free(pbuf); + pbuf = NULL; + } else { + for (uint8_t i = 0; i < vdc.mem_ndx; i++) { + rc = vdebug_mem_open(vdc.hsocket, pbuf, vdc.mem_path[i], i); + if (rc != 0) + LOG_ERROR("cannot connect to %s, rc 0x%x", vdc.mem_path[i], rc); + } + + LOG_INFO("vdebug %d connected to %s through %s:%" PRIu16, + VD_VERSION, vdc.bfm_path, vdc.server_name, vdc.server_port); + } + + return rc; +} + +static int vdebug_quit(void) +{ + for (uint8_t i = 0; i < vdc.mem_ndx; i++) + if (vdc.mem_width[i]) + vdebug_mem_close(vdc.hsocket, pbuf, i); + int rc = vdebug_close(vdc.hsocket, pbuf, vdc.bfm_type); + LOG_INFO("vdebug %d disconnected from %s through %s:%" PRIu16 " rc:%d", VD_VERSION, + vdc.bfm_path, vdc.server_name, vdc.server_port, rc); + if (vdc.hsocket) + close_socket(vdc.hsocket); + free(pbuf); + pbuf = NULL; + + return ERROR_OK; +} + +static int vdebug_reset(int trst, int srst) +{ + uint16_t sig_val = 0xffff; + uint16_t sig_mask = 0; + + sig_mask |= VD_SIG_RESET; + if (srst) + sig_val &= ~VD_SIG_RESET;/* active low */ + if (transport_is_jtag()) { + sig_mask |= VD_SIG_TRST; + if (trst) + sig_val &= ~VD_SIG_TRST; /* active low */ + } + + LOG_INFO("rst trst:%d srst:%d mask:%" PRIx16 " val:%" PRIx16, trst, srst, sig_mask, sig_val); + int rc = vdebug_sig_set(vdc.hsocket, pbuf, sig_mask, sig_val); + if (rc == 0) + rc = vdebug_wait(vdc.hsocket, pbuf, 20); /* 20 clock cycles pulse */ + + return rc; +} + +static int vdebug_jtag_tms_seq(const uint8_t *tms, int num, uint8_t f_flush) +{ + LOG_INFO("tms len:%d tms:%x", num, *(const uint32_t *)tms); + + return vdebug_jtag_shift_tap(vdc.hsocket, pbuf, num, *tms, 0, NULL, 0, 0, NULL, f_flush); +} + +static int vdebug_jtag_path_move(struct pathmove_command *cmd, uint8_t f_flush) +{ + uint8_t tms[DIV_ROUND_UP(cmd->num_states, 8)]; + LOG_INFO("path num states %d", cmd->num_states); + + memset(tms, 0, DIV_ROUND_UP(cmd->num_states, 8)); + + for (uint8_t i = 0; i < cmd->num_states; i++) { + if (tap_state_transition(tap_get_state(), true) == cmd->path[i]) + buf_set_u32(tms, i, 1, 1); + tap_set_state(cmd->path[i]); + } + + return vdebug_jtag_tms_seq(tms, cmd->num_states, f_flush); +} + +static int vdebug_jtag_tlr(tap_state_t state, uint8_t f_flush) +{ + int rc = ERROR_OK; + + uint8_t cur = tap_get_state(); + uint8_t tms_pre = tap_get_tms_path(cur, state); + uint8_t num_pre = tap_get_tms_path_len(cur, state); + LOG_INFO("tlr from %" PRIx8 " to %" PRIx8, cur, state); + if (cur != state) { + rc = vdebug_jtag_shift_tap(vdc.hsocket, pbuf, num_pre, tms_pre, 0, NULL, 0, 0, NULL, f_flush); + tap_set_state(state); + } + + return rc; +} + +static int vdebug_jtag_scan(struct scan_command *cmd, uint8_t f_flush) +{ + int rc = ERROR_OK; + + uint8_t cur = tap_get_state(); + uint8_t state = cmd->ir_scan ? TAP_IRSHIFT : TAP_DRSHIFT; + uint8_t tms_pre = tap_get_tms_path(cur, state); + uint8_t num_pre = tap_get_tms_path_len(cur, state); + uint8_t tms_post = tap_get_tms_path(state, cmd->end_state); + uint8_t num_post = tap_get_tms_path_len(state, cmd->end_state); + int num_bits = jtag_scan_size(cmd); + LOG_DEBUG("scan len:%d fields:%d ir/!dr:%d state cur:%x end:%x", + num_bits, cmd->num_fields, cmd->ir_scan, cur, cmd->end_state); + for (int i = 0; i < cmd->num_fields; i++) { + uint8_t cur_num_pre = i == 0 ? num_pre : 0; + uint8_t cur_tms_pre = i == 0 ? tms_pre : 0; + uint8_t cur_num_post = i == cmd->num_fields - 1 ? num_post : 0; + uint8_t cur_tms_post = i == cmd->num_fields - 1 ? tms_post : 0; + uint8_t cur_flush = i == cmd->num_fields - 1 ? f_flush : 0; + rc = vdebug_jtag_shift_tap(vdc.hsocket, pbuf, cur_num_pre, cur_tms_pre, + cmd->fields[i].num_bits, cmd->fields[i].out_value, cur_num_post, cur_tms_post, + cmd->fields[i].in_value, cur_flush); + if (rc) + break; + } + + if (cur != cmd->end_state) + tap_set_state(cmd->end_state); + + return rc; +} + +static int vdebug_jtag_runtest(int cycles, tap_state_t state, uint8_t f_flush) +{ + uint8_t cur = tap_get_state(); + uint8_t tms_pre = tap_get_tms_path(cur, state); + uint8_t num_pre = tap_get_tms_path_len(cur, state); + LOG_DEBUG("idle len:%d state cur:%x end:%x", cycles, cur, state); + int rc = vdebug_jtag_shift_tap(vdc.hsocket, pbuf, num_pre, tms_pre, cycles, NULL, 0, 0, NULL, f_flush); + if (cur != state) + tap_set_state(state); + + return rc; +} + +static int vdebug_jtag_stableclocks(int num, uint8_t f_flush) +{ + LOG_INFO("stab len:%d state cur:%x", num, tap_get_state()); + + return vdebug_jtag_shift_tap(vdc.hsocket, pbuf, 0, 0, num, NULL, 0, 0, NULL, f_flush); +} + +static int vdebug_sleep(int us) +{ + LOG_INFO("sleep %d us", us); + + return vdebug_wait(vdc.hsocket, pbuf, us / 1000); +} + +static int vdebug_jtag_speed(int speed) +{ + unsigned int clkmax = VD_SCALE_PSTOMS / (vdc.bfm_period * 2); /* kHz */ + unsigned int divval = clkmax / speed; + LOG_INFO("jclk speed:%d kHz set, BFM divider %u", speed, divval); + + return vdebug_jtag_clock(vdc.hsocket, pbuf, divval); +} + +static int vdebug_jtag_khz(int khz, int *jtag_speed) +{ + unsigned int clkmax = VD_SCALE_PSTOMS / (vdc.bfm_period * 2); /* kHz */ + unsigned int divval = khz ? clkmax / khz : 1; + *jtag_speed = clkmax / divval; + LOG_DEBUG("khz speed:%d from khz:%d", *jtag_speed, khz); + + return ERROR_OK; +} + +static int vdebug_jtag_div(int speed, int *khz) +{ + *khz = speed; + LOG_DEBUG("div khz:%d from speed:%d", *khz, speed); + + return ERROR_OK; +} + +static int vdebug_jtag_execute_queue(void) +{ + int rc = ERROR_OK; + + for (struct jtag_command *cmd = jtag_command_queue; rc == ERROR_OK && cmd; cmd = cmd->next) { + switch (cmd->type) { + case JTAG_RUNTEST: + rc = vdebug_jtag_runtest(cmd->cmd.runtest->num_cycles, cmd->cmd.runtest->end_state, !cmd->next); + break; + case JTAG_STABLECLOCKS: + rc = vdebug_jtag_stableclocks(cmd->cmd.stableclocks->num_cycles, !cmd->next); + break; + case JTAG_TLR_RESET: + rc = vdebug_jtag_tlr(cmd->cmd.statemove->end_state, !cmd->next); + break; + case JTAG_PATHMOVE: + rc = vdebug_jtag_path_move(cmd->cmd.pathmove, !cmd->next); + break; + case JTAG_TMS: + rc = vdebug_jtag_tms_seq(cmd->cmd.tms->bits, cmd->cmd.tms->num_bits, !cmd->next); + break; + case JTAG_SLEEP: + rc = vdebug_sleep(cmd->cmd.sleep->us); + break; + case JTAG_SCAN: + rc = vdebug_jtag_scan(cmd->cmd.scan, !cmd->next); + break; + default: + LOG_ERROR("Unknown JTAG command type 0x%x encountered", cmd->type); + rc = ERROR_FAIL; + } + } + + return rc; +} + +COMMAND_HANDLER(vdebug_set_server) +{ + if ((CMD_ARGC != 1) || !strchr(CMD_ARGV[0], ':')) + return ERROR_COMMAND_SYNTAX_ERROR; + + char *pchar = strchr(CMD_ARGV[0], ':'); + *pchar = '\0'; + strncpy(vdc.server_name, CMD_ARGV[0], sizeof(vdc.server_name) - 1); + int port = atoi(++pchar); + if (port < 0 || port > UINT16_MAX) { + LOG_ERROR("invalid port number %d specified", port); + return ERROR_COMMAND_SYNTAX_ERROR; + } + vdc.server_port = port; + LOG_DEBUG("server: %s port %u", vdc.server_name, vdc.server_port); + + return ERROR_OK; +} + +COMMAND_HANDLER(vdebug_set_bfm) +{ + char prefix; + + if ((CMD_ARGC != 2) || (sscanf(CMD_ARGV[1], "%u%c", &vdc.bfm_period, &prefix) != 2)) + return ERROR_COMMAND_SYNTAX_ERROR; + + strncpy(vdc.bfm_path, CMD_ARGV[0], sizeof(vdc.bfm_path) - 1); + switch (prefix) { + case 'u': + vdc.bfm_period *= 1000000; + break; + case 'n': + vdc.bfm_period *= 1000; + break; + case 'p': + default: + break; + } + vdc.bfm_type = VD_BFM_JTAG; + LOG_DEBUG("bfm_path: %s clk_period %ups", vdc.bfm_path, vdc.bfm_period); + + return ERROR_OK; +} + +COMMAND_HANDLER(vdebug_set_mem) +{ + if (CMD_ARGC != 3) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (vdc.mem_ndx >= VD_MAX_MEMORIES) { + LOG_ERROR("mem_path declared more than %d allowed times", VD_MAX_MEMORIES); + return ERROR_FAIL; + } + + strncpy(vdc.mem_path[vdc.mem_ndx], CMD_ARGV[0], sizeof(vdc.mem_path[vdc.mem_ndx]) - 1); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], vdc.mem_base[vdc.mem_ndx]); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], vdc.mem_size[vdc.mem_ndx]); + LOG_DEBUG("mem_path: set %s @ 0x%08x+0x%08x", vdc.mem_path[vdc.mem_ndx], + vdc.mem_base[vdc.mem_ndx], vdc.mem_size[vdc.mem_ndx]); + vdc.mem_ndx++; + + return ERROR_OK; +} + +COMMAND_HANDLER(vdebug_set_batching) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (isdigit((unsigned char)CMD_ARGV[0][0])) + vdc.trans_batch = (CMD_ARGV[0][0] == '0' ? 0 : (CMD_ARGV[0][0] == '1' ? 1 : 2)); + else if (CMD_ARGV[0][0] == 'r') + vdc.trans_batch = VD_BATCH_WR; + else if (CMD_ARGV[0][0] == 'w') + vdc.trans_batch = VD_BATCH_WO; + else + vdc.trans_batch = VD_BATCH_NO; + LOG_DEBUG("batching: set to %u", vdc.trans_batch); + + return ERROR_OK; +} + +COMMAND_HANDLER(vdebug_set_polling) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + vdc.poll_min = atoi(CMD_ARGV[0]); + vdc.poll_max = atoi(CMD_ARGV[1]); + LOG_DEBUG("polling: set min %u max %u", vdc.poll_min, vdc.poll_max); + + return ERROR_OK; +} + +static const struct command_registration vdebug_command_handlers[] = { + { + .name = "server", + .handler = &vdebug_set_server, + .mode = COMMAND_CONFIG, + .help = "set the vdebug server name or address", + .usage = "", + }, + { + .name = "bfm_path", + .handler = &vdebug_set_bfm, + .mode = COMMAND_CONFIG, + .help = "set the vdebug BFM hierarchical path", + .usage = " ", + }, + { + .name = "mem_path", + .handler = &vdebug_set_mem, + .mode = COMMAND_ANY, + .help = "set the design memory for the code load", + .usage = " ", + }, + { + .name = "batching", + .handler = &vdebug_set_batching, + .mode = COMMAND_CONFIG, + .help = "set the transaction batching no|wr|rd [0|1|2]", + .usage = "", + }, + { + .name = "polling", + .handler = &vdebug_set_polling, + .mode = COMMAND_CONFIG, + .help = "set the polling pause, executing hardware cycles between min and max", + .usage = " ", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration vdebug_command[] = { + { + .name = "vdebug", + .chain = vdebug_command_handlers, + .mode = COMMAND_ANY, + .help = "vdebug command group", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +static struct jtag_interface vdebug_jtag_ops = { + .supported = DEBUG_CAP_TMS_SEQ, + .execute_queue = vdebug_jtag_execute_queue, +}; + +struct adapter_driver vdebug_adapter_driver = { + .name = "vdebug", + .transports = jtag_only, + .speed = vdebug_jtag_speed, + .khz = vdebug_jtag_khz, + .speed_div = vdebug_jtag_div, + .commands = vdebug_command, + .init = vdebug_init, + .quit = vdebug_quit, + .reset = vdebug_reset, + .jtag_ops = &vdebug_jtag_ops, +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 63faa9561..ddf70cc24 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -57,6 +57,9 @@ extern struct adapter_driver usb_blaster_adapter_driver; #if BUILD_JTAG_VPI == 1 extern struct adapter_driver jtag_vpi_adapter_driver; #endif +#if BUILD_VDEBUG == 1 +extern struct adapter_driver vdebug_adapter_driver; +#endif #if BUILD_JTAG_DPI == 1 extern struct adapter_driver jtag_dpi_adapter_driver; #endif @@ -168,6 +171,9 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_JTAG_VPI == 1 &jtag_vpi_adapter_driver, #endif +#if BUILD_VDEBUG == 1 + &vdebug_adapter_driver, +#endif #if BUILD_JTAG_DPI == 1 &jtag_dpi_adapter_driver, #endif diff --git a/tcl/board/vd_a53x2_jtag.cfg b/tcl/board/vd_a53x2_jtag.cfg new file mode 100644 index 000000000..869bc4db0 --- /dev/null +++ b/tcl/board/vd_a53x2_jtag.cfg @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# Arm Cortex A53x2 through JTAG + +source [find interface/vdebug.cfg] + +set _CORES 2 +set _CHIPNAME a53 +set _MEMSTART 0x00000000 +set _MEMSIZE 0x1000000 +set _CPUTAPID 0x5ba00477 + +# vdebug select transport +#transport select jtag + +# JTAG reset config, frequency and reset delay +reset_config trst_and_srst +adapter speed 50000 +adapter srst delay 5 + +# BFM hierarchical path and input clk period +vdebug bfm_path tbench.u_vd_jtag_bfm 10ns + +# DMA Memories to access backdoor (up to 4) +vdebug mem_path tbench.u_memory.mem_array $_MEMSTART $_MEMSIZE + +jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID + +jtag arp_init-reset + +source [find target/vd_aarch64.cfg] diff --git a/tcl/board/vd_m4_jtag.cfg b/tcl/board/vd_m4_jtag.cfg new file mode 100644 index 000000000..ca21476d2 --- /dev/null +++ b/tcl/board/vd_m4_jtag.cfg @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# Arm Cortex m4 through JTAG + +source [find interface/vdebug.cfg] + +set _CHIPNAME m4 +set _MEMSTART 0x00000000 +set _MEMSIZE 0x10000 +set _CPUTAPID 0x4ba00477 + +# vdebug select transport +#transport select jtag + +# JTAG reset config, frequency and reset delay +reset_config trst_and_srst +adapter speed 25000 +adapter srst delay 5 + +# BFM hierarchical path and input clk period +vdebug bfm_path tbench.u_vd_jtag_bfm 20ns + +# DMA Memories to access backdoor (up to 4) +vdebug mem_path tbench.u_mcu.u_sys.u_rom.rom $_MEMSTART $_MEMSIZE + +jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID + +jtag arp_init-reset + +source [find target/vd_cortex_m.cfg] diff --git a/tcl/board/vd_pulpissimo_jtag.cfg b/tcl/board/vd_pulpissimo_jtag.cfg new file mode 100644 index 000000000..69dd9e6db --- /dev/null +++ b/tcl/board/vd_pulpissimo_jtag.cfg @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# RISCV Ibex core with Pulpissimo through JTAG + +source [find interface/vdebug.cfg] + +set _CHIPNAME ibex +set _HARTID 0x20 +set _CPUTAPID 0x249511c3 + +# vdebug select transport +#transport select jtag + +# JTAG reset config, frequency and reset delay +reset_config trst_and_srst +adapter speed 12500 +adapter srst delay 10 + +# BFM hierarchical path and input clk period +vdebug bfm_path tbench.u_vd_jtag_bfm 40ns + +# DMA Memories to access backdoor (up to 4) +vdebug mem_path tbench.soc_domain_i.pulp_soc_i.gen_mem_l2_pri\[0\].sram_i.mem_array 0x1c000000 0x8000 +vdebug mem_path tbench.soc_domain_i.pulp_soc_i.gen_mem_l2_pri\[1\].sram_i.mem_array 0x1c008000 0x8000 +vdebug mem_path tbench.soc_domain_i.pulp_soc_i.gen_mem_l2\[0\].sram_i.mem_array 0x1c010000 0x80000 + +# need to explicitly define riscv tap, autoprobing does not work for icapture != 0x01 +jtag newtap $_CHIPNAME cpu -irlen 5 -ircapture 0x05 -irmask 0x1f -expected-id $_CPUTAPID + +jtag arp_init-reset + +source [find target/vd_riscv.cfg] diff --git a/tcl/board/vd_swerv_jtag.cfg b/tcl/board/vd_swerv_jtag.cfg new file mode 100644 index 000000000..ff6c6835f --- /dev/null +++ b/tcl/board/vd_swerv_jtag.cfg @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# RISCV swerv core with Swerv through JTAG + +source [find interface/vdebug.cfg] + +set _CHIPNAME rv32 +set _HARTID 0x00 +set _CPUTAPID 0x1000008b +set _MEMSTART 0x00000000 +set _MEMSIZE 0x10000 + +# vdebug select transport +#transport select jtag + +# JTAG reset config, frequency and reset delay +reset_config trst_and_srst +adapter speed 50000 +adapter srst delay 5 + +# BFM hierarchical path and input clk period +vdebug bfm_path tbench.u_vd_jtag_bfm 10ns + +# DMA Memories to access backdoor (up to 4) +vdebug mem_path tbench.i_ahb_ic.mem $_MEMSTART $_MEMSIZE + +# need to explicitly define riscv tap, autoprobing does not work for icapture != 0x01 +jtag newtap $_CHIPNAME cpu -irlen 5 -ircapture 0x01 -irmask 0x1f -expected-id $_CPUTAPID + +jtag arp_init-reset + +source [find target/vd_riscv.cfg] diff --git a/tcl/interface/vdebug.cfg b/tcl/interface/vdebug.cfg new file mode 100644 index 000000000..9cca6aaab --- /dev/null +++ b/tcl/interface/vdebug.cfg @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface + +if { [info exists VDEBUGHOST] } { + set _VDEBUGHOST $VDEBUGHOST +} else { + set _VDEBUGHOST localhost +} +if { [info exists VDEBUGPORT] } { + set _VDEBUGPORT $VDEBUGPORT +} else { + set _VDEBUGPORT 8192 +} + +adapter driver vdebug +# vdebug server:port +vdebug server $_VDEBUGHOST:$_VDEBUGPORT + +# example config debug level and log +#debug_level 3 +#log_output vd_ocd.log + +# example config listen on all interfaces, disable tcl/telnet server +bindto 0.0.0.0 +#gdb_port 3333 +#telnet_port disabled +tcl_port disabled + +# transaction batching: 0 - no batching, 1 - (default) wr, 2 - rw +vdebug batching 1 + +# Polling values +vdebug polling 100 1000 \ No newline at end of file diff --git a/tcl/target/vd_aarch64.cfg b/tcl/target/vd_aarch64.cfg new file mode 100644 index 000000000..619134aa6 --- /dev/null +++ b/tcl/target/vd_aarch64.cfg @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# Arm v8 64b Cortex A + +if {![info exists _CORES]} { + set _CORES 1 +} +if {![info exists _CHIPNAME]} { + set _CHIPNAME aarch64 +} +set _TARGETNAME $_CHIPNAME.cpu +set _CTINAME $_CHIPNAME.cti + +set DBGBASE {0x80810000 0x80910000} +set CTIBASE {0x80820000 0x80920000} + +dap create $_CHIPNAME.dap -chain-position $_TARGETNAME +$_CHIPNAME.dap apsel 1 + +for { set _core 0 } { $_core < $_CORES } { incr _core } \ +{ + cti create $_CTINAME.$_core -dap $_CHIPNAME.dap -ap-num 1 -baseaddr [lindex $CTIBASE $_core] + set _command "target create $_TARGETNAME.$_core aarch64 -dap $_CHIPNAME.dap \ + -dbgbase [lindex $DBGBASE $_core] -cti $_CTINAME.$_core -coreid $_core" + if { $_core != 0 } { + # non-boot core examination may fail + set _command "$_command -defer-examine" + set _smp_command "$_smp_command $_TARGETNAME.$_core" + } else { + set _smp_command "target smp $_TARGETNAME.$_core" + } + eval $_command +} +eval $_smp_command + +# default target is core 0 +targets $_TARGETNAME.0 diff --git a/tcl/target/vd_cortex_m.cfg b/tcl/target/vd_cortex_m.cfg new file mode 100644 index 000000000..4d7b0df26 --- /dev/null +++ b/tcl/target/vd_cortex_m.cfg @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# ARM Cortex M + +if {![info exists _CHIPNAME]} { + set _CHIPNAME cortex_m +} +set _TARGETNAME $_CHIPNAME.cpu + +dap create $_CHIPNAME.dap -chain-position $_TARGETNAME + +target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap diff --git a/tcl/target/vd_riscv.cfg b/tcl/target/vd_riscv.cfg new file mode 100644 index 000000000..b42b25a3a --- /dev/null +++ b/tcl/target/vd_riscv.cfg @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# RISCV core + +if {![info exists _HARTID]} { + set _HARTID 0x00 +} +if {![info exists _CHIPNAME]} { + set _CHIPNAME riscv +} +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME riscv -chain-position $_TARGETNAME -coreid $_HARTID + +riscv set_reset_timeout_sec 120 +riscv set_command_timeout_sec 120 +# prefer to use sba for system bus access +riscv set_prefer_sba on -- 2.30.2