1
0
Fork 0

Add JTAG DTM and test support in simulation

Initial cut

checkpoint which compiles and runs but there is some off-by-1 in the protocol

Debugging the clock crossing logic

checkpoint which works

Clean up the AsyncMailbox black box
This commit is contained in:
Megan Wachs 2016-08-19 09:46:43 -07:00
parent ceff6dd0c8
commit dd4a50c452
14 changed files with 1813 additions and 23 deletions

3
.gitmodules vendored
View File

@ -16,3 +16,6 @@
[submodule "firrtl"]
path = firrtl
url = https://github.com/ucb-bar/firrtl.git
[submodule "openocd"]
path = openocd
url = http://github.com/sifive/openocd.git

419
csrc/jtag_vpi.c Normal file
View File

@ -0,0 +1,419 @@
/*
* TCP/IP controlled VPI JTAG Interface.
* Based on Julius Baxter's work on jp_vpi.c
*
* Copyright (C) 2012 Franck Jullien, <franck.jullien@gmail.com>
* All rights reserved.
*
* 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.
* 3. Neither the names of the copyright holders nor the names of any
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <arpa/inet.h>
#include <vpi_user.h>
#define RSP_SERVER_PORT 5555
#define XFERT_MAX_SIZE 512
const char * cmd_to_string[] = {"CMD_RESET",
"CMD_TMS_SEQ",
"CMD_SCAN_CHAIN"};
struct vpi_cmd {
int cmd;
unsigned char buffer_out[XFERT_MAX_SIZE];
unsigned char buffer_in[XFERT_MAX_SIZE];
int length;
int nb_bits;
};
int listenfd = 0;
int connfd = 0;
int init_jtag_server(int port)
{
struct sockaddr_in serv_addr;
int flags;
printf("Listening on port %d\n", port);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 10);
printf("Waiting for client connection...");
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
printf("ok\n");
flags = fcntl(listenfd, F_GETFL, 0);
fcntl(listenfd, F_SETFL, flags | O_NONBLOCK);
return 0;
}
// See if there's anything on the FIFO for us
void check_for_command(char *userdata)
{
vpiHandle systfref, args_iter, argh;
struct t_vpi_value argval;
struct vpi_cmd vpi;
int nb;
int loaded_words = 0;
(void)userdata;
// Get the command from TCP server
if(!connfd)
init_jtag_server(RSP_SERVER_PORT);
nb = read(connfd, &vpi, sizeof(struct vpi_cmd));
if (((nb < 0) && (errno == EAGAIN)) || (nb == 0)) {
// Nothing in the fifo this time, let's return
return;
} else {
if (nb < 0) {
// some sort of error
perror("check_for_command");
exit(1);
}
}
/************* vpi.cmd to VPI ******************************/
// Obtain a handle to the argument list
systfref = vpi_handle(vpiSysTfCall, NULL);
// Now call iterate with the vpiArgument parameter
args_iter = vpi_iterate(vpiArgument, systfref);
// get a handle on the variable passed to the function
argh = vpi_scan(args_iter);
// now store the command value back in the sim
argval.format = vpiIntVal;
// Now set the command value
vpi_get_value(argh, &argval);
argval.value.integer = (uint32_t)vpi.cmd;
// And vpi_put_value() it back into the sim
vpi_put_value(argh, &argval, NULL, vpiNoDelay);
/************* vpi.length to VPI ******************************/
// now get a handle on the next object (memory array)
argh = vpi_scan(args_iter);
// now store the command value back in the sim
argval.format = vpiIntVal;
// Now set the command value
vpi_get_value(argh, &argval);
argval.value.integer = (uint32_t)vpi.length;
// And vpi_put_value() it back into the sim
vpi_put_value(argh, &argval, NULL, vpiNoDelay);
/************* vpi.nb_bits to VPI ******************************/
// now get a handle on the next object (memory array)
argh = vpi_scan(args_iter);
// now store the command value back in the sim
argval.format = vpiIntVal;
// Now set the command value
vpi_get_value(argh, &argval);
argval.value.integer = (uint32_t)vpi.nb_bits;
// And vpi_put_value() it back into the sim
vpi_put_value(argh, &argval, NULL, vpiNoDelay);
/*****************vpi.buffer_out to VPI ********/
// now get a handle on the next object (memory array)
argh = vpi_scan(args_iter);
vpiHandle array_word;
// Loop to load the words
while (loaded_words < vpi.length) {
// now get a handle on the current word we want in the array that was passed to us
array_word = vpi_handle_by_index(argh, loaded_words);
if (array_word != NULL) {
argval.value.integer = (uint32_t)vpi.buffer_out[loaded_words];
// And vpi_put_value() it back into the sim
vpi_put_value(array_word, &argval, NULL, vpiNoDelay);
} else
return;
loaded_words++;
}
/*******************************************/
// Cleanup and return
vpi_free_object(args_iter);
}
void send_result_to_server(char *userdata)
{
vpiHandle systfref, args_iter, argh;
struct t_vpi_value argval;
ssize_t n;
struct vpi_cmd vpi;
int32_t length;
int sent_words;
vpiHandle array_word;
(void)userdata;
// Now setup the handles to verilog objects and check things
// Obtain a handle to the argument list
systfref = vpi_handle(vpiSysTfCall, NULL);
// Now call iterate with the vpiArgument parameter
args_iter = vpi_iterate(vpiArgument, systfref);
// get a handle on the length variable
argh = vpi_scan(args_iter);
argval.format = vpiIntVal;
// get the value for the length object
vpi_get_value(argh, &argval);
// now set length
length = argval.value.integer;
// now get a handle on the next object (memory array)
argh = vpi_scan(args_iter);
// check we got passed a memory (array of regs)
if (!((vpi_get(vpiType, argh) == vpiMemory)
#ifdef MODELSIM_VPI
|| (vpi_get(vpiType, argh) == vpiRegArray)
#endif
#ifdef VCS_VPI
|| (vpi_get(vpiType, argh) == vpiRegArray)
#endif
)) {
vpi_printf("jtag_vpi: ERROR: did not pass a memory to get_command_block_data\n");
vpi_printf("jtag_vpi: ERROR: was passed type %d\n", (int)vpi_get(vpiType, argh));
return;
}
// check the memory we're writing into is big enough
if (vpi_get(vpiSize, argh) < length ) {
vpi_printf("jtag_vpi: ERROR: buffer passed to get_command_block_data too small. size is %d words, needs to be %d\n",
vpi_get(vpiSize, argh), length);
return;
}
// Loop to load the words
sent_words = 0;
while (sent_words < length) {
// Get a handle on the current word we want in the array that was passed to us
array_word = vpi_handle_by_index(argh, sent_words);
if (array_word != NULL) {
vpi_get_value(array_word, &argval);
vpi.buffer_in[sent_words] = (uint32_t) argval.value.integer;
} else
return;
sent_words++;
}
n = write(connfd, &vpi, sizeof(struct vpi_cmd));
if (n < (ssize_t)sizeof(struct vpi_cmd))
vpi_printf("jtag_vpi: ERROR: error during write to server\n");
// Cleanup and return
vpi_free_object(args_iter);
}
void register_check_for_command(void)
{
s_vpi_systf_data data = {
vpiSysTask,
0,
"$check_for_command",
(void *)check_for_command,
0,
0,
0
};
vpi_register_systf(&data);
}
void register_send_result_to_server(void)
{
s_vpi_systf_data data = {
vpiSysTask,
0,
"$send_result_to_server",
(void *)send_result_to_server,
0,
0,
0
};
vpi_register_systf(&data);
}
void sim_reset_callback(void)
{
// nothing to do!
}
void setup_reset_callbacks(void)
{
// here we setup and install callbacks for
// the setup and management of connections to
// the simulator upon simulation start and
// reset
static s_vpi_time time_s = {vpiScaledRealTime, 0, 0, 0};
static s_vpi_value value_s = {.format = vpiBinStrVal};
static s_cb_data cb_data_s = {
cbEndOfReset, // or start of simulation - initing socket fds etc
(void *)sim_reset_callback,
NULL,
&time_s,
&value_s,
0,
NULL
};
cb_data_s.obj = NULL; /* trigger object */
cb_data_s.user_data = NULL;
// actual call to register the callback
vpi_register_cb(&cb_data_s);
}
void sim_endofcompile_callback(void)
{
}
void setup_endofcompile_callbacks(void)
{
// here we setup and install callbacks for
// simulation finish
static s_vpi_time time_s = {vpiScaledRealTime, 0, 0, 0};
static s_vpi_value value_s = {.format = vpiBinStrVal};
static s_cb_data cb_data_s = {
cbEndOfCompile, // end of compile
(void *)sim_endofcompile_callback,
NULL,
&time_s,
&value_s,
0,
NULL
};
cb_data_s.obj = NULL; /* trigger object */
cb_data_s.user_data = NULL;
// actual call to register the callback
vpi_register_cb(&cb_data_s);
}
void sim_finish_callback(void)
{
if(connfd)
printf("Closing RSP server\n");
close(connfd);
close(listenfd);
}
void setup_finish_callbacks(void)
{
// here we setup and install callbacks for
// simulation finish
static s_vpi_time time_s = {vpiScaledRealTime, 0, 0, 0};
static s_vpi_value value_s = {.format = vpiBinStrVal};
static s_cb_data cb_data_s = {
cbEndOfSimulation, // end of simulation
(void *)sim_finish_callback,
NULL,
&time_s,
&value_s,
0,
NULL
};
cb_data_s.obj = NULL; /* trigger object */
cb_data_s.user_data = NULL;
// actual call to register the callback
vpi_register_cb(&cb_data_s);
}
#ifndef VCS_VPI
// Register the new system task here
void (*vlog_startup_routines[])(void) = {
#ifdef CDS_VPI
// this installs a callback on simulator reset - something which
// icarus does not do, so we only do it for cadence currently
setup_reset_callbacks,
#endif
setup_endofcompile_callbacks,
setup_finish_callbacks,
register_check_for_command,
register_send_result_to_server,
0 // last entry must be 0
};
// Entry point for testing development of the vpi functions
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
return 0;
}
#endif

1
openocd Submodule

@ -0,0 +1 @@
Subproject commit e70bd48baa604ca7e823e87c18fb5f366de9c607

View File

@ -0,0 +1,124 @@
package rocketchip
import Chisel._
import uncore.devices.{DebugBusIO, AsyncDebugBusTo, AsyncDebugBusFrom, DebugBusReq, DebugBusResp, DMKey}
import junctions._
import cde.{Parameters}
/* JTAG-based Debug Transport Module
* and synchronization logic.
*
* This implements JTAG interface described
* in the RISC-V Debug Specification
*
* This Module is currently a
* wrapper around a number of black-boxed
* modules. The black-boxing is due to the fact that
* Chisel doesn't currently support:
* - Negative Edge Clocking
* - Asynchronous Resets
* (The tristate requirements of JTAG are exported from the
* Chisel domain with the DRV_TDO signal).
*
* The AsyncDebugBus parameter here is overloaded.
* The DebugTransportModule JTAG definately needs a synchronizer,
* the parameter just currently selects whether the Chisel-generated
* crossing is used or the black-boxed crossing is used.
* Although Top is also capable of generating the
* Chisel sychnronizers, it is done here for consistency
* of keeping the synchronizers in one place when
* instantiating the DTM.
*
*/
class JtagDTMWithSync(implicit val p: Parameters) extends Module {
// IO <-> [Chisel Sync?] <-> [DebugBusIO<->UInt] <-> [Black Box Sync?] <-> DTM Black Box
val io = new Bundle {
val jtag = new JtagIO(true).flip()
val debug = new DebugBusIO()(p)
}
val req_width = io.debug.req.bits.getWidth
val resp_width = io.debug.resp.bits.getWidth
val jtag_dtm = Module (new DebugTransportModuleJtag(req_width, resp_width))
jtag_dtm.io.jtag <> io.jtag
val dtm_req = Wire(new DecoupledIO(UInt(width = req_width)))
val dtm_resp = Wire(new DecoupledIO(UInt(width = resp_width)))
val io_debug_bus = Wire (new DebugBusIO)
// Optionally instantiate the Chisel synchronizers.
// These go on this side of the DebugBusIO->UInt translation
// because the Chisel synchronizers understand these data structures.
if (p(AsyncDebugBus)){
io.debug <> AsyncDebugBusFrom(io.jtag.TCK, io.jtag.TRST, io_debug_bus)
} else {
io.debug <> io_debug_bus
}
// Translate from straight 'bits' interface of the blackboxes
// into the Resp/Req data structures.
io_debug_bus.req.valid := dtm_req.valid
io_debug_bus.req.bits := new DebugBusReq(p(DMKey).nDebugBusAddrSize).fromBits(dtm_req.bits)
dtm_req.ready := io_debug_bus.req.ready
dtm_resp.valid := io_debug_bus.resp.valid
dtm_resp.bits := io_debug_bus.resp.bits.asUInt
io_debug_bus.resp.ready := dtm_resp.ready
// Optionally instantiate the black-box synchronizers
// instead of the chisel ones.
// These go on this side of the DebugBusIO->UInt translation
// because they do not understand the DebugBusIO data structures.
if (p(AsyncDebugBus)) {
dtm_req <> jtag_dtm.io.dtm_req
jtag_dtm.io.dtm_resp <> dtm_resp
} else {
val req_sync = Module (new AsyncMailbox())
val resp_sync = Module (new AsyncMailbox())
req_sync.io.enq := jtag_dtm.io.dtm_req
req_sync.io.enq_clock.get := io.jtag.TCK
req_sync.io.enq_reset.get := io.jtag.TRST
req_sync.io.deq_clock.get := clock
req_sync.io.deq_reset.get := reset
dtm_req := req_sync.io.deq
jtag_dtm.io.dtm_resp := resp_sync.io.deq
resp_sync.io.deq_clock.get := io.jtag.TCK
resp_sync.io.deq_reset.get := io.jtag.TRST
resp_sync.io.enq_clock.get := clock
resp_sync.io.enq_reset.get := reset
resp_sync.io.enq := dtm_resp
}
}
class DebugTransportModuleJtag(reqSize : Int, respSize : Int)(implicit val p: Parameters) extends BlackBox {
val io = new Bundle {
val jtag = new JtagIO(true).flip()
val dtm_req = new DecoupledIO(UInt(width = reqSize))
val dtm_resp = new DecoupledIO(UInt(width = respSize)).flip()
}
}
class AsyncMailbox extends BlackBox {
// This Verilog module is parameterized, but until this is supported by Chisel,
// this mailbox just has a fixed width of 64 bits, which is enough
// for our specific purpose here.
val io = new Crossing(UInt(width=64), true, true)
}

View File

@ -122,6 +122,7 @@ class BasePlatformConfig extends Config (
case BuildCoreplex => (p: Parameters) => Module(new DefaultCoreplex(p))
case NExtInterrupts => 2
case AsyncDebugBus => false
case IncludeJtagDTM => false
case AsyncMMIOChannels => false
case ExtraDevices => Nil
case ExtraTopPorts => (p: Parameters) => new Bundle
@ -277,4 +278,18 @@ class WithTestRAM extends Config(
}
Seq(new TestRAMDevice)
}
})
}
)
class WithAsyncDebug extends Config (
(pname, site, here) => pname match {
case AsyncDebugBus => true
}
)
class WithJtagDTM extends Config (
(pname, site, here) => pname match {
case IncludeJtagDTM => true
}
)

View File

@ -87,9 +87,10 @@ class TopIO(implicit p: Parameters) extends BasicTopIO()(p) {
val mmio_axi = Vec(p(NExtMMIOAXIChannels), new NastiIO)
val mmio_ahb = Vec(p(NExtMMIOAHBChannels), new HastiMasterIO)
val mmio_tl = Vec(p(NExtMMIOTLChannels), new ClientUncachedTileLinkIO()(outermostMMIOParams))
val debug_clk = if (p(AsyncDebugBus)) Some(Clock(INPUT)) else None
val debug_rst = if (p(AsyncDebugBus)) Some(Bool(INPUT)) else None
val debug = new DebugBusIO()(p).flip
val debug_clk = if (p(AsyncDebugBus) & !p(IncludeJtagDTM)) Some(Clock(INPUT)) else None
val debug_rst = if (p(AsyncDebugBus) & !p(IncludeJtagDTM)) Some(Bool(INPUT)) else None
val debug = if (!p(IncludeJtagDTM)) Some(new DebugBusIO()(p).flip) else None
val jtag = if ( p(IncludeJtagDTM)) Some(new JtagIO(true).flip) else None
val extra = p(ExtraTopPorts)(p)
}
@ -137,10 +138,18 @@ class Top(topParams: Parameters) extends Module with HasTopLevelParameters {
periphery.io.mem_in <> coreplex.io.mem
coreplex.io.ext_clients <> periphery.io.clients_out
coreplex.io.debug <>
if (p(IncludeJtagDTM)) {
// JtagDTMWithSync is a wrapper which
// handles the synchronization as well.
val jtag_dtm = Module (new JtagDTMWithSync()(p))
jtag_dtm.io.jtag <> io.jtag.get
coreplex.io.debug <> jtag_dtm.io.debug
} else {
coreplex.io.debug <>
(if (p(AsyncDebugBus))
AsyncDebugBusFrom(io.debug_clk.get, io.debug_rst.get, io.debug)
else io.debug)
AsyncDebugBusFrom(io.debug_clk.get, io.debug_rst.get, io.debug.get)
else io.debug.get)
}
def asyncAxiTo(clocks: Seq[Clock], resets: Seq[Bool], inner_axis: Seq[NastiIO]): Seq[NastiIO] =
(clocks, resets, inner_axis).zipped.map {

View File

@ -6,6 +6,7 @@ import Chisel._
import cde.{Parameters, Field}
import rocket.Util._
import junctions._
import uncore.devices.{IncludeJtagDTM}
class TestHarness(implicit p: Parameters) extends Module {
val io = new Bundle {
@ -26,9 +27,6 @@ class TestHarness(implicit p: Parameters) extends Module {
require(dut.io.mmio_axi.isEmpty)
require(dut.io.mmio_ahb.isEmpty)
require(dut.io.mmio_tl.isEmpty)
require(dut.io.debug_clk.isEmpty)
require(dut.io.debug_rst.isEmpty)
require(dut.io.debug_rst.isEmpty)
require(dut.io.extra.elements.isEmpty)
for (int <- dut.io.interrupts)
@ -41,14 +39,42 @@ class TestHarness(implicit p: Parameters) extends Module {
Module(new SimAXIMem(memSize / dut.io.mem_axi.size)).io.axi <> axi
}
val dtm = Module(new SimDTM)
dut.io.debug <> dtm.io.debug
dtm.io.clk := clock
dtm.io.reset := reset
io.success := dut.io.success.getOrElse(dtm.io.exit === 1)
when (dtm.io.exit >= 2) {
printf("*** FAILED *** (exit code = %d)\n", dtm.io.exit >> 1)
stop(1)
if (p(IncludeJtagDTM)) {
val jtag_vpi = Module (new JtagVpi)
dut.io.jtag.get <> jtag_vpi.io.jtag
// To be proper,
// TRST should really be synchronized
// with TCK. But this is a fairly
// accurate representation of how
// HW may drive this signal.
// Neither OpenOCD nor JtagVPI drive TRST.
dut.io.jtag.get.TRST := reset
jtag_vpi.io.enable := ~reset
jtag_vpi.io.init_done := ~reset
io.success := Bool(false)
}
else {
val dtm = Module(new SimDTM)
dut.io.debug.get <> dtm.io.debug
// Todo: enable the usage of different clocks.
val dtm_clock = clock
val dtm_reset = reset
dtm.io.clk := dtm_clock
dtm.io.reset := dtm_reset
if (dut.io.debug_clk.isDefined)
dut.io.debug_clk.get := dtm_clock
if (dut.io.debug_rst.isDefined)
dut.io.debug_rst.get := dtm_reset
io.success := dut.io.success.getOrElse(dtm.io.exit === 1)
when (dtm.io.exit >= 2) {
printf("*** FAILED *** (exit code = %d)\n", dtm.io.exit >> 1)
stop(1)
}
}
}
@ -114,3 +140,13 @@ class SimDTM(implicit p: Parameters) extends BlackBox {
val exit = UInt(OUTPUT, 32)
}
}
class JtagVpi(implicit val p: Parameters) extends BlackBox {
val io = new Bundle {
val jtag = new JtagIO(false)
val enable = Bool(INPUT)
val init_done = Bool(INPUT)
}
}

View File

@ -8,7 +8,7 @@ import uncore.util._
import junctions._
import cde.{Parameters, Config, Field}
// *****************************************
// *****************************************r
// Constants which are interesting even
// outside of this module
// *****************************************
@ -264,7 +264,7 @@ class DefaultDebugModuleConfig (val ncomponents : Int, val xlen:Int)
nNDResetCycles = 1)
case object DMKey extends Field[DebugModuleConfig]
case object IncludeJtagDTM extends Field[Boolean]
// *****************************************
// Module Interfaces
@ -277,8 +277,8 @@ case object DMKey extends Field[DebugModuleConfig]
class DebugBusReq(addrBits : Int) extends Bundle {
val addr = UInt(width = addrBits)
val op = UInt(width = DbBusConsts.dbOpSize)
val data = UInt(width = DbBusConsts.dbDataSize)
val op = UInt(width = DbBusConsts.dbOpSize)
override def cloneType = new DebugBusReq(addrBits).asInstanceOf[this.type]
}
@ -287,8 +287,9 @@ class DebugBusReq(addrBits : Int) extends Bundle {
/** Structure to define the contents of a Debug Bus Response
*/
class DebugBusResp( ) extends Bundle {
val resp = UInt(width = DbBusConsts.dbRespSize)
val data = UInt(width = DbBusConsts.dbDataSize)
val resp = UInt(width = DbBusConsts.dbRespSize)
}
/** Structure to define the top-level DebugBus interface
@ -999,3 +1000,5 @@ object AsyncDebugBusTo { // OutsideClockDomain
sink
}
}

View File

@ -4,16 +4,22 @@
# Verilog sources
bb_vsrcs = $(base_dir)/vsrc/DebugTransportModuleJtag.v \
$(base_dir)/vsrc/jtag_vpi.v \
$(base_dir)/vsrc/AsyncMailbox.v
sim_vsrcs = \
$(generated_dir)/$(MODEL).$(CONFIG).v \
$(generated_dir)/consts.$(CONFIG).vh \
$(base_dir)/vsrc/$(TB).v \
$(base_dir)/vsrc/$(TB).v \
$(base_dir)/vsrc/SimDTM.v \
$(bb_vsrcs)
# C sources
sim_csrcs = \
$(base_dir)/csrc/SimDTM.cc \
$(base_dir)/csrc/jtag_vpi.c
#--------------------------------------------------------------------
# Build Verilog
@ -45,6 +51,11 @@ VCS_OPTS = -notice -line +lint=all,noVCDE,noONGS,noUI -error=PCWM-L -timescale=1
+define+RANDOMIZE \
+libext+.v \
VCS_OPTS += +vpi
VCS_OPTS += -P $(base_dir)/vsrc/jtag_vpi.tab
VCS_OPTS += -CC "-DVCS_VPI"
#--------------------------------------------------------------------
# Build the simulator
#--------------------------------------------------------------------

271
vsrc/AsyncFifo.v Normal file
View File

@ -0,0 +1,271 @@
module AsyncFifo (
// Write Interface
enq_clock
, enq_reset
, enq_ready
, enq_valid
, enq_bits
// Read Interface
, deq_clock
, deq_reset
, deq_ready
, deq_valid
, deq_bits
);
//--------------------------------------------------------
// Parameter Declarations
parameter DEPTH_LG_2 = 0; // default is 'mailbox'.
parameter WIDTH = 64;
localparam DEPTH = 2**DEPTH_LG_2;
//--------------------------------------------------------
// I/O Declarations
// Write Interface
input enq_clock;
wire w_clock = enq_clock;
input enq_reset;
wire w_reset = enq_reset;
output enq_ready;
wire w_ready;
assign enq_ready = w_ready;
input enq_valid;
wire w_valid = enq_valid;
input [WIDTH - 1 : 0 ] enq_bits;
wire [WIDTH - 1 : 0 ] w_bits = enq_bits;
// Read Interface
input deq_clock;
wire r_clock = deq_clock;
input deq_reset;
wire r_reset = deq_reset;
output deq_valid;
wire r_valid;
assign deq_valid = r_valid;
input deq_ready;
wire r_ready = deq_ready;
output [WIDTH - 1 : 0] deq_bits;
wire [WIDTH - 1 : 0] r_bits;
assign deq_bits = r_bits;
//--------------------------------------------------------
// FIFO Memory Declaration
generate
if (DEPTH > 1) begin : mem1_scope
reg [WIDTH - 1 : 0] fifoMem [ 0 : DEPTH - 1];
end else begin : mem2_scope
reg [WIDTH - 1 :0] fifoMem;
end
endgenerate
//--------------------------------------------------------
// Reg and Wire Declarations
wire w_full;
wire w_fire;
wire r_empty;
wire r_fire;
// Read & Write Address Pointers
generate
if (DEPTH_LG_2 > 0) begin : reg1_scope
reg [DEPTH_LG_2 : 0 ] w_wrAddrReg;
wire [DEPTH_LG_2 : 0] w_wrAddrNxt;
reg [DEPTH_LG_2 : 0 ] w_wrAddrGrayReg;
wire [DEPTH_LG_2 : 0] w_wrAddrGrayNxt;
reg [DEPTH_LG_2 : 0 ] r_rdAddrReg;
wire [DEPTH_LG_2 : 0] r_rdAddrNxt;
reg [DEPTH_LG_2 : 0 ] r_rdAddrGrayReg;
wire [DEPTH_LG_2 : 0] r_rdAddrGrayNxt;
reg [DEPTH_LG_2 : 0 ] wrAddrGrayReg_sync;
reg [DEPTH_LG_2 : 0 ] rdAddrGrayReg_sync;
reg [DEPTH_LG_2 : 0 ] r_wrAddrGrayReg;
reg [DEPTH_LG_2 : 0 ] w_rdAddrGrayReg;
end else begin : reg2_scope
reg w_wrAddrReg;
wire w_wrAddrNxt;
reg r_rdAddrReg;
wire r_rdAddrNxt;
reg wrAddrReg_sync;
reg rdAddrReg_sync;
reg r_wrAddrReg;
reg w_rdAddrReg;
end
endgenerate
//--------------------------------------------------------
// Reg and Wire Declarations
// Pointer Logic
generate
if (DEPTH_LG_2 > 0) begin : ptr_logic_scope
assign w_full = ~(reg1_scope.w_wrAddrGrayReg[DEPTH_LG_2] == reg1_scope.w_rdAddrGrayReg[DEPTH_LG_2]) &
(reg1_scope.w_wrAddrGrayReg[DEPTH_LG_2 - 1 : 0] == reg1_scope.w_rdAddrGrayReg[DEPTH_LG_2 - 1 : 0 ]);
assign reg1_scope.w_wrAddrNxt = reg1_scope.w_wrAddrReg + 1'b1; // OK / expected to overflow
assign reg1_scope.w_wrAddrGrayNext = reg1_scope.w_wrAddrNxt ^ (reg1_scope.w_wrAddrNxt >> 1);
assign reg1_scope.r_rdAddrNxt = reg1_scope.r_rdAddrReg + 1'b1; // OK / expected to overflow
assign reg1_scope.r_rdAddrGrayNext = reg1_scope.r_rdAddrNxt ^ (reg1_scope.r_rdAddrNxt >> 1);
assign r_empty = (reg1_scope.r_wrAddrGrayReg == reg1_scope.r_rdAddrGrayReg);
end else begin : ptr_logic_scope
assign w_full = ~(reg2_scope.w_wrAddrReg == reg2_scope.r_rdAddrReg);
assign reg2_scope.w_wrAddrNxt = ~reg2_scope.w_wrAddrReg ;
assign reg2_scope.r_rdAddrNxt = ~reg2_scope.r_rdAddrReg;
assign r_empty = (reg2_scope.r_wrAddrReg == reg2_scope.r_rdAddrReg);
end
endgenerate
assign w_ready = ~w_full;
assign w_fire = w_ready & w_valid;
// Read Logic
assign r_valid = ~r_empty;
assign r_fire = r_ready & r_valid;
generate
if (DEPTH > 1) begin : rw_scope1
if (DEPTH > 2) begin : rw_scope2
assign r_bits = mem1_scope.fifoMem[reg1_scope.r_rdAddrReg[DEPTH_LG_2-1:0]];
always @(posedge w_clock) begin
if (w_fire) begin
mem1_scope.fifoMem[reg1_scope.w_wrAddrReg[DEPTH_LG_2-1:0]] <= w_bits;
end
end
end else begin
assign r_bits = mem1_scope.fifoMem[reg1_scope.r_rdAddrReg[0]];
always @(posedge w_clock) begin
if (w_fire) begin
mem1_scope.fifoMem[reg1_scope.w_wrAddrReg[0]] <= w_bits;
end
end
end
end else begin
assign r_bits = mem2_scope.fifoMem;
always @(posedge w_clock) begin
if (w_fire) begin
mem2_scope.fifoMem <= w_bits;
end
end
end
endgenerate
//--------------------------------------------------------
// Sequential logic
//
generate
if (DEPTH_LG_2 > 0) begin : seq1_scope
always @(posedge w_clock or posedge w_reset) begin
if (w_reset) begin
reg1_scope.w_wrAddrReg <= 'b0;
reg1_scope.w_wrAddrGrayReg <= 'b0;
reg1_scope.rdAddrGrayReg_sync <= 'b0;
reg1_scope.w_rdAddrGrayReg <= 'b0;
end else begin
if (w_fire) begin
reg1_scope.w_wrAddrReg <= reg1_scope.w_wrAddrNxt;
reg1_scope.w_wrAddrGrayReg <= reg1_scope.w_wrAddrGrayNxt;
end
reg1_scope.rdAddrGrayReg_sync <= reg1_scope.r_rdAddrGrayReg;
reg1_scope.w_rdAddrGrayReg <= reg1_scope.rdAddrGrayReg_sync;
end
end
always @(posedge r_clock or posedge r_reset) begin
if (r_reset) begin
reg1_scope.r_rdAddrReg <= 'b0;
reg1_scope.r_rdAddrGrayReg <= 'b0;
reg1_scope.reg1_scope.wrAddrGrayReg_sync <= 'b0;
reg1_scope.reg1_scope.r_wrAddrGrayReg <= 'b0;
end else begin
if (r_fire) begin
reg1_scope.r_rdAddrReg <= reg1_scope.r_rdAddrNxt;
reg1_scope.r_rdAddrGrayReg <= reg1_scope.r_rdAddrGrayNxt;
end
reg1_scope.wrAddrGrayReg_sync <= reg1_scope.w_wrAddrGrayReg;
reg1_scope.r_wrAddrGrayReg <= reg1_scope.wrAddrGrayReg_sync;
end
end // always @ (posedge r_clock)
end else begin : seq2_scope // block: seq1_scope
always @(posedge w_clock or posedge w_reset) begin
if (w_reset ) begin
reg2_scope.w_wrAddrReg <= 1'b0;
reg2_scope.rdAddrReg_sync <= 1'b0;
reg2_scope.w_rdAddrReg <= 1'b0;
end else begin
if (w_fire) begin
reg2_scope.w_wrAddrReg <= reg2_scope.w_wrAddrNxt;
end
reg2_scope.rdAddrReg_sync <= reg2_scope.r_rdAddrReg;
reg2_scope.w_rdAddrReg <= reg2_scope.rdAddrReg_sync;
end
end
always @(posedge r_clock or posedge r_reset) begin
if (r_reset) begin
reg2_scope.r_rdAddrReg <= 1'b0;
reg2_scope.wrAddrReg_sync <= 1'b0;
reg2_scope.r_wrAddrReg <= 1'b0;
end else begin
if (r_fire) begin
reg2_scope.r_rdAddrReg <= reg2_scope.r_rdAddrNxt;
end
reg2_scope.wrAddrReg_sync <= reg2_scope.w_wrAddrReg;
reg2_scope.r_wrAddrReg <= reg2_scope.wrAddrReg_sync;
end
end // always @ (posedge r_clock)
end // block: seq2_scope
endgenerate
endmodule // AsyncFifo

301
vsrc/DebugTransportModuleJtag.v Executable file
View File

@ -0,0 +1,301 @@
module DebugTransportModuleJtag (
//JTAG Interface
jtag_TDI,
jtag_TDO,
jtag_TCK,
jtag_TMS,
jtag_TRST,
jtag_DRV_TDO,
dtm_req_valid,
dtm_req_ready,
dtm_req_bits,
dtm_resp_valid,
dtm_resp_ready,
dtm_resp_bits
);
//--------------------------------------------------------
// Parameter Declarations
parameter DEBUG_DATA_BITS = 34;
parameter DEBUG_ADDR_BITS = 5; // Spec allows values are 5-7
parameter DEBUG_OP_BITS = 2; // OP and RESP are the same size.
parameter JTAG_VERSION = 4'h1;
parameter JTAG_PART_NUM = 16'h0E31; // E31
parameter JTAG_MANUF_ID = 11'h489; // As Assigned by JEDEC
localparam IR_BITS = 5;
localparam DEBUG_VERSION = 0;
// JTAG State Machine
localparam TEST_LOGIC_RESET = 4'h0;
localparam RUN_TEST_IDLE = 4'h1;
localparam SELECT_DR = 4'h2;
localparam CAPTURE_DR = 4'h3;
localparam SHIFT_DR = 4'h4;
localparam EXIT1_DR = 4'h5;
localparam PAUSE_DR = 4'h6;
localparam EXIT2_DR = 4'h7;
localparam UPDATE_DR = 4'h8;
localparam SELECT_IR = 4'h9;
localparam CAPTURE_IR = 4'hA;
localparam SHIFT_IR = 4'hB;
localparam EXIT1_IR = 4'hC;
localparam PAUSE_IR = 4'hD;
localparam EXIT2_IR = 4'hE;
localparam UPDATE_IR = 4'hF;
//RISCV DTM Registers (see RISC-V Debug Specification)
// All others are treated as 'BYPASS'.
localparam REG_BYPASS = 5'b11111;
localparam REG_IDCODE = 5'b00001;
localparam REG_DEBUG_ACCESS = 5'b10001;
localparam REG_DTM_INFO = 5'b10000;
localparam DBUS_REG_BITS = DEBUG_OP_BITS + DEBUG_ADDR_BITS + DEBUG_DATA_BITS;
localparam DBUS_REQ_BITS = DEBUG_OP_BITS + DEBUG_ADDR_BITS + DEBUG_DATA_BITS;
localparam DBUS_RESP_BITS = DEBUG_OP_BITS + DEBUG_DATA_BITS;
localparam SHIFT_REG_BITS = DBUS_REG_BITS;
//--------------------------------------------------------
// I/O Declarations
//JTAG SIDE
input jtag_TDI;
output reg jtag_TDO;
input jtag_TCK;
input jtag_TMS;
input jtag_TRST;
// To allow tri-state outside of this block.
output reg jtag_DRV_TDO;
// RISC-V Core Side
output dtm_req_valid;
input dtm_req_ready;
output [DBUS_REQ_BITS - 1 :0] dtm_req_bits;
input dtm_resp_valid;
output dtm_resp_ready;
input [DBUS_RESP_BITS - 1 : 0] dtm_resp_bits;
//--------------------------------------------------------
// Reg and Wire Declarations
reg [IR_BITS -1 : 0 ] irReg;
wire [31:0] idcode;
wire [31:0] dtminfo;
reg [DBUS_REG_BITS - 1 : 0] dbusReg;
reg dbusValidReg;
reg [3:0] jtagStateReg;
reg [SHIFT_REG_BITS -1 : 0] shiftReg;
reg doDbusWriteReg;
reg doDbusReadReg;
reg busyReg;
reg skipOpReg; // Skip op because we're busy.
reg downgradeOpReg; // Downgrade op because prev. op failed.
wire busy;
wire nonzeroResp;
wire [SHIFT_REG_BITS -1 : 0] busyResponse;
wire [SHIFT_REG_BITS -1 : 0] nonbusyResponse;
//--------------------------------------------------------
// Combo Logic
assign idcode = {JTAG_VERSION, JTAG_PART_NUM, JTAG_MANUF_ID, 1'h1};
wire [3:0] debugAddrBits = DEBUG_ADDR_BITS;
wire [3:0] debugVersion = DEBUG_VERSION;
assign dtminfo = {24'b0, debugAddrBits, debugVersion};
//busy, dtm_resp* is only valid during CAPTURE_DR,
// so these signals should only be used at that time.
// This assumes there is only one transaction in flight at a time.
assign busy = busyReg & ~dtm_resp_valid;
// This is needed especially for the first request.
assign nonzeroResp = dtm_resp_valid ? |{dtm_resp_bits[DEBUG_OP_BITS-1:0]} : 1'b0;
// Interface to DM.
// Note that this means dtm_resp_bits must only be used during CAPTURE_DR.
assign dtm_resp_ready = (jtagStateReg == CAPTURE_DR) &&
(irReg == REG_DEBUG_ACCESS) &&
dtm_resp_valid;
assign dtm_req_valid = dbusValidReg;
assign dtm_req_bits = dbusReg;
assign busyResponse = {{(DEBUG_ADDR_BITS + DEBUG_DATA_BITS){1'b0}},
{(DEBUG_OP_BITS){1'b1}}}; // Generalizing 'busy' to 'all-1'
assign nonbusyResponse = {dbusReg[(DEBUG_DATA_BITS + DEBUG_OP_BITS) +: DEBUG_ADDR_BITS] , // retain address bits from Req.
dtm_resp_bits[DEBUG_OP_BITS +: DEBUG_DATA_BITS] , // data
dtm_resp_bits[0 +: DEBUG_OP_BITS] // response
};
//--------------------------------------------------------
// Sequential Logic
// JTAG STATE MACHINE
always @(posedge jtag_TCK or posedge jtag_TRST) begin
if (jtag_TRST) begin
jtagStateReg <= TEST_LOGIC_RESET;
end else begin
case (jtagStateReg)
TEST_LOGIC_RESET : jtagStateReg <= jtag_TMS ? TEST_LOGIC_RESET : RUN_TEST_IDLE;
RUN_TEST_IDLE : jtagStateReg <= jtag_TMS ? SELECT_DR : RUN_TEST_IDLE;
SELECT_DR : jtagStateReg <= jtag_TMS ? SELECT_IR : CAPTURE_DR;
CAPTURE_DR : jtagStateReg <= jtag_TMS ? EXIT1_DR : SHIFT_DR;
SHIFT_DR : jtagStateReg <= jtag_TMS ? EXIT1_DR : SHIFT_DR;
EXIT1_DR : jtagStateReg <= jtag_TMS ? UPDATE_DR : PAUSE_DR;
PAUSE_DR : jtagStateReg <= jtag_TMS ? EXIT2_DR : PAUSE_DR;
EXIT2_DR : jtagStateReg <= jtag_TMS ? UPDATE_DR : SHIFT_DR;
UPDATE_DR : jtagStateReg <= jtag_TMS ? SELECT_DR : RUN_TEST_IDLE;
SELECT_IR : jtagStateReg <= jtag_TMS ? TEST_LOGIC_RESET : CAPTURE_IR;
CAPTURE_IR : jtagStateReg <= jtag_TMS ? EXIT1_IR : SHIFT_IR;
SHIFT_IR : jtagStateReg <= jtag_TMS ? EXIT1_IR : SHIFT_IR;
EXIT1_IR : jtagStateReg <= jtag_TMS ? UPDATE_IR : PAUSE_IR;
PAUSE_IR : jtagStateReg <= jtag_TMS ? EXIT2_IR : PAUSE_IR;
EXIT2_IR : jtagStateReg <= jtag_TMS ? UPDATE_IR : SHIFT_IR;
UPDATE_IR : jtagStateReg <= jtag_TMS ? SELECT_DR : RUN_TEST_IDLE;
endcase // case (jtagStateReg)
end // else: !if(jtag_TRST)
end // always @ (posedge jtag_TCK or posedge jtag_TRST)
// SHIFT REG
always @(posedge jtag_TCK) begin
case (jtagStateReg)
CAPTURE_IR : shiftReg <= {{(SHIFT_REG_BITS-1){1'b0}}, 1'b1}; //JTAG spec only says must end with 'b01.
SHIFT_IR : shiftReg <= {{(SHIFT_REG_BITS-IR_BITS){1'b0}}, jtag_TDI, shiftReg[IR_BITS-1 : 1]};
CAPTURE_DR : case (irReg)
REG_BYPASS : shiftReg <= {(SHIFT_REG_BITS){1'b0}};
REG_IDCODE : shiftReg <= {{(SHIFT_REG_BITS-32){1'b0}}, idcode};
REG_DTM_INFO : shiftReg <= {{(SHIFT_REG_BITS-32){1'b0}}, dtminfo};
REG_DEBUG_ACCESS : shiftReg <= busy ? busyResponse : nonbusyResponse;
default : //BYPASS
shiftReg <= {(SHIFT_REG_BITS){1'b0}};
endcase
SHIFT_DR : case (irReg)
REG_BYPASS : shiftReg <= {{(SHIFT_REG_BITS- 1){1'b0}}, jtag_TDI};
REG_IDCODE : shiftReg <= {{(SHIFT_REG_BITS-32){1'b0}}, jtag_TDI, shiftReg[31:1]};
REG_DTM_INFO : shiftReg <= {{(SHIFT_REG_BITS-32){1'b0}}, jtag_TDI, shiftReg[31:1]};
REG_DEBUG_ACCESS : shiftReg <= {jtag_TDI, shiftReg[SHIFT_REG_BITS -1 : 1 ]};
default: // BYPASS
shiftReg <= {{(SHIFT_REG_BITS- 1){1'b0}} , jtag_TDI};
endcase // case (irReg)
endcase // case (jtagStateReg)
end
// IR
always @(negedge jtag_TCK or posedge jtag_TRST) begin
if (jtag_TRST) begin
irReg <= REG_IDCODE;
end else if (jtagStateReg == TEST_LOGIC_RESET) begin
irReg <= REG_IDCODE;
end else if (jtagStateReg == UPDATE_IR) begin
irReg <= shiftReg[IR_BITS-1:0];
end
end
// Busy. We become busy when we first try to send a request.
// We stop being busy when we accept a response.
// This means that busyReg will still be set when we check it,
// so the logic for checking busy looks ahead.
always @(posedge jtag_TCK or posedge jtag_TRST) begin
if (jtag_TRST) begin
busyReg <= 1'b0;
end else if (dtm_req_valid) begin //UPDATE_DR onwards
busyReg <= 1'b1;
end else if (dtm_resp_valid & dtm_resp_ready) begin //only in CAPTURE_DR
busyReg <= 1'b0;
end
end // always @ (posedge jtag_TCK or posedge jtag_TRST)
// Downgrade/Skip. We make the decision to downgrade or skip
// during every CAPTURE_DR, and use the result in UPDATE_DR.
always @(posedge jtag_TCK or posedge jtag_TRST) begin
if (jtag_TRST) begin
skipOpReg <= 1'b0;
downgradeOpReg <= 1'b0;
end else if (irReg == REG_DEBUG_ACCESS) begin
case(jtagStateReg)
CAPTURE_DR: begin
skipOpReg <= busy;
downgradeOpReg <= (~busy & nonzeroResp);
end
UPDATE_DR: begin
skipOpReg <= 1'b0;
downgradeOpReg <= 1'b0;
end
endcase // case (jtagStateReg)
end
end // always @ (posedge jtag_TCK or posedge jtag_TRST)
//dbusReg, dbusValidReg.
always @(posedge jtag_TCK or posedge jtag_TRST) begin
if (jtag_TRST) begin
dbusReg <= {(DBUS_REG_BITS) {1'b0}};
dbusValidReg <= 1'b0;
end else if (jtagStateReg == UPDATE_DR) begin
if (irReg == REG_DEBUG_ACCESS) begin
if (skipOpReg) begin
// do nothing.
end else if (downgradeOpReg) begin
dbusReg <= {(DBUS_REG_BITS){1'b0}}; // NOP has encoding 2'b00.
dbusValidReg <= 1'b1;
end else begin
dbusReg <= shiftReg[DBUS_REG_BITS-1:0];
dbusValidReg <= 1'b1;
end
end
end else if (dtm_req_ready) begin
dbusValidReg <= 1'b0;
end
end // always @ (negedge jtag_TCK or posedge jtag_TRST)
//TDO
always @(negedge jtag_TCK or posedge jtag_TRST) begin
if (jtag_TRST) begin
jtag_TDO <= 1'b0;
jtag_DRV_TDO <= 1'b0;
end else if (jtagStateReg == SHIFT_IR) begin
jtag_TDO <= shiftReg[0];
jtag_DRV_TDO <= 1'b1;
end else if (jtagStateReg == SHIFT_DR) begin
jtag_TDO <= shiftReg[0];
jtag_DRV_TDO <= 1'b1;
end else begin
jtag_TDO <= 1'b0;
jtag_DRV_TDO <= 1'b0;
end
end // always @ (negedge jtag_TCK or posedge jtag_TRST)
endmodule

2
vsrc/jtag_vpi.tab Normal file
View File

@ -0,0 +1,2 @@
$send_result_to_server call=send_result_to_server
$check_for_command call=check_for_command

302
vsrc/jtag_vpi.v Normal file
View File

@ -0,0 +1,302 @@
/*
* TCP/IP controlled VPI JTAG Interface.
* Based on Julius Baxter's work on jp_vpi.c
*
* Copyright (C) 2012 Franck Jullien, <franck.jullien@gmail.com>
* All rights reserved.
*
* 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.
* 3. Neither the names of the copyright holders nor the names of any
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 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.
*/
`define CMD_RESET 0
`define CMD_TMS_SEQ 1
`define CMD_SCAN_CHAIN 2
`define CMD_SCAN_CHAIN_FLIP_TMS 3
`define CMD_STOP_SIMU 4
module JtagVpi
#( parameter DEBUG_INFO = 0,
parameter TP = 1,
parameter TCK_HALF_PERIOD = 2,// 50, // Clock half period (Clock period = 100 ns => 10 MHz)
parameter CMD_DELAY = 2 // 1000
)
(
output jtag_TMS,
output jtag_TCK,
output jtag_TDI,
input jtag_TDO,
input jtag_TRST, // unused
input enable,
input init_done);
reg tms;
reg tck;
reg tdi;
wire tdo;
assign jtag_TMS = tms;
assign jtag_TCK = tck;
assign jtag_TDI = tdi;
assign tdo = jtag_TDO;
integer cmd;
integer length;
integer nb_bits;
reg [31:0] buffer_out [0:4095]; // Data storage from the jtag server
reg [31:0] buffer_in [0:4095]; // Data storage to the jtag server
integer flip_tms;
reg [31:0] data_out;
reg [31:0] data_in;
integer debug;
assign tms_o = tms;
assign tck_o = tck;
assign tdi_o = tdi;
initial
begin
tck <= #TP 1'b0;
tdi <= #TP 1'bz;
tms <= #TP 1'b0;
data_out <= 32'h0;
data_in <= 32'h0;
// Insert a #delay here because we need to
// wait until the PC isn't pointing to flash anymore
// (this is around 20k ns if the flash_crash boot code
// is being booted from, else much bigger, around 10mil ns)
wait(init_done)
if($test$plusargs("jtag_vpi_enable")) main;
end
task main;
begin
$display("JTAG debug module with VPI interface enabled\n");
reset_tap;
goto_run_test_idle_from_reset;
while (1) begin
// Check for incoming command
// wait until a command is sent
// poll with a delay here
cmd = -1;
while (cmd == -1)
begin
#CMD_DELAY $check_for_command(cmd, length, nb_bits, buffer_out);
end
// now switch on the command
case (cmd)
`CMD_RESET :
begin
if (DEBUG_INFO)
$display("%t ----> CMD_RESET %h\n", $time, length);
reset_tap;
goto_run_test_idle_from_reset;
end
`CMD_TMS_SEQ :
begin
if (DEBUG_INFO)
$display("%t ----> CMD_TMS_SEQ\n", $time);
do_tms_seq;
end
`CMD_SCAN_CHAIN :
begin
if (DEBUG_INFO)
$display("%t ----> CMD_SCAN_CHAIN\n", $time);
flip_tms = 0;
do_scan_chain;
$send_result_to_server(length, buffer_in);
end
`CMD_SCAN_CHAIN_FLIP_TMS :
begin
if(DEBUG_INFO)
$display("%t ----> CMD_SCAN_CHAIN\n", $time);
flip_tms = 1;
do_scan_chain;
$send_result_to_server(length, buffer_in);
end
`CMD_STOP_SIMU :
begin
if(DEBUG_INFO)
$display("%t ----> End of simulation\n", $time);
$finish();
end
default:
begin
$display("Somehow got to the default case in the command case statement.");
$display("Command was: %x", cmd);
$display("Exiting...");
$finish();
end
endcase // case (cmd)
end // while (1)
end
endtask // main
// Generation of the TCK signal
task gen_clk;
input [31:0] number;
integer i;
begin
for (i = 0; i < number; i = i + 1)
begin
#TCK_HALF_PERIOD tck <= 1;
#TCK_HALF_PERIOD tck <= 0;
end
end
endtask
// TAP reset
task reset_tap;
begin
if (DEBUG_INFO)
$display("(%0t) Task reset_tap", $time);
tms <= #1 1'b1;
gen_clk(5);
end
endtask
// Goes to RunTestIdle state
task goto_run_test_idle_from_reset;
begin
if (DEBUG_INFO)
$display("(%0t) Task goto_run_test_idle_from_reset", $time);
tms <= #1 1'b0;
gen_clk(1);
end
endtask
//
task do_tms_seq;
integer i,j;
reg [31:0] data;
integer nb_bits_rem;
integer nb_bits_in_this_byte;
begin
if (DEBUG_INFO)
$display("(%0t) Task do_tms_seq of %d bits (length = %d)", $time, nb_bits, length);
// Number of bits to send in the last byte
nb_bits_rem = nb_bits % 8;
for (i = 0; i < length; i = i + 1)
begin
// If we are in the last byte, we have to send only
// nb_bits_rem bits. If not, we send the whole byte.
nb_bits_in_this_byte = (i == (length - 1)) ? nb_bits_rem : 8;
data = buffer_out[i];
for (j = 0; j < nb_bits_in_this_byte; j = j + 1)
begin
tms <= #1 1'b0;
if (data[j] == 1) begin
tms <= #1 1'b1;
end
gen_clk(1);
end
end
tms <= #1 1'b0;
end
endtask
//
task do_scan_chain;
integer _bit;
integer nb_bits_rem;
integer nb_bits_in_this_byte;
integer index;
begin
if(DEBUG_INFO)
$display("(%0t) Task do_scan_chain of %d bits (length = %d)", $time, nb_bits, length);
// Number of bits to send in the last byte
nb_bits_rem = nb_bits % 8;
for (index = 0; index < length; index = index + 1)
begin
// If we are in the last byte, we have to send only
// nb_bits_rem bits if it's not zero.
// If not, we send the whole byte.
nb_bits_in_this_byte = (index == (length - 1)) ? ((nb_bits_rem == 0) ? 8 : nb_bits_rem) : 8;
data_out = buffer_out[index];
for (_bit = 0; _bit < nb_bits_in_this_byte; _bit = _bit + 1)
begin
tdi <= 1'b0;
if (data_out[_bit] == 1'b1) begin
tdi <= 1'b1;
end
// On the last bit, set TMS to '1'
if (((_bit == (nb_bits_in_this_byte - 1)) && (index == (length - 1))) && (flip_tms == 1)) begin
tms <= 1'b1;
end
#TCK_HALF_PERIOD tck <= 1;
data_in[_bit] <= tdo;
#TCK_HALF_PERIOD tck <= 0;
end
buffer_in[index] = data_in;
end
tdi <= 1'b0;
tms <= 1'b0;
end
endtask
endmodule

293
vsrc/rocketDTMTestHarness.v Normal file
View File

@ -0,0 +1,293 @@
// See LICENSE for license details.
extern "A" void memory_tick
(
input reg [31:0] channel,
input reg ar_valid,
output reg ar_ready,
input reg [`MEM_ADDR_BITS-1:0] ar_addr,
input reg [`MEM_ID_BITS-1:0] ar_id,
input reg [2:0] ar_size,
input reg [7:0] ar_len,
input reg aw_valid,
output reg aw_ready,
input reg [`MEM_ADDR_BITS-1:0] aw_addr,
input reg [`MEM_ID_BITS-1:0] aw_id,
input reg [2:0] aw_size,
input reg [7:0] aw_len,
input reg w_valid,
output reg w_ready,
input reg [`MEM_STRB_BITS-1:0] w_strb,
input reg [`MEM_DATA_BITS-1:0] w_data,
input reg w_last,
output reg r_valid,
input reg r_ready,
output reg [1:0] r_resp,
output reg [`MEM_ID_BITS-1:0] r_id,
output reg [`MEM_DATA_BITS-1:0] r_data,
output reg r_last,
output reg b_valid,
input reg b_ready,
output reg [1:0] b_resp,
output reg [`MEM_ID_BITS-1:0] b_id
);
module rocketDTMTestHarness;
reg [31:0] seed;
initial seed = $get_initial_random_seed();
//-----------------------------------------------
// Instantiate the processor
reg clk = 1'b0;
reg reset = 1'b1;
reg r_reset = 1'b1;
reg start = 1'b0;
always #`CLOCK_PERIOD clk = ~clk;
reg [ 31:0] n_mem_channel = `N_MEM_CHANNELS;
reg [ 31:0] mem_width = `MEM_DATA_BITS;
reg [ 63:0] max_cycles = 0;
reg [ 63:0] trace_count = 0;
reg [1023:0] loadmem = 0;
reg [1023:0] vcdplusfile = 0;
reg [1023:0] vcdfile = 0;
reg verbose = 0;
wire printf_cond = verbose && !reset;
integer stderr = 32'h80000002;
`include `TBVFRAG
always @(posedge clk)
begin
r_reset <= reset;
end
reg [31:0] exit = 0;
//-----------------------------------------------
// Instantiate DTM and Synchronizers
// JTAG Interface
wire debug_TDI;
wire debug_TDO;
wire debug_TCK;
wire debug_TMS;
wire debug_TRST;
wire debug_DRV_TDO;
//=================================================
// JTAG VPI Server
wire cheater_TCK;
jtag_vpi #(
.DEBUG_INFO(0),
//parameter TP = 1,
.TCK_HALF_PERIOD(5),
.CMD_DELAY(10)
) // Clock half period (Clock period = 10 ns => 100 MHz)
jtag_vpi (
.tms(debug_TMS),
.tck(debug_TCK),
.tdi(debug_TDI),
.tdo(debug_TDO),
.enable(~reset),
.init_done(~reset)
);
//=================================================
// DTM <-> Synchronizers Interface
localparam DEBUG_ADDR_BITS = 5;
localparam DEBUG_DATA_BITS = 34;
localparam DEBUG_OP_BITS = 2;
wire dtm_req_ready;
wire dtm_req_valid;
wire [DEBUG_OP_BITS + DEBUG_ADDR_BITS + DEBUG_DATA_BITS - 1 : 0 ] dtm_req_data;
wire dtm_resp_ready;
wire dtm_resp_valid;
wire [DEBUG_OP_BITS + DEBUG_DATA_BITS - 1 :0 ] dtm_resp_data;
DebugTransportModuleJtag #(.DEBUG_OP_BITS(DEBUG_OP_BITS),
.DEBUG_ADDR_BITS(DEBUG_ADDR_BITS),
.DEBUG_DATA_BITS(DEBUG_DATA_BITS)
) debugTransportModuleJtag0 (
//JTAG Interface
.TDI(debug_TDI),
.TDO(debug_TDO),
.TCK(debug_TCK),
.TMS(debug_TMS),
.TRST(debug_TRST),
.DRV_TDO(debug_DRV_TDO),
.dtm_req_ready(dtm_req_ready),
.dtm_req_valid(dtm_req_valid),
.dtm_req_data(dtm_req_data),
.dtm_resp_ready(dtm_resp_ready),
.dtm_resp_valid(dtm_resp_valid),
.dtm_resp_data(dtm_resp_data)
);
`ifdef VERILOG_DEBUG_SYNC
AsyncFifo #(.DEPTH_LG_2(0),
.WIDTH(DEBUG_OP_BITS + DEBUG_ADDR_BITS + DEBUG_DATA_BITS))
debugBusReqFifo(
// Write Interface
.clk_w(~debug_TCK),
.reset_w(debug_TRST),
.ready_w(dtm_req_ready),
.valid_w(dtm_req_valid),
.data_w(dtm_req_data),
.clk_r(clk),
.reset_r(reset),
.ready_r(debug_req_ready),
.valid_r(debug_req_valid),
.data_r({debug_req_bits_addr, debug_req_bits_data, debug_req_bits_op})
);
AsyncFifo #(.DEPTH_LG_2(0),
.WIDTH(DEBUG_OP_BITS + DEBUG_DATA_BITS))
debugBusRespFifo(
.clk_w(clk),
.reset_w(reset),
.ready_w(debug_resp_ready),
.valid_w(debug_resp_valid),
.data_w({debug_resp_bits_data, debug_resp_bits_resp}),
.clk_r(debug_TCK),
.reset_r(debug_TRST),
.ready_r(dtm_resp_ready),
.valid_r(dtm_resp_valid),
.data_r(dtm_resp_data)
);
// This is cheating / potentially incorrect!!! It needs to be more
// clearly specified as to what the behavior of TRST is.
assign debug_TRST = reset;
// The TCK cheat is not needed for this side of the ifdef
// because both DTM and synchronizer
// logic is asynchronously reset as needed.
`else // !`ifdef VERILOG_DEBUG_SYNC
// This is cheating / potentially incorrect!!! It needs to be more
// clearly specified as to what the behavior of TRST is.
assign debug_TRST = reset;
// This is TOTAL cheating!!! The synchronizer
// logic should be asynchronously reset, or we should
// specify the TCK/TRST requirements for a synchronous reset.
assign debug_clk = reset ? clk : debug_TCK;
assign debug_reset = debug_TRST;
assign debug_req_valid = dtm_req_valid;
assign {debug_req_bits_addr, debug_req_bits_data, debug_req_bits_op} = dtm_req_data;
assign dtm_req_ready = debug_req_ready;
assign dtm_resp_valid = debug_resp_valid;
assign dtm_resp_data = {debug_resp_bits_data, debug_resp_bits_resp};
assign debug_resp_ready = dtm_resp_ready;
`endif // !`ifdef VERILOG_DEBUG_SYNC
//-----------------------------------------------
// Start the simulation
// Read input arguments and initialize
initial
begin
$value$plusargs("max-cycles=%d", max_cycles);
verbose = $test$plusargs("verbose");
`ifdef DEBUG
if ($value$plusargs("vcdplusfile=%s", vcdplusfile))
begin
$vcdplusfile(vcdplusfile);
$vcdpluson(0);
$vcdplusmemon(0);
end
if ($value$plusargs("vcdfile=%s", vcdfile))
begin
$dumpfile(vcdfile);
$dumpvars(0, dut);
$dumpon;
end
`define VCDPLUSCLOSE $vcdplusclose; $dumpoff;
`else
`define VCDPLUSCLOSE
`endif
// Strobe reset
#777.7 reset = 0;
end
reg [255:0] reason = 0;
always @(posedge clk)
begin
if (max_cycles > 0 && trace_count > max_cycles)
reason = "timeout";
if (exit > 1)
$sformat(reason, "tohost = %d", exit >> 1);
if (reason)
begin
$fdisplay(stderr, "*** FAILED *** (%s) after %d simulation cycles", reason, trace_count);
`VCDPLUSCLOSE
$fatal;
end
if (exit == 1)
begin
if (verbose)
$fdisplay(stderr, "Completed after %d simulation cycles", trace_count);
`VCDPLUSCLOSE
$finish;
end
end
always @(posedge clk)
begin
trace_count = trace_count + 1;
`ifdef GATE_LEVEL
if (verbose)
begin
$fdisplay(stderr, "C: %10d", trace_count-1);
end
`endif
end
endmodule