From 877e1cfba1c092cd17452c3e92f336afdcfd8cc3 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 27 Mar 2017 20:51:54 -0700 Subject: [PATCH 01/15] debug: add scripts to generate v13 Debug ROM contents. --- scripts/debug_rom/Makefile | 31 ++++++++++++++ scripts/debug_rom/debug_rom.S | 76 +++++++++++++++++++++++++++++++++++ scripts/debug_rom/link.ld | 16 ++++++++ 3 files changed, 123 insertions(+) create mode 100644 scripts/debug_rom/Makefile create mode 100755 scripts/debug_rom/debug_rom.S create mode 100644 scripts/debug_rom/link.ld diff --git a/scripts/debug_rom/Makefile b/scripts/debug_rom/Makefile new file mode 100644 index 00000000..46f5e811 --- /dev/null +++ b/scripts/debug_rom/Makefile @@ -0,0 +1,31 @@ +# See LICENSE.SiFive for license details +# Recursive make is bad, but in this case we're cross compiling which is a +# pretty unusual use case. + +CC = $(RISCV)/bin/riscv64-unknown-elf-gcc +OBJCOPY = $(RISCV)/bin/riscv64-unknown-elf-objcopy + +COMPILE = $(CC) -nostdlib -nostartfiles -I$(RISCV)/include/ -Tlink.ld + +ELFS = debug_rom +DEPS = debug_rom.S link.ld + +all: $(patsubst %,%.h,$(ELFS)) + +publish: debug_rom.scala + mv $< ../../src/main/scala/uncore/devices/debug/DebugRomContents.scala + +%.scala: %.raw + xxd -i $^ | sed -e "s/^unsigned char debug_rom_raw\[\] = {/\/\/ This file was auto-generated by 'make publish' in debug\/ directory.\n\npackage uncore.devices\n\nobject DebugRomContents {\n\n def apply() : Array[Byte] = { Array (/" \ + -e "s/};/ ).map(_.toByte) }\n\n}/" \ + -e "s/^unsigned int debug_rom_raw_len.*//" > $@ + + +%.raw: % + $(OBJCOPY) -O binary --only-section .text $^ $@ + +debug_rom: $(DEPS) + $(COMPILE) -o $@ $^ + +clean: + rm -f $(ELFS) debug_rom*.raw debug_rom*.h diff --git a/scripts/debug_rom/debug_rom.S b/scripts/debug_rom/debug_rom.S new file mode 100755 index 00000000..73267976 --- /dev/null +++ b/scripts/debug_rom/debug_rom.S @@ -0,0 +1,76 @@ +// See LICENSE.SiFive for license details. + +#include "spike/encoding.h" + +// These are implementation-specific addresses in the Debug Module +#define HALTED 0x100 +#define GOING 0x104 +#define RESUMING 0x108 +#define EXCEPTION 0x10C + +// Region of memory where each hart has 1 +// byte to read. +#define OK_GO 0x400 + + .option norvc + .global entry + .global exception + + // Entry location on ebreak, Halt, or Breakpoint + // It is the same for all harts. They branch when + // their specific OK_GO bit is set. + +entry: + jal zero, _entry +resume: + jal zero, _resume +exception: + jal zero, _exception + +_entry: + // This fence is required because the execution may have written something + // into the Abstract Data or Program Buffer registers. + fence + csrw CSR_DSCRATCH, s0 // Save s0 to allow signaling MHARTID + + // We continue to let the hart know that we are halted in order that + // a DM which was reset is still made aware that a hart is halted. + // We keep checking both whether there is something the debugger wants + // us to do, or whether we should not be halted anymore. +entry_loop: + csrr s0, CSR_MHARTID + sw s0, HALTED(zero) + lb s0, OK_GO(s0) // 1 byte flag per hart. Only one hart advances here. + bne zero, s0, going + jal zero, entry_loop + +_exception: + sw zero, EXCEPTION(zero) // Let debug module know you got an exception. + ebreak + +going: + csrr s0, CSR_DSCRATCH // Restore s0 here + sw zero, GOING(zero) // When debug module sees this write, the OK_GO flag is reset. + jalr zero, zero, %lo(whereto) // Rocket-Chip has a specific hack which is that jalr in + // Debug Mode will flush the I-Cache. We need that so that the + // remainder of the variable instructions will be what Debug Module + // intends. + +_resume: + csrw CSR_DSCRATCH, s0 // Save s0 to allow signaling MHARTID + csrr s0, CSR_MHARTID + sw s0, RESUMING(zero) // Let the Debug Module know you're not halted anymore. + csrr s0, CSR_DSCRATCH // Restore s0 + dret + + // END OF ACTUAL "ROM" CONTENTS. BELOW IS JUST FOR LINKER SCRIPT. + +.section .whereto +whereto: + nop + // Variable "ROM" This is : jal x0 abstract, jal x0 program_buffer, + // or jal x0 resume, as desired. + // Debug Module state machine tracks what is 'desired'. + // We don't need/want to use jalr here because all of the + // Variable ROM contents are set by + // Debug Module before setting the OK_GO byte. diff --git a/scripts/debug_rom/link.ld b/scripts/debug_rom/link.ld new file mode 100644 index 00000000..053bb43c --- /dev/null +++ b/scripts/debug_rom/link.ld @@ -0,0 +1,16 @@ +/* See LICENSE.SiFive for license details. */ +OUTPUT_ARCH( "riscv" ) +ENTRY( entry ) +SECTIONS +{ + .whereto 0x300 : + { + *(.whereto) + } + . = 0x800; + .text : + { + *(.text) + } + _end = .; +} From 0c3d85b52b4bdc882ded60eddd15ce5737c6318a Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 27 Mar 2017 21:01:36 -0700 Subject: [PATCH 02/15] debug: add generated ROM contents and register fields. --- .../devices/debug/DebugRomContents.scala | 18 + .../devices/debug/abstract_commands.scala | 72 ++ .../uncore/devices/debug/dm1_registers.scala | 899 ++++++++++++++++++ 3 files changed, 989 insertions(+) create mode 100644 src/main/scala/uncore/devices/debug/DebugRomContents.scala create mode 100644 src/main/scala/uncore/devices/debug/abstract_commands.scala create mode 100644 src/main/scala/uncore/devices/debug/dm1_registers.scala diff --git a/src/main/scala/uncore/devices/debug/DebugRomContents.scala b/src/main/scala/uncore/devices/debug/DebugRomContents.scala new file mode 100644 index 00000000..13438445 --- /dev/null +++ b/src/main/scala/uncore/devices/debug/DebugRomContents.scala @@ -0,0 +1,18 @@ +// This file was auto-generated by 'make publish' in debug/ directory. + +package uncore.devices + +object DebugRomContents { + + def apply() : Array[Byte] = { Array ( + 0x6f, 0x00, 0xc0, 0x00, 0x6f, 0x00, 0x80, 0x03, 0x6f, 0x00, 0x00, 0x02, + 0x0f, 0x00, 0xf0, 0x0f, 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x40, 0xf1, + 0x23, 0x20, 0x80, 0x10, 0x03, 0x04, 0x04, 0x40, 0x63, 0x18, 0x80, 0x00, + 0x6f, 0xf0, 0x1f, 0xff, 0x23, 0x26, 0x00, 0x10, 0x73, 0x00, 0x10, 0x00, + 0x73, 0x24, 0x20, 0x7b, 0x23, 0x22, 0x00, 0x10, 0x67, 0x00, 0x00, 0x30, + 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x40, 0xf1, 0x23, 0x24, 0x80, 0x10, + 0x73, 0x24, 0x20, 0x7b, 0x73, 0x00, 0x20, 0x7b + ).map(_.toByte) } + +} + diff --git a/src/main/scala/uncore/devices/debug/abstract_commands.scala b/src/main/scala/uncore/devices/debug/abstract_commands.scala new file mode 100644 index 00000000..2ac442ed --- /dev/null +++ b/src/main/scala/uncore/devices/debug/abstract_commands.scala @@ -0,0 +1,72 @@ +package uncore.devices + +import Chisel._ + +// This file was auto-generated from the repository at https://github.com/sifive/riscv-debug-spec.git, +// 'make chisel' + +object AC_RegAddrs { +} + +class ACCESS_REGISTERFields extends Bundle { + + /* This is 0 to indicate Access Register Command. + */ + val cmdtype = UInt(8.W) + + val reserved0 = UInt(1.W) + + /* 2: Access the lowest 32 bits of the register. + + 3: Access the lowest 64 bits of the register. + + 4: Access the lowest 128 bits of the register. + + If \Fsize specifies a size larger than the register's actual size, + then the access must fail. If a register is accessible, then reads of \Fsize + less than or equal to the register's actual size must be supported. + */ + val size = UInt(3.W) + + /* When 1, execute the program in the Program Buffer exactly once + before performing the transfer. + \textbf{WARNING: preexec is considered for removal.} + */ + val preexec = Bool() + + /* When 1, execute the program in the Program Buffer exactly once + after performing the transfer, if any. + */ + val postexec = Bool() + + /* 0: Don't do the operation specified by \Fwrite. + + 1: Do the operation specified by \Fwrite. + */ + val transfer = Bool() + + /* When \Ftransfer is set: + 0: Copy data from the specified register into {\tt arg0} portion + of {\tt data}. + + 1: Copy data from {\tt arg0} portion of {\tt data} into the + specified register. + */ + val write = Bool() + + /* Number of the register to access, as described in Table~\ref{tab:regno}. + */ + val regno = UInt(16.W) + +} + +class QUICK_ACCESSFields extends Bundle { + + /* This is 1 to indicate Quick Access command. + */ + val cmdtype = UInt(8.W) + + val reserved0 = UInt(24.W) + +} + diff --git a/src/main/scala/uncore/devices/debug/dm1_registers.scala b/src/main/scala/uncore/devices/debug/dm1_registers.scala new file mode 100644 index 00000000..2f0f9109 --- /dev/null +++ b/src/main/scala/uncore/devices/debug/dm1_registers.scala @@ -0,0 +1,899 @@ +package uncore.devices + +import Chisel._ + +// This file was auto-generated from the repository at https://github.com/sifive/riscv-debug-spec.git, +// 'make chisel' + +object DMI_RegAddrs { + /* The address of this register will not change in the future, because it + contains \Fversion. It has changed from version 0.11 of this spec. + + This register reports status for the overall debug module + as well as the currently selected harts, as defined in \Fhasel. + */ + def DMI_DMSTATUS = 0x11 + + /* This register controls the overall debug module + as well as the currently selected harts, as defined in \Fhasel. + */ + def DMI_DMCONTROL = 0x10 + + /* This register gives information about the hart currently + selected by \Fhartsel. + + This register is optional. If it is not present it should + read all-zero. + + If this register is included, the debugger can do more with + the Program Buffer by writing programs which + explicitly access the {\tt data} and/or {\tt dscratch} + registers. + */ + def DMI_HARTINFO = 0x12 + + /* This register contains a summary of which harts are halted. + + Each bit contains the logical OR of 32 halt bits. When there are a + large number of harts in the system, the debugger can first read this + register, and then read from the halt region (0x40--0x5f) to determine + which hart is the one that is halted. + */ + def DMI_HALTSUM = 0x13 + + /* This register selects which of the 32-bit portion of the hart array mask register + is accessible in \Rhawindow. + + The hart array mask register provides a mask of all harts controlled by + the debug module. A hart is part of the currently selected harts if + the corresponding bit is set in the hart array mask register and + \Fhasel in \Rdmcontrol is 1, or if the hart is selected by \Fhartsel. + */ + def DMI_HAWINDOWSEL = 0x14 + + /* This register provides R/W access to a 32-bit portion of the + hart array mask register. The position of the window is determined by + \Rhawindowsel. + */ + def DMI_HAWINDOW = 0x15 + + def DMI_ABSTRACTCS = 0x16 + + /* Writes to this register cause the corresponding abstract command to be + executed. + + Writing while an abstract command is executing causes \Fcmderr to be set. + + If \Fcmderr is non-zero, writes to this register are ignored. + + \begin{commentary} + \Fcmderr inhibits starting a new command to accommodate debuggers + that, for performance reasons, send several commands to be executed + in a row without checking \Fcmderr in between. They can safely do + so and check \Fcmderr at the end without worrying that one command + failed but then a later command (which might have depended on the + previous one succeeding) passed. + \end{commentary} + */ + def DMI_COMMAND = 0x17 + + /* This register is optional. Including it allows more efficient burst accesses. + Debugger can attempt to set bits and read them back to determine if the functionality is supported. + */ + def DMI_ABSTRACTAUTO = 0x18 + + /* The Configuration String is described in the RISC-V Priviledged Specification. + When {\tt cfgstrvalid} is set, reading this register returns bits 31:0 of the configuration + string address. Reading the other {\tt cfgstraddr} registers returns the upper bits of the + address. + + When system bus mastering is implemented, this should be the + address that should be used with the System Bus Access module. Otherwise, + this should be the address that should be used to access the + config string when \Fhartsel=0. + + If {\tt cfgstrvalid} is 0, then the {\tt cfgstraddr} registers + hold identifier information which is not + further specified in this document. + */ + def DMI_CFGSTRADDR0 = 0x19 + + def DMI_CFGSTRADDR1 = 0x1a + + def DMI_CFGSTRADDR2 = 0x1b + + def DMI_CFGSTRADDR3 = 0x1c + + /* Basic read/write registers that may be read or changed by abstract + commands. + + Accessing them while an abstract command is executing causes \Fcmderr + to be set. + + The values in these registers may not be preserved after an abstract + command is executed. The only guarantees on their contents are the ones + offered by the command in question. If the command fails, no + assumptions can be made about the contents of these registers. + */ + def DMI_DATA0 = 0x04 + + def DMI_DATA1 = 0x05 + + def DMI_DATA2 = 0x06 + + def DMI_DATA3 = 0x07 + + def DMI_DATA4 = 0x08 + + def DMI_DATA5 = 0x09 + + def DMI_DATA6 = 0x0a + + def DMI_DATA7 = 0x0b + + def DMI_DATA8 = 0x0c + + def DMI_DATA9 = 0x0d + + def DMI_DATA10 = 0x0e + + def DMI_DATA11 = 0x0f + + /* The {\tt progbuf} registers provide read/write access to the optional + program buffer. + */ + def DMI_PROGBUF0 = 0x20 + + def DMI_PROGBUF1 = 0x21 + + def DMI_PROGBUF2 = 0x22 + + def DMI_PROGBUF3 = 0x23 + + def DMI_PROGBUF4 = 0x24 + + def DMI_PROGBUF5 = 0x25 + + def DMI_PROGBUF6 = 0x26 + + def DMI_PROGBUF7 = 0x27 + + def DMI_PROGBUF8 = 0x28 + + def DMI_PROGBUF9 = 0x29 + + def DMI_PROGBUF10 = 0x2a + + def DMI_PROGBUF11 = 0x2b + + def DMI_PROGBUF12 = 0x2c + + def DMI_PROGBUF13 = 0x2d + + def DMI_PROGBUF14 = 0x2e + + def DMI_PROGBUF15 = 0x2f + + /* This register serves as a 32-bit serial port to the authentication + module. + + When \Fauthbusy is clear, the debugger can communicate with the + authentication module by reading or writing this register. There is no + separate mechanism to signal overflow/underflow. + */ + def DMI_AUTHDATA = 0x30 + + /* If \Fserialcount is 0, this register is not present. + */ + def DMI_SERCS = 0x34 + + /* If \Fserialcount is 0, this register is not present. + + This register provides access to the write data queue of the serial port + selected by \Fserial in \Rsercs. + + If the {\tt error} bit is not set and the queue is not full, a write to this register + adds the written data to the core-to-debugger queue. + Otherwise the {\tt error} bit is set and the write returns error. + + A read to this register returns the last data written. + */ + def DMI_SERTX = 0x35 + + /* If \Fserialcount is 0, this register is not present. + + This register provides access to the read data queues of the serial port + selected by \Fserial in \Rsercs. + + If the {\tt error} bit is not set and the queue is not empty, a read from this register reads the + oldest entry in the debugger-to-core queue, and removes that entry from the queue. + Otherwise the {\tt error} bit is set and the read returns error. + */ + def DMI_SERRX = 0x36 + + def DMI_SBCS = 0x38 + + /* If \Fsbasize is 0, then this register is not present. + + When the system bus master is busy, + writes to this register will return error + and \Fsberror is set. + + If \Fsberror is 0 and \Fsbautoread is set then the system bus + master will start + to read after updating the address from \Faddress. The access size is + controlled by \Fsbaccess in \Rsbcs. + + If \Fsbsingleread is set, the bit is cleared. + */ + def DMI_SBADDRESS0 = 0x39 + + def DMI_SBADDRESS1 = 0x3a + + /* If \Fsbasize is less than 65, then this register is not present. + */ + def DMI_SBADDRESS2 = 0x3b + + /* If all of the {\tt sbaccess} bits in \Rsbcs are 0, then this register + is not present. + + If \Fsberror isn't 0 then accesses return error, and don't do anything + else. + + Writes to this register: + + 1. If the bus master is busy then accesses set \Fsberror, return error, + and don't do anything else. + + 2. Update internal data. + + 3. Start a bus write of the internal data to the internal address. + + 4. If \Fsbautoincrement is set, increment the internal address. + + Reads to this register: + + 1. If bits 31:0 of the internal data register haven't been updated + since the last time this register was read, then set \Fsberror, return + error, and don't do anything else. + + 2. ``Return'' the data. + + 3. If \Fsbautoincrement is set, increment the internal address. + + 4. If \Fsbautoread is set, start another system bus read. + */ + def DMI_SBDATA0 = 0x3c + + /* If \Fsbaccesssixtyfour and \Fsbaccessonetwentyeight are 0, then this + register is not present. + */ + def DMI_SBDATA1 = 0x3d + + /* This register only exists if \Fsbaccessonetwentyeight is 1. + */ + def DMI_SBDATA2 = 0x3e + + /* This register only exists if \Fsbaccessonetwentyeight is 1. + */ + def DMI_SBDATA3 = 0x3f + +} + +class DMSTATUSFields extends Bundle { + + val reserved0 = UInt(16.W) + + /* This field is 1 when all currently selected harts do not exist in this system. + */ + val allnonexistent = Bool() + + /* This field is 1 when any currently selected hart does not exist in this system. + */ + val anynonexistent = Bool() + + /* This field is 1 when all currently selected harts are unavailable. + */ + val allunavail = Bool() + + /* This field is 1 when any currently selected hart is unavailable. + */ + val anyunavail = Bool() + + /* This field is 1 when all currently selected harts are running. + */ + val allrunning = Bool() + + /* This field is 1 when any currently selected hart is running. + */ + val anyrunning = Bool() + + /* This field is 1 when all currently selected harts are halted. + */ + val allhalted = Bool() + + /* This field is 1 when any currently selected hart is halted. + */ + val anyhalted = Bool() + + /* 0 when authentication is required before using the DM. 1 when the + authentication check has passed. On components that don't implement + authentication, this bit must be preset as 1. + */ + val authenticated = Bool() + + /* 0: The authentication module is ready to process the next + read/write to \Rauthdata. + + 1: The authentication module is busy. Accessing \Rauthdata results + in unspecified behavior. + + \Fauthbusy only becomes set in immediate response to an access to + \Rauthdata. + */ + val authbusy = Bool() + + val reserved1 = UInt(1.W) + + val cfgstrvalid = Bool() + + /* Reserved for future use. Reads as 0. + */ + val versionhi = UInt(2.W) + + /* 00: There is no Debug Module present. + + 01: There is a Debug Module and it conforms to version 0.11 of this + specification. + + 10: There is a Debug Module and it conforms to version 0.13 of this + specification. + + 11: Reserved for future use. + */ + val versionlo = UInt(2.W) + +} + +class DMCONTROLFields extends Bundle { + + /* Halt request signal for all currently selected harts. When 1, the + hart will halt if it is not currently halted. + Setting both \Fhaltreq and \Fresumereq leads to undefined behavior. + + Writes apply to the new value of \Fhartsel and \Fhasel. + */ + val haltreq = Bool() + + /* Resume request signal for all currently selected harts. When 1, + the hart will resume if it is currently halted. + Setting both \Fhaltreq and \Fresumereq leads to undefined behavior. + + Writes apply to the new value of \Fhartsel and \Fhasel. + */ + val resumereq = Bool() + + /* This optional bit controls reset to all the currently selected harts. + To perform a reset the debugger writes 1, and then writes 0 to + deassert the reset signal. + + If this feature is not implemented, the bit always stays 0, so + after writing 1 the debugger can read the register back to see if + the feature is supported. + + Writes apply to the new value of \Fhartsel and \Fhasel. + */ + val hartreset = Bool() + + val reserved0 = UInt(2.W) + + /* Selects the definition of currently selected harts. + + 0: There is a single currently selected hart, that selected by \Fhartsel. + + 1: There may be multiple currently selected harts -- that selected by \Fhartsel, + plus those selected by the hart array mask register. + + An implementation which does not implement the hart array mask register + should tie this field to 0. A debugger which wishes to use the hart array + mask register feature should set this bit and read back to see if the functionality + is supported. + */ + val hasel = Bool() + + /* The DM-specific index of the hart to select. This hart is always part of the + currently selected harts. + */ + val hartsel = UInt(10.W) + + val reserved1 = UInt(14.W) + + /* This bit controls the reset signal from the DM to the rest of the + system. To perform a reset the debugger writes 1, and then writes 0 + to deassert the reset. + */ + val ndmreset = Bool() + + /* This bit serves as a reset signal for the Debug Module itself. + + 0: The module's state, including authentication mechanism, + takes its reset values (the \Fdmactive bit is the only bit which can + be written to something other than its reset value). + + 1: The module functions normally. + + No other mechanism should exist that may result in resetting the + Debug Module after power up, including the platform's system reset + or Debug Transport reset signals. + + A debugger should pulse this bit low to ensure that the Debug + Module is fully reset and ready to use. + + Implementations may use this bit to aid debugging, for example by + preventing the Debug Module from being power gated while debugging + is active. + */ + val dmactive = Bool() + +} + +class HARTINFOFields extends Bundle { + + val reserved0 = UInt(8.W) + + /* Number of {\tt dscratch} registers available for the debugger + to use during program buffer execution, starting from \Rdscratchzero. + The debugger can make no assumptions about the contents of these + registers between commands. + */ + val nscratch = UInt(4.W) + + val reserved1 = UInt(3.W) + + /* 0: The {\tt data} registers are shadowed in the hart by CSR + registers. Each CSR register is XLEN bits in size, and corresponds + to a single argument, per Table~\ref{tab:datareg}. + + 1: The {\tt data} registers are shadowed in the hart's memory map. + Each register takes up 4 bytes in the memory map. + */ + val dataaccess = Bool() + + /* If \Fdataaccess is 0: Number of CSR registers dedicated to + shadowing the {\tt data} registers. + + If \Fdataaccess is 1: Number of 32-bit words in the memory map + dedicated to shadowing the {\tt data} registers. + */ + val datasize = UInt(4.W) + + /* If \Fdataaccess is 0: The number of the first CSR dedicated to + shadowing the {\tt data} registers. + + If \Fdataaccess is 1: Signed address of RAM where the {\tt data} + registers are shadowed. + */ + val dataaddr = UInt(12.W) + +} + +class HALTSUMFields extends Bundle { + + val halt1023_992 = Bool() + + val halt991_960 = Bool() + + val halt959_928 = Bool() + + val halt927_896 = Bool() + + val halt895_864 = Bool() + + val halt863_832 = Bool() + + val halt831_800 = Bool() + + val halt799_768 = Bool() + + val halt767_736 = Bool() + + val halt735_704 = Bool() + + val halt703_672 = Bool() + + val halt671_640 = Bool() + + val halt639_608 = Bool() + + val halt607_576 = Bool() + + val halt575_544 = Bool() + + val halt543_512 = Bool() + + val halt511_480 = Bool() + + val halt479_448 = Bool() + + val halt447_416 = Bool() + + val halt415_384 = Bool() + + val halt383_352 = Bool() + + val halt351_320 = Bool() + + val halt319_288 = Bool() + + val halt287_256 = Bool() + + val halt255_224 = Bool() + + val halt223_192 = Bool() + + val halt191_160 = Bool() + + val halt159_128 = Bool() + + val halt127_96 = Bool() + + val halt95_64 = Bool() + + val halt63_32 = Bool() + + val halt31_0 = Bool() + +} + +class HAWINDOWSELFields extends Bundle { + + val reserved0 = UInt(27.W) + + val hawindowsel = UInt(5.W) + +} + +class HAWINDOWFields extends Bundle { + + val maskdata = UInt(32.W) + +} + +class ABSTRACTCSFields extends Bundle { + + val reserved0 = UInt(3.W) + + /* Size of the Program Buffer, in 32-bit words. Valid sizes are 0 - 16. + + TODO: Explain what can be done with each size of the buffer, to suggest + why you would want more or less words. + */ + val progsize = UInt(5.W) + + val reserved1 = UInt(11.W) + + /* 1: An abstract command is currently being executed. + + This bit is set as soon as \Rcommand is written, and is + not cleared until that command has completed. + */ + val busy = Bool() + + val reserved2 = UInt(1.W) + + /* Gets set if an abstract command fails. The bits in this field remain set until + they are cleared by writing 1 to them. No abstract command is + started until the value is reset to 0. + + 0 (none): No error. + + 1 (busy): An abstract command was executing while \Rcommand or one + of the {\tt data} registers was accessed. + + 2 (not supported): The requested command is not supported. A + command that is not supported while the hart is running may be + supported when it is halted. + + 3 (exception): An exception occurred while executing the command + (eg. while executing the Program Buffer). + + 4 (halt/resume): An abstract command couldn't execute because the + hart wasn't in the expected state (running/halted). + + 7 (other): The command failed for another reason. + */ + val cmderr = UInt(3.W) + + val reserved3 = UInt(3.W) + + /* Number of {\tt data} registers that are implemented as part of the + abstract command interface. Valid sizes are 0 - 8. + */ + val datacount = UInt(5.W) + +} + +class COMMANDFields extends Bundle { + + /* The type determines the overall functionality of this + abstract command. + */ + val cmdtype = UInt(8.W) + + /* This field is interpreted in a command-specific manner, + described for each abstract command. + */ + val control = UInt(24.W) + +} + +class ABSTRACTAUTOFields extends Bundle { + + /* When a bit in this field is 1, read or write accesses the corresponding {\tt progbuf} word + cause the command in \Rcommand to be executed again. + */ + val autoexecprogbuf = UInt(16.W) + + val reserved0 = UInt(4.W) + + /* When a bit in this field is 1, read or write accesses the corresponding {\tt data} word + cause the command in \Rcommand to be executed again. + */ + val autoexecdata = UInt(12.W) + +} + +class CFGSTRADDR0Fields extends Bundle { + + val addr = UInt(32.W) + +} + +class DATA0Fields extends Bundle { + + val data = UInt(32.W) + +} + +class PROGBUF0Fields extends Bundle { + + val data = UInt(32.W) + +} + +class AUTHDATAFields extends Bundle { + + val data = UInt(32.W) + +} + +class SERCSFields extends Bundle { + + /* Number of supported serial ports. + */ + val serialcount = UInt(4.W) + + val reserved0 = UInt(1.W) + + /* Select which serial port is accessed by \Rserrx and \Rsertx. + */ + val serial = UInt(3.W) + + val error7 = Bool() + + val valid7 = Bool() + + val full7 = Bool() + + val error6 = Bool() + + val valid6 = Bool() + + val full6 = Bool() + + val error5 = Bool() + + val valid5 = Bool() + + val full5 = Bool() + + val error4 = Bool() + + val valid4 = Bool() + + val full4 = Bool() + + val error3 = Bool() + + val valid3 = Bool() + + val full3 = Bool() + + val error2 = Bool() + + val valid2 = Bool() + + val full2 = Bool() + + val error1 = Bool() + + val valid1 = Bool() + + val full1 = Bool() + + /* 1 when the debugger-to-core queue for serial port 0 has + over or underflowed. This bit will remain set until it is reset by + writing 1 to this bit. + */ + val error0 = Bool() + + /* 1 when the core-to-debugger queue for serial port 0 is not empty. + */ + val valid0 = Bool() + + /* 1 when the debugger-to-core queue for serial port 0 is full. + */ + val full0 = Bool() + +} + +class SERTXFields extends Bundle { + + val data = UInt(32.W) + +} + +class SERRXFields extends Bundle { + + val data = UInt(32.W) + +} + +class SBCSFields extends Bundle { + + val reserved0 = UInt(11.W) + + /* When a 1 is written here, triggers a read at the address in {\tt + sbaddress} using the access size set by \Fsbaccess. + */ + val sbsingleread = Bool() + + /* Select the access size to use for system bus accesses triggered by + writes to the {\tt sbaddress} registers or \Rsbdatazero. + + 0: 8-bit + + 1: 16-bit + + 2: 32-bit + + 3: 64-bit + + 4: 128-bit + + If an unsupported system bus access size is written here, + the DM may not perform the access, or may perform the access + with any access size. + */ + val sbaccess = UInt(3.W) + + /* When 1, the internal address value (used by the system bus master) + is incremented by the access size (in bytes) selected in \Fsbaccess + after every system bus access. + */ + val sbautoincrement = Bool() + + /* When 1, every read from \Rsbdatazero automatically triggers a system + bus read at the new address. + */ + val sbautoread = Bool() + + /* When the debug module's system bus + master causes a bus error, this field gets set. The bits in this + field remain set until they are cleared by writing 1 to them. + While this field is non-zero, no more system bus accesses can be + initiated by the debug module. + + 0: There was no bus error. + + 1: There was a timeout. + + 2: A bad address was accessed. + + 3: There was some other error (eg. alignment). + + 4: The system bus master was busy when a one of the + {\tt sbaddress} or {\tt sbdata} registers was written, + or the {\tt sbdata} register was read when it had + stale data. + */ + val sberror = UInt(3.W) + + /* Width of system bus addresses in bits. (0 indicates there is no bus + access support.) + */ + val sbasize = UInt(7.W) + + /* 1 when 128-bit system bus accesses are supported. + */ + val sbaccess128 = Bool() + + /* 1 when 64-bit system bus accesses are supported. + */ + val sbaccess64 = Bool() + + /* 1 when 32-bit system bus accesses are supported. + */ + val sbaccess32 = Bool() + + /* 1 when 16-bit system bus accesses are supported. + */ + val sbaccess16 = Bool() + + /* 1 when 8-bit system bus accesses are supported. + */ + val sbaccess8 = Bool() + +} + +class SBADDRESS0Fields extends Bundle { + + /* Accesses bits 31:0 of the internal address. + */ + val address = UInt(32.W) + +} + +class SBADDRESS1Fields extends Bundle { + + /* Accesses bits 63:32 of the internal address (if the system address + bus is that wide). + */ + val address = UInt(32.W) + +} + +class SBADDRESS2Fields extends Bundle { + + /* Accesses bits 95:64 of the internal address (if the system address + bus is that wide). + */ + val address = UInt(32.W) + +} + +class SBDATA0Fields extends Bundle { + + /* Accesses bits 31:0 of the internal data. + */ + val data = UInt(32.W) + +} + +class SBDATA1Fields extends Bundle { + + /* Accesses bits 63:32 of the internal data (if the system bus is + that wide). + */ + val data = UInt(32.W) + +} + +class SBDATA2Fields extends Bundle { + + /* Accesses bits 95:64 of the internal data (if the system bus is + that wide). + */ + val data = UInt(32.W) + +} + +class SBDATA3Fields extends Bundle { + + /* Accesses bits 127:96 of the internal data (if the system bus is + that wide). + */ + val data = UInt(32.W) + +} + From 43804726acbe27abe89ada5bac99ac523f474a15 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 27 Mar 2017 21:05:05 -0700 Subject: [PATCH 03/15] tilelink2: more helpful requirement message --- src/main/scala/uncore/tilelink2/Parameters.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index b22b1cc1..802e9c92 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -56,7 +56,7 @@ case class TLManagerParameters( // The device had better not support a transfer larger than it's alignment val minAlignment = address.map(_.alignment).min - require (minAlignment >= maxTransfer) + require (minAlignment >= maxTransfer, "minAlignment (" + minAlignment + ") must be >= maxTransfer (" + maxTransfer + ")") def toResource: ResourceAddress = { ResourceAddress(address, From 42ca597478659c921b56befd4e7b7cabf8eeb84a Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 27 Mar 2017 21:19:08 -0700 Subject: [PATCH 04/15] debug: Breaking change until FESVR is updated as well. * Replace v11 Debug Module with v13 module. * Correct all instantiating interfaces. * Rename "Debug Bus" to "DMI" (Debug Module Interface) * Use Diplomacy interrupts for DebugInterrupt * Seperate device for TLDebugROM --- src/main/scala/coreplex/Configs.scala | 2 +- src/main/scala/coreplex/RISCVPlatform.scala | 14 +- src/main/scala/coreplex/RocketTiles.scala | 8 +- src/main/scala/rocketchip/Configs.scala | 1 + .../scala/rocketchip/DebugTransport.scala | 277 +++- src/main/scala/rocketchip/RISCVPlatform.scala | 86 +- src/main/scala/rocketchip/TestHarness.scala | 24 +- src/main/scala/uncore/devices/Debug.scala | 887 ------------- .../scala/uncore/devices/debug/Debug.scala | 1177 +++++++++++++++++ vsrc/jtag_vpi.v | 20 +- 10 files changed, 1477 insertions(+), 1019 deletions(-) delete mode 100644 src/main/scala/uncore/devices/Debug.scala create mode 100644 src/main/scala/uncore/devices/debug/Debug.scala diff --git a/src/main/scala/coreplex/Configs.scala b/src/main/scala/coreplex/Configs.scala index 92a48115..0d7b2a50 100644 --- a/src/main/scala/coreplex/Configs.scala +++ b/src/main/scala/coreplex/Configs.scala @@ -22,7 +22,7 @@ class BaseCoreplexConfig extends Config ((site, here, up) => { case BuildCore => (p: Parameters) => new Rocket()(p) case RocketCrossing => Synchronous case RocketTilesKey => Nil - case DMKey => new DefaultDebugModuleConfig(site(NTiles), site(XLen)) + case DMKey => new DefaultDebugModuleConfig(site(XLen)) case NTiles => site(RocketTilesKey).size case CBusConfig => TLBusConfig(beatBytes = site(XLen)/8) case L1toL2Config => TLBusConfig(beatBytes = site(XLen)/8) // increase for more PCIe bandwidth diff --git a/src/main/scala/coreplex/RISCVPlatform.scala b/src/main/scala/coreplex/RISCVPlatform.scala index e2e6d079..446aaa87 100644 --- a/src/main/scala/coreplex/RISCVPlatform.scala +++ b/src/main/scala/coreplex/RISCVPlatform.scala @@ -15,10 +15,12 @@ trait CoreplexRISCVPlatform extends CoreplexNetwork { val module: CoreplexRISCVPlatformModule val debug = LazyModule(new TLDebugModule()) + val debug_rom = LazyModule(new TLDebugModuleROM()) val plic = LazyModule(new TLPLIC(maxPriorities = 7)) val clint = LazyModule(new CoreplexLocalInterrupter) debug.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node) + debug_rom.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node) plic.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node) clint.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node) @@ -32,7 +34,7 @@ trait CoreplexRISCVPlatform extends CoreplexNetwork { trait CoreplexRISCVPlatformBundle extends CoreplexNetworkBundle { val outer: CoreplexRISCVPlatform - val debug = new AsyncDebugBusIO().flip + val debug = new ClockedDMIIO().flip val rtcToggle = Bool(INPUT) val resetVector = UInt(INPUT, p(XLen)) } @@ -41,8 +43,14 @@ trait CoreplexRISCVPlatformModule extends CoreplexNetworkModule { val outer: CoreplexRISCVPlatform val io: CoreplexRISCVPlatformBundle - // Synchronize the debug bus into the coreplex - outer.debug.module.io.db <> FromAsyncDebugBus(io.debug) + outer.debug.module.io.dmi <> io.debug + // TODO in inheriting traits: Set this to something meaningful, e.g. "component is in reset or powered down" + val nDebugComponents = outer.debug.intnode.bundleOut.size + outer.debug.module.io.ctrl.debugUnavail := Vec.fill(nDebugComponents){Bool(false)} + // TODO in inheriting traits: Use these values in your power and reset controls. + // TODO Or move these signals to Coreplex Top Level + // ... := outer.debug.module.io.ctrl.dmactive + // ... := outer.debug.module.io.ctrl.ndreset // Synchronize the rtc into the coreplex val rtcSync = ShiftRegister(io.rtcToggle, 3) diff --git a/src/main/scala/coreplex/RocketTiles.scala b/src/main/scala/coreplex/RocketTiles.scala index 5026cda9..4990c6d3 100644 --- a/src/main/scala/coreplex/RocketTiles.scala +++ b/src/main/scala/coreplex/RocketTiles.scala @@ -33,11 +33,8 @@ trait HasRocketTiles extends CoreplexRISCVPlatform { case SharedMemoryTLEdge => l1tol2.node.edgesIn(0) } - // Hack debug interrupt into a node (future debug module should use diplomacy) - val debugNode = IntInternalInputNode(IntSourcePortSimple()) - val intBar = LazyModule(new IntXbar) - intBar.intnode := debugNode + intBar.intnode := debug.intnode // Debug Interrupt intBar.intnode := clint.intnode // msip+mtip intBar.intnode := plic.intnode // meip if (c.core.useVM) intBar.intnode := plic.intnode // seip @@ -56,7 +53,6 @@ trait HasRocketTiles extends CoreplexRISCVPlatform { // leave clock as default (simpler for hierarchical PnR) wrapper.module.io.hartid := UInt(i) wrapper.module.io.resetVector := io.resetVector - debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i) } } case Asynchronous(depth, sync) => { @@ -75,7 +71,6 @@ trait HasRocketTiles extends CoreplexRISCVPlatform { wrapper.module.reset := io.tcrs(i).reset wrapper.module.io.hartid := UInt(i) wrapper.module.io.resetVector := io.resetVector - debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i) } } case Rational => { @@ -94,7 +89,6 @@ trait HasRocketTiles extends CoreplexRISCVPlatform { wrapper.module.reset := io.tcrs(i).reset wrapper.module.io.hartid := UInt(i) wrapper.module.io.resetVector := io.resetVector - debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i) } } } diff --git a/src/main/scala/rocketchip/Configs.scala b/src/main/scala/rocketchip/Configs.scala index 2d4a2081..5c2a8eee 100644 --- a/src/main/scala/rocketchip/Configs.scala +++ b/src/main/scala/rocketchip/Configs.scala @@ -36,6 +36,7 @@ class BasePlatformConfig extends Config((site, here, up) => { case PeripheryBusArithmetic => true // Note that PLIC asserts that this is > 0. case IncludeJtagDTM => false + case JtagDTMKey => new JtagDTMKeyDefault() case ZeroConfig => ZeroConfig(base=0xa000000L, size=0x2000000L, beatBytes=8) case ExtMem => MasterConfig(base=0x80000000L, size=0x10000000L, beatBytes=8, idBits=4) case ExtBus => MasterConfig(base=0x60000000L, size=0x20000000L, beatBytes=8, idBits=4) diff --git a/src/main/scala/rocketchip/DebugTransport.scala b/src/main/scala/rocketchip/DebugTransport.scala index 5ab7ef30..86c9bf28 100644 --- a/src/main/scala/rocketchip/DebugTransport.scala +++ b/src/main/scala/rocketchip/DebugTransport.scala @@ -3,78 +3,227 @@ package rocketchip import Chisel._ -import uncore.devices._ -import junctions._ import util._ import config._ +import jtag._ +import uncore.devices.{DMIConsts, DMIIO, DMIReq, DMIResp} 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 JTAG implementation - * of the Debug Transport Module. - * This is black-boxed because - * 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 'TRST' input is used to asynchronously - * reset the Debug Transport Module and the - * DTM side of the synchronizer. - * This design requires that TRST be - * synchronized to TCK (for de-assert) outside - * of this module. Your top level code should ensure - * that TRST is asserted before the rocket-chip core - * comes out of reset. - * Note that TRST is an optional - * part of the JTAG protocol, but it is not - * optional for interfacing with this logic. - * - */ +case class JtagDTMConfig ( + idcodeVersion : Int, // chosen by manuf. + idcodePartNum : Int, // Chosen by manuf. + idcodeManufId : Int, // Assigned by JEDEC + debugIdleCycles : Int) -class JtagDTMWithSync(implicit val p: Parameters) extends Module { - // io.DebugBusIO <-> Sync <-> DebugBusIO <-> UInt <-> DTM Black Box +case object JtagDTMKey extends Field[JtagDTMConfig] - val io = new Bundle { - val jtag = new JTAGIO(true).flip - val debug = new AsyncDebugBusIO - } +class JtagDTMKeyDefault extends JtagDTMConfig( + idcodeVersion = 0, + idcodePartNum = 0, + idcodeManufId = 0, + debugIdleCycles = 5) // Reasonable guess for synchronization. - val req_width = io.debug.req.mem(0).getWidth - val resp_width = io.debug.resp.mem(0).getWidth - - val jtag_dtm = Module(new DebugTransportModuleJtag(req_width, resp_width)) - jtag_dtm.io.jtag <> io.jtag - - val io_debug_bus = Wire (new DebugBusIO) - io.debug <> ToAsyncDebugBus(io_debug_bus) - - val dtm_req = jtag_dtm.io.dtm_req - val dtm_resp = jtag_dtm.io.dtm_resp - - // 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 +object dtmJTAGAddrs { + def IDCODE = 0x1 + def DTM_INFO = 0x10 + def DMI_ACCESS = 0x11 } -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 DMIAccessUpdate(addrBits: Int) extends Bundle { + val addr = UInt(width = addrBits) + val data = UInt(width = DMIConsts.dmiDataSize) + val op = UInt(width = DMIConsts.dmiOpSize) + + override def cloneType = new DMIAccessUpdate(addrBits).asInstanceOf[this.type] +} + +class DMIAccessCapture(addrBits: Int) extends Bundle { + val addr = UInt(width = addrBits) + val data = UInt(width = DMIConsts.dmiDataSize) + val resp = UInt(width = DMIConsts.dmiRespSize) + + override def cloneType = new DMIAccessCapture(addrBits).asInstanceOf[this.type] + +} + +class DTMInfo extends Bundle { + val reserved1 = UInt(15.W) + val dmireset = Bool() + val reserved0 = UInt(1.W) + val dmiIdleCycles = UInt(3.W) + val dmiStatus = UInt(2.W) + val debugAddrBits = UInt(6.W) + val debugVersion = UInt(4.W) +} + +class DebugTransportModuleJTAG(debugAddrBits: Int, c: JtagDTMConfig) + (implicit val p: Parameters) extends Module { + + val io = new Bundle { + val dmi = new DMIIO()(p) + val jtag = Flipped(new JTAGIO(hasTRSTn = false)) + val jtag_reset = Bool(INPUT) + val fsmReset = Bool(OUTPUT) + } + + //-------------------------------------------------------- + // Reg and Wire Declarations + + val dtmInfo = Wire(new DTMInfo) + + val busyReg = RegInit(Bool(false)) + val stickyBusyReg = RegInit(Bool(false)) + val stickyNonzeroRespReg = RegInit(Bool(false)) + + val skipOpReg = Reg(init = Bool(false)) // Skip op because we're busy + val downgradeOpReg = Reg(init = Bool(false)) // downgrade op because prev. failed. + + val busy = Wire(Bool()) + val nonzeroResp = Wire(Bool()) + + val busyResp = Wire(new DMIAccessCapture(debugAddrBits)) + val nonbusyResp = Wire(new DMIAccessCapture(debugAddrBits)) + + val dmiReqReg = Reg(new DMIReq(debugAddrBits)) + val dmiReqValidReg = Reg(init = Bool(false)); + + val dmiStatus = Wire(UInt(width = 2)) + + //-------------------------------------------------------- + // DTM Info Chain Declaration + + dmiStatus := Cat(stickyNonzeroRespReg, stickyNonzeroRespReg | stickyBusyReg) + + dtmInfo.debugVersion := 1.U // This implements version 1 of the spec. + dtmInfo.debugAddrBits := UInt(debugAddrBits) + dtmInfo.dmiStatus := dmiStatus + dtmInfo.dmiIdleCycles := UInt(c.debugIdleCycles) + dtmInfo.reserved0 := 0.U + dtmInfo.dmireset := false.B // This is write-only + dtmInfo.reserved1 := 0.U + + val dtmInfoChain = Module (CaptureUpdateChain(gen = new DTMInfo())) + dtmInfoChain.io.capture.bits := dtmInfo + + //-------------------------------------------------------- + // Debug Access Chain Declaration + + val dmiAccessChain = Module(CaptureUpdateChain(genCapture = new DMIAccessCapture(debugAddrBits), + genUpdate = new DMIAccessUpdate(debugAddrBits))) + + //-------------------------------------------------------- + // Debug Access Support + + // Busy Register. We become busy when we first try to send a request. + // We stop being busy when we accept a response. + + when (io.dmi.req.valid) { + busyReg := Bool(true) + } + when (io.dmi.resp.fire()) { + busyReg := Bool(false) + } + + // We are busy during a given CAPTURE + // if we haven't received a valid response yet or if we + // were busy last time without a reset. + // busyReg will still be set when we check it, + // so the logic for checking busy looks ahead. + busy := (busyReg & !io.dmi.resp.valid) | stickyBusyReg; + + // Downgrade/Skip. We make the decision to downgrade or skip + // during every CAPTURE_DR, and use the result in UPDATE_DR. + // The sticky versions are reset by write to dmiReset in DTM_INFO. + when (dmiAccessChain.io.update.valid) { + skipOpReg := Bool(false) + downgradeOpReg := Bool(false) + } + when (dmiAccessChain.io.capture.capture) { + skipOpReg := busy + downgradeOpReg := (!busy & nonzeroResp) + stickyBusyReg := busy + stickyNonzeroRespReg := nonzeroResp + } + when (dtmInfoChain.io.update.valid) { + when (dtmInfoChain.io.update.bits.dmireset) { + stickyNonzeroRespReg := Bool(false) + stickyBusyReg := Bool(false) + } + } + + // Especially for the first request, we must consider dtmResp.valid, + // so that we don't consider junk in the FIFO to be an error response. + // The current specification says that any non-zero response is an error. + nonzeroResp := stickyNonzeroRespReg | (io.dmi.resp.valid & (io.dmi.resp.bits.resp != UInt(0))) + + busyResp.addr := UInt(0) + busyResp.resp := UInt(0) + busyResp.data := UInt(0) + + nonbusyResp.addr := dmiReqReg.addr + nonbusyResp.resp := io.dmi.resp.bits.resp + nonbusyResp.data := io.dmi.resp.bits.data + + //-------------------------------------------------------- + // Debug Access Chain Implementation + + dmiAccessChain.io.capture.bits := Mux(busy, busyResp, nonbusyResp) + when (dmiAccessChain.io.update.valid) { + skipOpReg := Bool(false) + downgradeOpReg := Bool(false) + } + when (dmiAccessChain.io.capture.capture) { + skipOpReg := busy + downgradeOpReg := (!busy & nonzeroResp) + stickyBusyReg := busy + stickyNonzeroRespReg := nonzeroResp + } + + //-------------------------------------------------------- + // Drive Ready Valid Interface + + when (dmiAccessChain.io.update.valid) { + when (skipOpReg) { + // Do Nothing + }.otherwise { + when (downgradeOpReg) { + dmiReqReg.addr := UInt(0) + dmiReqReg.data := UInt(0) + dmiReqReg.op := UInt(0) + }.otherwise { + dmiReqReg := dmiAccessChain.io.update.bits + } + dmiReqValidReg := Bool(true) + } + }.otherwise { + when (io.dmi.req.ready) { + dmiReqValidReg := Bool(false) + } + } + + io.dmi.resp.ready := dmiAccessChain.io.capture.capture + io.dmi.req.valid := dmiReqValidReg + + // This is a name-based, not type-based assignment. Do these still work? + io.dmi.req.bits := dmiReqReg + + //-------------------------------------------------------- + // Actual JTAG TAP + + val tapIO = JtagTapGenerator(irLength = 5, + instructions = Map(dtmJTAGAddrs.DMI_ACCESS -> dmiAccessChain, + dtmJTAGAddrs.DTM_INFO -> dtmInfoChain), + idcode = Some((dtmJTAGAddrs.IDCODE, JtagIdcode(c.idcodeVersion, c.idcodePartNum, c.idcodeManufId)))) + + tapIO.jtag <> io.jtag + + tapIO.control.jtag_reset := io.jtag_reset + + //-------------------------------------------------------- + // Reset Generation (this is fed back to us by the instantiating module, + // and is used to reset the debug registers). + + io.fsmReset := tapIO.output.reset + } diff --git a/src/main/scala/rocketchip/RISCVPlatform.scala b/src/main/scala/rocketchip/RISCVPlatform.scala index b3cc4071..137e4aa5 100644 --- a/src/main/scala/rocketchip/RISCVPlatform.scala +++ b/src/main/scala/rocketchip/RISCVPlatform.scala @@ -8,55 +8,64 @@ import diplomacy._ import uncore.tilelink2._ import uncore.devices._ import util._ -import junctions.JTAGIO +import jtag.JTAGIO import coreplex._ -/// Core with JTAG for debug only +// System with JTAG DTM Instantiated inside. JTAG interface is +// exported outside. -trait PeripheryJTAG extends HasTopLevelNetworks { - val module: PeripheryJTAGModule +trait PeripheryJTAGDTM extends HasTopLevelNetworks { + val module: PeripheryJTAGDTMModule val coreplex: CoreplexRISCVPlatform } -trait PeripheryJTAGBundle extends HasTopLevelNetworksBundle { - val outer: PeripheryJTAG +trait PeripheryJTAGDTMBundle extends HasTopLevelNetworksBundle { + val outer: PeripheryJTAGDTM + + val jtag = new JTAGIO(hasTRSTn = false).flip + val jtag_reset = Bool(INPUT) - val jtag = new JTAGIO(true).flip } -trait PeripheryJTAGModule extends HasTopLevelNetworksModule { - val outer: PeripheryJTAG - val io: PeripheryJTAGBundle +trait PeripheryJTAGDTMModule extends HasTopLevelNetworksModule { + val outer: PeripheryJTAGDTM + val io: PeripheryJTAGDTMBundle - val dtm = Module (new JtagDTMWithSync) + val dtm = Module (new DebugTransportModuleJTAG(p(DMKey).nDMIAddrSize, p(JtagDTMKey))) dtm.io.jtag <> io.jtag - outer.coreplex.module.io.debug <> dtm.io.debug + + dtm.clock := io.jtag.TCK + dtm.io.jtag_reset := io.jtag_reset + dtm.reset := dtm.io.fsmReset + + outer.coreplex.module.io.debug.dmi <> dtm.io.dmi + outer.coreplex.module.io.debug.dmiClock := io.jtag.TCK + outer.coreplex.module.io.debug.dmiReset := ResetCatchAndSync(io.jtag.TCK, io.jtag_reset, "dmiResetCatch") - dtm.clock := io.jtag.TCK - dtm.reset := io.jtag.TRST } -/// Core with DTM for debug only +// System with Debug Module Interface Only. Any sort of DTM +// can be connected outside. DMI Clock and Reset must be provided. -trait PeripheryDTM extends HasTopLevelNetworks { - val module: PeripheryDTMModule +trait PeripheryDMI extends HasTopLevelNetworks { + val module: PeripheryDMIModule val coreplex: CoreplexRISCVPlatform } -trait PeripheryDTMBundle extends HasTopLevelNetworksBundle { - val outer: PeripheryDTM +trait PeripheryDMIBundle extends HasTopLevelNetworksBundle { + val outer: PeripheryDMI - val debug = new DebugBusIO().flip + val debug = new ClockedDMIIO().flip } -trait PeripheryDTMModule extends HasTopLevelNetworksModule { - val outer: PeripheryDTM - val io: PeripheryDTMBundle +trait PeripheryDMIModule extends HasTopLevelNetworksModule { + val outer: PeripheryDMI + val io: PeripheryDMIBundle - outer.coreplex.module.io.debug <> ToAsyncDebugBus(io.debug) + outer.coreplex.module.io.debug <> io.debug } -/// Core with DTM or JTAG based on a parameter +// System with DMI or JTAG interface based on a parameter trait PeripheryDebug extends HasTopLevelNetworks { val module: PeripheryDebugModule @@ -66,21 +75,30 @@ trait PeripheryDebug extends HasTopLevelNetworks { trait PeripheryDebugBundle extends HasTopLevelNetworksBundle { val outer: PeripheryDebug - val debug = (!p(IncludeJtagDTM)).option(new DebugBusIO().flip) - val jtag = (p(IncludeJtagDTM)).option(new JTAGIO(true).flip) + val debug = (!p(IncludeJtagDTM)).option(new ClockedDMIIO().flip) + + val jtag = (p(IncludeJtagDTM)).option(new JTAGIO(hasTRSTn = false).flip) + val jtag_reset = (p(IncludeJtagDTM)).option(Bool(INPUT)) + } trait PeripheryDebugModule extends HasTopLevelNetworksModule { val outer: PeripheryDebug val io: PeripheryDebugBundle - io.debug.foreach { dbg => outer.coreplex.module.io.debug <> ToAsyncDebugBus(dbg) } - io.jtag.foreach { jtag => - val dtm = Module (new JtagDTMWithSync) - dtm.clock := jtag.TCK - dtm.reset := jtag.TRST - dtm.io.jtag <> jtag - outer.coreplex.module.io.debug <> dtm.io.debug + io.debug.foreach { dbg => outer.coreplex.module.io.debug <> dbg } + + val dtm = if (io.jtag.isDefined) Some[DebugTransportModuleJTAG](Module (new DebugTransportModuleJTAG(p(DMKey).nDMIAddrSize, p(JtagDTMKey)))) else None + dtm.foreach { dtm => + dtm.io.jtag <> io.jtag.get + + dtm.clock := io.jtag.get.TCK + dtm.io.jtag_reset := io.jtag_reset.get + dtm.reset := dtm.io.fsmReset + + outer.coreplex.module.io.debug.dmi <> dtm.io.dmi + outer.coreplex.module.io.debug.dmiClock := io.jtag.get.TCK + outer.coreplex.module.io.debug.dmiReset := ResetCatchAndSync(io.jtag.get.TCK, io.jtag_reset.get, "dmiResetCatch") } } diff --git a/src/main/scala/rocketchip/TestHarness.scala b/src/main/scala/rocketchip/TestHarness.scala index 3d50bed5..7470ed87 100644 --- a/src/main/scala/rocketchip/TestHarness.scala +++ b/src/main/scala/rocketchip/TestHarness.scala @@ -8,6 +8,7 @@ import junctions._ import diplomacy._ import coreplex._ import uncore.axi4._ +import jtag.JTAGIO class TestHarness()(implicit p: Parameters) extends Module { val io = new Bundle { @@ -23,7 +24,7 @@ class TestHarness()(implicit p: Parameters) extends Module { if (!p(IncludeJtagDTM)) { val dtm = Module(new SimDTM).connect(clock, reset, dut.io.debug.get, io.success) } else { - val jtag = Module(new JTAGVPI).connect(dut.io.jtag.get, reset, io.success) + val jtag = Module(new JTAGVPI).connect(dut.io.jtag.get, dut.io.jtag_reset.get, reset, io.success) } val mmio_sim = Module(LazyModule(new SimAXIMem(1, 4096)).module) @@ -62,14 +63,16 @@ class SimDTM(implicit p: Parameters) extends BlackBox { val io = new Bundle { val clk = Clock(INPUT) val reset = Bool(INPUT) - val debug = new uncore.devices.DebugBusIO + val debug = new uncore.devices.DMIIO val exit = UInt(OUTPUT, 32) } - def connect(tbclk: Clock, tbreset: Bool, dutio: uncore.devices.DebugBusIO, tbsuccess: Bool) = { + def connect(tbclk: Clock, tbreset: Bool, dutio: uncore.devices.ClockedDMIIO, tbsuccess: Bool) = { io.clk := tbclk io.reset := tbreset dutio <> io.debug + dutio.dmiClock := tbclk + dutio.dmiReset := tbreset tbsuccess := io.exit === UInt(1) when (io.exit >= UInt(2)) { @@ -81,23 +84,18 @@ class SimDTM(implicit p: Parameters) extends BlackBox { class JTAGVPI(implicit val p: Parameters) extends BlackBox { val io = new Bundle { - val jtag = new JTAGIO(false) + val jtag = new JTAGIO(hasTRSTn = false) val enable = Bool(INPUT) val init_done = Bool(INPUT) } - def connect(dutio: JTAGIO, tbreset: Bool, tbsuccess: Bool) = { + def connect(dutio: JTAGIO, jtag_reset: Bool, tbreset: Bool, tbsuccess: Bool) = { dutio <> 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. + dutio.TRSTn.foreach{ _:= false.B} + jtag_reset := tbreset - dutio.TRST := tbreset - io.enable := ~tbreset + io.enable := ~tbreset io.init_done := ~tbreset // Success is determined by the gdbserver diff --git a/src/main/scala/uncore/devices/Debug.scala b/src/main/scala/uncore/devices/Debug.scala deleted file mode 100644 index 15f14f47..00000000 --- a/src/main/scala/uncore/devices/Debug.scala +++ /dev/null @@ -1,887 +0,0 @@ -// See LICENSE.SiFive for license details. - -package uncore.devices - -import Chisel._ -import junctions._ -import util._ -import regmapper._ -import tile.XLen -import uncore.tilelink2._ -import config._ - -// ***************************************** -// Constants which are interesting even -// outside of this module -// ***************************************** - -object DbRegAddrs{ - - def DMRAMBASE = UInt(0x0) - def DMCONTROL = UInt(0x10) - - def DMINFO = UInt(0x11) - def AUTHDATA0 = UInt(0x12) - def AUTHDATA1 = UInt(0x13) - def SERDATA = UInt(0x14) - def SERSTATUS = UInt(0x15) - def SBUSADDRESS0 = UInt(0x16) - def SBUSADDRESS1 = UInt(0x17) - def SBDATA0 = UInt(0x18) - def SBDATA1 = UInt(0x19) - //1a - def HALTSUM = UInt(0x1B) - //1c - 3b are the halt notification registers. - def SBADDRESS2 = UInt(0x3d) - // 3c - def SBDATA2 = UInt(0x3e) - def SBDATA3 = UInt(0x3f) -} - -/** Constant values used by both Debug Bus Response & Request - */ - -object DbBusConsts{ - - def dbDataSize = 34 - - def dbRamWordBits = 32 - - def dbOpSize = 2 - def db_OP_NONE = UInt("b00") - def db_OP_READ = UInt("b01") - def db_OP_READ_WRITE = UInt("b10") - def db_OP_READ_COND_WRITE = UInt("b11") - - def dbRespSize = 2 - def db_RESP_SUCCESS = UInt("b00") - def db_RESP_FAILURE = UInt("b01") - def db_RESP_HW_FAILURE = UInt("b10") - // This is used outside this block - // to indicate 'busy'. - def db_RESP_RESERVED = UInt("b11") - -} - -object DsbBusConsts { - - def sbAddrWidth = 12 - def sbIdWidth = 10 - - //These are the default ROM contents, which support RV32 and RV64. - // See $RISCV/riscv-tools/riscv-isa-sim/debug_rom/debug_rom.h/S - // The code assumes 64 bytes of Debug RAM. - - def xlenAnyRomContents : Array[Byte] = Array( - 0x6f, 0x00, 0xc0, 0x04, 0x6f, 0x00, 0xc0, 0x00, 0x13, 0x04, 0xf0, 0xff, - 0x6f, 0x00, 0x80, 0x00, 0x13, 0x04, 0x00, 0x00, 0x0f, 0x00, 0xf0, 0x0f, - 0xf3, 0x24, 0x00, 0xf1, 0x63, 0xc6, 0x04, 0x00, 0x83, 0x24, 0xc0, 0x43, - 0x6f, 0x00, 0x80, 0x00, 0x83, 0x34, 0x80, 0x43, 0x23, 0x2e, 0x80, 0x42, - 0x73, 0x24, 0x40, 0xf1, 0x23, 0x20, 0x80, 0x10, 0x73, 0x24, 0x00, 0x7b, - 0x13, 0x74, 0x84, 0x00, 0x63, 0x12, 0x04, 0x04, 0x73, 0x24, 0x20, 0x7b, - 0x73, 0x00, 0x20, 0x7b, 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x00, 0x7b, - 0x13, 0x74, 0x04, 0x1c, 0x13, 0x04, 0x04, 0xf4, 0x63, 0x1e, 0x04, 0x00, - 0x73, 0x24, 0x00, 0xf1, 0x63, 0x46, 0x04, 0x00, 0x23, 0x2e, 0x90, 0x42, - 0x67, 0x00, 0x00, 0x40, 0x23, 0x3c, 0x90, 0x42, 0x67, 0x00, 0x00, 0x40, - 0x73, 0x24, 0x40, 0xf1, 0x23, 0x26, 0x80, 0x10, 0x73, 0x60, 0x04, 0x7b, - 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x02, 0xe3, 0x0c, 0x04, 0xfe, - 0x6f, 0xf0, 0x1f, 0xfd).map(_.toByte) - - // These ROM contents support only RV32 - // See $RISCV/riscv-tools/riscv-isa-sim/debug_rom/debug_rom.h/S - // The code assumes only 28 bytes of Debug RAM. - - def xlen32OnlyRomContents : Array[Byte] = Array( - 0x6f, 0x00, 0xc0, 0x03, 0x6f, 0x00, 0xc0, 0x00, 0x13, 0x04, 0xf0, 0xff, - 0x6f, 0x00, 0x80, 0x00, 0x13, 0x04, 0x00, 0x00, 0x0f, 0x00, 0xf0, 0x0f, - 0x83, 0x24, 0x80, 0x41, 0x23, 0x2c, 0x80, 0x40, 0x73, 0x24, 0x40, 0xf1, - 0x23, 0x20, 0x80, 0x10, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x84, 0x00, - 0x63, 0x1a, 0x04, 0x02, 0x73, 0x24, 0x20, 0x7b, 0x73, 0x00, 0x20, 0x7b, - 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x1c, - 0x13, 0x04, 0x04, 0xf4, 0x63, 0x16, 0x04, 0x00, 0x23, 0x2c, 0x90, 0x40, - 0x67, 0x00, 0x00, 0x40, 0x73, 0x24, 0x40, 0xf1, 0x23, 0x26, 0x80, 0x10, - 0x73, 0x60, 0x04, 0x7b, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x02, - 0xe3, 0x0c, 0x04, 0xfe, 0x6f, 0xf0, 0x1f, 0xfe).map(_.toByte) - - // These ROM contents support only RV64 - // See $RISCV/riscv-tools/riscv-isa-sim/debug_rom/debug_rom.h/S - // The code assumes 64 bytes of Debug RAM. - - def xlen64OnlyRomContents : Array[Byte] = Array( - 0x6f, 0x00, 0xc0, 0x03, 0x6f, 0x00, 0xc0, 0x00, 0x13, 0x04, 0xf0, 0xff, - 0x6f, 0x00, 0x80, 0x00, 0x13, 0x04, 0x00, 0x00, 0x0f, 0x00, 0xf0, 0x0f, - 0x83, 0x34, 0x80, 0x43, 0x23, 0x2e, 0x80, 0x42, 0x73, 0x24, 0x40, 0xf1, - 0x23, 0x20, 0x80, 0x10, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x84, 0x00, - 0x63, 0x1a, 0x04, 0x02, 0x73, 0x24, 0x20, 0x7b, 0x73, 0x00, 0x20, 0x7b, - 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x1c, - 0x13, 0x04, 0x04, 0xf4, 0x63, 0x16, 0x04, 0x00, 0x23, 0x3c, 0x90, 0x42, - 0x67, 0x00, 0x00, 0x40, 0x73, 0x24, 0x40, 0xf1, 0x23, 0x26, 0x80, 0x10, - 0x73, 0x60, 0x04, 0x7b, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x02, - 0xe3, 0x0c, 0x04, 0xfe, 0x6f, 0xf0, 0x1f, 0xfe).map(_.toByte) -} - - - -object DsbRegAddrs{ - - def CLEARDEBINT = 0x100 - def SETHALTNOT = 0x10C - def SERINFO = 0x110 - def SERBASE = 0x114 - // For each serial, there are - // 3 registers starting here: - // SERSEND0 - // SERRECEIVE0 - // SERSTATUS0 - // ... - // SERSTATUS7 - def SERTX_OFFSET = 0 - def SERRX_OFFSET = 4 - def SERSTAT_OFFSET = 8 - def RAMBASE = 0x400 - def ROMBASE = 0x800 - -} - - -// ***************************************** -// Configuration & Parameters for this Module -// -// ***************************************** - -/** Enumerations used both in the hardware - * and in the configuration specification. - */ - -object DebugModuleAuthType extends scala.Enumeration { - type DebugModuleAuthType = Value - val None, Password, ChallengeResponse, Reserved = Value -} -import DebugModuleAuthType._ - -object DebugModuleAccessType extends scala.Enumeration { - type DebugModuleAccessType = Value - val Access8Bit, Access16Bit, Access32Bit, Access64Bit, Access128Bit = Value -} -import DebugModuleAccessType._ - - -/** Parameters exposed to the top-level design, set based on - * external requirements, etc. - * - * This object checks that the parameters conform to the - * full specification. The implementation which receives this - * object can perform more checks on what that implementation - * actually supports. - * nComponents : The number of components to support debugging. - * nDebugBusAddrSize : Size of the Debug Bus Address - * nDebugRam Bytes: Size of the Debug RAM (depends on the XLEN of the machine). - * debugRomContents: Optional Sequence of bytes which form the Debug ROM contents. - * hasBusMaster: Whether or not a bus master should be included - * The size of the accesses supported by the Bus Master. - * nSerialPorts : Number of serial ports to instantiate - * authType : The Authorization Type - * Number of cycles to assert ndreset when pulsed. - **/ - - -case class DebugModuleConfig ( - nComponents : Int, - nDebugBusAddrSize : Int, - nDebugRamBytes : Int, - debugRomContents : Option[Seq[Byte]], - hasBusMaster : Boolean, - hasAccess128 : Boolean, - hasAccess64 : Boolean, - hasAccess32 : Boolean, - hasAccess16 : Boolean, - hasAccess8 : Boolean, - nSerialPorts : Int, - authType : DebugModuleAuthType, - nNDResetCycles : Int -) { - - if (hasBusMaster == false){ - require (hasAccess128 == false) - require (hasAccess64 == false) - require (hasAccess32 == false) - require (hasAccess16 == false) - require (hasAccess8 == false) - } - - require (nSerialPorts <= 8) - - require ((nDebugBusAddrSize >= 5) && (nDebugBusAddrSize <= 7)) - - private val maxComponents = nDebugBusAddrSize match { - case 5 => (32*4) - case 6 => (32*32) - case 7 => (32*32) - } - require (nComponents > 0 && nComponents <= maxComponents) - - private val maxRam = nDebugBusAddrSize match { - case 5 => (4 * 16) - case 6 => (4 * 16) - case 7 => (4 * 64) - } - - require (nDebugRamBytes > 0 && nDebugRamBytes <= maxRam) - - val hasHaltSum = (nComponents > 64) || (nSerialPorts > 0) - - val hasDebugRom = debugRomContents.nonEmpty - - if (hasDebugRom) { - require (debugRomContents.get.size > 0) - require (debugRomContents.get.size <= 512) - } - - require (nNDResetCycles > 0) - -} - -class DefaultDebugModuleConfig (val ncomponents : Int, val xlen:Int) - extends DebugModuleConfig( - nComponents = ncomponents, - nDebugBusAddrSize = 5, - // While smaller numbers are theoretically - // possible as noted in the Spec, - // the ROM image would need to be - // adjusted accordingly. - nDebugRamBytes = xlen match{ - case 32 => 28 - case 64 => 64 - case 128 => 64 - }, - debugRomContents = xlen match { - case 32 => Some(DsbBusConsts.xlen32OnlyRomContents) - case 64 => Some(DsbBusConsts.xlen64OnlyRomContents) - }, - hasBusMaster = false, - hasAccess128 = false, - hasAccess64 = false, - hasAccess32 = false, - hasAccess16 = false, - hasAccess8 = false, - nSerialPorts = 0, - authType = DebugModuleAuthType.None, - nNDResetCycles = 1) - -case object DMKey extends Field[DebugModuleConfig] - -// ***************************************** -// Module Interfaces -// -// ***************************************** - - -/** Structure to define the contents of a Debug Bus Request - */ - -class DebugBusReq(addrBits : Int) extends Bundle { - val addr = UInt(width = addrBits) - val data = UInt(width = DbBusConsts.dbDataSize) - val op = UInt(width = DbBusConsts.dbOpSize) - - override def cloneType = new DebugBusReq(addrBits).asInstanceOf[this.type] -} - - -/** Structure to define the contents of a Debug Bus Response - */ -class DebugBusResp( ) extends Bundle { - val data = UInt(width = DbBusConsts.dbDataSize) - val resp = UInt(width = DbBusConsts.dbRespSize) - -} - -/** Structure to define the top-level DebugBus interface - * of DebugModule. - * DebugModule is the consumer of this interface. - * Therefore it has the 'flipped' version of this. - */ - -class DebugBusIO(implicit val p: Parameters) extends ParameterizedBundle()(p) { - val req = new DecoupledIO(new DebugBusReq(p(DMKey).nDebugBusAddrSize)) - val resp = new DecoupledIO(new DebugBusResp).flip() -} - -class AsyncDebugBusIO(implicit val p: Parameters) extends ParameterizedBundle()(p) { - val req = new AsyncBundle(1, new DebugBusReq(p(DMKey).nDebugBusAddrSize)) - val resp = new AsyncBundle(1, new DebugBusResp).flip -} - -object FromAsyncDebugBus -{ - def apply(x: AsyncDebugBusIO) = { - val out = Wire(new DebugBusIO()(x.p)) - out.req <> FromAsyncBundle(x.req) - x.resp <> ToAsyncBundle(out.resp, 1) - out - } -} - -object ToAsyncDebugBus -{ - def apply(x: DebugBusIO) = { - val out = Wire(new AsyncDebugBusIO()(x.p)) - out.req <> ToAsyncBundle(x.req, 1) - x.resp <> FromAsyncBundle(out.resp) - out - } -} - -trait HasDebugModuleParameters { - implicit val p: Parameters - val cfg = p(DMKey) -} - -/** Debug Module I/O, with the exclusion of the RegisterRouter - * Access interface. - */ - -trait DebugModuleBundle extends Bundle with HasDebugModuleParameters { - val db = new DebugBusIO()(p).flip() - val debugInterrupts = Vec(cfg.nComponents, Bool()).asOutput - val ndreset = Bool(OUTPUT) - val fullreset = Bool(OUTPUT) -} - - -// ***************************************** -// The Module -// -// ***************************************** - -/** Parameterized version of the Debug Module defined in the - * RISC-V Debug Specification - * - * DebugModule is a slave to two masters: - * The Debug Bus -- implemented as a generic Decoupled IO with request - * and response channels - * The System Bus -- implemented as generic RegisterRouter - * - * DebugModule is responsible for holding registers, RAM, and ROM - * to support debug interactions, as well as driving interrupts - * to a configurable number of components in the system. - * It is also responsible for some reset lines. - */ - -trait DebugModule extends Module with HasDebugModuleParameters with HasRegMap { - - val io: DebugModuleBundle - - //-------------------------------------------------------------- - // Import constants for shorter variable names - //-------------------------------------------------------------- - - import DbRegAddrs._ - import DsbRegAddrs._ - import DsbBusConsts._ - import DbBusConsts._ - - //-------------------------------------------------------------- - // Sanity Check Configuration For this implementation. - //-------------------------------------------------------------- - - require (cfg.nComponents <= 128) - require (cfg.nSerialPorts == 0) - require (cfg.hasBusMaster == false) - require (cfg.nDebugRamBytes <= 64) - require (cfg.authType == DebugModuleAuthType.None) - require((DbBusConsts.dbRamWordBits % 8) == 0) - - //-------------------------------------------------------------- - // Private Classes (Register Fields) - //-------------------------------------------------------------- - - class RAMFields() extends Bundle { - val interrupt = Bool() - val haltnot = Bool() - val data = Bits(width = 32) - - override def cloneType = new RAMFields().asInstanceOf[this.type] - } - - class CONTROLFields() extends Bundle { - val interrupt = Bool() - val haltnot = Bool() - val reserved0 = Bits(width = 31-22 + 1) - val buserror = Bits(width = 3) - val serial = Bits(width = 3) - val autoincrement = Bool() - val access = UInt(width = 3) - val hartid = Bits(width = 10) - val ndreset = Bool() - val fullreset = Bool() - - override def cloneType = new CONTROLFields().asInstanceOf[this.type] - - } - - class DMINFOFields() extends Bundle { - val reserved0 = Bits(width = 2) - val abussize = UInt(width = 7) - val serialcount = UInt(width = 4) - val access128 = Bool() - val access64 = Bool() - val access32 = Bool() - val access16 = Bool() - val accesss8 = Bool() - val dramsize = UInt(width = 6) - val haltsum = Bool() - val reserved1 = Bits(width = 3) - val authenticated = Bool() - val authbusy = Bool() - val authtype = UInt(width = 2) - val version = UInt(width = 2) - - override def cloneType = new DMINFOFields().asInstanceOf[this.type] - - } - - class HALTSUMFields() extends Bundle { - val serialfull = Bool() - val serialvalid = Bool() - val acks = Bits(width = 32) - - override def cloneType = new HALTSUMFields().asInstanceOf[this.type] - - } - - - //-------------------------------------------------------------- - // Register & Wire Declarations - //-------------------------------------------------------------- - - // --- Debug Bus Registers - val CONTROLReset = Wire(new CONTROLFields()) - val CONTROLWrEn = Wire(Bool()) - val CONTROLReg = Reg(new CONTROLFields()) - val CONTROLWrData = Wire (new CONTROLFields()) - val CONTROLRdData = Wire (new CONTROLFields()) - val ndresetCtrReg = Reg(UInt(cfg.nNDResetCycles)) - - val DMINFORdData = Wire (new DMINFOFields()) - - val HALTSUMRdData = Wire (new HALTSUMFields()) - - val RAMWrData = Wire (new RAMFields()) - val RAMRdData = Wire (new RAMFields()) - - // --- System Bus Registers - - val SETHALTNOTWrEn = Wire(Bool()) - val SETHALTNOTWrData = Wire(UInt(width = sbIdWidth)) - val CLEARDEBINTWrEn = Wire(Bool()) - val CLEARDEBINTWrData = Wire(UInt(width = sbIdWidth)) - - // --- Interrupt & Halt Notification Registers - - val interruptRegs = Reg(init=Vec.fill(cfg.nComponents){Bool(false)}) - - val haltnotRegs = Reg(init=Vec.fill(cfg.nComponents){Bool(false)}) - val numHaltnotStatus = ((cfg.nComponents - 1) / 32) + 1 - - val haltnotStatus = Wire(Vec(numHaltnotStatus, Bits(width = 32))) - val rdHaltnotStatus = Wire(Bits(width = 32)) - - val haltnotSummary = Cat(haltnotStatus.map(_.orR).reverse) - - // --- Debug RAM - - val ramDataWidth = DbBusConsts.dbRamWordBits - val ramDataBytes = ramDataWidth / 8; - val ramAddrWidth = log2Up(cfg.nDebugRamBytes / ramDataBytes) - - val ramMem = Reg(init = Vec.fill(cfg.nDebugRamBytes){UInt(0, width = 8)}) - - val dbRamAddr = Wire(UInt(width=ramAddrWidth)) - val dbRamAddrValid = Wire(Bool()) - val dbRamRdData = Wire (UInt(width=ramDataWidth)) - val dbRamWrData = Wire(UInt(width=ramDataWidth)) - val dbRamWrEn = Wire(Bool()) - val dbRamRdEn = Wire(Bool()) - val dbRamWrEnFinal = Wire(Bool()) - val dbRamRdEnFinal = Wire(Bool()) - require((cfg.nDebugRamBytes % ramDataBytes) == 0) - val dbRamDataOffset = log2Up(ramDataBytes) - - - // --- Debug Bus Accesses - - val dbRdEn = Wire(Bool()) - val dbWrEn = Wire(Bool()) - val dbRdData = Wire(UInt(width = DbBusConsts.dbDataSize)) - - val s_DB_READY :: s_DB_RESP :: Nil = Enum(Bits(), 2) - val dbStateReg = Reg(init = s_DB_READY) - - val dbResult = Wire(io.db.resp.bits) - - val dbReq = Wire(io.db.req.bits) - val dbRespReg = Reg(io.db.resp.bits) - - val rdCondWrFailure = Wire(Bool()) - val dbWrNeeded = Wire(Bool()) - - //-------------------------------------------------------------- - // Interrupt Registers - //-------------------------------------------------------------- - - for (component <- 0 until cfg.nComponents) { - io.debugInterrupts(component) := interruptRegs(component) - } - - // Interrupt Registers are written by write to CONTROL or debugRAM addresses - // for Debug Bus, and cleared by writes to CLEARDEBINT by System Bus. - // It is "unspecified" what should happen if both - // SET and CLEAR happen at the same time. In this - // implementation, the SET wins. - - for (component <- 0 until cfg.nComponents) { - when (CONTROLWrEn) { - when (CONTROLWrData.hartid === UInt(component)) { - interruptRegs(component) := interruptRegs(component) | CONTROLWrData.interrupt - } - }.elsewhen (dbRamWrEn) { - when (CONTROLReg.hartid === UInt(component)){ - interruptRegs(component) := interruptRegs(component) | RAMWrData.interrupt - } - }.elsewhen (CLEARDEBINTWrEn){ - when (CLEARDEBINTWrData === UInt(component, width = sbIdWidth)) { - interruptRegs(component) := Bool(false) - } - } - } - - //-------------------------------------------------------------- - // Halt Notification Registers - //-------------------------------------------------------------- - - // Halt Notifications Registers are cleared by zero write to CONTROL or debugRAM addresses - // for Debug Bus, and set by write to SETHALTNOT by System Bus. - // It is "unspecified" what should happen if both - // SET and CLEAR happen at the same time. In this - // implementation, the SET wins. - - for (component <- 0 until cfg.nComponents) { - when (SETHALTNOTWrEn){ - when (SETHALTNOTWrData === UInt(component, width = sbIdWidth)) { - haltnotRegs(component) := Bool(true) - } - } .elsewhen (CONTROLWrEn) { - when (CONTROLWrData.hartid === UInt(component)) { - haltnotRegs(component) := haltnotRegs(component) & CONTROLWrData.haltnot - } - }.elsewhen (dbRamWrEn) { - when (CONTROLReg.hartid === UInt(component)){ - haltnotRegs(component) := haltnotRegs(component) & RAMWrData.haltnot - } - } - } - - for (ii <- 0 until numHaltnotStatus) { - haltnotStatus(ii) := Cat(haltnotRegs.slice(ii * 32, (ii + 1) * 32).reverse) - } - - //-------------------------------------------------------------- - // Other Registers - //-------------------------------------------------------------- - - CONTROLReset.interrupt := Bool(false) - CONTROLReset.haltnot := Bool(false) - CONTROLReset.reserved0 := Bits(0) - CONTROLReset.buserror := Bits(0) - CONTROLReset.serial := Bits(0) - CONTROLReset.autoincrement := Bool(false) - CONTROLReset.access := UInt(DebugModuleAccessType.Access32Bit.id) - CONTROLReset.hartid := Bits(0) - CONTROLReset.ndreset := Bool(false) - CONTROLReset.fullreset := Bool(false) - - // Because this version of DebugModule doesn't - // support authentication, this entire register is - // Read-Only constant wires. - DMINFORdData.reserved0 := Bits(0) - DMINFORdData.abussize := UInt(0) // Not Implemented. - DMINFORdData.serialcount := UInt(cfg.nSerialPorts) - DMINFORdData.access128 := Bool(cfg.hasAccess128) - DMINFORdData.access64 := Bool(cfg.hasAccess64) - DMINFORdData.access32 := Bool(cfg.hasAccess32) - DMINFORdData.access16 := Bool(cfg.hasAccess16) - DMINFORdData.accesss8 := Bool(cfg.hasAccess8) - DMINFORdData.dramsize := Bits((cfg.nDebugRamBytes >> 2) - 1) // Size in 32-bit words minus 1. - DMINFORdData.haltsum := Bool(cfg.hasHaltSum) - DMINFORdData.reserved1 := Bits(0) - DMINFORdData.authenticated := Bool(true) // Not Implemented. - DMINFORdData.authbusy := Bool(false) // Not Implemented. - DMINFORdData.authtype := UInt(cfg.authType.id) - DMINFORdData.version := UInt(1) // Conforms to RISC-V Debug Spec - - HALTSUMRdData.serialfull := Bool(false) // Not Implemented - HALTSUMRdData.serialvalid := Bool(false) // Not Implemented - HALTSUMRdData.acks := haltnotSummary - - //-------------------------------------------------------------- - // Debug RAM Access (Debug Bus ... System Bus can override) - //-------------------------------------------------------------- - - dbReq := io.db.req.bits - // Debug Bus RAM Access - // From Specification: Debug RAM is 0x00 - 0x0F - // 0x40 - 0x6F Not Implemented - dbRamAddr := dbReq.addr( ramAddrWidth-1 , 0) - dbRamWrData := dbReq.data - dbRamAddrValid := (dbReq.addr(3,0) <= UInt((cfg.nDebugRamBytes/ramDataBytes))) - - val dbRamRdDataFields = List.tabulate(cfg.nDebugRamBytes / ramDataBytes) { ii => - val slice = ramMem.slice(ii * ramDataBytes, (ii+1)*ramDataBytes) - slice.reduce[UInt]{ case (x: UInt, y: UInt) => Cat(y, x)} - } - - dbRamRdData := dbRamRdDataFields(dbRamAddr) - - when (dbRamWrEnFinal) { - for (ii <- 0 until ramDataBytes) { - ramMem((dbRamAddr << UInt(dbRamDataOffset)) + UInt(ii)) := dbRamWrData((8*(ii+1)-1), (8*ii)) - } - } - - //-------------------------------------------------------------- - // Debug Bus Access - //-------------------------------------------------------------- - - // 0x00 - 0x0F Debug RAM - // 0x10 - 0x1B Registers - // 0x1C - 0x3B Halt Notification Registers - // 0x3C - 0x3F Registers - // 0x40 - 0x6F Debug RAM - - - // ----------------------------------------- - // DB Access Write Decoder - - CONTROLWrData := new CONTROLFields().fromBits(dbReq.data) - RAMWrData := new RAMFields().fromBits(dbReq.data) - - dbRamWrEn := Bool(false) - dbRamWrEnFinal := Bool(false) - CONTROLWrEn := Bool(false) - when ((dbReq.addr >> 4) === Bits(0)) { // 0x00 - 0x0F Debug RAM - dbRamWrEn := dbWrEn - when (dbRamAddrValid) { - dbRamWrEnFinal := dbWrEn - } - }.elsewhen (dbReq.addr === DMCONTROL) { - CONTROLWrEn := dbWrEn - }.otherwise { - //Other registers/RAM are Not Implemented. - } - - when (reset) { - CONTROLReg := CONTROLReset - ndresetCtrReg := UInt(0) - }.elsewhen (CONTROLWrEn) { - // interrupt handled in other logic - // haltnot handled in other logic - if (cfg.hasBusMaster){ - // buserror is set 'until 0 is written to any bit in this field'. - CONTROLReg.buserror := Mux(CONTROLWrData.buserror.andR, CONTROLReg.buserror, UInt(0)) - CONTROLReg.autoincrement := CONTROLWrData.autoincrement - CONTROLReg.access := CONTROLWrData.access - } - if (cfg.nSerialPorts > 0){ - CONTROLReg.serial := CONTROLWrData.serial - } - CONTROLReg.hartid := CONTROLWrData.hartid - CONTROLReg.fullreset := CONTROLReg.fullreset | CONTROLWrData.fullreset - when (CONTROLWrData.ndreset){ - ndresetCtrReg := UInt(cfg.nNDResetCycles) - }.otherwise { - ndresetCtrReg := Mux(ndresetCtrReg === UInt(0) , UInt(0), ndresetCtrReg - UInt(1)) - } - }.otherwise { - ndresetCtrReg := Mux(ndresetCtrReg === UInt(0) , UInt(0), ndresetCtrReg - UInt(1)) - } - - // ----------------------------------------- - // DB Access Read Mux - - CONTROLRdData := CONTROLReg; - CONTROLRdData.interrupt := interruptRegs(CONTROLReg.hartid) - CONTROLRdData.haltnot := haltnotRegs(CONTROLReg.hartid) - CONTROLRdData.ndreset := ndresetCtrReg.orR - - RAMRdData.interrupt := interruptRegs(CONTROLReg.hartid) - RAMRdData.haltnot := haltnotRegs(CONTROLReg.hartid) - RAMRdData.data := dbRamRdData - - dbRdData := UInt(0) - - // Higher numbers of numHaltnotStatus Not Implemented. - // This logic assumes only up to 128 components. - rdHaltnotStatus := Bits(0) - for (ii <- 0 until numHaltnotStatus) { - when (dbReq.addr(1, 0) === UInt(ii)) { - rdHaltnotStatus := haltnotStatus(ii) - } - } - - dbRamRdEn := Bool(false) - dbRamRdEnFinal := Bool(false) - when ((dbReq.addr >> 4) === Bits(0)) { // 0x00 - 0x0F Debug RAM - dbRamRdEn := dbRdEn - when (dbRamAddrValid) { - dbRdData := RAMRdData.asUInt - dbRamRdEnFinal := dbRdEn - } - }.elsewhen (dbReq.addr === DMCONTROL) { - dbRdData := CONTROLRdData.asUInt - }.elsewhen (dbReq.addr === DMINFO) { - dbRdData := DMINFORdData.asUInt - }.elsewhen (dbReq.addr === HALTSUM) { - if (cfg.hasHaltSum){ - dbRdData := HALTSUMRdData.asUInt - } else { - dbRdData := UInt(0) - } - }.elsewhen ((dbReq.addr >> 2) === UInt(7)) { // 0x1C - 0x1F Haltnot - dbRdData := rdHaltnotStatus - } .otherwise { - //These Registers are not implemented in this version of DebugModule: - // AUTHDATA0 - // AUTHDATA1 - // SERDATA - // SERSTATUS - // SBUSADDRESS0 - // SBUSADDRESS1 - // SBDATA0 - // SBDATA1 - // SBADDRESS2 - // SBDATA2 - // SBDATA3 - // 0x20 - 0x3B haltnot - // Upper bytes of Debug RAM. - dbRdData := UInt(0) - } - - // Conditional write fails if MSB is set of the read data. - rdCondWrFailure := dbRdData(dbDataSize - 1 ) && - (dbReq.op === db_OP_READ_COND_WRITE) - - dbWrNeeded := (dbReq.op === db_OP_READ_WRITE) || - ((dbReq.op === db_OP_READ_COND_WRITE) && ~rdCondWrFailure) - - // This is only relevant at end of s_DB_READ. - dbResult.resp := Mux(rdCondWrFailure, - db_RESP_FAILURE, - db_RESP_SUCCESS) - dbResult.data := dbRdData - - // ----------------------------------------- - // DB Access State Machine Decode (Combo) - io.db.req.ready := (dbStateReg === s_DB_READY) || - (dbStateReg === s_DB_RESP && io.db.resp.fire()) - - io.db.resp.valid := (dbStateReg === s_DB_RESP) - io.db.resp.bits := dbRespReg - - dbRdEn := io.db.req.fire() - dbWrEn := dbWrNeeded && io.db.req.fire() - - // ----------------------------------------- - // DB Access State Machine Update (Seq) - - when (dbStateReg === s_DB_READY){ - when (io.db.req.fire()){ - dbStateReg := s_DB_RESP - dbRespReg := dbResult - } - } .elsewhen (dbStateReg === s_DB_RESP){ - when (io.db.req.fire()){ - dbStateReg := s_DB_RESP - dbRespReg := dbResult - }.elsewhen (io.db.resp.fire()){ - dbStateReg := s_DB_READY - } - } - - - //-------------------------------------------------------------- - // Debug ROM - //-------------------------------------------------------------- - - val romRegFields = if (cfg.hasDebugRom) { - cfg.debugRomContents.get.map( x => RegField.r(8, UInt(x.toInt & 0xFF))) - } else { - Seq(RegField(8)) - } - - //-------------------------------------------------------------- - // System Bus Access - //-------------------------------------------------------------- - - // Local reg mapper function : Notify when written, but give the value. - def wValue (n: Int, value: UInt, set: Bool) : RegField = { - RegField(n, value, RegWriteFn((valid, data) => {set := valid ; value := data; Bool(true)})) - } - - regmap( - CLEARDEBINT -> Seq(wValue(sbIdWidth, CLEARDEBINTWrData, CLEARDEBINTWrEn)), - SETHALTNOT -> Seq(wValue(sbIdWidth, SETHALTNOTWrData, SETHALTNOTWrEn)), - RAMBASE -> ramMem.map(x => RegField(8, x)), - ROMBASE -> romRegFields - ) - - //-------------------------------------------------------------- - // Misc. Outputs - //-------------------------------------------------------------- - - io.ndreset := ndresetCtrReg.orR - io.fullreset := CONTROLReg.fullreset - -} - -/** Create a concrete TL2 Slave for the DebugModule RegMapper interface. - * - */ - -class TLDebugModule(address: BigInt = 0)(implicit p: Parameters) - extends TLRegisterRouter(address, "debug", Nil, beatBytes=p(XLen)/8, executable=true)( - new TLRegBundle((), _ ) with DebugModuleBundle)( - new TLRegModule((), _, _) with DebugModule) - - -/** Synchronizers for DebugBus - * - */ - - -object AsyncDebugBusCrossing { - // takes from_source from the 'from' clock domain to the 'to' clock domain - def apply(from_clock: Clock, from_reset: Bool, from_source: DebugBusIO, to_clock: Clock, to_reset: Bool, depth: Int = 1, sync: Int = 3) = { - val to_sink = Wire(new DebugBusIO()(from_source.p)) - to_sink.req <> AsyncDecoupledCrossing(from_clock, from_reset, from_source.req, to_clock, to_reset, depth, sync) - from_source.resp <> AsyncDecoupledCrossing(to_clock, to_reset, to_sink.resp, from_clock, from_reset, depth, sync) - to_sink // is now to_source - } -} - - -object AsyncDebugBusFrom { // OutsideClockDomain - // takes from_source from the 'from' clock domain and puts it into your clock domain - def apply(from_clock: Clock, from_reset: Bool, from_source: DebugBusIO, depth: Int = 1, sync: Int = 3): DebugBusIO = { - val scope = AsyncScope() - AsyncDebugBusCrossing(from_clock, from_reset, from_source, scope.clock, scope.reset, depth, sync) - } -} - -object AsyncDebugBusTo { // OutsideClockDomain - // takes source from your clock domain and puts it into the 'to' clock domain - def apply(to_clock: Clock, to_reset: Bool, source: DebugBusIO, depth: Int = 1, sync: Int = 3): DebugBusIO = { - val scope = AsyncScope() - AsyncDebugBusCrossing(scope.clock, scope.reset, source, to_clock, to_reset, depth, sync) - } -} diff --git a/src/main/scala/uncore/devices/debug/Debug.scala b/src/main/scala/uncore/devices/debug/Debug.scala new file mode 100644 index 00000000..6ae5522e --- /dev/null +++ b/src/main/scala/uncore/devices/debug/Debug.scala @@ -0,0 +1,1177 @@ +// See LICENSE.SiFive for license details. + +package uncore.devices + +import Chisel._ +import junctions._ +import util._ +import regmapper._ +import tile.XLen +import uncore.tilelink2._ +import config._ +import diplomacy._ + + +/** Constant values used by both Debug Bus Response & Request + */ + +object DMIConsts{ + + def dmiDataSize = 32 + + def dmiOpSize = 2 + def dmi_OP_NONE = "b00".U + def dmi_OP_READ = "b01".U + def dmi_OP_WRITE = "b10".U + + def dmiRespSize = 2 + def dmi_RESP_SUCCESS = "b00".U + def dmi_RESP_FAILURE = "b01".U + def dmi_RESP_HW_FAILURE = "b10".U + // This is used outside this block + // to indicate 'busy'. + def dmi_RESP_RESERVED = "b11".U + + def dmi_haltStatusAddr = 0x40 +} + +object DsbBusConsts { + + def sbAddrWidth = 12 + def sbIdWidth = 10 + +} + +object DsbRegAddrs{ + + // These may need to move around to be used by the serial interface. + + // These are used by the ROM. + def HALTED = 0x100 + def GOING = 0x104 + def RESUMING = 0x108 + def EXCEPTION = 0x10C + + def GO = 0x400 + + def ROMBASE = 0x800 + def RESUME = 0x804 + + def WHERETO = 0x300 + def ABSTRACT = 0x340 - 8 + def PROGBUF = 0x340 + + // This shows up in HartInfo + def DATA = 0x380 + + //Not implemented: Serial. + +} + +/** Enumerations used both in the hardware + * and in the configuration specification. + */ + +object DebugModuleAccessType extends scala.Enumeration { + type DebugModuleAccessType = Value + val Access8Bit, Access16Bit, Access32Bit, Access64Bit, Access128Bit = Value +} +import DebugModuleAccessType._ + +object DebugAbstractCommandError extends scala.Enumeration { + type DebugAbstractCommandError = Value + val None, ErrBusy, ErrNotSupported, ErrException, ErrHaltResume = Value +} +import DebugAbstractCommandError._ + +object DebugAbstractCommandType extends scala.Enumeration { + type DebugAbstractCommandType = Value + val AccessRegister, QuickAccess = Value +} +import DebugAbstractCommandType._ + +/** Parameters exposed to the top-level design, set based on + * external requirements, etc. + * + * This object checks that the parameters conform to the + * full specification. The implementation which receives this + * object can perform more checks on what that implementation + * actually supports. + * nComponents : The number of components to support debugging. + * nDMIAddrSize : Size of the Debug Bus Address + * nAbstractDataWords: Number of 32-bit words for Abstract Commands + * nProgamBufferWords: Number of 32-bit words for Program Buffer + * hasBusMaster: Whethr or not a bus master should be included + * The size of the accesses supported by the Bus Master. + * nSerialPorts : Number of serial ports to instantiate + * supportQuickAccess : Whether or not to support the quick access command. + * supportHartArray : Whether or not to implement the hart array register. + **/ + +case class DebugModuleConfig ( + nDMIAddrSize : Int, + nProgramBufferWords: Int, + nAbstractDataWords : Int, + nScratch : Int, + //TODO: Use diplomacy to decide if you want this. + hasBusMaster : Boolean, + hasAccess128 : Boolean, + hasAccess64 : Boolean, + hasAccess32 : Boolean, + hasAccess16 : Boolean, + hasAccess8 : Boolean, + nSerialPorts : Int, + supportQuickAccess : Boolean, + supportHartArray : Boolean +) { + + if (hasBusMaster == false){ + require (hasAccess128 == false) + require (hasAccess64 == false) + require (hasAccess32 == false) + require (hasAccess16 == false) + require (hasAccess8 == false) + } + + require (nSerialPorts <= 8) + + require ((nDMIAddrSize >= 7) && (nDMIAddrSize <= 32)) + + require ((nAbstractDataWords > 0) && (nAbstractDataWords <= 16)) + require ((nProgramBufferWords >= 0) && (nProgramBufferWords <= 16)) + + if (supportQuickAccess) { + // TODO: Check that quick access requirements are met. + } + +} + +class DefaultDebugModuleConfig (val xlen:Int /*TODO , val configStringAddr: Int*/) + extends DebugModuleConfig( + nDMIAddrSize = 7, + //TODO use more words to support arbitrary sequences. + nProgramBufferWords = 15, + // TODO use less for small XLEN? + nAbstractDataWords = 4, + nScratch = 1, + hasBusMaster = false, + hasAccess128 = false, + hasAccess64 = false, + hasAccess32 = false, + hasAccess16 = false, + hasAccess8 = false, + nSerialPorts = 0, + supportQuickAccess = false, + supportHartArray = false + // TODO configStringAddr = configStringAddr + // TODO: accept a mapping function from HARTID -> HARTSEL + ) + +case object DMKey extends Field[DebugModuleConfig] + +// ***************************************** +// Module Interfaces +// +// ***************************************** + +/** Structure to define the contents of a Debug Bus Request + */ +class DMIReq(addrBits : Int) extends Bundle { + val addr = UInt(addrBits.W) + val data = UInt(DMIConsts.dmiDataSize.W) + val op = UInt(DMIConsts.dmiOpSize.W) + + override def cloneType = new DMIReq(addrBits).asInstanceOf[this.type] +} + +/** Structure to define the contents of a Debug Bus Response + */ +class DMIResp( ) extends Bundle { + val data = UInt(DMIConsts.dmiDataSize.W) + val resp = UInt(DMIConsts.dmiRespSize.W) +} + +/** Structure to define the top-level DMI interface + * of DebugModule. + * DebugModule is the consumer of this interface. + * Therefore it has the 'flipped' version of this. + */ +class DMIIO(implicit val p: Parameters) extends ParameterizedBundle()(p) { + val req = new DecoupledIO(new DMIReq(p(DMKey).nDMIAddrSize)) + val resp = new DecoupledIO(new DMIResp).flip() +} + +/* structure for passing hartsel between the "Outer" and "Inner" + */ + +class DebugInternalBundle ()(implicit val p: Parameters) extends ParameterizedBundle()(p) { + val resumereq = Bool() + val hartsel = UInt(10.W) +} + +/* structure for top-level Debug Module signals which aren't the bus interfaces. + */ + +class DebugCtrlBundle (nComponents: Int)(implicit val p: Parameters) extends ParameterizedBundle()(p) { + val debugUnavail = Vec(nComponents, Bool()).asInput + val ndreset = Bool(OUTPUT) + val debugActive = Bool(OUTPUT) +} + +//***************************************** +// Debug ROM +// +// ***************************************** + +class TLDebugModuleROM()(implicit p: Parameters) extends TLROM( + base = DsbRegAddrs.ROMBASE, // This is required for correct functionality. It's not a parameter. + size = 0x800, + contentsDelayed = DebugRomContents(), + executable = true, + beatBytes = p(XLen)/8, + resources = new SimpleDevice("debug_rom", Seq("sifive,debug-013")).reg +) + +// ***************************************** +// Debug Module +// +// ***************************************** + +/** Parameterized version of the Debug Module defined in the + * RISC-V Debug Specification + * + * DebugModule is a slave to two asynchronous masters: + * The Debug Bus (DMI) -- This is driven by an external debugger + * + * The System Bus -- This services requests from the cores. Generally + * this interface should only be active at the request + * of the debugger, but the Debug Module may also + * provide the default MTVEC since it is mapped + * to address 0x0. + * + * DebugModule is responsible for control registers and RAM. The Debug ROM is in a + * seperate module. It runs partially off of the dmiClk (e.g. TCK) and + * the TL clock. Therefore, it is divided into "Outer" portion (running + * of off dmiClock and dmiReset) and "Inner" (running off tlClock and tlReset). + * This allows DMCONTROL.haltreq, hartsel, dmactive, and ndreset to be + * modified even while the Core is in reset or not being clocked. + * Not all reads from the Debugger to the Debug Module will actually complete + * in these scenarios either, they will just block until tlClock and tlReset + * allow them to complete. This is not strictly necessary for + * proper debugger functionality. + */ + +// Local reg mapper function : Notify when written, but give the value as well. +object WNotify { + def apply(n: Int, value: UInt, set: Bool) : RegField = { + RegField(n, value, RegWriteFn((valid, data) => { + set := valid + when(valid) {value := data} + Bool(true) + })) + } +} + +// Local reg mapper function : Notify when accessed either as read or write. +object RWNotify { + def apply (n: Int, rVal: UInt, wVal: UInt, rNotify: Bool, wNotify: Bool) : RegField = { + RegField(n, + RegReadFn ((ready) => {rNotify := ready ; (Bool(true), rVal)}), + RegWriteFn((valid, data) => { + wNotify := valid + when (valid) {wVal := data} + Bool(true) + } + )) + } +} + +class TLDebugModuleOuter(device: Device)(implicit p: Parameters) extends LazyModule { + + // For Shorter Register Names + import DMI_RegAddrs._ + + val intnode = IntNexusNode( + numSourcePorts = 1 to 1024, + numSinkPorts = 0 to 0, + sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(1, Seq(Resource(device, "int"))))) }, + sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) } + ) + + val dmiNode = TLRegisterNode ( + address = AddressSet.misaligned(DMI_DMCONTROL << 2, 4), + device = device, + deviceKey = "reg", + beatBytes = 4, + executable = false + ) + + lazy val module = new LazyModuleImp(this) { + + val nComponents = intnode.bundleOut.size + + val io = new Bundle { + val ctrl = (new DebugCtrlBundle(nComponents)) + val tlIn = dmiNode.bundleIn + val debugInterrupts = intnode.bundleOut + val innerCtrl = new DecoupledIO(new DebugInternalBundle()) + } + + //----DMCONTROL (The whole point of 'Outer' is to maintain this register on dmiClock (e.g. TCK) domain, so that it + // can be written even if 'Inner' is not being clocked or is in reset. This allows halting + // harts while the rest of the system is in reset. It doesn't really allow any other + // register accesses, which will keep returning 'busy' to the debugger interface. + + val DMCONTROLReset = Wire(init = (new DMCONTROLFields().fromBits(0.U))) + val DMCONTROLNxt = Wire(init = new DMCONTROLFields().fromBits(0.U)) + + val DMCONTROLReg = Wire(init = new DMCONTROLFields().fromBits(AsyncResetReg(updateData = DMCONTROLNxt.asUInt, + resetData = BigInt(0), + enable = true.B, + name = "DMCONTROL" + ))) + + val DMCONTROLRdData = Wire(init = DMCONTROLReg) + + val DMCONTROLWrDataVal = Wire(init = 0.U(32.W)) + val DMCONTROLWrData = (new DMCONTROLFields()).fromBits(DMCONTROLWrDataVal) + val DMCONTROLWrEn = Wire(init = false.B) + val DMCONTROLRdEn = Wire(init = false.B) + + val dmactive = DMCONTROLReg.dmactive + + DMCONTROLNxt := DMCONTROLReg + when (~dmactive) { + DMCONTROLNxt := DMCONTROLReset + } .otherwise { + when (DMCONTROLWrEn) { + DMCONTROLNxt.ndmreset := DMCONTROLWrData.ndmreset + DMCONTROLNxt.hartsel := DMCONTROLWrData.hartsel + } + } + + // Put this last to override its own effects. + when (DMCONTROLWrEn) { + DMCONTROLNxt.dmactive := DMCONTROLWrData.dmactive + } + + // DMCONTROL is the only register, so it's at offset 0. + dmiNode.regmap( + 0 -> Seq(RWNotify(32, DMCONTROLRdData.asUInt(), + DMCONTROLWrDataVal, DMCONTROLRdEn, DMCONTROLWrEn)) + ) + + //-------------------------------------------------------------- + // Interrupt Registers + //-------------------------------------------------------------- + + val debugIntNxt = Wire(init = Vec.fill(nComponents){false.B}) + val debugIntRegs = Wire(init = Vec(AsyncResetReg(updateData = debugIntNxt.asUInt, + resetData = 0, + enable = true.B, + name = "debugInterrupts").toBools)) + + debugIntNxt := debugIntRegs + + for (component <- 0 until nComponents) { + io.debugInterrupts(component)(0) := debugIntRegs(component) + } + + // Halt request registers are written by write to DMCONTROL.haltreq + // and cleared by writes to DMCONTROL.resumereq. + // resumereq also causes the core to execute a 'dret', + // so resumereq is passed through to Inner. + // hartsel must also be used by the DebugModule state machine, + // so it is passed to Inner. + // It is true that there is no backpressure -- writes + // which occur 'too fast' will be dropped. + + for (component <- 0 until nComponents) { + when (~dmactive) { + debugIntNxt(component) := false.B + }. otherwise { + when (DMCONTROLWrEn) { + when (DMCONTROLWrData.hartsel === component.U) { + debugIntNxt(component) := (debugIntRegs(component) | DMCONTROLWrData.haltreq) & + ~(DMCONTROLWrData.resumereq) + } + } + } + } + + io.innerCtrl.valid := DMCONTROLWrEn + io.innerCtrl.bits.hartsel := DMCONTROLWrData.hartsel + io.innerCtrl.bits.resumereq := DMCONTROLWrData.resumereq + + io.ctrl.ndreset := DMCONTROLReg.ndmreset + io.ctrl.debugActive := DMCONTROLReg.dmactive + + } +} + +class TLDebugModuleOuterAsync(device: Device)(implicit p: Parameters) extends LazyModule { + + val dmi2tl = LazyModule(new DMIToTL()) + val dmiXbar = LazyModule (new TLXbar()) + + val dmOuter = LazyModule( new TLDebugModuleOuter(device)) + val intnode = IntOutputNode() + + val dmiInnerNode = TLAsyncOutputNode() + + intnode :*= dmOuter.intnode + + dmiXbar.node := dmi2tl.node + dmOuter.dmiNode := dmiXbar.node + dmiInnerNode := TLAsyncCrossingSource()(dmiXbar.node) + + lazy val module = new LazyModuleImp(this) { + + val nComponents = intnode.bundleOut.size + + val io = new Bundle { + val dmi = new DMIIO()(p).flip() + val dmiInner = dmiInnerNode.bundleOut + val ctrl = new DebugCtrlBundle(nComponents) + val debugInterrupts = intnode.bundleOut + val innerCtrl = new AsyncBundle(depth=1, new DebugInternalBundle()) + } + + dmi2tl.module.io.dmi <> io.dmi + + io.ctrl <> dmOuter.module.io.ctrl + io.innerCtrl := ToAsyncBundle(dmOuter.module.io.innerCtrl, depth=1) + + } +} + +class TLDebugModuleInner(device: Device, getNComponents: () => Int)(implicit p: Parameters) extends LazyModule +{ + + val dmiNode = TLRegisterNode( + address = AddressSet.misaligned(0, DMI_RegAddrs.DMI_DMCONTROL << 2) ++ + AddressSet.misaligned((DMI_RegAddrs.DMI_DMCONTROL + 1) << 2, (0x200 - ((DMI_RegAddrs.DMI_DMCONTROL + 1) << 2))), + device = device, + deviceKey = "reg", + beatBytes = 4, + executable = false + ) + + val tlNode = TLRegisterNode( + address=Seq(AddressSet(0, 0x7FF)), // This is required for correct functionality, it's not configurable. + device=device, + deviceKey="reg", + beatBytes=p(XLen)/8, + executable=true + ) + + lazy val module = new LazyModuleImp(this){ + + val cfg = p(DMKey) + + val nComponents = getNComponents() + + val io = new Bundle { + val hart_in = tlNode.bundleIn + val dmi_in = dmiNode.bundleIn + val dmactive = Bool(INPUT) + val innerCtrl = (new DecoupledIO(new DebugInternalBundle())).flip + val debugUnavail = Vec(nComponents, Bool()).asInput + } + + //-------------------------------------------------------------- + // Import constants for shorter variable names + //-------------------------------------------------------------- + + import DMI_RegAddrs._ + import DsbRegAddrs._ + import DsbBusConsts._ + import DMIConsts._ + + //-------------------------------------------------------------- + // Sanity Check Configuration For this implementation. + //-------------------------------------------------------------- + + require (cfg.nSerialPorts == 0) + require (cfg.hasBusMaster == false) + require (cfg.supportQuickAccess == false) + require (cfg.supportHartArray == false) + + //-------------------------------------------------------------- + // Register & Wire Declarations (which need to be pre-declared) + //-------------------------------------------------------------- + + val haltedBitRegs = RegInit(Vec.fill(nComponents){false.B}) + + // --- regmapper outputs + + val hartHaltedWrEn = Wire(Bool()) + val hartHaltedId = Wire(UInt(sbIdWidth.W)) + val hartGoingWrEn = Wire(Bool()) + val hartGoingId = Wire(UInt(sbIdWidth.W)) + val hartResumingWrEn = Wire(Bool()) + val hartResumingId = Wire(UInt(sbIdWidth.W)) + val hartExceptionWrEn = Wire(Bool()) + val hartExceptionId = Wire(UInt(sbIdWidth.W)) + + val dmiProgramBufferRdEn = Wire(init = Vec.fill(cfg.nProgramBufferWords * 4){false.B}) + val dmiProgramBufferWrEn = Wire(init = Vec.fill(cfg.nProgramBufferWords * 4){false.B}) + + val dmiAbstractDataRdEn = Wire(init = Vec.fill(cfg.nAbstractDataWords * 4){false.B}) + val dmiAbstractDataWrEn = Wire(init = Vec.fill(cfg.nAbstractDataWords * 4){false.B}) + + //-------------------------------------------------------------- + // Registers coming from 'CONTROL' in Outer + //-------------------------------------------------------------- + + val selectedHartReg = RegInit(0.U(10.W)) + + when (io.innerCtrl.fire()){ + selectedHartReg := io.innerCtrl.bits.hartsel + } + + io.innerCtrl.ready := true.B + + //-------------------------------------------------------------- + // DMI Registers + //-------------------------------------------------------------- + + //----DMSTATUS + + val DMSTATUSRdData = Wire(init = (new DMSTATUSFields()).fromBits(0.U)) + DMSTATUSRdData.authenticated := true.B // Not implemented + DMSTATUSRdData.versionlo := "b10".U + + // Chisel3 Issue #527 , have to do intermediate assignment. + val unavailVec = Wire(init = Vec.fill(nComponents){false.B}) + unavailVec := io.debugUnavail + + when (selectedHartReg > nComponents.U) { + DMSTATUSRdData.allnonexistent := true.B + DMSTATUSRdData.anynonexistent := true.B + }.elsewhen (unavailVec(selectedHartReg)) { + DMSTATUSRdData.allunavail := true.B + DMSTATUSRdData.anyunavail := true.B + }.elsewhen (haltedBitRegs(selectedHartReg)) { + DMSTATUSRdData.allhalted := true.B + DMSTATUSRdData.anyhalted := true.B + }.otherwise { + DMSTATUSRdData.allrunning := true.B + DMSTATUSRdData.anyrunning := true.B + } + + //TODO + DMSTATUSRdData.cfgstrvalid := false.B + + //----HARTINFO + + val HARTINFORdData = Wire (init = (new HARTINFOFields()).fromBits(0.U)) + HARTINFORdData.dataaccess := true.B + HARTINFORdData.datasize := cfg.nAbstractDataWords.U + HARTINFORdData.dataaddr := DsbRegAddrs.DATA.U + HARTINFORdData.nscratch := cfg.nScratch.U + + //----HALTSUM (and halted registers) + val numHaltedStatus = ((nComponents - 1) / 32) + 1 + val haltedStatus = Wire(Vec(numHaltedStatus, Bits(width = 32))) + + for (ii <- 0 until numHaltedStatus) { + haltedStatus(ii) := Cat(haltedBitRegs.slice(ii * 32, (ii + 1) * 32).reverse) + } + + val haltedSummary = Cat(haltedStatus.map(_.orR).reverse) + val HALTSUMRdData = (new HALTSUMFields()).fromBits(haltedSummary) + + //----ABSTRACTCS + + val ABSTRACTCSReset = Wire(init = (new ABSTRACTCSFields()).fromBits(0.U)) + ABSTRACTCSReset.datacount := cfg.nAbstractDataWords.U + ABSTRACTCSReset.progsize := cfg.nProgramBufferWords.U + + val ABSTRACTCSReg = Reg(new ABSTRACTCSFields()) + val ABSTRACTCSWrDataVal = Wire(init = 0.U(32.W)) + val ABSTRACTCSWrData = (new ABSTRACTCSFields()).fromBits(ABSTRACTCSWrDataVal) + val ABSTRACTCSRdData = Wire(init = ABSTRACTCSReg) + + val ABSTRACTCSRdEn = Wire(init = false.B) + val ABSTRACTCSWrEnMaybe = Wire(init = false.B) + + val ABSTRACTCSWrEnLegal = Wire(init = false.B) + val ABSTRACTCSWrEn = ABSTRACTCSWrEnMaybe && ABSTRACTCSWrEnLegal + + val errorBusy = Wire(init = false.B) + val errorException = Wire(init = false.B) + val errorUnsupported = Wire(init = false.B) + val errorHaltResume = Wire(init = false.B) + + when(~io.dmactive){ + ABSTRACTCSReg := ABSTRACTCSReset + }.otherwise { + when (errorBusy){ + ABSTRACTCSReg.cmderr := DebugAbstractCommandError.ErrBusy.id.U + }.elsewhen (errorException) { + ABSTRACTCSReg.cmderr := DebugAbstractCommandError.ErrException.id.U + }.elsewhen (errorUnsupported) { + ABSTRACTCSReg.cmderr := DebugAbstractCommandError.ErrNotSupported.id.U + }.elsewhen (errorHaltResume) { + ABSTRACTCSReg.cmderr := DebugAbstractCommandError.ErrHaltResume.id.U + }.otherwise { + //TODO: Should be write-1-to-clear & ~ABSTRACTCSWrData.cmderr + when (ABSTRACTCSWrEn && ABSTRACTCSWrData.cmderr === 0.U){ + ABSTRACTCSReg.cmderr := 0.U + } + } + } + + // For busy, see below state machine. + val abstractCommandBusy = Wire(init = true.B) + ABSTRACTCSRdData.busy := abstractCommandBusy + + //---- ABSTRACTAUTO + + val ABSTRACTAUTOReset = Wire(init = (new ABSTRACTAUTOFields()).fromBits(0.U)) + val ABSTRACTAUTOReg = Reg(new ABSTRACTAUTOFields()) + val ABSTRACTAUTOWrDataVal = Wire(init = 0.U(32.W)) + val ABSTRACTAUTOWrData = (new ABSTRACTAUTOFields()).fromBits(ABSTRACTAUTOWrDataVal) + val ABSTRACTAUTORdData = Wire(init = ABSTRACTAUTOReg) + + val ABSTRACTAUTORdEn = Wire(init = false.B) + val ABSTRACTAUTOWrEnMaybe = Wire(init = false.B) + + val ABSTRACTAUTOWrEnLegal = Wire(init = false.B) + val ABSTRACTAUTOWrEn = ABSTRACTAUTOWrEnMaybe && ABSTRACTAUTOWrEnLegal + + when (~io.dmactive) { + ABSTRACTAUTOReg := ABSTRACTAUTOReset + }.elsewhen (ABSTRACTAUTOWrEn) { + ABSTRACTAUTOReg.autoexecprogbuf := ABSTRACTAUTOWrData.autoexecprogbuf & ( (1 << cfg.nProgramBufferWords) - 1).U + ABSTRACTAUTOReg.autoexecdata := ABSTRACTAUTOWrData.autoexecdata & ( (1 << cfg.nAbstractDataWords) - 1).U + } + val dmiAbstractDataAccessVec = Wire(init = Vec.fill(cfg.nAbstractDataWords * 4){false.B}) + dmiAbstractDataAccessVec := (dmiAbstractDataWrEn zip dmiAbstractDataRdEn).map{ case (r,w) => r | w} + + val dmiProgramBufferAccessVec = Wire(init = Vec.fill(cfg.nProgramBufferWords * 4){false.B}) + dmiProgramBufferAccessVec := (dmiProgramBufferWrEn zip dmiProgramBufferRdEn).map{ case (r,w) => r | w} + + val dmiAbstractDataAccess = dmiAbstractDataAccessVec.reduce(_ || _ ) + val dmiProgramBufferAccess = dmiProgramBufferAccessVec.reduce(_ || _) + + // This will take the shorter of the lists, which is what we want. + val autoexecData = Wire(init = Vec.fill(cfg.nAbstractDataWords){false.B}) + val autoexecProg = Wire(init = Vec.fill(cfg.nProgramBufferWords){false.B}) + (autoexecData zip ABSTRACTAUTOReg.autoexecdata.toBools).zipWithIndex.foreach {case (t, i) => t._1 := dmiAbstractDataAccessVec(i * 4) && t._2 } + (autoexecProg zip ABSTRACTAUTOReg.autoexecprogbuf.toBools).zipWithIndex.foreach {case (t, i) => t._1 := dmiProgramBufferAccessVec(i * 4) && t._2} + + val autoexec = autoexecData.reduce(_ || _) || autoexecProg.reduce(_ || _) + + //---- COMMAND + + val COMMANDReset = Wire(init = (new COMMANDFields()).fromBits(0.U)) + val COMMANDReg = Reg(new COMMANDFields()) + + val COMMANDWrDataVal = Wire(init = 0.U(32.W)) + val COMMANDWrData = Wire(init = (new COMMANDFields()).fromBits(COMMANDWrDataVal)) + val COMMANDWrEnMaybe = Wire(init = false.B) + val COMMANDWrEnLegal = Wire(init = false.B) + val COMMANDRdEn = Wire(init = false.B) + + val COMMANDWrEn = COMMANDWrEnMaybe && COMMANDWrEnLegal + val COMMANDRdData = COMMANDReg + + when (~io.dmactive) { + COMMANDReg := COMMANDReset + }.otherwise { + when (COMMANDWrEn) { + COMMANDReg := COMMANDWrData + } + } + + // --- Abstract Data + + // These are byte addressible, s.t. the Processor can use + // byte-addressible instructions to store to them. + val abstractDataMem = Reg(Vec(cfg.nAbstractDataWords*4, UInt(8.W))) + val abstractDataWords = List.tabulate(cfg.nAbstractDataWords) { ii => + val slice = abstractDataMem.slice(ii * 4, (ii+1)*4) + slice.reduce[UInt]{ case (x: UInt, y: UInt) => Cat(y, x) + } + } + + // --- Program Buffer + val programBufferMem = Reg(Vec(cfg.nProgramBufferWords*4, UInt(8.W))) + + //-------------------------------------------------------------- + // These bits are implementation-specific bits set + // by harts executing code. + //-------------------------------------------------------------- + + for (component <- 0 until nComponents) { + when (~io.dmactive) { + haltedBitRegs(component) := false.B + }.otherwise { + when (hartHaltedWrEn) { + when (hartHaltedId === component.U) { + haltedBitRegs(component) := true.B + } + }.elsewhen (hartResumingWrEn) { + when (hartResumingId === component.U) { + haltedBitRegs(component) := false.B + } + } + } + } + + //-------------------------------------------------------------- + // Program Buffer Access (DMI ... System Bus can override) + //-------------------------------------------------------------- + dmiNode.regmap( + (DMI_DMSTATUS << 2) -> Seq(RegField.r(32, DMSTATUSRdData.asUInt())), + //TODO (DMI_CFGSTRADDR0 << 2) -> cfgStrAddrFields, + (DMI_HARTINFO << 2) -> Seq(RegField.r(32, HARTINFORdData.asUInt())), + (DMI_HALTSUM << 2) -> Seq(RegField.r(32, HALTSUMRdData.asUInt())), + (DMI_ABSTRACTCS << 2) -> Seq(RWNotify(32, ABSTRACTCSRdData.asUInt(), ABSTRACTCSWrDataVal, ABSTRACTCSRdEn, ABSTRACTCSWrEnMaybe)), + (DMI_ABSTRACTAUTO<< 2) -> Seq(RWNotify(32, ABSTRACTAUTORdData.asUInt(), ABSTRACTAUTOWrDataVal, ABSTRACTAUTORdEn, ABSTRACTAUTOWrEnMaybe)), + (DMI_COMMAND << 2) -> Seq(RWNotify(32, COMMANDRdData.asUInt(), COMMANDWrDataVal, COMMANDRdEn, COMMANDWrEnMaybe)), + (DMI_DATA0 << 2) -> abstractDataMem.zipWithIndex.map{case (x, i) => RWNotify(8, x, x, + dmiAbstractDataRdEn(i), + dmiAbstractDataWrEn(i))}, + (DMI_PROGBUF0 << 2) -> programBufferMem.zipWithIndex.map{case (x, i) => RWNotify(8, x, x, + dmiProgramBufferRdEn(i), + dmiProgramBufferWrEn(i))}, + (DMIConsts.dmi_haltStatusAddr << 2) -> haltedStatus.map(x => RegField.r(32, x)) + ) + + //-------------------------------------------------------------- + // "Variable" ROM Generation + //-------------------------------------------------------------- + + val goProgramBuffer = Wire(init = false.B) + val goResume = Wire(init = false.B) + val goAbstract = Wire(init = false.B) + + val whereToReg = Reg(UInt(32.W)) + + val jalProgBuf = Wire(init = (new GeneratedUJ()).fromBits(rocket.Instructions.JAL.value.U)) + jalProgBuf.setImm(PROGBUF - WHERETO) + jalProgBuf.rd := 0.U + + val jalAbstract = Wire(init = (new GeneratedUJ()).fromBits(rocket.Instructions.JAL.value.U)) + jalAbstract.setImm(ABSTRACT - WHERETO) + jalProgBuf.rd := 0.U + + val jalResume = Wire(init = (new GeneratedUJ()).fromBits(rocket.Instructions.JAL.value.U)) + jalResume.setImm(RESUME - WHERETO) + jalResume.rd := 0.U + + when (~io.dmactive) { + whereToReg := 0.U + }.otherwise{ + when (goProgramBuffer) { + whereToReg := jalProgBuf.asUInt() + }.elsewhen (goResume) { + whereToReg := jalResume.asUInt() + }.elsewhen (goAbstract) { + whereToReg := jalAbstract.asUInt() + } + } + + val goReg = Reg(Bool()) + + when (~io.dmactive){ + goReg := false.B + }.otherwise { + when (goProgramBuffer | goResume | goAbstract) { + goReg := true.B + }.elsewhen (hartGoingWrEn){ + assert(hartGoingId === 0.U, "Unexpected 'GOING' hart.")//Chisel3 #540 %x, expected %x", hartGoingId, 0.U) + goReg := false.B + } + } + + val goBytes = Wire(init = Vec.fill(nComponents){0.U(8.W)}) + goBytes(selectedHartReg) := Cat(0.U(7.W), goReg) + + //---------------------------- + // Abstract Command Decoding & Generation + //---------------------------- + + val accessRegisterCommandWr = Wire(init = (new ACCESS_REGISTERFields()).fromBits(COMMANDWrData.asUInt())) + val accessRegisterCommandReg = Wire(init = (new ACCESS_REGISTERFields()).fromBits(COMMANDReg.asUInt())) + + // TODO: Quick Access + + class GeneratedI extends Bundle { + val imm = UInt(12.W) + val rs1 = UInt(5.W) + val funct3 = UInt(3.W) + val rd = UInt(5.W) + val opcode = UInt(7.W) + } + + class GeneratedS extends Bundle { + val immhi = UInt(7.W) + val rs2 = UInt(5.W) + val rs1 = UInt(5.W) + val funct3 = UInt(3.W) + val immlo = UInt(5.W) + val opcode = UInt(7.W) + } + + class GeneratedUJ extends Bundle { + val imm3 = UInt(1.W) + val imm0 = UInt(10.W) + val imm1 = UInt(1.W) + val imm2 = UInt(8.W) + val rd = UInt(5.W) + val opcode = UInt(7.W) + + def setImm(imm: Int) : Unit = { + // TODO: Check bounds of imm. + + require(imm % 2 == 0, "Immediate must be even for UJ encoding.") + val immWire = Wire(init = imm.S(21.W)) + val immBits = Wire(init = Vec(immWire.toBools)) + + imm0 := immBits.slice(1, 1 + 10).asUInt() + imm1 := immBits.slice(11, 11 + 11).asUInt() + imm2 := immBits.slice(12, 12 + 8).asUInt() + imm3 := immBits.slice(20, 20 + 1).asUInt() + } + } + + val abstractGeneratedMem = Reg(Vec(2, (UInt(32.W)))) + val abstractGeneratedI = Wire(new GeneratedI()) + val abstractGeneratedS = Wire(new GeneratedS()) + val nop = Wire(new GeneratedI()) + + abstractGeneratedI.opcode := ((new GeneratedI()).fromBits(rocket.Instructions.LW.value.U)).opcode + abstractGeneratedI.rd := (accessRegisterCommandReg.regno & 0x1F.U) + abstractGeneratedI.funct3 := accessRegisterCommandReg.size + abstractGeneratedI.rs1 := 0.U + abstractGeneratedI.imm := DATA.U + + abstractGeneratedS.opcode := ((new GeneratedI()).fromBits(rocket.Instructions.SW.value.U)).opcode + abstractGeneratedS.immlo := (DATA & 0x1F).U + abstractGeneratedS.funct3 := accessRegisterCommandReg.size + abstractGeneratedS.rs1 := 0.U + abstractGeneratedS.rs2 := (accessRegisterCommandReg.regno & 0x1F.U) + abstractGeneratedS.immhi := (DATA >> 5).U + + nop := ((new GeneratedI()).fromBits(rocket.Instructions.ADDI.value.U)) + nop.rd := 0.U + nop.rs1 := 0.U + nop.imm := 0.U + + when (goAbstract) { + abstractGeneratedMem(0) := Mux(/*TODO: accessRegisterCommandReg.transfer*/true.B, + Mux(accessRegisterCommandReg.write, + // To write a register, we need to do LW. + abstractGeneratedI.asUInt(), + // To read a register, we need to do SW. + abstractGeneratedS.asUInt()), + nop.asUInt() + ) + abstractGeneratedMem(1) := Mux(/*TODO accessRegisterCommandReg.postexec*/ false.B, + nop.asUInt(), + rocket.Instructions.EBREAK.value.U) + } + + //-------------------------------------------------------------- + // System Bus Access + //-------------------------------------------------------------- + + tlNode.regmap( + // This memory is writable. + HALTED -> Seq(WNotify(sbIdWidth, hartHaltedId, hartHaltedWrEn)), + GOING -> Seq(WNotify(sbIdWidth, hartGoingId, hartGoingWrEn)), + RESUMING -> Seq(WNotify(sbIdWidth, hartResumingId, hartResumingWrEn)), + EXCEPTION -> Seq(WNotify(sbIdWidth, hartExceptionId, hartExceptionWrEn)), + DATA -> abstractDataMem.map(x => RegField(8, x)), + PROGBUF -> programBufferMem.map(x => RegField(8, x)), + + // These sections are read-only. + // ROMBASE -> romRegFields, + GO -> goBytes.map(x => RegField.r(8, x)), + WHERETO -> Seq(RegField.r(32, whereToReg)), + ABSTRACT -> abstractGeneratedMem.map(x => RegField.r(32, x)) + ) + + // Override System Bus accesses with dmactive reset. + when (~io.dmactive){ + abstractDataMem.foreach {x => x := 0.U} + programBufferMem.foreach {x => x := 0.U} + } + + //-------------------------------------------------------------- + // Abstract Command State Machine + //-------------------------------------------------------------- + + object CtrlState extends scala.Enumeration { + type CtrlState = Value + val Waiting, CheckGenerate, PreExec, Abstract, PostExec = Value + + def apply( t : Value) : UInt = { + t.id.U(log2Up(values.size).W) + } + } + import CtrlState._ + + // This is not an initialization! + val ctrlStateReg = Reg(CtrlState(Waiting)) + + val hartHalted = haltedBitRegs(selectedHartReg) + val ctrlStateNxt = Wire(init = ctrlStateReg) + + //------------------------ + // DMI Register Control and Status + + abstractCommandBusy := (ctrlStateReg != CtrlState(Waiting)) + + ABSTRACTCSWrEnLegal := (ctrlStateReg === CtrlState(Waiting)) + COMMANDWrEnLegal := (ctrlStateReg === CtrlState(Waiting)) + ABSTRACTAUTOWrEnLegal := (ctrlStateReg === CtrlState(Waiting)) + + errorBusy := (ABSTRACTCSWrEnMaybe && ~ABSTRACTCSWrEnLegal) || + (ABSTRACTAUTOWrEnMaybe && ~ABSTRACTAUTOWrEnLegal) || + (COMMANDWrEnMaybe && ~COMMANDWrEnLegal) || + (dmiAbstractDataAccess && abstractCommandBusy) || + (dmiProgramBufferAccess && abstractCommandBusy) + + // TODO: Maybe Quick Access + val commandWrIsAccessRegister = (COMMANDWrData.cmdtype === DebugAbstractCommandType.AccessRegister.id.U) + val commandRegIsAccessRegister = (COMMANDReg.cmdtype === DebugAbstractCommandType.AccessRegister.id.U) + + val commandRegIsUnsupported = Wire(init = true.B) + val commandRegBadHaltResume = Wire(init = false.B) + when (commandRegIsAccessRegister) { + when ((accessRegisterCommandReg.regno >= 0x1000.U && accessRegisterCommandReg.regno <= 0x101F.U)){ + commandRegIsUnsupported := false.B + commandRegBadHaltResume := ~hartHalted + } + } + + val wrAccessRegisterCommand = COMMANDWrEn && commandWrIsAccessRegister && (ABSTRACTCSReg.cmderr === 0.U) + val regAccessRegisterCommand = autoexec && commandRegIsAccessRegister && (ABSTRACTCSReg.cmderr === 0.U) + + //------------------------ + // Variable ROM STATE MACHINE + // ----------------------- + + when (ctrlStateReg === CtrlState(Waiting)){ + + when (wrAccessRegisterCommand || regAccessRegisterCommand) { + ctrlStateNxt := CtrlState(CheckGenerate) + }.elsewhen(io.innerCtrl.fire() && io.innerCtrl.bits.resumereq) { + goResume := true.B + } + }.elsewhen (ctrlStateReg === CtrlState(CheckGenerate)){ + + // We use this state to ensure that the COMMAND has been + // registered by the time that we need to use it, to avoid + // generating it directly from the COMMANDWrData. + + when (commandRegIsUnsupported) { + errorUnsupported := true.B + ctrlStateNxt := CtrlState(Waiting) + }.elsewhen (commandRegBadHaltResume){ + errorHaltResume := true.B + ctrlStateNxt := CtrlState(Waiting) + }.otherwise { + when (accessRegisterCommandReg.preexec) { + ctrlStateNxt := CtrlState(PreExec) + goProgramBuffer := true.B + }.otherwise { + ctrlStateNxt := CtrlState(Abstract) + goAbstract := true.B + } + } + }.elsewhen (ctrlStateReg === CtrlState(PreExec)) { + + // We can't just look at 'hartHalted' here, because + // hartHaltedWrEn is overloaded to mean 'got an ebreak' + // which may have happened when we were already halted. + when(goReg === false.B && hartHaltedWrEn && (hartHaltedId === selectedHartReg)){ + ctrlStateNxt := CtrlState(Abstract) + goAbstract := true.B + } + when(hartExceptionWrEn) { + assert(hartExceptionId === 0.U, "Unexpected 'EXCEPTION' hart")// Chisel3 #540, %x, expected %x", hartExceptionId, 0.U) + ctrlStateNxt := CtrlState(Waiting) + errorException := true.B + } + }.elsewhen (ctrlStateReg === CtrlState(Abstract)) { + + // We can't just look at 'hartHalted' here, because + // hartHaltedWrEn is overloaded to mean 'got an ebreak' + // which may have happened when we were already halted. + when(goReg === false.B && hartHaltedWrEn && (hartHaltedId === selectedHartReg)){ + when (accessRegisterCommandReg.postexec) { + ctrlStateNxt := CtrlState(PostExec) + goProgramBuffer := true.B + }.otherwise { + ctrlStateNxt := CtrlState(Waiting) + } + } + when(hartExceptionWrEn) { + assert(hartExceptionId === 0.U, "Unexpected 'EXCEPTION' hart")//Chisel3 #540 %x, expected %x", hartExceptionId, selectedHartReg) + ctrlStateNxt := CtrlState(Waiting) + errorUnsupported := true.B + } + }.elsewhen (ctrlStateReg === CtrlState(PostExec)) { + + // We can't just look at 'hartHalted' here, because + // hartHaltedWrEn is overloaded to mean 'got an ebreak' + // which may have happened when we were already halted. + when(goReg === false.B && hartHaltedWrEn && (hartHaltedId === selectedHartReg)){ + ctrlStateNxt := CtrlState(Waiting) + } + when(hartExceptionWrEn) { + assert(hartExceptionId === 0.U, "Unexpected 'EXCEPTION' hart")//Chisel3 #540, %x, expected %x", hartExceptionId, 0.U) + ctrlStateNxt := CtrlState(Waiting) + errorException := true.B + } + } + + when (~io.dmactive) { + ctrlStateReg := CtrlState(Waiting) + }.otherwise { + ctrlStateReg := ctrlStateNxt + } + } +} + +// Wrapper around TL Debug Module Inner and an Async DMI Sink interface. +// Handles the synchronization of dmactive, which is used as a synchronous reset +// inside the Inner block. +// Also is the Sink side of hartsel & resumereq fields of DMCONTROL. +class TLDebugModuleInnerAsync(device: Device, getNComponents: () => Int)(implicit p: Parameters) extends LazyModule{ + + val dmInner = LazyModule(new TLDebugModuleInner(device, getNComponents)(p)) + val dmiNode = TLAsyncInputNode() + val tlNode = TLInputNode() + + dmInner.dmiNode := TLAsyncCrossingSink(depth=1)(dmiNode) + dmInner.tlNode := tlNode + + lazy val module = new LazyModuleImp(this) { + + val io = new Bundle { + // this comes from tlClk domain. + val tl_in = tlNode.bundleIn + // These are all asynchronous and come from Outer + val dmi_in = dmiNode.bundleIn + val dmactive = Bool(INPUT) + val innerCtrl = new AsyncBundle(1, new DebugInternalBundle()).flip + // This comes from tlClk domain. + val debugUnavail = Vec(getNComponents(), Bool()).asInput + } + + dmInner.module.io.innerCtrl := FromAsyncBundle(io.innerCtrl) + dmInner.module.io.dmactive := ~ResetCatchAndSync(clock, ~io.dmactive) + dmInner.module.io.debugUnavail := io.debugUnavail + + } +} + +/** Create a version of the TLDebugModule which includes a synchronization interface + * internally for the DMI. This is no longer optional outside of this module + * because the Clock must run when tlClock isn't running or tlReset is asserted. + */ + +class TLDebugModule(implicit p: Parameters) extends LazyModule { + + val device = new SimpleDevice("debug-controller", Seq("riscv,debug-013")){ + override val alwaysExtended = true + } + + val node = TLInputNode() + val intnode = IntOutputNode() + + val dmOuter = LazyModule(new TLDebugModuleOuterAsync(device)(p)) + val dmInner = LazyModule(new TLDebugModuleInnerAsync(device, () => {intnode.bundleOut.size})(p)) + + dmInner.dmiNode := dmOuter.dmiInnerNode + dmInner.tlNode := node + intnode :*= dmOuter.intnode + + lazy val module = new LazyModuleImp(this) { + val nComponents = intnode.bundleOut.size + + val io = new Bundle { + val ctrl = new DebugCtrlBundle(nComponents) + val dmi = new ClockedDMIIO().flip + val in = node.bundleIn + val debugInterrupts = intnode.bundleOut + } + + dmOuter.module.io.dmi <> io.dmi.dmi + dmOuter.module.reset := io.dmi.dmiReset + dmOuter.module.clock := io.dmi.dmiClock + + dmInner.module.io.innerCtrl := dmOuter.module.io.innerCtrl + dmInner.module.io.dmactive := dmOuter.module.io.ctrl.debugActive + dmInner.module.io.debugUnavail := io.ctrl.debugUnavail + + io.ctrl <> dmOuter.module.io.ctrl + + } +} + +/** This includes the clock and reset as these are passed through the + * hierarchy until the Debug Module is actually instantiated. + * + */ + +class ClockedDMIIO(implicit val p: Parameters) extends ParameterizedBundle()(p){ + val dmi = new DMIIO()(p) + val dmiClock = Clock(OUTPUT) + val dmiReset = Bool(OUTPUT) +} + +/** Convert DMI to TL. Avoids using special DMI synchronizers and register accesses + * + */ + +class DMIToTL(implicit p: Parameters) extends LazyModule { + + val node = TLClientNode(TLClientParameters()) + + lazy val module = new LazyModuleImp(this) { + val io = new Bundle { + val dmi = new DMIIO()(p).flip() + val out = node.bundleOut + } + + val tl = io.out(0) + val edge = node.edgesOut(0) + + val src = Wire(init = 0.U) + val addr = Wire(init = (io.dmi.req.bits.addr << 2)) + val size = (log2Ceil(DMIConsts.dmiDataSize / 8)).U + + val (_, gbits) = edge.Get(src, addr, size) + val (_, pfbits) = edge.Put(src, addr, size, io.dmi.req.bits.data) + // This is just used for the DMI's NOP. TODO: Consider whether to send this + // across TL at all or just respond immediately. + val (_, nbits) = edge.Put(src, addr, size, io.dmi.req.bits.data, mask = 0.U) + + when (io.dmi.req.bits.op === DMIConsts.dmi_OP_WRITE) { tl.a.bits := pfbits + }.elsewhen (io.dmi.req.bits.op === DMIConsts.dmi_OP_READ) { tl.a.bits := gbits + }.otherwise { tl.a.bits := nbits + } + + tl.a.valid := io.dmi.req.valid + io.dmi.req.ready := tl.a.ready + + io.dmi.resp.valid := tl.d.valid + tl.d.ready := io.dmi.resp.ready + io.dmi.resp.bits.resp := Mux(tl.d.bits.error, DMIConsts.dmi_RESP_FAILURE, DMIConsts.dmi_RESP_SUCCESS) + io.dmi.resp.bits.data := tl.d.bits.data + + // Tie off unused channels + tl.b.ready := false.B + tl.c.valid := false.B + tl.e.valid := false.B + + } +} diff --git a/vsrc/jtag_vpi.v b/vsrc/jtag_vpi.v index 0d95ea18..c2d83438 100644 --- a/vsrc/jtag_vpi.v +++ b/vsrc/jtag_vpi.v @@ -45,13 +45,14 @@ module JTAGVPI 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); + output jtag_TMS, + output jtag_TCK, + output jtag_TDI, + input jtag_TDO_data, + input jtag_TDO_driven, + + input enable, + input init_done); reg tms; reg tck; @@ -61,9 +62,8 @@ module JTAGVPI assign jtag_TMS = tms; assign jtag_TCK = tck; assign jtag_TDI = tdi; - assign tdo = jtag_TDO; - - + assign tdo = jtag_TDO_driven ? jtag_TDO_data : 1'bz; + integer cmd; integer length; integer nb_bits; From bb64c9290643ccba8bfc440405f423b777f9a052 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 27 Mar 2017 21:21:48 -0700 Subject: [PATCH 05/15] csr: Bring functionality in line with v13 spec. ebreak does not cause exception in Debug Mode, it just starts at Debug ROM again. --- src/main/scala/rocket/CSR.scala | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/scala/rocket/CSR.scala b/src/main/scala/rocket/CSR.scala index 5ff0626a..3f0ed479 100644 --- a/src/main/scala/rocket/CSR.scala +++ b/src/main/scala/rocket/CSR.scala @@ -47,8 +47,7 @@ class MStatus extends Bundle { class DCSR extends Bundle { val xdebugver = UInt(width = 2) - val ndreset = Bool() - val fullreset = Bool() + val zero4 = UInt(width=2) val zero3 = UInt(width = 12) val ebreakm = Bool() val ebreakh = Bool() @@ -58,9 +57,9 @@ class DCSR extends Bundle { val stopcycle = Bool() val stoptime = Bool() val cause = UInt(width = 3) + // TODO: debugint is not in the Debug Spec v13 val debugint = Bool() - val zero1 = Bool() - val halt = Bool() + val zero1 = UInt(width=2) val step = Bool() val prv = UInt(width = PRV.SZ) } @@ -468,8 +467,8 @@ class CSRFile(perfEventSets: EventSets = new EventSets(Seq()))(implicit p: Param val causeIsDebugTrigger = !cause(xLen-1) && cause_lsbs === CSR.debugTriggerCause val causeIsDebugBreak = !cause(xLen-1) && insn_break && Cat(reg_dcsr.ebreakm, reg_dcsr.ebreakh, reg_dcsr.ebreaks, reg_dcsr.ebreaku)(reg_mstatus.prv) val trapToDebug = Bool(usingDebug) && (reg_singleStepped || causeIsDebugInt || causeIsDebugTrigger || causeIsDebugBreak || reg_debug) + val debugTVec = Mux(reg_debug, Mux(insn_break, UInt(0x800), UInt(0x808)), UInt(0x800)) val delegate = Bool(usingVM) && reg_mstatus.prv <= PRV.S && Mux(cause(xLen-1), reg_mideleg(cause_lsbs), reg_medeleg(cause_lsbs)) - val debugTVec = Mux(reg_debug, UInt(0x808), UInt(0x800)) val tvec = Mux(trapToDebug, debugTVec, Mux(delegate, reg_stvec.sextTo(vaddrBitsExtended), reg_mtvec)) io.evec := tvec io.ptbr := reg_sptbr @@ -505,11 +504,13 @@ class CSRFile(perfEventSets: EventSets = new EventSets(Seq()))(implicit p: Param ) when (trapToDebug) { - reg_debug := true - reg_dpc := epc - reg_dcsr.cause := Mux(reg_singleStepped, 4, Mux(causeIsDebugInt, 3, Mux[UInt](causeIsDebugTrigger, 2, 1))) - reg_dcsr.prv := trimPrivilege(reg_mstatus.prv) - new_prv := PRV.M + when (!reg_debug) { + reg_debug := true + reg_dpc := epc + reg_dcsr.cause := Mux(reg_singleStepped, 4, Mux(causeIsDebugInt, 3, Mux[UInt](causeIsDebugTrigger, 2, 1))) + reg_dcsr.prv := trimPrivilege(reg_mstatus.prv) + new_prv := PRV.M + } }.elsewhen (delegate) { reg_sepc := formEPC(epc) reg_scause := cause @@ -623,7 +624,6 @@ class CSRFile(perfEventSets: EventSets = new EventSets(Seq()))(implicit p: Param if (usingDebug) { when (decoded_addr(CSRs.dcsr)) { val new_dcsr = new DCSR().fromBits(wdata) - reg_dcsr.halt := new_dcsr.halt reg_dcsr.step := new_dcsr.step reg_dcsr.ebreakm := new_dcsr.ebreakm if (usingVM) reg_dcsr.ebreaks := new_dcsr.ebreaks From cbc8d2400acd9af248e3591bffcf32a399edc99b Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 27 Mar 2017 21:24:44 -0700 Subject: [PATCH 06/15] debug: remove old Verilog DebugTransportModuleJtag file as it has been replaced by Chisel version --- vsim/Makefrag | 2 +- vsrc/DebugTransportModuleJtag.v | 337 -------------------------------- 2 files changed, 1 insertion(+), 338 deletions(-) delete mode 100644 vsrc/DebugTransportModuleJtag.v diff --git a/vsim/Makefrag b/vsim/Makefrag index 93c38d8f..1429ba1c 100644 --- a/vsim/Makefrag +++ b/vsim/Makefrag @@ -4,7 +4,7 @@ # Verilog sources -bb_vsrcs = $(base_dir)/vsrc/DebugTransportModuleJtag.v \ +bb_vsrcs = \ $(base_dir)/vsrc/jtag_vpi.v \ $(base_dir)/vsrc/ClockDivider2.v \ $(base_dir)/vsrc/AsyncResetReg.v \ diff --git a/vsrc/DebugTransportModuleJtag.v b/vsrc/DebugTransportModuleJtag.v deleted file mode 100644 index 316353d7..00000000 --- a/vsrc/DebugTransportModuleJtag.v +++ /dev/null @@ -1,337 +0,0 @@ -// See LICENSE.SiFive for license details. - -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 - - // Number of cycles which must remain in IDLE - // The software should handle even if the - // answer is actually higher than this, or - // the software may choose to ignore it entirely - // and just check for busy. - parameter DBUS_IDLE_CYCLES = 3'h5; - - 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 stickyBusyReg; - reg stickyNonzeroRespReg; - - 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[3:0]; - wire [3:0] debugVersion = DEBUG_VERSION[3:0]; - - wire [1:0] dbusStatus; - wire [2:0] dbusIdleCycles; - - wire dbusReset; - - assign dbusIdleCycles = DBUS_IDLE_CYCLES; - assign dbusStatus = {stickyNonzeroRespReg, stickyNonzeroRespReg | stickyBusyReg}; - assign dbusReset = shiftReg[16]; - - assign dtminfo = {15'b0, - 1'b0, // dbusreset goes here but is write-only - 3'b0, - dbusIdleCycles, - dbusStatus, - 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) | stickyBusyReg; - // This is needed especially for the first request. - assign nonzeroResp = (dtm_resp_valid ? | {dtm_resp_bits[DEBUG_OP_BITS-1:0]} : 1'b0) | stickyNonzeroRespReg; - - // 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; - stickyBusyReg <= 1'b0; - stickyNonzeroRespReg <= 1'b0; - end else if (irReg == REG_DEBUG_ACCESS) begin - case(jtagStateReg) - CAPTURE_DR: begin - skipOpReg <= busy; - downgradeOpReg <= (~busy & nonzeroResp); - stickyBusyReg <= busy; - stickyNonzeroRespReg <= nonzeroResp; - end - UPDATE_DR: begin - skipOpReg <= 1'b0; - downgradeOpReg <= 1'b0; - end - endcase // case (jtagStateReg) - end else if (irReg == REG_DTM_INFO) begin - case(jtagStateReg) - UPDATE_DR: begin - if (dbusReset) begin - stickyNonzeroRespReg <= 1'b0; - stickyBusyReg <= 1'b0; - end - 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 - - From d6ab929c41d782aed81998cf0dab1a4ab7a9fdcd Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 27 Mar 2017 21:25:37 -0700 Subject: [PATCH 07/15] debug: Remove older version of JTAG interface as it is superseded by the one in jtag package. --- src/main/scala/junctions/jtag.scala | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/main/scala/junctions/jtag.scala diff --git a/src/main/scala/junctions/jtag.scala b/src/main/scala/junctions/jtag.scala deleted file mode 100644 index 1b878488..00000000 --- a/src/main/scala/junctions/jtag.scala +++ /dev/null @@ -1,16 +0,0 @@ -// See LICENSE.SiFive for license details. - -package junctions -import Chisel._ -import config._ - -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] -} From ff38ebdf5ef20c77bb656f79be7a0a1892f553f5 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Tue, 28 Mar 2017 21:12:57 -0700 Subject: [PATCH 08/15] debug: Bump FESVR version to initial Debug v13. Doesn't work yet. --- riscv-tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv-tools b/riscv-tools index 89d48702..adf1cb4a 160000 --- a/riscv-tools +++ b/riscv-tools @@ -1 +1 @@ -Subproject commit 89d487023c1e59ff574872e2f51ee479cda380ab +Subproject commit adf1cb4a75eebed12bf64cbcd38ceccc50721552 From ca9a5a1cf74ee02dbc1a8b746d5c136669d060c8 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Tue, 28 Mar 2017 21:13:45 -0700 Subject: [PATCH 09/15] debug: Fixes in how the SimDTM was hooked up to FESVR --- csrc/SimDTM.cc | 4 ++-- src/main/scala/rocketchip/TestHarness.scala | 2 +- vsrc/SimDTM.v | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/csrc/SimDTM.cc b/csrc/SimDTM.cc index 2948da4e..0688c0df 100644 --- a/csrc/SimDTM.cc +++ b/csrc/SimDTM.cc @@ -33,11 +33,11 @@ extern "C" int debug_tick unsigned char debug_req_ready, int* debug_req_bits_addr, int* debug_req_bits_op, - long long* debug_req_bits_data, + int* debug_req_bits_data, unsigned char debug_resp_valid, unsigned char* debug_resp_ready, int debug_resp_bits_resp, - long long debug_resp_bits_data + int debug_resp_bits_data ) { if (!dtm) { diff --git a/src/main/scala/rocketchip/TestHarness.scala b/src/main/scala/rocketchip/TestHarness.scala index 7470ed87..85ab5ea3 100644 --- a/src/main/scala/rocketchip/TestHarness.scala +++ b/src/main/scala/rocketchip/TestHarness.scala @@ -70,7 +70,7 @@ class SimDTM(implicit p: Parameters) extends BlackBox { def connect(tbclk: Clock, tbreset: Bool, dutio: uncore.devices.ClockedDMIIO, tbsuccess: Bool) = { io.clk := tbclk io.reset := tbreset - dutio <> io.debug + dutio.dmi <> io.debug dutio.dmiClock := tbclk dutio.dmiReset := tbreset diff --git a/vsrc/SimDTM.v b/vsrc/SimDTM.v index ca2171be..a5f8c233 100644 --- a/vsrc/SimDTM.v +++ b/vsrc/SimDTM.v @@ -6,12 +6,12 @@ import "DPI-C" function int debug_tick input bit debug_req_ready, output int debug_req_bits_addr, output int debug_req_bits_op, - output longint debug_req_bits_data, + output int debug_req_bits_data, input bit debug_resp_valid, output bit debug_resp_ready, input int debug_resp_bits_resp, - input longint debug_resp_bits_data + input int debug_resp_bits_data ); module SimDTM( @@ -20,14 +20,14 @@ module SimDTM( output debug_req_valid, input debug_req_ready, - output [ 4:0] debug_req_bits_addr, + output [ 6:0] debug_req_bits_addr, output [ 1:0] debug_req_bits_op, - output [33:0] debug_req_bits_data, + output [31:0] debug_req_bits_data, input debug_resp_valid, output debug_resp_ready, input [ 1:0] debug_resp_bits_resp, - input [33:0] debug_resp_bits_data, + input [31:0] debug_resp_bits_data, output [31:0] exit ); @@ -37,19 +37,19 @@ module SimDTM( wire #0.1 __debug_req_ready = debug_req_ready; wire #0.1 __debug_resp_valid = debug_resp_valid; wire [31:0] #0.1 __debug_resp_bits_resp = {30'b0, debug_resp_bits_resp}; - wire [63:0] #0.1 __debug_resp_bits_data = {30'b0, debug_resp_bits_data}; + wire [31:0] #0.1 __debug_resp_bits_data = debug_resp_bits_data; bit __debug_req_valid; int __debug_req_bits_addr; int __debug_req_bits_op; - longint __debug_req_bits_data; + int __debug_req_bits_data; bit __debug_resp_ready; int __exit; assign #0.1 debug_req_valid = __debug_req_valid; - assign #0.1 debug_req_bits_addr = __debug_req_bits_addr[4:0]; + assign #0.1 debug_req_bits_addr = __debug_req_bits_addr[6:0]; assign #0.1 debug_req_bits_op = __debug_req_bits_op[1:0]; - assign #0.1 debug_req_bits_data = __debug_req_bits_data[33:0]; + assign #0.1 debug_req_bits_data = __debug_req_bits_data[31:0]; assign #0.1 debug_resp_ready = __debug_resp_ready; assign #0.1 exit = __exit; From 375a0392794619bee7380dd2b6d66eb3efdf9c68 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Tue, 28 Mar 2017 21:14:22 -0700 Subject: [PATCH 10/15] debug: Use proper write-1-to-clear ABSTRACTCS.cmderr behavior (because fesvr code is using correct spec) --- src/main/scala/uncore/devices/debug/Debug.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/uncore/devices/debug/Debug.scala b/src/main/scala/uncore/devices/debug/Debug.scala index 6ae5522e..657a1b9c 100644 --- a/src/main/scala/uncore/devices/debug/Debug.scala +++ b/src/main/scala/uncore/devices/debug/Debug.scala @@ -617,8 +617,8 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int)(implicit p: ABSTRACTCSReg.cmderr := DebugAbstractCommandError.ErrHaltResume.id.U }.otherwise { //TODO: Should be write-1-to-clear & ~ABSTRACTCSWrData.cmderr - when (ABSTRACTCSWrEn && ABSTRACTCSWrData.cmderr === 0.U){ - ABSTRACTCSReg.cmderr := 0.U + when (ABSTRACTCSWrEn /* && ABSTRACTCSWrData.cmderr === 0.U*/){ + ABSTRACTCSReg.cmderr := ABSTRACTCSReg.cmderr & ~(ABSTRACTCSWrData.cmderr); } } } From f6e72a3ef63e223f15b028c622d5fbe0a8dbf55c Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Wed, 29 Mar 2017 14:46:01 -0700 Subject: [PATCH 11/15] debug: Bump riscv-tools to pick up FESVR to version that works with debug v013 --- riscv-tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv-tools b/riscv-tools index adf1cb4a..70a6a085 160000 --- a/riscv-tools +++ b/riscv-tools @@ -1 +1 @@ -Subproject commit adf1cb4a75eebed12bf64cbcd38ceccc50721552 +Subproject commit 70a6a0851c5ac0f8749df83d1876f15df9419755 From 24509fc69f269d698090629e9644eeb284195f17 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Wed, 29 Mar 2017 15:20:07 -0700 Subject: [PATCH 12/15] debug_v013: Bump FESVR to pick up minor off-by-1 in error printing code. --- riscv-tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv-tools b/riscv-tools index 70a6a085..eba11b0d 160000 --- a/riscv-tools +++ b/riscv-tools @@ -1 +1 @@ -Subproject commit 70a6a0851c5ac0f8749df83d1876f15df9419755 +Subproject commit eba11b0de2a8e7500b3a1c075c5ac49da718680e From a14b7b5794047bd17a7951cbcf03f01616ac6dc8 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Wed, 29 Mar 2017 21:42:36 -0700 Subject: [PATCH 13/15] debug_v013: bump riscv-tools for slightly more efficient FESVR --- riscv-tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv-tools b/riscv-tools index eba11b0d..af8f5ee2 160000 --- a/riscv-tools +++ b/riscv-tools @@ -1 +1 @@ -Subproject commit eba11b0de2a8e7500b3a1c075c5ac49da718680e +Subproject commit af8f5ee2fc951a4a0b030e4a8d82fc4b1a1571e2 From 0828ebe9111d4741e0825ea5ef1a94824e1948d6 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Thu, 30 Mar 2017 11:46:28 -0700 Subject: [PATCH 14/15] debug_v013: bump fesvr to use autoexec feature for memory writes. --- riscv-tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv-tools b/riscv-tools index 4dbb81b3..6e32ffa7 160000 --- a/riscv-tools +++ b/riscv-tools @@ -1 +1 @@ -Subproject commit 4dbb81b31698ebaa6a3d548ae628a3c716c5df72 +Subproject commit 6e32ffa7a4e03254f934623f083078f82a721be7 From bcaee9834ce5d0d1700c43da61cde41701788de6 Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Thu, 30 Mar 2017 13:22:33 -0700 Subject: [PATCH 15/15] travis_wait 30 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index aadb05c6..54161c51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,7 @@ before_install: script: - make emulator-ndebug -C regression SUITE=$SUITE - - travis_wait make emulator-regression-tests -C regression SUITE=$SUITE + - travis_wait 30 make emulator-regression-tests -C regression SUITE=$SUITE before_cache: - ls -t regression/install | tail -n+2 | sed s@^@regression/install/@ | xargs rm -rf