From 77ec3d58c6c49a8f165462f1726171e6868fa064 Mon Sep 17 00:00:00 2001 From: James Stine Date: Mon, 3 Jun 2024 16:37:13 -0500 Subject: [PATCH] seed debug module for Wally --- bin/hw_interface.py | 404 +++++++++++++++++++++++++++++ bin/hw_test.py | 110 ++++++++ config/shared/debug.vh | 211 ++++++++++++++++ openocd.cfg | 30 +++ src/cvw.sv | 3 + src/debug/dm.sv | 436 ++++++++++++++++++++++++++++++++ src/debug/dtm.sv | 163 ++++++++++++ src/debug/hartcontrol.sv | 75 ++++++ src/debug/idreg.sv | 48 ++++ src/debug/ir.sv | 81 ++++++ src/debug/jtag.sv | 136 ++++++++++ src/debug/notes.txt | 49 ++++ src/debug/rad.sv | 142 +++++++++++ src/debug/tap.sv | 97 +++++++ src/generic/flop/flopenrcs.sv | 55 ++++ src/generic/scanreg.sv | 65 +++++ src/ieu/controller.sv | 18 +- src/ieu/datapath.sv | 41 ++- src/ifu/ifu.sv | 39 ++- src/lsu/lsu.sv | 15 +- src/privileged/csrm.sv | 12 +- src/wally/wallypipelinedcore.sv | 72 ++++-- src/wally/wallypipelinedsoc.sv | 97 ++++--- 23 files changed, 2329 insertions(+), 70 deletions(-) create mode 100755 bin/hw_interface.py create mode 100644 bin/hw_test.py create mode 100755 config/shared/debug.vh create mode 100644 openocd.cfg create mode 100644 src/debug/dm.sv create mode 100644 src/debug/dtm.sv create mode 100644 src/debug/hartcontrol.sv create mode 100755 src/debug/idreg.sv create mode 100644 src/debug/ir.sv create mode 100644 src/debug/jtag.sv create mode 100755 src/debug/notes.txt create mode 100644 src/debug/rad.sv create mode 100644 src/debug/tap.sv create mode 100644 src/generic/flop/flopenrcs.sv create mode 100644 src/generic/scanreg.sv diff --git a/bin/hw_interface.py b/bin/hw_interface.py new file mode 100755 index 000000000..e894c9e06 --- /dev/null +++ b/bin/hw_interface.py @@ -0,0 +1,404 @@ +######################################################################################### +# hw_interface.py +# +# Written: matthew.n.otto@okstate.edu +# Created: 19 April 2024 +# +# Purpose: Send debugging commands to OpenOCD via local telnet connection +# +# A component of the CORE-V-WALLY configurable RISC-V project. +# https:#github.com/openhwgroup/cvw +# +# Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +# except in compliance with the License, or, at your option, the Apache License version 2.0. You +# may obtain a copy of the License at +# +# https:#solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work distributed under the +# License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. +######################################################################################### + +# This script uses python to send text commands to OpenOCD via telnet +# OpenOCD also supports tcl commands directly + +import atexit +import time +from telnetlib import Telnet + +debug = False +XLEN = 64 # TODO: infer this value from the MISA + +tapname = "cvw.cpu" # this is set via the openocd config. It can be found by running `scan_chain` + + +# TODO: if JTAG clk is fast enough, need to check for busy between absract commands + +def main(): + global tn + with Telnet("127.0.0.1", 4444) as tn: + read() # clear welcome message from read buffer + activate_dm() # necessary if openocd init is disabled + status() + halt() + GPR = dump_GPR() + print(GPR) + check_errors() + print(f"PCM: '{read_data("PCM")}'") + resume() + status() + #clear_abstrcmd_err() + #write_data("READDATAM", "0xAA0987210000FFFF") + #print(f"READDATAM'{read_data("READDATAM")}'") + #print(f"WRITEDATAM: '{read_data("WRITEDATAM")}'") + #print(f"IEUADRM: '{read_data("IEUADRM")}'") + #write_data("TRAPM", "0x0") + #print(f"INSTRVALIDM: '{read_data("INSTRVALIDM")}'") + #print(f"MEMRWM: '{read_data("MEMRWM")}'") + #write_data("MEMRWM", "0x3") + #write_data("PCM", "0x100000") + #dmi_reset() + #clear_abstrcmd_err() + + +def dump_GPR(): + gpr = {} + for i in range(1,32): + addr = f"X{i}" + gpr[addr] = read_data(addr) + # DM will assert Abstract Command Err if GPR X16-X31 isn't implemented (CMDERR_EXCEPTION) + # This will clear that error and return early. + if i == 16: + abstractcs = int(read_dmi("0x16"), 16) + cmderr = (abstractcs & 0x700) >> 8 + if cmderr == 3: + clear_abstrcmd_err() + break + return gpr + + + +def write_data(register, data): + """Writes data of width XLEN to specified register""" + # Translate register alias to DM regno + regno = int(register_translations[register], 16) + # Write data to 32 bit message registers + data = int(data, 16) + write_dmi("0x4", hex(data & 0xffffffff)) + if XLEN == 64: + write_dmi("0x5", hex((data >> 32) & 0xffffffff)) + if XLEN == 128: + write_dmi("0x6", hex((data >> 64) & 0xffffffff)) + write_dmi("0x7", hex((data >> 96) & 0xffffffff)) + # Transfer data from msg registers to target register + access_register(write=True, regno=regno, addr_size=XLEN) + # Check that operations completed without error + if acerr := check_absrtcmderr(): + raise Exception(acerr) + + +def read_data(register): + """Read data of width XLEN from specified register""" + # Translate register alias to DM regno + regno = int(register_translations[register], 16) + # Transfer data from target register to msg registers + access_register(write=False, regno=regno, addr_size=XLEN) + # Read data from 32 bit message registers + data = "" + data = read_dmi("0x4").replace("0x", "").zfill(8) + if XLEN >= 64: + data = read_dmi("0x5").replace("0x", "").zfill(8) + data + if XLEN == 128: + data = read_dmi("0x6").replace("0x", "").zfill(8) + data + data = read_dmi("0x7").replace("0x", "").zfill(8) + data + # Check that operations completed without error + if acerr := check_absrtcmderr(): + raise Exception(acerr) + return f"0x{data}" + + +def access_register(write, regno, addr_size): + """3.7.1.1 + Before starting an abstract command, a debugger must ensure that haltreq, resumereq, and + ackhavereset are all 0.""" + addr = "0x17" + data = 1 << 17 # transfer bit always set + if addr_size == 32: + data += 2 << 20 + elif addr_size == 64: + data += 3 << 20 + elif addr_size == 128: + data += 4 << 20 + else: + raise Exception("must provide valid register access size (32, 64, 128). See: 3.7.1.1 aarsize") + if write: + data += 1<<16 + data += regno + data = hex(data) + write_dmi(addr, data) + + +def halt(): + write_dmi("0x10", "0x80000001") + check_errors() + + +def resume(): + write_dmi("0x10", "0x40000001") + check_errors() + + +def step(): + write_dmi("0x10", "0xC0000001") + check_errors() + + +def set_haltonreset(): + write_dmi("0x10", "0x9") + + +def clear_haltonreset(): + write_dmi("0x10", "0x5") + + +def reset_hart(): + write_dmi("0x10", "0x3") + write_dmi("0x10", "0x1") + + +def status(): + dmstatus = int(read_dmi("0x11"), 16) + print("Core status:::") + print(f"Running: {bool((dmstatus >> 11) & 0x1)}") + print(f"Halted: {bool((dmstatus >> 9) & 0x1)}") + + +def check_errors(): + # TODO: update this + """Checks various status bits and reports any potential errors + Returns true if any errors are found""" + # check dtmcs + dtmcs = int(read_dtmcs(), 16) + errinfo = (dtmcs & 0x1C0000) >> 18 + dmistat = (dtmcs & 0xC00) >> 10 + if errinfo > 0 and errinfo < 4: + print(f"DTM Error: {errinfo_translations[errinfo]}") + return True + if dmistat: + print(f"DMI status error: {op_translations[dmistat]}") + return True + # check if DM is inactive + dm_active = int(read_dmi("0x10"), 16) & 0x1 + if not dm_active: + print("DMControl Error: Debug module is not active") + return True + # check abstract command error + abstractcs = int(read_dmi("0x16"), 16) + busy = (abstractcs & 0x1000) >> 12 + cmderr = (abstractcs & 0x700) >> 8 + if not busy and cmderr: + print(f"Abstract Command Error: {cmderr_translations[cmderr]}") + return True + + +def check_busy(): + """If an Abstract Command OP is attempted while busy, an abstrcmderr will be asserted""" + abstractcs = int(read_dmi("0x16"), 16) + return bool((abstractcs & 0x1000) >> 12) + + +def check_absrtcmderr(): + """These errors must be cleared using clear_abstrcmd_err() before another OP can be executed""" + abstractcs = int(read_dmi("0x16"), 16) + # CmdErr is only valid if Busy is 0 + busy = bool((abstractcs & 0x1000) >> 12) + while busy: + time.sleep(0.05) + abstractcs = int(read_dmi("0x16"), 16) + busy = bool((abstractcs & 0x1000) >> 12) + return cmderr_translations[(abstractcs & 0x700) >> 8] + + +def clear_abstrcmd_err(): + write_dmi("0x16", "0x700") + + +def reset_dm(): + deactivate_dm() + activate_dm() + + +def activate_dm(): + write_dmi("0x10", "0x1") + return int(read_dmi("0x10"), 16) & 0x1 + + +def deactivate_dm(): + write_dmi("0x10", "0x0") + return not int(read_dmi("0x10"), 16) & 0x1 + + +def dmi_reset(): + """Reset sticky dmi error status in DTM""" + write_dtmcs(dmireset=True) + check_errors() + + +def write_dmi(address, data): + cmd = f"riscv dmi_write {address} {data}" + rsp = execute(cmd) + if "Failed" in rsp: + print(rsp) + + +def read_dmi(address): + cmd = f"riscv dmi_read {address}" + return execute(cmd) + + +def write_dtmcs(dtmhardreset=False, dmireset=False): + data = 0 + if dtmhardreset: + data += 0x1 << 17 + if dmireset: + data += 0x1 << 16 + execute(f"irscan {tapname} 0x10") # dtmcs instruction + execute(f"drscan {tapname} 32 {hex(data)}") + + +def read_dtmcs(): + execute(f"irscan {tapname} 0x10") # dtmcs instruction + dtmcs = execute(f"drscan {tapname} 32 0x0") + return dtmcs + + +def trst(): + execute("pathmove RESET IDLE") + + +def execute(cmd): + write(cmd) + return read() + + +def write(cmd): + if debug: + print(f"Executing command: '{cmd}'") + tn.write(cmd.encode('ascii') + b"\n") + tn.read_until(b"\n") + + +def read(): + data = b"" + data = tn.read_until(b"> ").decode('ascii') + data = data.replace("\r", "").replace("\n", "").replace("> ", "") + if debug: + print(data) + return data + + +def init(): + global tn + tn = Telnet("127.0.0.1", 4444) + atexit.register(cleanup) + read() # clear welcome message from read buffer + activate_dm() + # TODO: query misa and get gpr count + + +def cleanup(): + tn.close() + +# 6.1.4 dtmcs errinfo translation table +errinfo_translations = { + 0 : "not implemented", + 1 : "dmi error", + 2 : "communication error", + 3 : "device error", + 4 : "unknown", +} + + +# 6.1.5 DMI op translation table +op_translations = { + 0 : "success", + 1 : "reserved", + 2 : "failed", + 3 : "busy", +} + + +# 3.14.6 Abstract command CmdErr value translation table +cmderr_translations = { + 0 : None, + 1 : "busy", + 2 : "not supported", + 3 : "exception", + 4 : "halt/resume", + 5 : "bus", + 6 : "reserved", + 7 : "other", +} + + +# Register alias to regno translation table +register_translations = { + "MISA" : "0x0301", + "TRAPM" : "0xC000", + "PCM" : "0xC001", + "INSTRM" : "0xC002", + "MEMRWM" : "0xC003", + "INSTRVALIDM" : "0xC004", + "WRITEDATAM" : "0xC005", + "IEUADRM" : "0xC006", + "READDATAM" : "0xC007", + "X0" : "0x1000", + "X1" : "0x1001", + "X2" : "0x1002", + "X3" : "0x1003", + "X4" : "0x1004", + "X5" : "0x1005", + "X6" : "0x1006", + "X7" : "0x1007", + "X8" : "0x1008", + "X9" : "0x1009", + "X10" : "0x100A", + "X11" : "0x100B", + "X12" : "0x100C", + "X13" : "0x100D", + "X14" : "0x100E", + "X15" : "0x100F", + "X16" : "0x1010", + "X17" : "0x1011", + "X18" : "0x1012", + "X19" : "0x1013", + "X20" : "0x1014", + "X21" : "0x1015", + "X22" : "0x1016", + "X23" : "0x1017", + "X24" : "0x1018", + "X25" : "0x1019", + "X26" : "0x101A", + "X27" : "0x101B", + "X28" : "0x101C", + "X29" : "0x101D", + "X30" : "0x101E", + "X31" : "0x101F", +} + +nonstandard_register_lengths = { + "TRAPM" : 1, + "INSTRM" : 32, + "MEMRWM" : 2, + "INSTRVALIDM" : 1, + #"READDATAM" : P.LLEN +} + +if __name__ == "__main__": + main() diff --git a/bin/hw_test.py b/bin/hw_test.py new file mode 100644 index 000000000..1b1215e9f --- /dev/null +++ b/bin/hw_test.py @@ -0,0 +1,110 @@ +import random +import time + +from hw_interface import * + +random_stimulus = False + +def main(): + registers = dict.fromkeys(register_translations.keys(),[]) + reg_addrs = list(registers.keys()) + + init() + reset_dm() + reset_hart() + + time.sleep(70) # wait for OpenSBI + + halt() + status() + + # dump data in all registers + for r in reg_addrs: + try: + data = read_data(r) + registers[r] = data + print(f"{r}: {data}") + except Exception as e: + if e.args[0] == "exception": # Invalid register (not implemented) + del registers[r] + clear_abstrcmd_err() + else: + raise e + input("Compare values to ILA, press any key to continue") + + # Write random data to all registers + reg_addrs = list(registers.keys()) + if random_stimulus: + random.shuffle(reg_addrs) + test_reg_data = {} + for r in reg_addrs: + test_data = random_hex(r) + try: + write_data(r, test_data) + test_reg_data[r] = test_data + print(f"Writing {test_data} to {r}") + except Exception as e: + if e.args[0] == "not supported": # Register is read only + del registers[r] + clear_abstrcmd_err() + else: + raise e + + check_errors() + + # GPR X0 is always 0 + test_reg_data["X0"] = "0x" + "0"*(XLEN//4) + + # Confirm data was written correctly + reg_addrs = list(registers.keys()) + if random_stimulus: + random.shuffle(reg_addrs) + for r in reg_addrs: + try: + rdata = read_data(r) + except Exception as e: + raise e + if rdata != test_reg_data[r]: + raise Exception(f"Register {r} read did not return correct data: {rdata} != {test_reg_data[r]}") + else: + print(f"Read {rdata} from {r}") + + # Return all registers to original state + reg_addrs = list(registers.keys()) + for r in reg_addrs: + print(f"Writing {registers[r]} to {r}") + try: + write_data(r, registers[r]) + except Exception as e: + raise e + + # Confirm data was written correctly + for r in reg_addrs: + try: + rdata = read_data(r) + except Exception as e: + raise e + if rdata != registers[r]: + raise Exception(f"Register {r} read did not return correct data: {rdata} != {registers[r]}") + print("All writes successful") + + resume() + status() + + +def random_hex(reg_name): + if reg_name in nonstandard_register_lengths: + size = nonstandard_register_lengths[reg_name] + pad = (XLEN-size) // 4 + else: + size = XLEN + pad = 0 + + if random_stimulus: + return f"0x{"0"*pad}{random.getrandbits(size):x}" + else: + data = 0xa5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5 + return f"0x{"0"*pad}{(data & (2**size-1)):x}" + + +main() diff --git a/config/shared/debug.vh b/config/shared/debug.vh new file mode 100755 index 000000000..5d9732fea --- /dev/null +++ b/config/shared/debug.vh @@ -0,0 +1,211 @@ +/////////////////////////////////////////// +// debug.vh +// +// Written: matthew.n.otto@okstate.edu +// Created: 15 March 2024 +// +// Purpose: debug port definitions +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +// DMI op field constants +`define OP_NOP 2'b00 +`define OP_READ 2'b01 +`define OP_WRITE 2'b10 +`define OP_SUCCESS 2'b00 +`define OP_FAILED 2'b10 +`define OP_BUSY 2'b11 + +// Debug Bus Address Width +`define ADDR_WIDTH 7 + +// Debug Module Debug Bus Register Addresses +// DM Internal registers +`define DATA0 `ADDR_WIDTH'h04 +`define DATA1 `ADDR_WIDTH'h05 +`define DATA2 `ADDR_WIDTH'h06 +`define DATA3 `ADDR_WIDTH'h07 +`define DATA4 `ADDR_WIDTH'h08 +`define DATA5 `ADDR_WIDTH'h09 +`define DATA6 `ADDR_WIDTH'h0A +`define DATA7 `ADDR_WIDTH'h0B +`define DATA8 `ADDR_WIDTH'h0C +`define DATA9 `ADDR_WIDTH'h0D +`define DATA10 `ADDR_WIDTH'h0E +`define DATA11 `ADDR_WIDTH'h0F +`define DMCONTROL `ADDR_WIDTH'h10 +`define DMSTATUS `ADDR_WIDTH'h11 +`define HARTINFO `ADDR_WIDTH'h12 +`define ABSTRACTCS `ADDR_WIDTH'h16 +`define COMMAND `ADDR_WIDTH'h17 +`define ABSTRACTAUTO `ADDR_WIDTH'h18 +`define NEXTDM `ADDR_WIDTH'h1d +//`define dmcs2 `ADDR_WIDTH'h32 +`define SBCS `ADDR_WIDTH'h38 + + +//// Register field ranges +// DMCONTROL 0x10 +`define HALTREQ 31 +`define RESUMEREQ 30 +`define HARTRESET 29 +`define ACKHAVERESET 28 +`define ACKUNAVAIL 27 +`define HASEL 26 +`define HARTSELLO 25:16 +`define HARTSELHI 15:6 +`define SETKEEPALIVE 5 +`define CLRKEEPALIVE 4 +`define SETRESETHALTREQ 3 +`define CLRRESETHALTREQ 2 +`define NDMRESET 1 +`define DMACTIVE 0 + +// DMSTATUS 0x11 +`define NDMRESETPENDING 24 +`define STICKYUNAVAIL 23 +`define IMPEBREAK 22 +`define ALLHAVERESET 19 +`define ANYHAVERESET 18 +`define ALLRESUMEACK 17 +`define ANYRESUMEACK 16 +`define ALLNONEXISTENT 15 +`define ANYNONEXISTENT 14 +`define ALLUNAVAIL 13 +`define ANYUNAVAIL 12 +`define ALLRUNNING 11 +`define ANYRUNNING 10 +`define ALLHALTED 9 +`define ANYHALTED 8 +`define AUTHENTICATED 7 +`define AUTHBUSY 6 +`define HASRESETHALTREQ 5 +`define CONFSTRPTRVALID 4 +`define VERSION 3:0 + +// ABSTRACTCS 0x16 +`define PROGBUFSIZE 28:24 +`define BUSY 12 +`define RELAXEDPRIV 11 +`define CMDERR 10:8 +`define DATACOUNT 3:0 + +// COMMAND 0x17 +`define CMDTYPE 31:24 +`define CONTROL 23:0 + +//// Abstract Commands +// cmderr +`define CMDERR_NONE 3'h0 +`define CMDERR_BUSY 3'h1 +`define CMDERR_NOT_SUPPORTED 3'h2 +`define CMDERR_EXCEPTION 3'h3 +`define CMDERR_HALTRESUME 3'h4 +`define CMDERR_BUS 3'h5 +`define CMDERR_OTHER 3'h7 + +// Abstract CmdType Constants (3.7.1) +`define ACCESS_REGISTER 0 +`define QUICK_ACCESS 1 +`define ACCESS_MEMORY 2 + +// ACCESS_REGISTER Control ranges +`define AARSIZE 22:20 +`define AARPOSTINCREMENT 19 +`define POSTEXEC 18 +`define TRANSFER 17 +`define AARWRITE 16 +`define REGNO 15:0 + +// aarsize +`define AAR32 2 +`define AAR64 3 +`define AAR128 4 + +// Register Numbers (regno) +// (Table 3.3) +// 0x0000 – 0x0fff | CSRs. The “PC” can be accessed here through dpc. +// 0x1000 – 0x101f | GPRs +// 0x1020 – 0x103f | Floating point registers +// 0xc000 – 0xffff | Reserved for non-standard extensions and internal use. + +// privileged/csr/csrm +`define MISA 16'h0301 // XLEN P.ZICSR_SUPPORTED (Read Only) +// wallypipelinedcore +`define TRAPM 16'hC000 // 1'b P.ZICSR_SUPPORTED (Read Only) +// src/ifu +`define PCM 16'hC001 // XLEN P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED +`define INSTRM 16'hC002 // 32'b P.ZICSR_SUPPORTED | P.A_SUPPORTED +// ieu/controller +`define MEMRWM 16'hC003 // 2'b +`define INSTRVALIDM 16'hC004 // 1'b +// ieu/datapath +`define WRITEDATAM 16'hC005 // XLEN +// lsu +`define IEUADRM 16'hC006 // XLEN +`define READDATAM 16'hC007 // LLEN (Read Only) + +// src/ieu/datapath +`define X0 16'h1000 +`define X1 16'h1001 +`define X2 16'h1002 +`define X3 16'h1003 +`define X4 16'h1004 +`define X5 16'h1005 +`define X6 16'h1006 +`define X7 16'h1007 +`define X8 16'h1008 +`define X9 16'h1009 +`define X10 16'h100A +`define X11 16'h100B +`define X12 16'h100C +`define X13 16'h100D +`define X14 16'h100E +`define X15 16'h100F +`define X16 16'h1010 // E_SUPPORTED +`define X17 16'h1011 // E_SUPPORTED +`define X18 16'h1012 // E_SUPPORTED +`define X19 16'h1013 // E_SUPPORTED +`define X20 16'h1014 // E_SUPPORTED +`define X21 16'h1015 // E_SUPPORTED +`define X22 16'h1016 // E_SUPPORTED +`define X23 16'h1017 // E_SUPPORTED +`define X24 16'h1018 // E_SUPPORTED +`define X25 16'h1019 // E_SUPPORTED +`define X26 16'h101A // E_SUPPORTED +`define X27 16'h101B // E_SUPPORTED +`define X28 16'h101C // E_SUPPORTED +`define X29 16'h101D // E_SUPPORTED +`define X30 16'h101E // E_SUPPORTED +`define X31 16'h101F // E_SUPPORTED + +// ACCESS_MEMORY Control ranges (Not implemented) +//`define AAMVIRTUAL 23 +//`define AAMSIZE 22:20 +//`define AAMPOSTINCREMENT 19 +//`define AAMWRITE 16 +//`define TARGET_SPECIFIC 15:14 + +// aamsize +//`define AAM8 0 +//`define AAM16 1 +//`define AAM32 2 +//`define AAM64 3 +//`define AAM128 4 \ No newline at end of file diff --git a/openocd.cfg b/openocd.cfg new file mode 100644 index 000000000..2a8182804 --- /dev/null +++ b/openocd.cfg @@ -0,0 +1,30 @@ +# OpenOCD config file for Core V Wally +# Users can find example material in /usr/share/openocd/scripts/ + +adapter driver ftdi + +# when multiple adapters with the same vid_pid are connected (ex: arty-a7 and usb-jtag) +# need to specify which usb port to drive +# find numerical path using command "lsusb -t" (-) +adapter usb location 1-3 + +ftdi vid_pid 0x0403 0x6010 +ftdi channel 0 + +#TODO: figure out which of these bits need to be set +# data MSB..LSB direction (1:out) MSB..LSB +# 0000'0000'0011'0000 0000'0000'0011'1011 +ftdi layout_init 0x0030 0x003b + +transport select jtag +adapter speed 1000 + +set _CHIPNAME cvw +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0xDEADBEEF + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME + +init +# this is useful for manual debugging, but breaks gdb +poll off diff --git a/src/cvw.sv b/src/cvw.sv index a06feb0b9..d183d7353 100644 --- a/src/cvw.sv +++ b/src/cvw.sv @@ -294,6 +294,9 @@ typedef struct packed { int DURLEN ; int DIVb ; int DIVBLEN ; + +// Debug Module + logic DEBUG_SUPPORTED; } cvw_t; endpackage diff --git a/src/debug/dm.sv b/src/debug/dm.sv new file mode 100644 index 000000000..2ebc13a7e --- /dev/null +++ b/src/debug/dm.sv @@ -0,0 +1,436 @@ +/////////////////////////////////////////// +// dm.sv +// +// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu +// Created: 15 March 2024 +// +// Purpose: Main debug module (dm) for Debug Specification +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License Version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +module dm import cvw::*; #(parameter cvw_t P) ( + input logic clk, + input logic rst, // Full hardware reset signal (reset button) + + // External JTAG signals + input logic tck, + input logic tdi, + input logic tms, + output logic tdo, + + // Platform reset signal + output logic NdmReset, + // Core hazard signal + output logic DebugStall, + + // Scan Chain + output logic ScanEn, + input logic ScanIn, + output logic ScanOut, + output logic GPRSel, + output logic DebugCapture, + output logic DebugGPRUpdate, + output logic [P.E_SUPPORTED+3:0] GPRAddr, + output logic GPRScanEn, + input logic GPRScanIn, + output logic GPRScanOut +); + + `include "debug.vh" + + // DMI Signals + logic ReqReady; + logic ReqValid; + logic [`ADDR_WIDTH-1:0] ReqAddress; + logic [31:0] ReqData; + logic [1:0] ReqOP; + logic RspReady; + logic RspValid; + logic [31:0] RspData; + logic [1:0] RspOP; + + localparam JTAG_DEVICE_ID = 32'hdeadbeef; // TODO: put JTAG device ID in parameter struct + + dtm #(`ADDR_WIDTH, JTAG_DEVICE_ID) dtm (.clk, .tck, .tdi, .tms, .tdo, + .ReqReady, .ReqValid, .ReqAddress, .ReqData, .ReqOP, .RspReady, + .RspValid, .RspData, .RspOP); + + // Core control signals + logic HaltReq; + logic ResumeReq; + logic HaltOnReset; + logic Halted; + + hartcontrol hartcontrol(.clk, .rst(rst | ~DmActive), .NdmReset, .HaltReq, + .ResumeReq, .HaltOnReset, .DebugStall, .Halted, .AllRunning, + .AnyRunning, .AllHalted, .AnyHalted, .AllResumeAck, .AnyResumeAck); + + enum logic [3:0] {INACTIVE, IDLE, ACK, R_DATA, W_DATA, DMSTATUS, W_DMCONTROL, R_DMCONTROL, + W_ABSTRACTCS, R_ABSTRACTCS, ABST_COMMAND, R_SYSBUSCS, READ_ZERO, + INVALID} State; + + enum logic [1:0] {AC_IDLE, AC_GPRUPDATE, AC_SCAN, AC_CAPTURE} AcState, NewAcState; + + // AbsCmd internal state + logic AcWrite; // Abstract Command write state + logic [P.XLEN:0] ScanReg; // The part of the debug scan chain located within DM + logic [P.XLEN-1:0] ScanNext; // New ScanReg value + logic [P.XLEN-1:0] ARMask; // Masks which bits of the ScanReg get updated + logic [P.XLEN-1:0] PackedDataReg; // Combines DataX msg registers into a single XLEN wide register + logic [P.XLEN-1:0] MaskedScanReg; // Masks which bits of the ScanReg get written to DataX + logic [9:0] ShiftCount; // Position of the selected register on the debug scan chain + logic [9:0] ScanChainLen; // Total length of currently selected scan chain + logic [9:0] Cycle; // DM's current position in the scan chain + logic InvalidRegNo; // Requested RegNo is invalid + logic RegReadOnly; // Current RegNo points to a readonly register + logic GPRRegNo; // Requested RegNo is a GPR + logic StoreScanChain; // Store current value of ScanReg into DataX + logic WriteMsgReg; // Write to DataX + logic WriteScanReg; // Insert data from DataX into ScanReg + logic [31:0] Data0Wr; // Muxed inputs to DataX regs + logic [31:0] Data1Wr; // Muxed inputs to DataX regs + logic [31:0] Data2Wr; // Muxed inputs to DataX regs + logic [31:0] Data3Wr; // Muxed inputs to DataX regs + + // message registers + logic [31:0] Data0; // 0x04 + logic [31:0] Data1; // 0x05 + logic [31:0] Data2; // 0x06 + logic [31:0] Data3; // 0x07 + + // debug module registers + logic [31:0] DMControl; // 0x10 + logic [31:0] DMStatus; // 0x11 + logic [31:0] AbstractCS; // 0x16 + logic [31:0] SysBusCS; // 0x38 + + // DM register fields + + // DMControl + logic AckUnavail; + logic DmActive; // This bit is used to (de)activate the DM. Toggling acts as reset + // DMStatus + logic StickyUnavail; + logic ImpEBreak; + logic AllResumeAck; + logic AnyResumeAck; + logic AllNonExistent; + logic AnyNonExistent; + logic AllUnavail; // TODO + logic AnyUnavail; + logic AllRunning; + logic AnyRunning; + logic AllHalted; + logic AnyHalted; + const logic Authenticated = 1; + logic AuthBusy; + const logic HasResetHaltReq = 1; + logic ConfStrPtrValid; + const logic [3:0] Version = 3; // DM Version + // AbstractCS + const logic [4:0] ProgBufSize = 0; + logic Busy; + const logic RelaxedPriv = 1; + logic [2:0] CmdErr; + const logic [3:0] DataCount = (P.XLEN/32); + + // Pack registers + assign DMControl = {2'b0, 1'b0, 2'b0, 1'b0, 10'b0, + 10'b0, 4'b0, NdmReset, DmActive}; + + assign DMStatus = {7'b0, 1'b0, StickyUnavail, ImpEBreak, 2'b0, + 2'b0, AllResumeAck, AnyResumeAck, AllNonExistent, + AnyNonExistent, AllUnavail, AnyUnavail, AllRunning, AnyRunning, AllHalted, + AnyHalted, Authenticated, AuthBusy, HasResetHaltReq, ConfStrPtrValid, Version}; + + assign AbstractCS = {3'b0, ProgBufSize, 11'b0, Busy, RelaxedPriv, CmdErr, 4'b0, DataCount}; + + assign SysBusCS = 32'h20000000; // SBVersion = 1 + + assign RspValid = (State == ACK); + assign ReqReady = (State != ACK); + + always_ff @(posedge clk) begin + if (rst) begin + DmActive <= 0; + State <= INACTIVE; + end else begin + case (State) + INACTIVE : begin + // Reset Values + RspData <= 0; + HaltReq <= 0; + HaltOnReset <= 0; + NdmReset <= 0; + StickyUnavail <= 0; + ImpEBreak <= 0; + AuthBusy <= 0; + ConfStrPtrValid <= 0; + CmdErr <= 0; + if (ReqValid) begin + if (ReqAddress == `DMCONTROL & ReqOP == `OP_WRITE & ReqData[`DMACTIVE]) begin + DmActive <= ReqData[`DMACTIVE]; + RspOP <= `OP_SUCCESS; + end + State <= ACK; // acknowledge all Reqs even if they don't activate DM + end + end + + ACK : begin + NewAcState <= AC_IDLE; + ResumeReq <= 0; + if (~ReqValid) + State <= ~DmActive ? INACTIVE : IDLE; + end + + IDLE : begin + if (ReqValid) + case ({ReqOP, ReqAddress}) inside + {`OP_WRITE,`DATA0} : State <= W_DATA; + {`OP_READ,`DATA0} : State <= R_DATA; + {`OP_WRITE,`DATA1} : State <= (P.XLEN >= 64) ? W_DATA : INVALID; + {`OP_READ,`DATA1} : State <= (P.XLEN >= 64) ? R_DATA : INVALID; + [{`OP_WRITE,`DATA2}:{`OP_WRITE,`DATA3}] : State <= (P.XLEN >= 128) ? W_DATA : INVALID; + [{`OP_READ,`DATA2}:{`OP_READ,`DATA3}] : State <= (P.XLEN >= 128) ? R_DATA : INVALID; + {`OP_WRITE,`DMCONTROL} : State <= W_DMCONTROL; + {`OP_READ,`DMCONTROL} : State <= R_DMCONTROL; + {`OP_READ,`DMSTATUS} : State <= DMSTATUS; + {`OP_WRITE,`ABSTRACTCS} : State <= W_ABSTRACTCS; + {`OP_READ,`ABSTRACTCS} : State <= R_ABSTRACTCS; + {`OP_WRITE,`COMMAND} : State <= ABST_COMMAND; + {`OP_READ,`COMMAND} : State <= READ_ZERO; + {`OP_WRITE,`SBCS} : State <= READ_ZERO; + {`OP_READ,`SBCS} : State <= R_SYSBUSCS; + {2'bx,`HARTINFO}, + {2'bx,`ABSTRACTAUTO}, + {2'bx,`NEXTDM} : State <= READ_ZERO; + default : State <= INVALID; + endcase + end + + R_DATA : begin + if (Busy) + CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr; + case (ReqAddress) + `DATA0 : RspData <= Data0; + `DATA1 : RspData <= Data1; + `DATA2 : RspData <= Data2; + `DATA3 : RspData <= Data3; + endcase + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + W_DATA : begin + if (Busy) + CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr; + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + W_DMCONTROL : begin + // While an abstract command is executing (busy in abstractcs is high), a debugger must not change + // hartsel, and must not write 1 to haltreq, resumereq, ackhavereset, setresethaltreq, or clrresethaltreq + if (Busy & (ReqData[`HALTREQ] | ReqData[`RESUMEREQ] | ReqData[`SETRESETHALTREQ] | ReqData[`CLRRESETHALTREQ])) + CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr; + else begin + HaltReq <= ReqData[`HALTREQ]; + AckUnavail <= ReqData[`ACKUNAVAIL]; + NdmReset <= ReqData[`NDMRESET]; + DmActive <= ReqData[`DMACTIVE]; // Writing 0 here resets the DM + + // On any given write, a debugger may only write 1 to at most one of the following bits: resumereq, + // hartreset, ackhavereset, setresethaltreq, and clrresethaltreq. The others must be written 0 + case ({ReqData[`RESUMEREQ],ReqData[`SETRESETHALTREQ],ReqData[`CLRRESETHALTREQ]}) + 3'b000 :; // None + 3'b100 : ResumeReq <= 1; + 3'b010 : HaltOnReset <= 1; + 3'b001 : HaltOnReset <= 0; + default : begin // Invalid (not onehot), dont write any changes + HaltReq <= HaltReq; + AckUnavail <= AckUnavail; + NdmReset <= NdmReset; + DmActive <= DmActive; + end + endcase + end + + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + R_DMCONTROL : begin + RspData <= DMControl; + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + DMSTATUS : begin + RspData <= DMStatus; + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + W_ABSTRACTCS : begin + if (Busy) + CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr; + else + CmdErr <= |ReqData[`CMDERR] ? `CMDERR_NONE : CmdErr; // clear CmdErr + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + R_ABSTRACTCS : begin + RspData <= AbstractCS; + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + ABST_COMMAND : begin + if (CmdErr != `CMDERR_NONE); // If CmdErr, do nothing + else if (Busy) + CmdErr <= `CMDERR_BUSY; // If Busy, set CmdErr, do nothing + else if (~Halted) + CmdErr <= `CMDERR_HALTRESUME; // If not halted, set CmdErr, do nothing + else begin + case (ReqData[`CMDTYPE]) + `ACCESS_REGISTER : begin + if (ReqData[`AARSIZE] > $clog2(P.XLEN/8)) // if AARSIZE (encoded) is greater than P.XLEN, set CmdErr, do nothing + CmdErr <= `CMDERR_BUS; + else if (~ReqData[`TRANSFER]); // If not TRANSFER, do nothing + else if (InvalidRegNo) + CmdErr <= `CMDERR_EXCEPTION; // If InvalidRegNo, set CmdErr, do nothing + else if (ReqData[`AARWRITE] & RegReadOnly) + CmdErr <= `CMDERR_NOT_SUPPORTED; // If writing to a read only register, set CmdErr, do nothing + else begin + AcWrite <= ReqData[`AARWRITE]; + NewAcState <= AC_SCAN; + end + end + //`QUICK_ACCESS : State <= QUICK_ACCESS; + //`ACCESS_MEMORY : State <= ACCESS_MEMORY; + default : CmdErr <= `CMDERR_NOT_SUPPORTED; + endcase + end + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + R_SYSBUSCS : begin + RspData <= SysBusCS; + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + READ_ZERO : begin // Writes ignored, Read Zero + RspData <= 0; + RspOP <= `OP_SUCCESS; + State <= ACK; + end + + INVALID : begin + RspOP <= `OP_FAILED; + State <= ACK; + end + endcase + end + end + + // Abstract command engine + // Due to length of the register scan chain, + // abstract commands execute independently of other DM operations + always_ff @(posedge clk) begin + if (rst) + AcState <= AC_IDLE; + else begin + case (AcState) + AC_IDLE : begin + Cycle <= 0; + case (NewAcState) + AC_SCAN : AcState <= ~AcWrite ? AC_CAPTURE : AC_SCAN; + endcase + end + + AC_CAPTURE : begin + AcState <= AC_SCAN; + end + + AC_SCAN : begin + if (Cycle == ScanChainLen) + AcState <= (GPRRegNo & AcWrite) ? AC_GPRUPDATE : AC_IDLE; + else + Cycle <= Cycle + 1; + end + + AC_GPRUPDATE : begin + AcState <= AC_IDLE; + end + endcase + end + end + + assign Busy = ~(AcState == AC_IDLE); + assign DebugCapture = (AcState == AC_CAPTURE); + assign DebugGPRUpdate = (AcState == AC_GPRUPDATE); + + // Scan Chain + assign GPRSel = GPRRegNo & (AcState != AC_IDLE); + assign ScanReg[P.XLEN] = GPRSel ? GPRScanIn : ScanIn; + assign ScanOut = GPRSel ? 1'b0 : ScanReg[0]; + assign GPRScanOut = GPRSel ? ScanReg[0] : 1'b0; + assign ScanEn = ~GPRSel & (AcState == AC_SCAN); + assign GPRScanEn = GPRSel & (AcState == AC_SCAN); + + // Load data from message registers into scan chain + if (P.XLEN == 32) + assign PackedDataReg = Data0; + else if (P.XLEN == 64) + assign PackedDataReg = {Data1,Data0}; + else if (P.XLEN == 128) + assign PackedDataReg = {Data3,Data2,Data1,Data0}; + + assign WriteScanReg = AcWrite & (~GPRRegNo & (Cycle == ShiftCount) | GPRRegNo & (Cycle == 0)); + genvar i; + for (i=0; i= 64) begin + assign Data1Wr = StoreScanChain ? MaskedScanReg[63:32] : ReqData; + flopenr #(32) data1reg (.clk, .reset(rst), .en(StoreScanChain | WriteMsgReg & (ReqAddress == `DATA1)), .d(Data1Wr), .q(Data1)); + end + if (P.XLEN == 128) begin + assign Data2Wr = StoreScanChain ? MaskedScanReg[95:64] : ReqData; + assign Data3Wr = StoreScanChain ? MaskedScanReg[127:96] : ReqData; + flopenr #(32) data2reg (.clk, .reset(rst), .en(StoreScanChain | WriteMsgReg & (ReqAddress == `DATA2)), .d(Data2Wr), .q(Data2)); + flopenr #(32) data3reg (.clk, .reset(rst), .en(StoreScanChain | WriteMsgReg & (ReqAddress == `DATA3)), .d(Data3Wr), .q(Data3)); + end + + rad #(P) regnodecode(.AarSize(ReqData[`AARSIZE]),.Regno(ReqData[`REGNO]),.GPRRegNo,.ScanChainLen,.ShiftCount,.InvalidRegNo,.RegReadOnly,.GPRAddr,.ARMask); +endmodule diff --git a/src/debug/dtm.sv b/src/debug/dtm.sv new file mode 100644 index 000000000..2ecd1ad72 --- /dev/null +++ b/src/debug/dtm.sv @@ -0,0 +1,163 @@ +/////////////////////////////////////////// +// dtm.sv +// +// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu +// Created: 15 March 2024 +// +// Purpose: debug transport module (dtm) : allows external debugger to communicate with dm +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +// To recovert from a core reset, DTM will need to DtmHardReset (or trstn / tms zeroscan). +// This is mentioned in spec +// To recover from DTM reset, core will probably need to be reset + +module dtm #(parameter ADDR_WIDTH, parameter JTAG_DEVICE_ID) ( + // System clock + input logic clk, + // External JTAG signals + input logic tck, + input logic tdi, + input logic tms, + output logic tdo, + + // DMI signals + input logic ReqReady, + output logic ReqValid, + output logic [ADDR_WIDTH-1:0] ReqAddress, + output logic [31:0] ReqData, + output logic [1:0] ReqOP, + output logic RspReady, + input logic RspValid, + input logic [31:0] RspData, + input logic [1:0] RspOP +); + `include "debug.vh" + + enum logic [1:0] {IDLE, START, WAIT, COMPLETE} DMIState; + + // Clock Domain Crossing + logic tcks; // Synchronized JTAG clock + logic resetn; + logic UpdateDtmcs; + logic [31:0] DtmcsIn; + logic [31:0] DtmcsOut; + logic UpdateDmi; + logic CaptureDmi; + logic [34+ADDR_WIDTH-1:0] DmiIn; + logic [34+ADDR_WIDTH-1:0] DmiOut; + + // DTMCS Register + const logic [2:0] ErrInfo = 0; + logic DtmHardReset; + logic DmiReset; + const logic [2:0] Idle = 0; + logic [1:0] DmiStat; + const logic [5:0] ABits = ADDR_WIDTH; + const logic [3:0] Version = 1; // DTM spec version 1 + + logic [31:0] ValRspData; + logic [1:0] ValRspOP; + logic Sticky; + + assign DmiOut = {ReqAddress, ValRspData, ValRspOP}; + assign DmiStat = ValRspOP; + + // Synchronize the edges of tck to the system clock + synchronizer clksync (.clk(clk), .d(tck), .q(tcks)); + + jtag #(.ADDR_WIDTH(ADDR_WIDTH), .DEVICE_ID(JTAG_DEVICE_ID)) jtag (.tck(tcks), .tdi, .tms, .tdo, + .resetn, .UpdateDtmcs, .DtmcsIn, .DtmcsOut, .CaptureDmi, .UpdateDmi, .DmiIn, .DmiOut); + + // DTMCS + assign DtmcsOut = {11'b0, ErrInfo, 3'b0, Idle, DmiStat, ABits, Version}; + always_ff @(posedge clk) begin + if (~resetn | DtmHardReset) begin + DtmHardReset <= 0; + DmiReset <= 0; + end else if (UpdateDtmcs) begin + DtmHardReset <= DtmcsIn[17]; + DmiReset <= DtmcsIn[16]; + end else if (DmiReset) begin + DmiReset <= 0; + end + end + + // DMI + always_ff @(posedge clk) begin + if (~resetn | DtmHardReset) begin + ValRspData <= 0; + ValRspOP <= `OP_SUCCESS; + //ErrInfo <= 4; + Sticky <= 0; + DMIState <= IDLE; + end else if (DmiReset) begin + ValRspOP <= `OP_SUCCESS; + //ErrInfo <= 4; + Sticky <= 0; + end else + case (DMIState) + IDLE : begin + if (UpdateDmi & ~Sticky & DmiIn[1:0] != `OP_NOP) begin + {ReqAddress, ReqData, ReqOP} <= DmiIn; + ReqValid <= 1; + // DmiOut is captured immediately on CaptureDmi + // this preemptively sets BUSY for next capture unless overwritten + ValRspOP <= `OP_BUSY; + DMIState <= START; + end else begin + ReqValid <= 0; + if (~Sticky) + ValRspOP <= `OP_SUCCESS; + end + end + + START : begin + if (ReqReady) begin + ReqValid <= 0; + RspReady <= 1; + DMIState <= WAIT; + end + end + + WAIT : begin + if (RspValid) begin + ValRspData <= RspData; + if (~Sticky) // update OP if it isn't currently a sticky value + ValRspOP <= RspOP; + if (RspOP == `OP_FAILED | RspOP == `OP_BUSY) + Sticky <= 1; + //if (RspOP == `OP_FAILED) + // ErrInfo <= 3; + DMIState <= COMPLETE; + end else if (CaptureDmi) + Sticky <= 1; + end + + COMPLETE : begin + if (CaptureDmi) begin + RspReady <= 0; + DMIState <= IDLE; + end + end + endcase + end + +endmodule diff --git a/src/debug/hartcontrol.sv b/src/debug/hartcontrol.sv new file mode 100644 index 000000000..5080eb2be --- /dev/null +++ b/src/debug/hartcontrol.sv @@ -0,0 +1,75 @@ +/////////////////////////////////////////// +// hartcontrol.sv +// +// Written: matthew.n.otto@okstate.edu 10 May 2024 +// Modified: +// +// Purpose: Controls the state of connected hart +// +// Documentation: RISC-V System on Chip Design +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +module hartcontrol( + input logic clk, rst, + input logic NdmReset, // Triggers HaltOnReset behavior + + input logic HaltReq, // Initiate core halt + input logic ResumeReq, // Initiates core resume + input logic HaltOnReset, // Halts core immediately on hart reset + + output logic DebugStall, // Stall signal goes to hazard unit + + // DMStatus bits + output logic Halted, + output logic AllRunning, + output logic AnyRunning, + output logic AllHalted, + output logic AnyHalted, + output logic AllResumeAck, + output logic AnyResumeAck +); + + assign Halted = DebugStall; + assign AllRunning = ~DebugStall; + assign AnyRunning = ~DebugStall; + assign AllHalted = DebugStall; + assign AnyHalted = DebugStall; + assign AllResumeAck = ~DebugStall; + assign AnyResumeAck = ~DebugStall; + + enum logic {RUNNING, HALTED} State; + + assign DebugStall = (State == HALTED); + + always_ff @(posedge clk) begin + if (rst) + State <= RUNNING; + else if (NdmReset) + State <= HaltOnReset ? HALTED : RUNNING; + else begin + case (State) + RUNNING : State <= HaltReq ? HALTED : RUNNING; + + HALTED : State <= ResumeReq ? RUNNING : HALTED; + endcase + end + end +endmodule diff --git a/src/debug/idreg.sv b/src/debug/idreg.sv new file mode 100755 index 000000000..602262051 --- /dev/null +++ b/src/debug/idreg.sv @@ -0,0 +1,48 @@ +/////////////////////////////////////////// +// idreg.sv +// +// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu +// Created: 15 March 2024 +// +// Purpose: JTAG device identification register +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +module idreg #(parameter DEVICE_ID) ( + input logic tdi, + input logic clockDR, + input logic captureDR, + output logic tdo +); + + logic [32:0] ShiftReg; + assign ShiftReg[32] = tdi; + assign tdo = ShiftReg[0]; + + genvar i; + for (i = 0; i < 32; i = i + 1) begin + always @(posedge clockDR) begin + if (i > 0) + ShiftReg[i] <= captureDR ? DEVICE_ID[i] : ShiftReg[i+1]; + else + ShiftReg[0] <= captureDR ? 1'b1 : ShiftReg[i+1]; + end + end +endmodule diff --git a/src/debug/ir.sv b/src/debug/ir.sv new file mode 100644 index 000000000..dd99c7cc1 --- /dev/null +++ b/src/debug/ir.sv @@ -0,0 +1,81 @@ +/////////////////////////////////////////// +// ir.sv +// +// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu +// Created: 15 March 2024 +// +// Purpose: JTAG instruction register +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +module ir ( + input logic clockIR, + input logic tdi, + input logic resetn, + input logic captureIR, + input logic updateIR, + output logic tdo, + output logic BypassInstr, + output logic IDCodeInstr, + output logic DtmcsIntrs, + output logic DmiInstr +); + + localparam INST_REG_WIDTH = 5; + + logic [INST_REG_WIDTH:0] shift_reg; + logic [3:0] decoded; + + + assign shift_reg[INST_REG_WIDTH] = tdi; + assign tdo = shift_reg[0]; + + // Shift register + always @(posedge clockIR) begin + shift_reg[0] <= shift_reg[1] | captureIR; + end + genvar i; + for (i = INST_REG_WIDTH; i > 1; i = i - 1) begin + always @(posedge clockIR) begin + shift_reg[i-1] <= shift_reg[i] & ~captureIR; + end + end + + // Instruction decoder + // 6.1.2 + always_comb begin + unique case (shift_reg[INST_REG_WIDTH-1:0]) + 5'h00 : decoded <= 4'b1000; // bypass + 5'h01 : decoded <= 4'b0100; // idcode + 5'h10 : decoded <= 4'b0010; // dtmcs + 5'h11 : decoded <= 4'b0001; // dmi + 5'h1F : decoded <= 4'b1000; // bypass + default : decoded <= 4'b1000; // bypass + endcase + end + + // Flop decoded instruction to minimizes switching during shiftIR + always @(posedge updateIR or negedge resetn) begin + if (~resetn) + {BypassInstr, IDCodeInstr, DtmcsIntrs, DmiInstr} <= 4'b0100; + else if (updateIR) + {BypassInstr, IDCodeInstr, DtmcsIntrs, DmiInstr} <= decoded; + end +endmodule diff --git a/src/debug/jtag.sv b/src/debug/jtag.sv new file mode 100644 index 000000000..edb63f8f8 --- /dev/null +++ b/src/debug/jtag.sv @@ -0,0 +1,136 @@ +/////////////////////////////////////////// +// jtag.sv +// +// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu +// Created: 15 March 2024 +// +// Purpose: JTAG portion of DTM +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +module jtag #(parameter ADDR_WIDTH, parameter DEVICE_ID) ( + // JTAG signals + input logic tck, + input logic tdi, + input logic tms, + output logic tdo, + output logic resetn, + + // DTM signals + output logic UpdateDtmcs, + output logic [31:0] DtmcsIn, + input logic [31:0] DtmcsOut, + + output logic CaptureDmi, + output logic UpdateDmi, + output logic [34+ADDR_WIDTH-1:0] DmiIn, + input logic [34+ADDR_WIDTH-1:0] DmiOut +); + + genvar i; + + // Data signals + logic tdi_ir, tdi_dr; + logic tdo_ir, tdo_dr; + logic tdo_bypass; + logic tdo_idcode; + logic tdo_dtmcs; + logic tdo_dmi; + + // TAP controller logic + logic tdo_en; + logic captureIR; + logic clockIR; + logic updateIR; + logic shiftDR; + logic captureDR; + logic clockDR; + logic updateDR; + logic select; + + // Instruction signals + logic BypassInstr; + logic IDCodeInstr; + logic DtmcsIntrs; + logic DmiInstr; + + logic [32:0] DtmcsShiftReg; + logic [34+ADDR_WIDTH:0] DmiShiftReg; + + assign UpdateDtmcs = updateDR & DtmcsIntrs; + + assign CaptureDmi = captureDR & DmiInstr; + assign UpdateDmi = updateDR & DmiInstr; + + tap tap (.tck, .tms, .resetn, .tdo_en, .captureIR, + .clockIR, .updateIR, .shiftDR, .captureDR, .clockDR, .updateDR, .select); + + // IR/DR input demux + assign tdi_ir = select ? tdi : 1'bz; + assign tdi_dr = select ? 1'bz : tdi; + // IR/DR output mux + assign tdo = ~tdo_en ? 1'bz : + select ? tdo_ir : tdo_dr; + + ir ir (.clockIR, .tdi(tdi_ir), .resetn, .captureIR, .updateIR, .tdo(tdo_ir), + .BypassInstr, .IDCodeInstr, .DtmcsIntrs, .DmiInstr); + + // DR demux + always_comb begin + unique case ({BypassInstr, IDCodeInstr, DtmcsIntrs, DmiInstr}) + 4'b1000 : tdo_dr <= tdo_bypass; + 4'b0100 : tdo_dr <= tdo_idcode; + 4'b0010 : tdo_dr <= tdo_dtmcs; + 4'b0001 : tdo_dr <= tdo_dmi; + default : tdo_dr <= tdo_bypass; + endcase + end + + always_ff @(posedge UpdateDtmcs) + DtmcsIn <= DtmcsShiftReg[31:0]; + + always_ff @(posedge UpdateDmi) + DmiIn <= DmiShiftReg[34+ADDR_WIDTH-1:0]; + + assign DtmcsShiftReg[32] = tdi_dr; + assign tdo_dtmcs = DtmcsShiftReg[0]; + for (i = 0; i < 32; i = i + 1) begin + always_ff @(posedge clockDR) begin + DtmcsShiftReg[i] <= captureDR ? DtmcsOut[i] : DtmcsShiftReg[i+1]; + end + end + + assign DmiShiftReg[34+ADDR_WIDTH] = tdi_dr; + assign tdo_dmi = DmiShiftReg[0]; + for (i = 0; i < 34+ADDR_WIDTH; i = i + 1) begin + always_ff @(posedge clockDR) begin + DmiShiftReg[i] <= captureDR ? DmiOut[i] : DmiShiftReg[i+1]; + end + end + + // jtag id register + idreg #(DEVICE_ID) id (.tdi(tdi_dr), .clockDR, .captureDR, .tdo(tdo_idcode)); + + // bypass register + always_ff @(posedge clockDR) begin + tdo_bypass <= tdi_dr & shiftDR; + end + +endmodule diff --git a/src/debug/notes.txt b/src/debug/notes.txt new file mode 100755 index 000000000..c191927d2 --- /dev/null +++ b/src/debug/notes.txt @@ -0,0 +1,49 @@ +Connect JTAG adapter (SiPEED) to FPGA (Arty-A7) + +Using Pmod JA (side closest to ethernet port) +Connect 5 jumpers on the top row beginning with GND +The order of the wires matches the order of the USB Adapter +GND TDI TMS TDO TCK +(see jtag_pinout.jpg) + +To debug Wally using OpenOCD: + +1. Select correct jtag adapter + +If using "SiPEED" adapters, openocd.cfg already contains the correct adapter ftdi +with vid_pid 0x0403 0x6010 + +If there are multiple ft2232 chips connected to the same system (ex: Arty-A7), +you will need to tell OpenOCD which one to use. + +On linux: +list your USB devices usign the command: "lsusb -t" +example output: +/: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/10p, 480M + |__ Port 003: Dev 002, If 0, Class=Vendor Specific Class, Driver=usbfs, 480M + |__ Port 003: Dev 002, If 1, Class=Vendor Specific Class, Driver=ftdi_sio, 480M + |__ Port 004: Dev 004, If 0, Class=Vendor Specific Class, Driver=usbfs, 12M + |__ Port 004: Dev 004, If 1, Class=Vendor Specific Class, Driver=ftdi_sio, 12M <- This is my JTAG adapter + +In the openOCD config, add the line: "adapter usb location 1-4" +where the numbers 1-4 correspond to - + + + +2. Run openocd +run openocd using the command "openocd -f openocd.cfg" +where openocd.cfg is the path of the config file +If everything is working corretly, OpenOCD should start without any errors and begin listening for telnet and gdb connections +connect to openocd via telnet (telnet 127.0.0.1 4444) to send commands via the command line + +3. read and write to the DMI bus +the riscv debug module can be controlled by writing to various registers via the DMI. +We can access these registers using the two commands: + riscv dmi_read
+ riscv dmi_write
+ +4. Initialize the DM +The debug module starts in an inactive state. In this state, it will not respond to any commands +To activate it, write 0x1 to the DMCONTROL register (0x10): +"riscv dmi_write 0x10 0x1" +Now you should have full control over the debug module. diff --git a/src/debug/rad.sv b/src/debug/rad.sv new file mode 100644 index 000000000..565316ea1 --- /dev/null +++ b/src/debug/rad.sv @@ -0,0 +1,142 @@ +/////////////////////////////////////////// +// rad.sv +// +// Written: matthew.n.otto@okstate.edu +// Created: 28 April 2024 +// +// Purpose: Calculates the numbers of shifts required to access target register on the debug scan chain +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License Version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +module rad import cvw::*; #(parameter cvw_t P) ( + input logic [2:0] AarSize, + input logic [15:0] Regno, + output logic GPRRegNo, + output logic [9:0] ScanChainLen, + output logic [9:0] ShiftCount, + output logic InvalidRegNo, + output logic RegReadOnly, + output logic [P.E_SUPPORTED+3:0] GPRAddr, + output logic [P.XLEN-1:0] ARMask +); + `include "debug.vh" + + localparam MISALEN = P.ZICSR_SUPPORTED ? P.XLEN : 0; + localparam TRAPMLEN = P.ZICSR_SUPPORTED ? 1 : 0; + localparam PCMLEN = (P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED) ? P.XLEN : 0; + localparam INSTRMLEN = (P.ZICSR_SUPPORTED | P.A_SUPPORTED) ? 32 : 0; + localparam MEMRWMLEN = 2; + localparam INSTRVALIDMLEN = 1; + localparam WRITEDATAMLEN = P.XLEN; + localparam IEUADRMLEN = P.XLEN; + localparam READDATAMLEN = P.LLEN; + localparam SCANCHAINLEN = P.XLEN - 1 + + MISALEN + TRAPMLEN + PCMLEN + INSTRMLEN + + MEMRWMLEN + INSTRVALIDMLEN + WRITEDATAMLEN + + IEUADRMLEN + READDATAMLEN; + localparam GPRCHAINLEN = P.XLEN; + + localparam MISA_IDX = MISALEN; + localparam TRAPM_IDX = MISA_IDX + TRAPMLEN; + localparam PCM_IDX = TRAPM_IDX + PCMLEN; + localparam INSTRM_IDX = PCM_IDX + INSTRMLEN; + localparam MEMRWM_IDX = INSTRM_IDX + MEMRWMLEN; + localparam INSTRVALIDM_IDX = MEMRWM_IDX + INSTRVALIDMLEN; + localparam WRITEDATAM_IDX = INSTRVALIDM_IDX + WRITEDATAMLEN; + localparam IEUADRM_IDX = WRITEDATAM_IDX + IEUADRMLEN; + localparam READDATAM_IDX = IEUADRM_IDX + READDATAMLEN; + + logic [P.XLEN:0] Mask; + + assign ScanChainLen = GPRRegNo ? GPRCHAINLEN : SCANCHAINLEN; + + if (P.E_SUPPORTED) + assign GPRAddr = Regno[4:0]; + else + assign GPRAddr = Regno[3:0]; + + // Register decoder + always_comb begin + InvalidRegNo = 0; + RegReadOnly = 0; + GPRRegNo = 0; + casez (Regno) + 16'h100? : begin + ShiftCount = P.XLEN - 1; + GPRRegNo = 1; + end + 16'h101? : begin + ShiftCount = P.XLEN - 1; + InvalidRegNo = ~P.E_SUPPORTED; + GPRRegNo = 1; + end + `MISA : begin + ShiftCount = SCANCHAINLEN - MISA_IDX; + InvalidRegNo = ~P.ZICSR_SUPPORTED; + RegReadOnly = 1; + end + `TRAPM : begin + ShiftCount = SCANCHAINLEN - TRAPM_IDX; + InvalidRegNo = ~P.ZICSR_SUPPORTED; + RegReadOnly = 1; + end + `PCM : begin + ShiftCount = SCANCHAINLEN - PCM_IDX; + InvalidRegNo = ~(P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED); + end + `INSTRM : begin + ShiftCount = SCANCHAINLEN - INSTRM_IDX; + InvalidRegNo = ~(P.ZICSR_SUPPORTED | P.A_SUPPORTED); + end + `MEMRWM : ShiftCount = SCANCHAINLEN - MEMRWM_IDX; + `INSTRVALIDM : ShiftCount = SCANCHAINLEN - INSTRVALIDM_IDX; + `WRITEDATAM : ShiftCount = SCANCHAINLEN - WRITEDATAM_IDX; + `IEUADRM : ShiftCount = SCANCHAINLEN - IEUADRM_IDX; + `READDATAM : begin + ShiftCount = SCANCHAINLEN - READDATAM_IDX; + RegReadOnly = 1; + end + default : begin + ShiftCount = 0; + InvalidRegNo = 1; + end + endcase + end + + // Mask calculator + always_comb begin + Mask = 0; + case(Regno) + `TRAPM : Mask = {1{1'b1}}; + `INSTRM : Mask = {32{1'b1}}; + `MEMRWM : Mask = {2{1'b1}}; + `INSTRVALIDM : Mask = {1{1'b1}}; + `READDATAM : Mask = {P.LLEN{1'b1}}; + default : Mask = {P.XLEN{1'b1}}; + endcase + end + + assign ARMask[31:0] = Mask[31:0]; + if (P.XLEN >= 64) + assign ARMask[63:32] = (AarSize == 3'b011 || AarSize == 3'b100) ? Mask[63:32] : '0; + if (P.XLEN == 128) + assign ARMask[127:64] = (AarSize == 3'b100) ? Mask[127:64] : '0; + +endmodule \ No newline at end of file diff --git a/src/debug/tap.sv b/src/debug/tap.sv new file mode 100644 index 000000000..ab7f46a93 --- /dev/null +++ b/src/debug/tap.sv @@ -0,0 +1,97 @@ +/////////////////////////////////////////// +// tap.sv +// +// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu +// Created: 15 March 2024 +// +// Purpose: JTAG tap controller +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +module tap ( + input logic tck, + input logic tms, + output logic resetn, + output logic tdo_en, + output logic captureIR, + output logic clockIR, + output logic updateIR, + output logic shiftDR, + output logic captureDR, + output logic clockDR, + output logic updateDR, + output logic select +); + + enum logic [3:0] { + Exit2DR = 4'h0, + Exit1DR = 4'h1, + ShiftDR = 4'h2, + PauseDR = 4'h3, + SelectIR = 4'h4, + UpdateDR = 4'h5, + CaptureDR = 4'h6, + SelectDR = 4'h7, + Exit2IR = 4'h8, + Exit1IR = 4'h9, + ShiftIR = 4'hA, + PauseIR = 4'hB, + RunTestIdle = 4'hC, + UpdateIR = 4'hD, + CaptureIR = 4'hE, + TLReset = 4'hF + } State; + + always @(posedge tck) begin + case (State) + TLReset : State <= tms ? TLReset : RunTestIdle; + RunTestIdle : State <= tms ? SelectDR : RunTestIdle; + SelectDR : State <= tms ? SelectIR : CaptureDR; + CaptureDR : State <= tms ? Exit1DR : ShiftDR; + ShiftDR : State <= tms ? Exit1DR : ShiftDR; + Exit1DR : State <= tms ? UpdateDR : PauseDR; + PauseDR : State <= tms ? Exit2DR : PauseDR; + Exit2DR : State <= tms ? UpdateDR : ShiftDR; + UpdateDR : State <= tms ? SelectDR : RunTestIdle; + SelectIR : State <= tms ? TLReset : CaptureIR; + CaptureIR : State <= tms ? Exit1IR : ShiftIR; + ShiftIR : State <= tms ? Exit1IR : ShiftIR; + Exit1IR : State <= tms ? UpdateIR : PauseIR; + PauseIR : State <= tms ? Exit2IR : PauseIR; + Exit2IR : State <= tms ? UpdateIR : ShiftIR; + UpdateIR : State <= tms ? SelectDR : RunTestIdle; + endcase + end + + always @(negedge tck) begin + resetn <= ~(State == TLReset); + tdo_en <= State == ShiftIR || State == ShiftDR; + captureIR <= State == CaptureIR; + updateIR <= State == UpdateIR; + shiftDR <= State == ShiftDR; + captureDR <= State == CaptureDR; + updateDR <= State == UpdateDR; + end + + assign clockIR = tck || State[0] || ~State[1] || ~State[3]; + assign clockDR = tck || State[0] || ~State[1] || State[3]; + assign select = State[3]; + +endmodule diff --git a/src/generic/flop/flopenrcs.sv b/src/generic/flop/flopenrcs.sv new file mode 100644 index 000000000..c78b7d359 --- /dev/null +++ b/src/generic/flop/flopenrcs.sv @@ -0,0 +1,55 @@ +/////////////////////////////////////////// +// flopenrcs.sv +// +// Written: matthew.n.otto@okstate.edu 15 April 2024 +// Modified: +// +// Purpose: Scannable D flip-flop with enable, synchronous reset, enabled clear +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License Version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +module flopenrcs #(parameter WIDTH = 8) ( + input logic clk, reset, clear, en, + input logic [WIDTH-1:0] d, + output logic [WIDTH-1:0] q, + input logic scan, // scan enable + input logic scanin, + output logic scanout +); + logic [WIDTH-1:0] dmux; + + assign dmux[WIDTH-1] = scan ? scanin : d[WIDTH-1]; + assign scanout = q[0]; + + genvar i; + for (i=0; i