diff --git a/.gitmodules b/.gitmodules index 50163ec5..89e53c38 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/csrc/jtag_vpi.c b/csrc/jtag_vpi.c new file mode 100644 index 00000000..9c6198ad --- /dev/null +++ b/csrc/jtag_vpi.c @@ -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, + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#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 diff --git a/regression/Makefile b/regression/Makefile index db9e4bea..0b4f34c5 100644 --- a/regression/Makefile +++ b/regression/Makefile @@ -49,6 +49,13 @@ CONFIGS=MemtestConfig MemtestBufferlessConfig MemtestStatelessConfig FancyMemtes UnitTestConfig endif +ifeq ($(SUITE), JtagDtmSuite) +CONFIGS_32=WithJtagDTM_DefaultRV32Config +CONFIGS_64=WithJtagDTM_DefaultConfig +CONFIGS += $(CONFIGS_32) +CONFIGS += $(CONFIGS_64) +endif + # These are the named regression targets. While it's expected you run them in # this order, since there's dependencies for everything it doesn't actually # matter. They're here to make running the various targets from the @@ -182,3 +189,57 @@ stamps/%/emulator-torture-$(TORTURE_CONFIG).stamp: stamps/%/emulator-debug.stamp mkdir -p $(dir $@) $(MAKE) -C $(abspath $(TOP))/torture cnight RTL_CONFIG=$* RISCV=$(abspath $(RISCV)) PATH="$(abspath $(RISCV)/bin:$(PATH))" OPTIONS="-C $(abspath $(TOP)/torture/config/$(TORTURE_CONFIG).config) -p $(abspath $(TORTURE_SAVE_DIR)) -m 30 -t 10" date > $@ + + +# Targets for JTAG DTM full-chain simulation + +OPENOCD_HEAD ?= riscv +OPENOCD_INSTALL ?= $(abspath $(TOP))/openocd-install +OPENOCD_VERSION = $(shell git ls-remote http://github.com/sifive/openocd.git $(OPENOCD_HEAD) | awk '{print $$1}') +OPENOCD_DIR = $(OPENOCD_INSTALL)_$(OPENOCD_VERSION)/ + +$(OPENOCD_DIR)/bin/openocd: + rm -rf openocd + git clone http://github.com/sifive/openocd.git + cd openocd ; \ + git checkout $(OPENOCD_VERSION) ; \ + ./bootstrap ; \ + ./configure --enable-jtag-vpi --prefix=$(OPENOCD_INSTALL)_$(OPENOCD_VERSION) ; \ + make ; \ + make install + +install_openocd: $(OPENOCD_DIR)/bin/openocd + +# If this is defined empty, then all tests would run. +# Running a list of tests is not supported. +JTAG_DTM_TEST ?= SimpleRegisterTest.test_s0 + +stamps/%/jtag-dtm-32-$(JTAG_DTM_TEST).stamp: install_openocd stamps/%/vsim-ndebug.stamp + $(abspath $(TOP))/riscv-tools/riscv-tests/debug/gdbserver.py \ + --run $(abspath $(TOP))/vsim/simv-TestHarness-$* \ + --cmd="$(OPENOCD_DIR)/bin/openocd \ + --s $(OPENOCD_DIR)/share/openocd/scripts" \ + --freedom-e300-sim \ + $(JTAG_DTM_TEST) + date > $@ + +stamps/%/jtag-dtm-64-$(JTAG_DTM_TEST).stamp: install_openocd stamps/%/vsim-ndebug.stamp + $(abspath $(TOP))/riscv-tools/riscv-tests/debug/gdbserver.py \ + --run $(abspath $(TOP))/vsim/simv-TestHarness-$* \ + --cmd="$(OPENOCD_INSTALL)_$(OPENOCD_VERSION)/bin/openocd \ + --s $(OPENOCD_INSTALL)_$(OPENOCD_VERSION)/share/openocd/scripts" \ + --freedom-u500-sim \ + $(JTAG_DTM_TEST) + date > $@ + +JTAG_DTM_32_TEST_STAMPS=$(foreach config,$(CONFIGS_32),stamps/$(config)/jtag-dtm-32-$(JTAG_DTM_TEST).stamp) +JTAG_DTM_64_TEST_STAMPS=$(foreach config,$(CONFIGS_64),stamps/$(config)/jtag-dtm-64-$(JTAG_DTM_TEST).stamp) + +jtag-dtm-tests-32 : $(JTAG_DTM_32_TEST_STAMPS) +jtag-dtm-tests-64 : $(JTAG_DTM_64_TEST_STAMPS) + +# Targets for JTAG DTM full-chain simulation +jtag-dtm-regression: jtag-dtm-tests-32 jtag-dtm-tests-64 + + + diff --git a/src/main/scala/junctions/jtag.scala b/src/main/scala/junctions/jtag.scala new file mode 100644 index 00000000..b022ed6e --- /dev/null +++ b/src/main/scala/junctions/jtag.scala @@ -0,0 +1,16 @@ +package junctions +import Chisel._ +import cde.{Parameters} + +class JtagIO(drvTdo : Boolean = false) extends Bundle { + + val TCK = Clock(OUTPUT) + val TMS = Bool(OUTPUT) + val TDI = Bool(OUTPUT) + val TDO = Bool(INPUT) + val TRST = Bool(OUTPUT) + + val DRV_TDO = if (drvTdo) Some(Bool(INPUT)) else None + override def cloneType = new JtagIO(drvTdo).asInstanceOf[this.type] + +} diff --git a/src/main/scala/rocketchip/Configs.scala b/src/main/scala/rocketchip/Configs.scala index 390605ba..29437d44 100644 --- a/src/main/scala/rocketchip/Configs.scala +++ b/src/main/scala/rocketchip/Configs.scala @@ -117,6 +117,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 => new EmptyDeviceBlock case ExtraTopPorts => (p: Parameters) => new Bundle @@ -268,4 +269,18 @@ class WithTestRAM extends Config( } 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 + } +) diff --git a/src/main/scala/rocketchip/DebugTransport.scala b/src/main/scala/rocketchip/DebugTransport.scala new file mode 100644 index 00000000..169cf6f2 --- /dev/null +++ b/src/main/scala/rocketchip/DebugTransport.scala @@ -0,0 +1,126 @@ +package rocketchip + +import Chisel._ +import uncore.devices.{DebugBusIO, AsyncDebugBusTo, AsyncDebugBusFrom, DebugBusReq, DebugBusResp, DMKey} +import junctions._ +import cde.{Parameters, Field} + +case object IncludeJtagDTM extends Field[Boolean] + +/* 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) + +} diff --git a/src/main/scala/rocketchip/RocketChip.scala b/src/main/scala/rocketchip/RocketChip.scala index 60bc2e94..2ddabbbf 100644 --- a/src/main/scala/rocketchip/RocketChip.scala +++ b/src/main/scala/rocketchip/RocketChip.scala @@ -88,9 +88,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) } @@ -138,10 +139,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 { diff --git a/src/main/scala/rocketchip/TestHarness.scala b/src/main/scala/rocketchip/TestHarness.scala index 0081ea9d..3e598625 100644 --- a/src/main/scala/rocketchip/TestHarness.scala +++ b/src/main/scala/rocketchip/TestHarness.scala @@ -24,9 +24,6 @@ class TestHarness(implicit p: Parameters) extends Module { require(dut.io.mmio_rst.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) @@ -39,6 +36,48 @@ class TestHarness(implicit p: Parameters) extends Module { Module(new SimAXIMem(memSize / dut.io.mem_axi.size)).io.axi <> axi } + 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 + + // Success is determined by the gdbserver + // which is controlling this simulation. + io.success := Bool(false) + } + else { + val dtm = Module(new SimDTM) + dut.io.debug.get <> dtm.io.debug + + // Todo: enable the usage of different clocks + // to test the synchronizer more aggressively. + 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) + } + } + for (bus_axi <- dut.io.bus_axi) { bus_axi.ar.valid := Bool(false) bus_axi.aw.valid := Bool(false) @@ -52,15 +91,6 @@ class TestHarness(implicit p: Parameters) extends Module { slave.io <> mmio_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) - } } class SimAXIMem(size: BigInt)(implicit p: Parameters) extends Module { @@ -125,3 +155,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) + } +} diff --git a/src/main/scala/uncore/devices/Debug.scala b/src/main/scala/uncore/devices/Debug.scala index 531fa6a1..7443c9ac 100644 --- a/src/main/scala/uncore/devices/Debug.scala +++ b/src/main/scala/uncore/devices/Debug.scala @@ -265,7 +265,6 @@ class DefaultDebugModuleConfig (val ncomponents : Int, val xlen:Int) case object DMKey extends Field[DebugModuleConfig] - // ***************************************** // Module Interfaces // @@ -277,8 +276,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 +286,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 +999,5 @@ object AsyncDebugBusTo { // OutsideClockDomain sink } } + + diff --git a/vsim/Makefrag b/vsim/Makefrag index f4bcb7a5..a2e3e5db 100644 --- a/vsim/Makefrag +++ b/vsim/Makefrag @@ -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 #-------------------------------------------------------------------- diff --git a/vsrc/AsyncMailbox.v b/vsrc/AsyncMailbox.v new file mode 100644 index 00000000..4460e9bf --- /dev/null +++ b/vsrc/AsyncMailbox.v @@ -0,0 +1,154 @@ + + +module AsyncMailbox ( + // 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 WIDTH = 64; + + //-------------------------------------------------------- + // 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 + + reg [WIDTH - 1 :0] mailboxReg; + + //-------------------------------------------------------- + // Reg and Wire Declarations + + wire w_full; + wire w_fire; + + wire r_empty; + wire r_fire; + + + // Read & Write Address Pointers + reg w_wrAddrReg; + wire w_wrAddrNxt; + reg r_rdAddrReg; + wire r_rdAddrNxt; + + + reg wrAddrReg_sync; + reg rdAddrReg_sync; + + reg r_wrAddrReg; + reg w_rdAddrReg; + + //-------------------------------------------------------- + // Reg and Wire Declarations + + assign w_full = ~(w_wrAddrReg == r_rdAddrReg); + + assign w_wrAddrNxt = ~w_wrAddrReg ; + + assign r_rdAddrNxt = ~r_rdAddrReg; + + assign r_empty = (r_wrAddrReg == r_rdAddrReg); + + 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; + + + assign r_bits = mailboxReg; + + always @(posedge w_clock) begin + if (w_fire) begin + mailboxReg <= w_bits; + end + end + + //-------------------------------------------------------- + // Sequential logic + // + + always @(posedge w_clock or posedge w_reset) begin + if (w_reset ) begin + w_wrAddrReg <= 1'b0; + + rdAddrReg_sync <= 1'b0; + w_rdAddrReg <= 1'b0; + end else begin + if (w_fire) begin + w_wrAddrReg <= w_wrAddrNxt; + end + rdAddrReg_sync <= r_rdAddrReg; + w_rdAddrReg <= rdAddrReg_sync; + end + end + + always @(posedge r_clock or posedge r_reset) begin + if (r_reset) begin + r_rdAddrReg <= 1'b0; + + wrAddrReg_sync <= 1'b0; + r_wrAddrReg <= 1'b0; + end else begin + if (r_fire) begin + r_rdAddrReg <= r_rdAddrNxt; + end + wrAddrReg_sync <= w_wrAddrReg; + r_wrAddrReg <= wrAddrReg_sync; + end + end // always @ (posedge r_clock) + +endmodule // AsyncMailbox diff --git a/vsrc/DebugTransportModuleJtag.v b/vsrc/DebugTransportModuleJtag.v new file mode 100755 index 00000000..0c8a07b0 --- /dev/null +++ b/vsrc/DebugTransportModuleJtag.v @@ -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 + + diff --git a/vsrc/jtag_vpi.tab b/vsrc/jtag_vpi.tab new file mode 100644 index 00000000..c042ce05 --- /dev/null +++ b/vsrc/jtag_vpi.tab @@ -0,0 +1,2 @@ +$send_result_to_server call=send_result_to_server +$check_for_command call=check_for_command \ No newline at end of file diff --git a/vsrc/jtag_vpi.v b/vsrc/jtag_vpi.v new file mode 100644 index 00000000..78571153 --- /dev/null +++ b/vsrc/jtag_vpi.v @@ -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, + * 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