ARM|Driver: Add DPI Driver for emulation

This driver provides support for Cadence JTAG BFM

The "jtag_dpi" driver implements 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 interface of a hardware model
written in SystemVerilog, for example, on an emulation model of
target hardware.

Tested on Ampere emulation with Altra and Altra Max models

Change-Id: Iaef8ba5cc1398ee2c888f39a606e8cb592484625
Signed-off-by: Kevin Burke <kevinb@os.amperecomputing.com>
Signed-off-by: Daniel Goehring <dgoehrin@os.amperecomputing.com>
Reviewed-on: http://openocd.zylin.com/5573
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
Kevin Burke 2020-04-03 20:47:50 -04:00 committed by Antonio Borneo
parent 87b95ab212
commit cbbec2dce5
6 changed files with 490 additions and 0 deletions

View File

@ -267,6 +267,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([jtag_dpi],
AS_HELP_STRING([--enable-jtag_dpi], [Enable building support for JTAG DPI]),
[build_jtag_dpi=$enableval], [build_jtag_dpi=no])
AC_ARG_ENABLE([amtjtagaccel],
AS_HELP_STRING([--enable-amtjtagaccel], [Enable building the Amontec JTAG-Accelerator driver]),
[build_amtjtagaccel=$enableval], [build_amtjtagaccel=no])
@ -569,6 +573,13 @@ 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_jtag_dpi" = "xyes"], [
AC_DEFINE([BUILD_JTAG_DPI], [1], [1 if you want JTAG DPI.])
], [
AC_DEFINE([BUILD_JTAG_DPI], [0], [0 if you don't want JTAG DPI.])
])
AS_IF([test "x$build_amtjtagaccel" = "xyes"], [
AC_DEFINE([BUILD_AMTJTAGACCEL], [1], [1 if you want the Amontec JTAG-Accelerator driver.])
], [
@ -746,6 +757,7 @@ 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([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"])

View File

@ -615,6 +615,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{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
interface of a hardware model written in SystemVerilog, for example, on an
emulation model of target hardware.
@item @b{xlnx_pcie_xvc}
@* A JTAG driver exposing Xilinx Virtual Cable over PCI Express to OpenOCD as JTAG/SWD interface.
@ -3235,6 +3241,22 @@ This value is only used with the standard variant.
@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
DPI server interface.
@deffn {Config Command} {jtag_dpi_set_port} port
Specifies the TCP/IP port number of the SystemVerilog DPI server interface.
@end deffn
@deffn {Config Command} {jtag_dpi_set_address} address
Specifies the TCP/IP address of the SystemVerilog DPI server interface.
@end deffn
@end deffn
@section Transport Configuration
@cindex Transport
As noted earlier, depending on the version of OpenOCD you use,

View File

@ -82,6 +82,9 @@ endif
if JTAG_VPI
DRIVERFILES += %D%/jtag_vpi.c
endif
if JTAG_DPI
DRIVERFILES += %D%/jtag_dpi.c
endif
if USB_BLASTER_DRIVER
%C%_libocdjtagdrivers_la_LIBADD += %D%/usb_blaster/libocdusbblaster.la
include %D%/usb_blaster/Makefile.am

407
src/jtag/drivers/jtag_dpi.c Normal file
View File

@ -0,0 +1,407 @@
/*
* JTAG to DPI driver
*
* Copyright (C) 2013 Franck Jullien, <elec4fun@gmail.com>
*
* Copyright (C) 2019-2020, Ampere Computing LLC
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <jtag/interface.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifndef _WIN32
#include <netinet/tcp.h>
#endif
#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT 5555
static uint16_t server_port = SERVER_PORT;
static char *server_address;
static int sockfd;
static struct sockaddr_in serv_addr;
static uint8_t *last_ir_buf;
static int last_ir_num_bits;
static int write_sock(char *buf, size_t len)
{
if (buf == NULL) {
LOG_ERROR("%s: NULL 'buf' argument, file %s, line %d",
__func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
if (write(sockfd, buf, len) != (ssize_t)len) {
LOG_ERROR("%s: %s, file %s, line %d", __func__,
strerror(errno), __FILE__, __LINE__);
return ERROR_FAIL;
}
return ERROR_OK;
}
static int read_sock(char *buf, size_t len)
{
if (buf == NULL) {
LOG_ERROR("%s: NULL 'buf' argument, file %s, line %d",
__func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
if (read(sockfd, buf, len) != (ssize_t)len) {
LOG_ERROR("%s: %s, file %s, line %d", __func__,
strerror(errno), __FILE__, __LINE__);
return ERROR_FAIL;
}
return ERROR_OK;
}
/**
* jtag_dpi_reset - ask to reset the JTAG device
* @trst: 1 if TRST is to be asserted
* @srst: 1 if SRST is to be asserted
*/
static int jtag_dpi_reset(int trst, int srst)
{
char *buf = "reset\n";
int ret = ERROR_OK;
LOG_DEBUG_IO("JTAG DRIVER DEBUG: reset trst: %i srst %i", trst, srst);
if (trst == 1) {
/* reset the JTAG TAP controller */
ret = write_sock(buf, strlen(buf));
if (ret != ERROR_OK) {
LOG_ERROR("write_sock() fail, file %s, line %d",
__FILE__, __LINE__);
}
}
if (srst == 1) {
/* System target reset not supported */
LOG_ERROR("DPI SRST not supported");
ret = ERROR_FAIL;
}
return ret;
}
/**
* jtag_dpi_scan - launches a DR-scan or IR-scan
* @cmd: the command to launch
*
* Launch a JTAG IR-scan or DR-scan
*
* Returns ERROR_OK if OK, ERROR_xxx if a read/write error occured.
*/
static int jtag_dpi_scan(struct scan_command *cmd)
{
char buf[20];
uint8_t *data_buf;
int num_bits, bytes;
int ret = ERROR_OK;
num_bits = jtag_build_buffer(cmd, &data_buf);
if (data_buf == NULL) {
LOG_ERROR("jtag_build_buffer call failed, data_buf == NULL, "
"file %s, line %d", __FILE__, __LINE__);
return ERROR_FAIL;
}
bytes = DIV_ROUND_UP(num_bits, 8);
if (cmd->ir_scan) {
free(last_ir_buf);
last_ir_buf = (uint8_t *)malloc(bytes * sizeof(uint8_t));
if (last_ir_buf == NULL) {
LOG_ERROR("%s: malloc fail, file %s, line %d",
__func__, __FILE__, __LINE__);
ret = ERROR_FAIL;
goto out;
}
memcpy(last_ir_buf, data_buf, bytes);
last_ir_num_bits = num_bits;
}
snprintf(buf, sizeof(buf), "%s %d\n", cmd->ir_scan ? "ib" : "db", num_bits);
ret = write_sock(buf, strlen(buf));
if (ret != ERROR_OK) {
LOG_ERROR("write_sock() fail, file %s, line %d",
__FILE__, __LINE__);
goto out;
}
ret = write_sock((char *)data_buf, bytes);
if (ret != ERROR_OK) {
LOG_ERROR("write_sock() fail, file %s, line %d",
__FILE__, __LINE__);
goto out;
}
ret = read_sock((char *)data_buf, bytes);
if (ret != ERROR_OK) {
LOG_ERROR("read_sock() fail, file %s, line %d",
__FILE__, __LINE__);
goto out;
}
ret = jtag_read_buffer(data_buf, cmd);
if (ret != ERROR_OK) {
LOG_ERROR("jtag_read_buffer() fail, file %s, line %d",
__FILE__, __LINE__);
goto out;
}
out:
free(data_buf);
return ret;
}
static int jtag_dpi_runtest(int cycles)
{
char buf[20];
uint8_t *data_buf = last_ir_buf, *read_scan;
int num_bits = last_ir_num_bits, bytes;
int ret = ERROR_OK;
if (data_buf == NULL) {
LOG_ERROR("%s: NULL 'data_buf' argument, file %s, line %d",
__func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
if (num_bits <= 0) {
LOG_ERROR("%s: 'num_bits' invalid value, file %s, line %d",
__func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
bytes = DIV_ROUND_UP(num_bits, 8);
read_scan = (uint8_t *)malloc(bytes * sizeof(uint8_t));
if (read_scan == NULL) {
LOG_ERROR("%s: malloc fail, file %s, line %d",
__func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
snprintf(buf, sizeof(buf), "ib %d\n", num_bits);
while (cycles > 0) {
ret = write_sock(buf, strlen(buf));
if (ret != ERROR_OK) {
LOG_ERROR("write_sock() fail, file %s, line %d",
__FILE__, __LINE__);
goto out;
}
ret = write_sock((char *)data_buf, bytes);
if (ret != ERROR_OK) {
LOG_ERROR("write_sock() fail, file %s, line %d",
__FILE__, __LINE__);
goto out;
}
ret = read_sock((char *)read_scan, bytes);
if (ret != ERROR_OK) {
LOG_ERROR("read_sock() fail, file %s, line %d",
__FILE__, __LINE__);
goto out;
}
cycles -= num_bits + 6;
}
out:
free(read_scan);
return ret;
}
static int jtag_dpi_stableclocks(int cycles)
{
return jtag_dpi_runtest(cycles);
}
static int jtag_dpi_execute_queue(void)
{
struct jtag_command *cmd;
int ret = ERROR_OK;
for (cmd = jtag_command_queue; ret == ERROR_OK && cmd != NULL;
cmd = cmd->next) {
switch (cmd->type) {
case JTAG_RUNTEST:
ret = jtag_dpi_runtest(cmd->cmd.runtest->num_cycles);
break;
case JTAG_STABLECLOCKS:
ret = jtag_dpi_stableclocks(cmd->cmd.stableclocks->num_cycles);
break;
case JTAG_TLR_RESET:
/* Enter Test-Logic-Reset state by asserting TRST */
if (cmd->cmd.statemove->end_state == TAP_RESET)
jtag_dpi_reset(1, 0);
break;
case JTAG_PATHMOVE:
/* unsupported */
break;
case JTAG_TMS:
/* unsupported */
break;
case JTAG_SLEEP:
jtag_sleep(cmd->cmd.sleep->us);
break;
case JTAG_SCAN:
ret = jtag_dpi_scan(cmd->cmd.scan);
break;
default:
LOG_ERROR("BUG: unknown JTAG command type 0x%X",
cmd->type);
ret = ERROR_FAIL;
break;
}
}
return ret;
}
static int jtag_dpi_init(void)
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
LOG_ERROR("socket: %s, function %s, file %s, line %d",
strerror(errno), __func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(server_port);
if (server_address == NULL) {
server_address = strdup(SERVER_ADDRESS);
if (server_address == NULL) {
LOG_ERROR("%s: strdup fail, file %s, line %d",
__func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
}
serv_addr.sin_addr.s_addr = inet_addr(server_address);
if (serv_addr.sin_addr.s_addr == INADDR_NONE) {
LOG_ERROR("inet_addr error occured");
return ERROR_FAIL;
}
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
close(sockfd);
LOG_ERROR("Can't connect to %s : %" PRIu16, server_address, server_port);
return ERROR_FAIL;
}
if (serv_addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
/* This increases performance dramatically for local
* connections, which is the most likely arrangement
* for a DPI connection. */
int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
}
LOG_INFO("Connection to %s : %" PRIu16 " succeed", server_address, server_port);
return ERROR_OK;
}
static int jtag_dpi_quit(void)
{
free(server_address);
server_address = NULL;
return close(sockfd);
}
COMMAND_HANDLER(jtag_dpi_set_port)
{
if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
else if (CMD_ARGC == 0)
LOG_INFO("Using server port %" PRIu16, server_port);
else {
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], server_port);
LOG_INFO("Set server port to %" PRIu16, server_port);
}
return ERROR_OK;
}
COMMAND_HANDLER(jtag_dpi_set_address)
{
if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
else if (CMD_ARGC == 0) {
if (server_address == NULL) {
server_address = strdup(SERVER_ADDRESS);
if (server_address == NULL) {
LOG_ERROR("%s: strdup fail, file %s, line %d",
__func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
}
LOG_INFO("Using server address %s", server_address);
} else {
free(server_address);
server_address = strdup(CMD_ARGV[0]);
if (server_address == NULL) {
LOG_ERROR("%s: strdup fail, file %s, line %d",
__func__, __FILE__, __LINE__);
return ERROR_FAIL;
}
LOG_INFO("Set server address to %s", server_address);
}
return ERROR_OK;
}
static const struct command_registration jtag_dpi_command_handlers[] = {
{
.name = "jtag_dpi_set_port",
.handler = &jtag_dpi_set_port,
.mode = COMMAND_CONFIG,
.help = "set the port of the DPI server",
.usage = "[port]",
},
{
.name = "jtag_dpi_set_address",
.handler = &jtag_dpi_set_address,
.mode = COMMAND_CONFIG,
.help = "set the address of the DPI server",
.usage = "[address]",
},
COMMAND_REGISTRATION_DONE
};
static struct jtag_interface jtag_dpi_interface = {
.supported = DEBUG_CAP_TMS_SEQ,
.execute_queue = jtag_dpi_execute_queue,
};
struct adapter_driver jtag_dpi_adapter_driver = {
.name = "jtag_dpi",
.transports = jtag_only,
.commands = jtag_dpi_command_handlers,
.init = jtag_dpi_init,
.quit = jtag_dpi_quit,
.reset = jtag_dpi_reset,
.jtag_ops = &jtag_dpi_interface,
};

View File

@ -12,6 +12,8 @@
* Copyright (C) 2009 Zachary T Welch *
* zw@superlucidity.net *
* *
* Copyright (C) 2020, Ampere Computing LLC *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
@ -60,6 +62,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_JTAG_DPI == 1
extern struct adapter_driver jtag_dpi_adapter_driver;
#endif
#if BUILD_FT232R == 1
extern struct adapter_driver ft232r_adapter_driver;
#endif
@ -177,6 +182,9 @@ struct adapter_driver *adapter_drivers[] = {
#if BUILD_JTAG_VPI == 1
&jtag_vpi_adapter_driver,
#endif
#if BUILD_JTAG_DPI == 1
&jtag_dpi_adapter_driver,
#endif
#if BUILD_FT232R == 1
&ft232r_adapter_driver,
#endif

View File

@ -0,0 +1,38 @@
#
# Provide support for the Cadence JTAG BFM
#
# Copyright (c) 2020, Ampere Computing LLC
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program;
#
#
adapter driver jtag_dpi
# Set the DPI JTAG server port
if { [info exists DPI_PORT] } {
set _DPI_PORT $DPI_PORT
} else {
set _DPI_PORT 5555
}
# Set the DPI JTAG server address
if { [info exists DPI_ADDRESS] } {
set _DPI_ADDRESS $DPI_ADDRESS
} else {
set _DPI_ADDRESS "127.0.0.1"
}
jtag_dpi_set_port $_DPI_PORT
jtag_dpi_set_address $_DPI_ADDRESS