diff --git a/bin/hw_debug_test.py b/bin/hw_debug_test.py new file mode 100755 index 0000000000..2cebebf96a --- /dev/null +++ b/bin/hw_debug_test.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 + +######################################################################################### +# hw_debug_test.py +# +# Written: matthew.n.otto@okstate.edu +# Created: 19 April 2024 +# +# Purpose: script to automate testing of hardware debug interface +# +# 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. +######################################################################################### + +import random +import time + +from openocd_tcl_wrapper import OpenOCD + +random_stimulus = True +random_order = False + + +def prog_buff_test(cvw): + cvw.halt() + pb = ["0x00840413", "0xd2e3ca40", "0x00100073"] + cvw.write_data("DCSR", hex(0x1 << 15)) + cvw.write_progbuf(pb) + cvw.exec_progbuf() + + cvw.resume() + print() + + +def flow_control_test(cvw): + #time.sleep(200) # wait for full boot + + #cvw.halt() + for _ in range(5): + time.sleep(random.randint(5,10)) + cvw.halt() + cvw.step() + cvw.step() + cvw.resume() + return + + time.sleep(1) + #cvw.read_data("DCSR") + for _ in range(100): + time.sleep(random.randint(5,10)) + print("Halting") + cvw.halt() + cvw.resume() + #cvw.step() + #print(cvw.read_data("PCM")) + #cvw.resume() + + +def register_rw_test(cvw): + registers = dict.fromkeys(cvw.register_translations.keys(),[]) + reg_addrs = list(registers.keys()) + + global XLEN + XLEN = cvw.LLEN + global nonstandard_register_lengths + nonstandard_register_lengths = cvw.nonstandard_register_lengths + + #time.sleep(70) # wait for OpenSBI + + cvw.halt() + + # dump data in all registers + for r in reg_addrs: + try: + data = cvw.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] + cvw.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_order: + random.shuffle(reg_addrs) + test_reg_data = {} + for r in reg_addrs: + test_data = random_hex(r) + try: + cvw.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] + cvw.clear_abstrcmd_err() + else: + raise e + + # GPR X0 is always 0 + test_reg_data["x0"] = "0x" + "0"*(cvw.LLEN//4) + + # Confirm data was written correctly + reg_addrs = list(registers.keys()) + if random_order: + random.shuffle(reg_addrs) + for r in reg_addrs: + try: + rdata = cvw.read_data(r) + except Exception as e: + raise e + if rdata != test_reg_data[r]: + print(f"Error: register {r} read did not return correct data: {rdata} != {test_reg_data[r]}") + else: + print(f"Reading {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: + cvw.write_data(r, registers[r]) + except Exception as e: + raise e + + # Confirm data was written correctly + for r in reg_addrs: + try: + rdata = cvw.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") + + cvw.resume() + + +def random_hex(reg_name): + pad = XLEN // 4 + if reg_name in nonstandard_register_lengths: + size = nonstandard_register_lengths[reg_name] + else: + size = XLEN + + # Reset ReadDataM to a value + nonstandard_register_lengths["READDATAM"] = XLEN + if random_stimulus: + return "0x" + f"{random.getrandbits(size):x}".rjust(pad, "0") + else: + data = 0xa5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5 + return "0x" + f"{(data & (2**size-1)):x}".rjust(pad, "0") + + +with OpenOCD() as cvw: + #cvw.trst() + cvw.reset_dm() + time.sleep(1) + cvw.reset_hart() + time.sleep(1) + #register_rw_test(cvw) + #flow_control_test(cvw) + prog_buff_test(cvw) diff --git a/bin/openocd_tcl_wrapper.py b/bin/openocd_tcl_wrapper.py new file mode 100644 index 0000000000..93079d2492 --- /dev/null +++ b/bin/openocd_tcl_wrapper.py @@ -0,0 +1,708 @@ +######################################################################################### +# openocd_tcl_wrapper.py +# +# Written: matthew.n.otto@okstate.edu +# Created: 8 June 2024 +# +# Purpose: Python wrapper library used to send debug commands to OpenOCD +# +# 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. +######################################################################################### + +import math +import os +import socket +import sys +import time + +ENDMSG = b'\x1a' + +class OpenOCD: + def __init__(self): + self.tcl = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + def __enter__(self): + self.tcl.connect(("127.0.0.1", 6666)) + self.LLEN = 64 #TODO: find this + return self + + def __exit__(self, type, value, traceback): + try: + self.send("exit") + finally: + self.tcl.close() + + def capture(self, cmd): + return self.send(f"capture \"{cmd}\"") + + def send(self, cmd): + data = cmd.encode("ascii") + ENDMSG + self.tcl.send(data) + return self.receive() + + def receive(self): + data = bytes() + while True: + byte = self.tcl.recv(1) + if byte == ENDMSG: + break + else: + data += byte + data = data.decode("ascii").rstrip() + return data + + def trst(self): + self.send("pathmove RESET IDLE") + + def write_dtmcs(self, dtmhardreset=False, dmireset=False): + """Send reset commands to DTMCS. Used to clear sticky DMI OP error status""" + data = 0 + data |= dtmhardreset << 17 + data |= dmireset << 16 + if not data: + print("Warning: not writing DTMCS (dtmhardreset and dmireset are both false)") + return + tapname = "cvw.cpu" + self.send(f"irscan {tapname} 0x10") # dtmcs instruction + self.send(f"drscan {tapname} 32 {hex(data)}") + op = self.capture(f"drscan {tapname} 32 0x0") + if (int(op) >> 10) & 0x3: + raise Exception("Error: failed to reset DTMCS (nonzero dmistat)") + + def write_dmi(self, address, data): + cmd = f"riscv dmi_write {address} {data}" + rsp = self.capture(cmd) + if "Failed" in rsp: + raise Exception(rsp) + + def read_dmi(self, address): + cmd = f"riscv dmi_read {address}" + return self.capture(cmd) + + def activate_dm(self): + self.write_dmi("0x10", "0x1") + dmstat = int(self.read_dmi("0x10"), 16) + if not dmstat & 0x1: + raise Exception("Error: failed to activate debug module") + + def reset_dm(self): + self.write_dmi("0x10", "0x0") + dmstat = int(self.read_dmi("0x10"), 16) + if dmstat & 0x1: + raise Exception("Error: failed to deactivate debug module") + self.activate_dm() + + def reset_hart(self): + self.write_dmi("0x10", "0x3") + self.write_dmi("0x10", "0x1") + dmstat = int(self.read_dmi("0x11"), 16) # check HaveReset + if not ((dmstat >> 18) & 0x3): + raise Exception("Error: Hart failed to reset") + self.write_dmi("0x10", "0x10000001") # ack HaveReset + + def write_progbuf(self, data): + #TODO query progbuf size and error if len(data) is greater + baseaddr = 0x20 + for idx, instr in enumerate(data): + z = hex(baseaddr+idx) #debug + self.write_dmi(hex(baseaddr+idx), instr) + + def exec_progbuf(self): + self.write_dmi("0x17", hex(0x1 << 18)) + + def set_haltonreset(self): + self.write_dmi("0x10", "0x9") + + def clear_haltonreset(self): + self.write_dmi("0x10", "0x5") + + def halt(self): + self.write_dmi("0x10", "0x80000001") + dmstat = int(self.read_dmi("0x11"), 16) # Check halted bit + if not ((dmstat >> 8) & 0x3): + raise Exception("Error: Hart failed to halt") + self.write_dmi("0x10", "0x1") # Deassert HaltReq + + def resume(self): + self.write_dmi("0x10", "0x40000001") # Send resume command + dmstat = int(self.read_dmi("0x11"), 16) # Check resumeack bit + if not ((dmstat >> 16) & 0x3): + raise Exception("Error: Hart failed to resume") + + def step(self): + # Set step bit if it isn't already set + dcsr = int(self.read_data("DCSR"), 16) + if not (dcsr >> 2) & 0x1: + dcsr |= 0x4 + self.write_data("DCSR", hex(dcsr)) + # Resume once + self.write_dmi("0x10", "0x40000001") + # Unset step bit + dcsr &= ~0x4 + self.write_data("DCSR", hex(dcsr)) + + def access_register(self, write, regno, addr_size=None): + data = 1 << 17 # transfer bit always set + if not addr_size: + addr_size = self.LLEN + elif addr_size not in (32, 64, 128): + raise Exception("must provide valid register access size (32, 64, 128). See: 3.7.1.1 aarsize") + data += int(math.log2(addr_size // 8)) << 20 + data += write << 16 + data += regno + self.write_dmi("0x17", hex(data)) + + def write_data(self, register, data): + """Write data to specified register""" + # Write data to 32 bit message registers + data = int(data, 16) + self.write_dmi("0x4", hex(data & 0xffffffff)) + if self.LLEN >= 64: + self.write_dmi("0x5", hex((data >> 32) & 0xffffffff)) + if self.LLEN == 128: + self.write_dmi("0x6", hex((data >> 64) & 0xffffffff)) + self.write_dmi("0x7", hex((data >> 96) & 0xffffffff)) + # Translate register alias to DM regno + regno = translate_regno(register) + # Transfer data from msg registers to target register + self.access_register(write=True, regno=regno) + # Check that operations completed without error + if acerr := self.check_abstrcmderr(): + raise Exception(acerr) + + def read_data(self, register): + """Read data from specified register""" + # Translate register alias to DM regno + regno = translate_regno(register) + # Transfer data from target register to msg registers + self.access_register(write=False, regno=regno) + # Read data from 32 bit message registers + data = "" + data = self.read_dmi("0x4").replace("0x", "").zfill(8) + if self.LLEN >= 64: + data = self.read_dmi("0x5").replace("0x", "").zfill(8) + data + if self.LLEN == 128: + data = self.read_dmi("0x6").replace("0x", "").zfill(8) + data + data = self.read_dmi("0x7").replace("0x", "").zfill(8) + data + # Check that operations completed without error + if acerr := self.check_abstrcmderr(): + raise Exception(acerr) + return f"0x{data}" + + def check_abstrcmderr(self): + """These errors must be cleared using clear_abstrcmd_err() before another OP can be executed""" + abstractcs = int(self.read_dmi("0x16"), 16) + # CmdErr is only valid if Busy is 0 + while True: + if not bool((abstractcs & 0x1000) >> 12): # if not Busy + break + time.sleep(0.05) + abstractcs = int(self.read_dmi("0x16"), 16) + return cmderr_translations[(abstractcs & 0x700) >> 8] + + def clear_abstrcmd_err(self): + self.write_dmi("0x16", "0x700") + if self.check_abstrcmderr(): + raise Exception("Error: failed to clear AbstrCmdErr") + + + + +class SVF_Generator: + def __init__(self, writeout=False, XLEN=64): + self.writeout = writeout + if XLEN not in (32, 64): + raise Exception("Error: Invalid XLEN value entered (supports 32, 64)") + self.XLEN = XLEN + self.INSTR = 0x01 + self.DCSR = 0x0 + + def __enter__(self): + if self.writeout: + filename = sys.argv[0].replace(".py", "") + filename += ".svf" + self.file = open(filename, "w") + return self + + def __exit__(self, type, value, traceback): + if self.writeout: + self.file.close() + + def print_svf(self, svf): + if self.file: + print(svf, file=self.file) + else: + print(svf) + + def comment(self, comment): + self.print_svf(f"// {comment}") + + def spin(self, cycles): + self.print_svf(f"RUNTEST {cycles};") + + def instruction(self, instr): + if self.INSTR != instr: + self.print_svf(f"SIR 5 TDI({hex(instr)[2:]});") + self.INSTR = instr + + def compare_value(self, expected_value, mask=None): + if self.INSTR == 0x11: + length = 41 + expected_value = expected_value << 2 + if mask: + mask = (mask << 2) + 3 + else: + length = 32 + svf = f"SDR {length} TDO({hex(expected_value)[2:]})" + if mask: + svf += f" MASK({hex(mask)[2:]})" + svf += ";" + self.print_svf(svf) + + def check_jtag_id(self, expected_id): + self.instruction(0x01) + self.compare_value(expected_id) + + def write_dtmcs(self, dtmhardreset=False, dmireset=False): + self.instruction(0x10) + data = 0 + data |= dtmhardreset << 17 + data |= dmireset << 16 + self.print_svf(f"SDR 32 TDI({hex(data)[2:]});") + + def write_dmi(self, address, data): + self.instruction(0x11) + if not isinstance(address, int): + address = int(address, 16) + if not isinstance(data, int): + data = int(data, 16) + payload = (address << 34) + (data << 2) + 0x2 + self.print_svf(f"SDR 41 TDI({hex(payload)[2:]});") + + def read_dmi(self, address, expected_data, mask=None): + """This function will send a read command to DM for the specified register + and then perform a second scan, comparing the TDO scanout value to """ + self.instruction(0x11) + if not isinstance(address, int): + address = int(address, 16) + if not isinstance(expected_data, int): + expected_data = int(expected_data, 16) + payload = (address << 34) + 0x1 + self.print_svf(f"SDR 41 TDI({hex(payload)[2:]});") + self.compare_value(expected_data, mask) + + def activate_dm(self): + self.write_dmi("0x10", "0x1") + + def reset_dm(self): + self.write_dmi("0x10", "0x0") + self.write_dmi("0x10", "0x1") + + def reset_hart(self): + self.write_dmi("0x10", "0x3") + self.write_dmi("0x10", "0x1") + self.write_dmi("0x10", "0x10000001") # ack HaveReset + + def write_progbuf(self, instructions): + baseaddr = 0x20 + for idx, instr in enumerate(instructions): + self.write_dmi(baseaddr+idx, instr) + + def exec_progbuf(self): + self.write_dmi(0x17, 0x1 << 18) + self.spin(10) + + def set_haltonreset(self): + self.write_dmi("0x10", "0x9") + + def clear_haltonreset(self): + self.write_dmi("0x10", "0x5") + + def halt(self): + self.write_dmi(0x10, 0x80000001) # Set HaltReq + self.write_dmi(0x10, 0x1) # Release HaltReq + self.read_dmi(0x11, 0x300, 0x300) # Check halted bit + + def resume(self): + self.write_dmi(0x10, 0x40000001) # Send resume command + self.read_dmi(0x11, 0x30000, 0x30000) # Check resumeack bit + + def access_register(self, write, regno): + data = 1 << 17 # transfer bit always set + data += int(math.log2(self.XLEN // 8)) << 20 + data += int(write) << 16 + data += regno + self.write_dmi("0x17", hex(data)) + self.spin(self.XLEN) # required wait duration depends on which register was accessed + + def write_data(self, register, data): + if data > 0 and math.log2(data) > self.XLEN: + raise Exception(f"Error: value passed to write_data ({data}) exceeds XLEN") + self.write_dmi("0x4", data & 0xffffffff) + if self.XLEN >= 64: + self.write_dmi("0x5", (data >> 32) & 0xffffffff) + regno = translate_regno(register) + self.access_register(write=True, regno=regno) + + def read_data(self, register, expected_data): + regno = translate_regno(register) + self.access_register(write=False, regno=regno) + self.read_dmi("0x4", expected_data & 0xffffffff) + if self.LLEN >= 64: + self.read_dmi("0x5", (expected_data >> 32) & 0xffffffff) + + def step(self): + if not (self.DCSR >> 2) & 0x1: + self.DCSR |= 0x4 + self.write_data("DCSR", self.DCSR) + # Resume once + self.write_dmi("0x10", "0x40000001") + # Unset step bit + self.DCSR &= ~0x4 + self.write_data("DCSR", self.DCSR) + + + + +def translate_regno(register): + if register in register_translations: + return int(register_translations[register], 16) + elif register in abi_translations: + register = abi_translations[register] + return int(register_translations[register], 16) + else: + return None + + +# 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 = { + "FFLAGS" : "0x0001", + "FRM" : "0x0002", + "FCSR" : "0x0003", + "MSTATUS" : "0x0300", + "MISA" : "0x0301", + "MEDELEG" : "0x0302", + "MIDELEG" : "0x0303", + "MIE" : "0x0304", + "MTVEC" : "0x0305", + "MCOUNTEREN" : "0x0306", + "MENVCFG" : "0x030A", + "MSTATUSH" : "0x0310", + "MENVCFGH" : "0x031A", + "MCOUNTINHIBIT" : "0x0320", + "MSCRATCH" : "0x0340", + "MEPC" : "0x0341", + "MCAUSE" : "0x0342", + "MTVAL" : "0x0343", + "MIP" : "0x0344", + "PMPCFG0" : "0x03A0", + "PMPCFG1" : "0x03A1", + "PMPCFG2" : "0x03A2", + "PMPCFG3" : "0x03A3", + "PMPCFG4" : "0x03A4", + "PMPCFG5" : "0x03A5", + "PMPCFG6" : "0x03A6", + "PMPCFG7" : "0x03A7", + "PMPCFG8" : "0x03A8", + "PMPCFG9" : "0x03A9", + "PMPCFGA" : "0x03AA", + "PMPCFGB" : "0x03AB", + "PMPCFGC" : "0x03AC", + "PMPCFGD" : "0x03AD", + "PMPCFGE" : "0x03AE", + "PMPCFGF" : "0x03AF", + "PMPADDR0" : "0x03B0", + "PMPADDR1" : "0x03B1", + "PMPADDR2" : "0x03B2", + "PMPADDR3" : "0x03B3", + "PMPADDR4" : "0x03B4", + "PMPADDR5" : "0x03B5", + "PMPADDR6" : "0x03B6", + "PMPADDR7" : "0x03B7", + "PMPADDR8" : "0x03B8", + "PMPADDR9" : "0x03B9", + "PMPADDRA" : "0x03BA", + "PMPADDRB" : "0x03BB", + "PMPADDRC" : "0x03BC", + "PMPADDRD" : "0x03BD", + "PMPADDRE" : "0x03BE", + "PMPADDRF" : "0x03BF", + "PMPADDR10" : "0x03C0", + "PMPADDR11" : "0x03C1", + "PMPADDR12" : "0x03C2", + "PMPADDR13" : "0x03C3", + "PMPADDR14" : "0x03C4", + "PMPADDR15" : "0x03C5", + "PMPADDR16" : "0x03C6", + "PMPADDR17" : "0x03C7", + "PMPADDR18" : "0x03C8", + "PMPADDR19" : "0x03C9", + "PMPADDR1A" : "0x03CA", + "PMPADDR1B" : "0x03CB", + "PMPADDR1C" : "0x03CC", + "PMPADDR1D" : "0x03CD", + "PMPADDR1E" : "0x03CE", + "PMPADDR1F" : "0x03CF", + "PMPADDR20" : "0x03D0", + "PMPADDR21" : "0x03D1", + "PMPADDR22" : "0x03D2", + "PMPADDR23" : "0x03D3", + "PMPADDR24" : "0x03D4", + "PMPADDR25" : "0x03D5", + "PMPADDR26" : "0x03D6", + "PMPADDR27" : "0x03D7", + "PMPADDR28" : "0x03D8", + "PMPADDR29" : "0x03D9", + "PMPADDR2A" : "0x03DA", + "PMPADDR2B" : "0x03DB", + "PMPADDR2C" : "0x03DC", + "PMPADDR2D" : "0x03DD", + "PMPADDR2E" : "0x03DE", + "PMPADDR2F" : "0x03DF", + "PMPADDR30" : "0x03E0", + "PMPADDR31" : "0x03E1", + "PMPADDR32" : "0x03E2", + "PMPADDR33" : "0x03E3", + "PMPADDR34" : "0x03E4", + "PMPADDR35" : "0x03E5", + "PMPADDR36" : "0x03E6", + "PMPADDR37" : "0x03E7", + "PMPADDR38" : "0x03E8", + "PMPADDR39" : "0x03E9", + "PMPADDR3A" : "0x03EA", + "PMPADDR3B" : "0x03EB", + "PMPADDR3C" : "0x03EC", + "PMPADDR3D" : "0x03ED", + "PMPADDR3E" : "0x03EE", + "PMPADDR3F" : "0x03EF", + "TSELECT" : "0x07A0", + "TDATA1" : "0x07A1", + "TDATA2" : "0x07A2", + "TDATA3" : "0x07A3", + "DCSR" : "0x07B0", + "DPC" : "0x07B1", + "MVENDORID" : "0x0F11", + "MARCHID" : "0x0F12", + "MIMPID" : "0x0F13", + "MHARTID" : "0x0F14", + "MCONFIGPTR" : "0x0F15", + "SIP" : "0x0144", + "MIP" : "0x0344", + "MHPMEVENTBASE" : "0x0320", + "MHPMCOUNTERBASE" : "0x0B00", + "MHPMCOUNTERHBASE" : "0x0B80", + "HPMCOUNTERBASE" : "0x0C00", + "TIME" : "0x0C01", + "HPMCOUNTERHBASE" : "0x0C80", + "TIMEH" : "0x0C81", + "SSTATUS" : "0x0100", + "SIE" : "0x0104", + "STVEC" : "0x0105", + "SCOUNTEREN" : "0x0106", + "SENVCFG" : "0x010A", + "SSCRATCH" : "0x0140", + "SEPC" : "0x0141", + "SCAUSE" : "0x0142", + "STVAL" : "0x0143", + "SIP" : "0x0144", + "STIMECMP" : "0x014D", + "STIMECMPH" : "0x015D", + "SATP" : "0x0180", + "SIE" : "0x0104", + "SIP" : "0x0144", + "MIE" : "0x0304", + "MIP" : "0x0344", + "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", + "F0" : "0x1020", + "F1" : "0x1021", + "F2" : "0x1022", + "F3" : "0x1023", + "F4" : "0x1024", + "F5" : "0x1025", + "F6" : "0x1026", + "F7" : "0x1027", + "F8" : "0x1028", + "F9" : "0x1029", + "F10" : "0x102A", + "F11" : "0x102B", + "F12" : "0x102C", + "F13" : "0x102D", + "F14" : "0x102E", + "F15" : "0x102F", + "F16" : "0x1030", + "F17" : "0x1031", + "F18" : "0x1032", + "F19" : "0x1033", + "F20" : "0x1034", + "F21" : "0x1035", + "F22" : "0x1036", + "F23" : "0x1037", + "F24" : "0x1038", + "F25" : "0x1039", + "F26" : "0x103A", + "F27" : "0x103B", + "F28" : "0x103C", + "F29" : "0x103D", + "F30" : "0x103E", + "F31" : "0x103F", +} + +abi_translations = { + "x0" : "zero", + "x1" : "ra", + "x2" : "sp", + "x3" : "gp", + "x4" : "tp", + "x5" : "t0", + "x6" : "t1", + "x7" : "t2", + "x8" : "s0/fp", + "x9" : "s1", + "x10" : "a0", + "x11" : "a1", + "x12" : "a2", + "x13" : "a3", + "x14" : "a4", + "x15" : "a5", + "x16" : "a6", + "x17" : "a7", + "x18" : "s2", + "x19" : "s3", + "x20" : "s4", + "x21" : "s5", + "x22" : "s6", + "x23" : "s7", + "x24" : "s8", + "x25" : "s9", + "x26" : "s10", + "x27" : "s11", + "x28" : "t3", + "x29" : "t4", + "x30" : "t5", + "x31" : "t6", + "f0" : "ft0", + "f1" : "ft1", + "f2" : "ft2", + "f3" : "ft3", + "f4" : "ft4", + "f5" : "ft5", + "f6" : "ft6", + "f7" : "ft7", + "f8" : "fs0", + "f9" : "fs1", + "f10" : "fa0", + "f11" : "fa1", + "f12" : "fa2", + "f13" : "fa3", + "f14" : "fa4", + "f15" : "fa5", + "f16" : "fa6", + "f17" : "fa7", + "f18" : "fs2", + "f19" : "fs3", + "f20" : "fs4", + "f21" : "fs5", + "f22" : "fs6", + "f23" : "fs7", + "f24" : "fs8", + "f25" : "fs9", + "f26" : "fs10", + "f27" : "fs11", + "f28" : "ft8", + "f29" : "ft9", + "f30" : "ft10", + "f31" : "ft11", +} +abi_translations |= dict(map(reversed, abi_translations.items())) # two way translations + +nonstandard_register_lengths = { + "TRAPM" : 1, + "INSTRM" : 32, + "MEMRWM" : 2, + "INSTRVALIDM" : 1, + "READDATAM" : 64 +} diff --git a/bin/svf_convert.py b/bin/svf_convert.py new file mode 100755 index 0000000000..0e7ebaa101 --- /dev/null +++ b/bin/svf_convert.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 + +################################################ +# svf_convert.py +# +# Written: matthew.n.otto@okstate.edu +# Created: 28 June 2024 +# +# Purpose: Converts JTAG SVF files to hexfile format for driving simulation stimulus +# +# 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. +################################################ + +import argparse +from enum import Enum +from math import log2 +import os +import sys + +# Assembled SVF command format +CMD_BITS = 3 +LENGTH_BITS = 10 +DATA_BITS = 48 + +# Derived constants +MAXLEN = 2**LENGTH_BITS + +usage = f""" +Converts SVF file to proprietary memfile for use with jtag_driver.sv\n + +Usage: svf_convert.py +""" + +def main(): + parser = argparse.ArgumentParser(description=usage, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument(dest="source_directory", help="The absolute path of the directory contianing SVF test files") + parser.add_argument(dest="output_directory", help="The absolute path of the directory where memfiles will be generated") + args = parser.parse_args() + srcdir = args.source_directory + workdir = args.output_directory + + if not os.path.exists(srcdir): + print(f"Error: source directory '{srcdir}' does not exist") + sys.exit(1) + if not os.path.exists(workdir): + print(f"Error: output directory '{workdir}' does not exist") + sys.exit(1) + + for file in os.scandir(srcdir): + if file.is_file() and file.name.endswith(".svf"): + memfilename = file.name.replace(".svf", ".memfile") + memfilepath = os.path.join(workdir, memfilename) + convert(file.path, memfilepath) + print(f"Successfully converted {file.name} -> {memfilename}") + + +def convert(input_path, output_path): + with open(input_path, "r") as file: + data = file.read() + data = data.lower() + + tokens = svf_tokenizer(data) + tokens = remove_comments(tokens) + cmds = parse_tokens(tokens) + with open(output_path, "w") as file: + for cmd in cmds: + asm = assemble_svf(cmd) + file.write(asm + '\n') + + +def svf_tokenizer(data): + """Reads raw SVF ascii and converts to a list of tokens""" + keywords = ["sir", "sdr", "runtest", "tdi", "tdo", "mask", "smask", "(", ")", ";"] + comment_keywords = ["//", "!"] + keywords += comment_keywords + + tokens = [] + eof = len(data) + token = "" + for idx, char in enumerate(data): + if char != " " and char != "\n": + token += char + if (idx+1 < eof): + if data[idx+1] == " ": + if token: + tokens.append(token) + token = "" + elif data[idx+1] == "\n": + if token: + tokens.append(token) + token = "" + tokens.append("\n") + elif token in keywords or data[idx+1] in keywords: + if token: + tokens.append(token) + token = "" + return tokens + + +def remove_comments(tokens): + """Removes comments and newlines from list of tokens""" + pruned = [] + comment = False + for t in tokens: + if t == "\n": + comment = False + continue + if comment: + continue + if t in ["//", "!"]: + comment = True + continue + pruned.append(t) + return pruned + +def parse_tokens(tokens): + """groups tokens belonging to the same SVF command and checks if valid""" + cmds = [] + + cmd = Command() + start_idx = 0 + i = -1 + while i+1 < len(tokens): + i += 1 + t = tokens[i] + if t == ";": + if cmd.complete(): + cmds.append(cmd) + cmd = Command() + start_idx = i+1 + continue + else: + raise Exception(f"Error: incomplete SVF command terminated : '{' '.join(tokens[start_idx:i+1])}'") + + if cmd.op is None: + try: + cmd.op = SVF[t] + except KeyError: + raise Exception(f"Error: expected an SVF command, got '{t}' : '{' '.join(tokens[start_idx:i+1])}'") + continue + + if cmd.length is None: + try: + cmd.length = int(t) + except Exception: + raise Exception(f"Error: expected a length value, got '{t}' : '{' '.join(tokens[start_idx:i+1])}'") + if cmd.length == 0: + raise Exception(f"Error: length parameter must not be 0 : '{' '.join(tokens[start_idx:i+1])}'") + if cmd.length >= MAXLEN: + raise Exception(f"Error: not enough bits to represent command length : {cmd.length} > {MAXLEN-1} : '{' '.join(tokens[start_idx:i+2])}'") + if cmd.op != SVF.runtest and cmd.length > DATA_BITS: + raise Exception(f"Error: length exceeds number of bits in data field : {cmd.length} > {DATA_BITS} : '{' '.join(tokens[start_idx:i+2])}'") + continue + + match cmd.op: + case SVF.runtest: + continue + case SVF.sdr | SVF.sir: + if t not in ("tdi", "tdo", "mask"): + raise Exception(f"Error: unknown keyword '{t}' : '{' '.join(tokens[start_idx:i+1])}'") + if not (tokens[i+1] == "(" and tokens[i+3] == ")"): + raise Exception(f"Error: could not interpret value following token '{t}' (missing parentheses) : '{' '.join(tokens[start_idx:i+2])}'") + try: + val = int(tokens[i+2], 16) + i += 3 + if t == "tdi": + cmd.tdi = val + elif t == "tdo": + cmd.tdo = val + elif t == "mask": + cmd.mask = val + except Exception: + raise Exception(f"Error: could not interpret {t} value: '{tokens[i+2]}' : '{' '.join(tokens[start_idx:i+1])}'") + if val > 0 and int(log2(val)) > cmd.length: + raise Exception(f"Error: shift data cannot be a value larger than the maximum implied by the length parameter : log2({val}) > {cmd.length}" + + f" : '{' '.join(tokens[start_idx:i+2])}'") + continue + case _: + raise Exception(f"Error: did not match on valid SVF command : '{' '.join(tokens[start_idx:i+1])}'") + if t != ";": + raise Exception(f"Error: file ended with incomplete command") + return cmds + + + +def assemble_svf(cmd): + """Converts svf command object to proprietary format for Wally simulation""" + cmdcode = (cmd.op.value << (LENGTH_BITS + DATA_BITS*3)) + match cmd.op: + case SVF.runtest: + cmdcode += (cmd.length << DATA_BITS*3) + case SVF.sdr | SVF.sir: + if cmd.length: + cmdcode += (cmd.length << DATA_BITS*3) + if cmd.tdi: + cmdcode += (cmd.tdi << DATA_BITS*2) + if cmd.tdo: + cmdcode += (cmd.tdo << DATA_BITS) + if cmd.mask: + cmdcode += (cmd.mask) + elif not cmd.mask: # If mask isnt specified, set all bits to 1 + cmdcode += 2**DATA_BITS-1 + hexcmd = hex(cmdcode)[2:] + # TODO: pad left with 0 if needed (if CMD_BITS increases) + return hexcmd + + +class Command: + def __init__(self): + self.op = None + self.length = None + self.tdi = None + self.tdo = None + self.mask = None + + def complete(self): + """Returns true if object contains enough information to form a complete command""" + if self.op == SVF.runtest: + return self.length + elif self.op in (SVF.sdr, SVF.sir): + z = self.length and (self.tdi is not None or self.tdo is not None) + return z + else: + return False + + +# Supported SVF commands +class SVF(Enum): + runtest = 0 + sir = 1 + sdr = 2 + + +if __name__ == "__main__": + main() diff --git a/bin/wsim b/bin/wsim index b2288deb4e..0de6f593d8 100755 --- a/bin/wsim +++ b/bin/wsim @@ -33,6 +33,7 @@ parser.add_argument("--vcd", "-v", help="Generate testbench.vcd", action="store_ parser.add_argument("--lockstep", "-l", help="Run ImperasDV lock, step, and compare.", action="store_true") parser.add_argument("--locksteplog", "-b", help="Retired instruction number to be begin logging.", default=0) parser.add_argument("--covlog", "-d", help="Log coverage after n instructions.", default=0) +parser.add_argument("--jtag", "-j", help="JTAG SVF file to load", default=0) parser.add_argument("--rvvi", "-r", help="Simulate rvvi hardware interface and ethernet.", action="store_true") args = parser.parse_args() print("Config=" + args.config + " tests=" + args.testsuite + " sim=" + args.sim + " gui=" + str(args.gui) + " args='" + args.args + "'") @@ -114,11 +115,14 @@ if (args.sim == "questa"): cmd = "do wally.do " + args.config + " " + args.testsuite + " " + args.tb + " " + args.args + " " + ElfFile + " " + flags if (args.gui): # launch Questa with GUI; add +acc to keep variables accessible if(args.tb == "testbench"): - cmd = cd + "; " + prefix + " vsim -do \"" + cmd + " +acc -GDEBUG=1\"" + cmd = cd + "; " + prefix + " vsim -do \"" + cmd + " +acc -GDEBUG=1" elif(args.tb == "testbench_fp"): - cmd = cd + "; " + prefix + " vsim -do \"" + cmd + " +acc\"" + cmd = cd + "; " + prefix + " vsim -do \"" + cmd + " +acc" else: # launch Questa in batch mode - cmd = cd + "; " + prefix + " vsim -c -do \"" + cmd + "\"" + cmd = cd + "; " + prefix + " vsim -c -do \"" + cmd + if (args.jtag): + cmd += " +JTAGTESTFILE=" + args.jtag + cmd += "\"" print("Running Questa with command: " + cmd) os.system(cmd) elif (args.sim == "verilator"): diff --git a/config/derivlist.txt b/config/derivlist.txt index c689d2cb98..b7e19345f6 100644 --- a/config/derivlist.txt +++ b/config/derivlist.txt @@ -319,9 +319,6 @@ BTB_SIZE 32'd14 deriv bpred_GSHARE_10_16_16_1_rv32gc rv32gc BTB_SIZE 32'd16 - - - deriv bpred_GSHARE_6_16_10_0_rv32gc bpred_GSHARE_6_16_10_1_rv32gc INSTR_CLASS_PRED 0 @@ -964,7 +961,6 @@ D_SUPPORTED 0 ZCD_SUPPORTED 0 ZFH_SUPPORTED 0 - #### FH_only, RK variable deriv fh_div_2_1_rv32gc div_2_1_rv32gc D_SUPPORTED 0 @@ -1064,7 +1060,6 @@ ZFH_SUPPORTED 0 deriv fd_div_4_4_rv64gc div_4_4_rv64gc ZFH_SUPPORTED 0 - # FDH only , rk variable deriv fdh_div_2_1_rv32gc div_2_1_rv32gc diff --git a/config/rv32e/config.vh b/config/rv32e/config.vh index 4ec0123d12..f271022632 100644 --- a/config/rv32e/config.vh +++ b/config/rv32e/config.vh @@ -33,6 +33,9 @@ localparam XLEN = 32'd32; // IEEE 754 compliance localparam IEEE754 = 0; +// Debug Module implemented +localparam logic DEBUG_SUPPORTED = 1'b0; + // RISC-V configuration per specification // Base instruction set (defaults to I if E is not supported) localparam logic E_SUPPORTED = 1; @@ -183,6 +186,9 @@ localparam logic [63:0] SDC_RANGE = 64'h0000007F; localparam logic SPI_SUPPORTED = 0; localparam logic [63:0] SPI_BASE = 64'h10040000; localparam logic [63:0] SPI_RANGE = 64'h00000FFF; +// Debug program buffer support is enabled with DEBUG_SUPPORTED +localparam logic [63:0] PROGBUF_BASE = 64'h00002000; +localparam logic [63:0] PROGBUF_RANGE = 64'h0000000F; // Bus Interface width localparam AHBW = (XLEN); diff --git a/config/rv32gc/config.vh b/config/rv32gc/config.vh index c861759d92..035f1ecc16 100644 --- a/config/rv32gc/config.vh +++ b/config/rv32gc/config.vh @@ -33,6 +33,9 @@ localparam XLEN = 32'd32; // IEEE 754 compliance localparam IEEE754 = 0; +// Debug Module implemented +localparam logic DEBUG_SUPPORTED = 1'b1; + // RISC-V configuration per specification // Base instruction set (defaults to I if E is not supported) localparam logic E_SUPPORTED = 0; @@ -183,6 +186,9 @@ localparam logic [63:0] SDC_RANGE = 64'h0000007F; localparam logic SPI_SUPPORTED = 1; localparam logic [63:0] SPI_BASE = 64'h10040000; localparam logic [63:0] SPI_RANGE = 64'h00000FFF; +// Debug program buffer support is enabled with DEBUG_SUPPORTED +localparam logic [63:0] PROGBUF_BASE = 64'h00002000; +localparam logic [63:0] PROGBUF_RANGE = 64'h0000000F; // Bus Interface width localparam AHBW = (XLEN); diff --git a/config/rv32i/config.vh b/config/rv32i/config.vh index 01818afc26..7cf1f3403f 100644 --- a/config/rv32i/config.vh +++ b/config/rv32i/config.vh @@ -33,6 +33,9 @@ localparam XLEN = 32'd32; // IEEE 754 compliance localparam IEEE754 = 0; +// Debug Module implemented +localparam logic DEBUG_SUPPORTED = 1'b0; + // RISC-V configuration per specification // Base instruction set (defaults to I if E is not supported) localparam logic E_SUPPORTED = 0; @@ -183,6 +186,9 @@ localparam logic [63:0] SDC_RANGE = 64'h0000007F; localparam logic SPI_SUPPORTED = 0; localparam logic [63:0] SPI_BASE = 64'h10040000; localparam logic [63:0] SPI_RANGE = 64'h00000FFF; +// Debug program buffer support is enabled with DEBUG_SUPPORTED +localparam logic [63:0] PROGBUF_BASE = 64'h00002000; +localparam logic [63:0] PROGBUF_RANGE = 64'h0000000F; // Bus Interface width localparam AHBW = (XLEN); diff --git a/config/rv32imc/config.vh b/config/rv32imc/config.vh index 05a8fd2420..f79ad5b106 100644 --- a/config/rv32imc/config.vh +++ b/config/rv32imc/config.vh @@ -33,6 +33,9 @@ localparam XLEN = 32'd32; // IEEE 754 compliance localparam IEEE754 = 0; +// Debug Module implemented +localparam logic DEBUG_SUPPORTED = 1'b0; + // RISC-V configuration per specification // Base instruction set (defaults to I if E is not supported) localparam logic E_SUPPORTED = 0; @@ -183,6 +186,9 @@ localparam logic [63:0] SDC_RANGE = 64'h0000007F; localparam logic SPI_SUPPORTED = 1; localparam logic [63:0] SPI_BASE = 64'h10040000; localparam logic [63:0] SPI_RANGE = 64'h00000FFF; +// Debug program buffer support is enabled with DEBUG_SUPPORTED +localparam logic [63:0] PROGBUF_BASE = 64'h00002000; +localparam logic [63:0] PROGBUF_RANGE = 64'h0000000F; // Bus Interface width localparam AHBW = (XLEN); diff --git a/config/rv64gc/config.vh b/config/rv64gc/config.vh index b8ed8dc47b..347a2a87b4 100644 --- a/config/rv64gc/config.vh +++ b/config/rv64gc/config.vh @@ -33,6 +33,9 @@ localparam XLEN = 32'd64; // IEEE 754 compliance localparam IEEE754 = 0; +// Debug Module implemented +localparam logic DEBUG_SUPPORTED = 1'b1; + // RISC-V configuration per specification // Base instruction set (defaults to I if E is not supported) localparam logic E_SUPPORTED = 0; @@ -183,6 +186,9 @@ localparam logic [63:0] SDC_RANGE = 64'h0000007F; localparam logic SPI_SUPPORTED = 1; localparam logic [63:0] SPI_BASE = 64'h10040000; localparam logic [63:0] SPI_RANGE = 64'h00000FFF; +// Debug program buffer support is enabled with DEBUG_SUPPORTED +localparam logic [63:0] PROGBUF_BASE = 64'h00002000; +localparam logic [63:0] PROGBUF_RANGE = 64'h0000000F; // Bus Interface width localparam AHBW = (XLEN); diff --git a/config/rv64i/config.vh b/config/rv64i/config.vh index 94360877fa..af7dad0493 100644 --- a/config/rv64i/config.vh +++ b/config/rv64i/config.vh @@ -33,6 +33,9 @@ localparam XLEN = 32'd64; // IEEE 754 compliance localparam IEEE754 = 0; +// Debug Module implemented +localparam logic DEBUG_SUPPORTED = 1'b0; + // RISC-V configuration per specification // Base instruction set (defaults to I if E is not supported) localparam logic E_SUPPORTED = 0; @@ -183,6 +186,9 @@ localparam logic [63:0] SDC_RANGE = 64'h0000007F; localparam logic SPI_SUPPORTED = 0; localparam logic [63:0] SPI_BASE = 64'h10040000; localparam logic [63:0] SPI_RANGE = 64'h00000FFF; +// Debug program buffer support is enabled with DEBUG_SUPPORTED +localparam logic [63:0] PROGBUF_BASE = 64'h00002000; +localparam logic [63:0] PROGBUF_RANGE = 64'h0000000F; // Bus Interface width localparam AHBW = (XLEN); diff --git a/config/shared/debug.vh b/config/shared/debug.vh new file mode 100755 index 0000000000..24091620b2 --- /dev/null +++ b/config/shared/debug.vh @@ -0,0 +1,360 @@ +/////////////////////////////////////////// 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 + +// DMI Bus Address Width +`define DMI_ADDR_WIDTH 7 + +// Debug Module Debug Bus Register Addresses +// DM Internal registers +`define DATA0 `DMI_ADDR_WIDTH'h04 +`define DATA1 `DMI_ADDR_WIDTH'h05 +`define DATA2 `DMI_ADDR_WIDTH'h06 +`define DATA3 `DMI_ADDR_WIDTH'h07 +`define DATA4 `DMI_ADDR_WIDTH'h08 +`define DATA5 `DMI_ADDR_WIDTH'h09 +`define DATA6 `DMI_ADDR_WIDTH'h0A +`define DATA7 `DMI_ADDR_WIDTH'h0B +`define DATA8 `DMI_ADDR_WIDTH'h0C +`define DATA9 `DMI_ADDR_WIDTH'h0D +`define DATA10 `DMI_ADDR_WIDTH'h0E +`define DATA11 `DMI_ADDR_WIDTH'h0F +`define DMCONTROL `DMI_ADDR_WIDTH'h10 +`define DMSTATUS `DMI_ADDR_WIDTH'h11 +`define HARTINFO `DMI_ADDR_WIDTH'h12 +`define ABSTRACTCS `DMI_ADDR_WIDTH'h16 +`define COMMAND `DMI_ADDR_WIDTH'h17 +`define ABSTRACTAUTO `DMI_ADDR_WIDTH'h18 +`define NEXTDM `DMI_ADDR_WIDTH'h1d +`define PROGBUF0 `DMI_ADDR_WIDTH'h20 +`define PROGBUF1 `DMI_ADDR_WIDTH'h21 +`define PROGBUF2 `DMI_ADDR_WIDTH'h22 +`define PROGBUF3 `DMI_ADDR_WIDTH'h23 +`define PROGBUF4 `DMI_ADDR_WIDTH'h24 +`define PROGBUF5 `DMI_ADDR_WIDTH'h25 +`define PROGBUF6 `DMI_ADDR_WIDTH'h26 +`define PROGBUF7 `DMI_ADDR_WIDTH'h27 +`define PROGBUF8 `DMI_ADDR_WIDTH'h28 +`define PROGBUF9 `DMI_ADDR_WIDTH'h29 +`define PROGBUFA `DMI_ADDR_WIDTH'h2A +`define PROGBUFB `DMI_ADDR_WIDTH'h2B +`define PROGBUFC `DMI_ADDR_WIDTH'h2C +`define PROGBUFD `DMI_ADDR_WIDTH'h2D +`define PROGBUFE `DMI_ADDR_WIDTH'h2E +`define PROGBUFF `DMI_ADDR_WIDTH'h2F +//`define dmcs2 `DMI_ADDR_WIDTH'h32 +`define SBCS `DMI_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 + +// DCSR +`define EBREAKVS 17 +`define EBREAKVU 16 +`define EBREAKM 15 +`define EBREAKS 13 +`define EBREAKU 12 +`define STEPIE 11 +`define STOPCOUNT 10 +`define STOPTIME 9 +`define V 5 +`define MPRVEN 4 +`define STEP 2 +`define PRV 1: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 + +// debug mode cause +`define CAUSE_EBREAK 3'h1 +`define CAUSE_TRIGGER 3'h2 +`define CAUSE_HALTREQ 3'h3 +`define CAUSE_STEP 3'h4 +`define CAUSE_RESETHALTREQ 3'h5 +`define CAUSE_GROUP 3'h6 + +// 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. + +// wallypipelinedcore +`define TRAPM_REGNO 16'hC000 // 1'b P.ZICSR_SUPPORTED (Read Only) +// src/ifu +`define PCM_REGNO 16'hC001 // XLEN P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED +`define INSTRM_REGNO 16'hC002 // 32'b P.ZICSR_SUPPORTED | P.A_SUPPORTED +// ieu/controller +`define MEMRWM_REGNO 16'hC003 // 2'b +`define INSTRVALIDM_REGNO 16'hC004 // 1'b +// ieu/datapath +`define WRITEDATAM_REGNO 16'hC005 // XLEN +// lsu +`define IEUADRM_REGNO 16'hC006 // XLEN +`define READDATAM_REGNO 16'hC007 // LLEN (Read Only) + +// CSR +// privileged/csru +`define FFLAGS_REGNO 16'h0001 +`define FRM_REGNO 16'h0002 +`define FCSR_REGNO 16'h0003 +// privileged/csrm +`define MSTATUS_REGNO 16'h0300 +`define MISA_REGNO 16'h0301 +`define MEDELEG_REGNO 16'h0302 +`define MIDELEG_REGNO 16'h0303 +`define MIE_REGNO 16'h0304 +`define MTVEC_REGNO 16'h0305 +`define MCOUNTEREN_REGNO 16'h0306 + +`define MENVCFG_REGNO 16'h030A + +`define MSTATUSH_REGNO 16'h0310 + +`define MENVCFGH_REGNO 16'h031A + +`define MCOUNTINHIBIT_REGNO 16'h0320 + +`define MSCRATCH_REGNO 16'h0340 +`define MEPC_REGNO 16'h0341 +`define MCAUSE_REGNO 16'h0342 +`define MTVAL_REGNO 16'h0343 +`define MIP_REGNO 16'h0344 + +`define PMPCFG0_REGNO 16'h03A0 +//range +`define PMPCFGF_REGNO 16'h03AF +`define PMPADDR0_REGNO 16'h03B0// P.PA_BITS +//range +`define PMPADDR3F_REGNO 16'h03EF + +`define TSELECT_REGNO 16'h07A0 +`define TDATA1_REGNO 16'h07A1 +`define TDATA2_REGNO 16'h07A2 +`define TDATA3_REGNO 16'h07A3 + +`define DCSR_REGNO 16'h07B0 +`define DPC_REGNO 16'h07B1 + +`define MVENDORID_REGNO 16'h0F11 +`define MARCHID_REGNO 16'h0F12 +`define MIMPID_REGNO 16'h0F13 +`define MHARTID_REGNO 16'h0F14 +`define MCONFIGPTR_REGNO 16'h0F15 +// privileged/csr +`define SIP_REGNO 16'h0144 +`define MIP_REGNO 16'h0344 +// privileged/csrc +`define MHPMEVENTBASE_REGNO 16'h0320 + +`define MHPMCOUNTERBASE_REGNO 16'h0B00 + +`define MHPMCOUNTERHBASE_REGNO 16'h0B80 + +`define HPMCOUNTERBASE_REGNO 16'h0C00 +`define TIME_REGNO 16'h0C01 + +`define HPMCOUNTERHBASE_REGNO 16'h0C80 +`define TIMEH_REGNO 16'h0C81 +// privileged/csrs +`define SSTATUS_REGNO 16'h0100 + +`define SIE_REGNO 16'h0104 +`define STVEC_REGNO 16'h0105 +`define SCOUNTEREN_REGNO 16'h0106 + +`define SENVCFG_REGNO 16'h010A + +`define SSCRATCH_REGNO 16'h0140 +`define SEPC_REGNO 16'h0141 +`define SCAUSE_REGNO 16'h0142 +`define STVAL_REGNO 16'h0143 +`define SIP_REGNO 16'h0144 + +`define STIMECMP_REGNO 16'h014D + +`define STIMECMPH_REGNO 16'h015D + +`define SATP_REGNO 16'h0180 +// privileged/csri +`define SIE_REGNO 16'h0104 +`define SIP_REGNO 16'h0144 +`define MIE_REGNO 16'h0304 +`define MIP_REGNO 16'h0344 + +// src/ieu/datapath +`define X0_REGNO 16'h1000 +`define X1_REGNO 16'h1001 +`define X2_REGNO 16'h1002 +`define X3_REGNO 16'h1003 +`define X4_REGNO 16'h1004 +`define X5_REGNO 16'h1005 +`define X6_REGNO 16'h1006 +`define X7_REGNO 16'h1007 +`define X8_REGNO 16'h1008 +`define X9_REGNO 16'h1009 +`define X10_REGNO 16'h100A +`define X11_REGNO 16'h100B +`define X12_REGNO 16'h100C +`define X13_REGNO 16'h100D +`define X14_REGNO 16'h100E +`define X15_REGNO 16'h100F +`define X16_REGNO 16'h1010 // E_SUPPORTED +`define X17_REGNO 16'h1011 // E_SUPPORTED +`define X18_REGNO 16'h1012 // E_SUPPORTED +`define X19_REGNO 16'h1013 // E_SUPPORTED +`define X20_REGNO 16'h1014 // E_SUPPORTED +`define X21_REGNO 16'h1015 // E_SUPPORTED +`define X22_REGNO 16'h1016 // E_SUPPORTED +`define X23_REGNO 16'h1017 // E_SUPPORTED +`define X24_REGNO 16'h1018 // E_SUPPORTED +`define X25_REGNO 16'h1019 // E_SUPPORTED +`define X26_REGNO 16'h101A // E_SUPPORTED +`define X27_REGNO 16'h101B // E_SUPPORTED +`define X28_REGNO 16'h101C // E_SUPPORTED +`define X29_REGNO 16'h101D // E_SUPPORTED +`define X30_REGNO 16'h101E // E_SUPPORTED +`define X31_REGNO 16'h101F // E_SUPPORTED + +// src/fpu/fpu +`define FP0_REGNO 16'h1020 // F/D_SUPPORTED +`define FP1_REGNO 16'h1021 // F/D_SUPPORTED +`define FP2_REGNO 16'h1022 // F/D_SUPPORTED +`define FP3_REGNO 16'h1023 // F/D_SUPPORTED +`define FP4_REGNO 16'h1024 // F/D_SUPPORTED +`define FP5_REGNO 16'h1025 // F/D_SUPPORTED +`define FP6_REGNO 16'h1026 // F/D_SUPPORTED +`define FP7_REGNO 16'h1027 // F/D_SUPPORTED +`define FP8_REGNO 16'h1028 // F/D_SUPPORTED +`define FP9_REGNO 16'h1029 // F/D_SUPPORTED +`define FP10_REGNO 16'h102A // F/D_SUPPORTED +`define FP11_REGNO 16'h102B // F/D_SUPPORTED +`define FP12_REGNO 16'h102C // F/D_SUPPORTED +`define FP13_REGNO 16'h102D // F/D_SUPPORTED +`define FP14_REGNO 16'h102E // F/D_SUPPORTED +`define FP15_REGNO 16'h102F // F/D_SUPPORTED +`define FP16_REGNO 16'h1030 // F/D_SUPPORTED +`define FP17_REGNO 16'h1031 // F/D_SUPPORTED +`define FP18_REGNO 16'h1032 // F/D_SUPPORTED +`define FP19_REGNO 16'h1033 // F/D_SUPPORTED +`define FP20_REGNO 16'h1034 // F/D_SUPPORTED +`define FP21_REGNO 16'h1035 // F/D_SUPPORTED +`define FP22_REGNO 16'h1036 // F/D_SUPPORTED +`define FP23_REGNO 16'h1037 // F/D_SUPPORTED +`define FP24_REGNO 16'h1038 // F/D_SUPPORTED +`define FP25_REGNO 16'h1039 // F/D_SUPPORTED +`define FP26_REGNO 16'h103A // F/D_SUPPORTED +`define FP27_REGNO 16'h103B // F/D_SUPPORTED +`define FP28_REGNO 16'h103C // F/D_SUPPORTED +`define FP29_REGNO 16'h103D // F/D_SUPPORTED +`define FP30_REGNO 16'h103E // F/D_SUPPORTED +`define FP31_REGNO 16'h103F // F/D_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 diff --git a/config/shared/parameter-defs.vh b/config/shared/parameter-defs.vh index c80b002322..38792770f0 100644 --- a/config/shared/parameter-defs.vh +++ b/config/shared/parameter-defs.vh @@ -85,6 +85,8 @@ localparam cvw_t P = '{ SPI_SUPPORTED : SPI_SUPPORTED, SPI_BASE : SPI_BASE, SPI_RANGE : SPI_RANGE, + PROGBUF_BASE : PROGBUF_BASE, + PROGBUF_RANGE : PROGBUF_RANGE, GPIO_LOOPBACK_TEST : GPIO_LOOPBACK_TEST, SPI_LOOPBACK_TEST : SPI_LOOPBACK_TEST, UART_PRESCALE : UART_PRESCALE , @@ -200,6 +202,7 @@ localparam cvw_t P = '{ DURLEN : DURLEN, DIVb : DIVb, DIVBLEN : DIVBLEN, - INTDIVb : INTDIVb + INTDIVb : INTDIVb, + DEBUG_SUPPORTED : DEBUG_SUPPORTED }; diff --git a/fpga/constraints/constraints-ArtyA7.xdc b/fpga/constraints/constraints-ArtyA7.xdc index 556c75c43e..dfbb098a8e 100644 --- a/fpga/constraints/constraints-ArtyA7.xdc +++ b/fpga/constraints/constraints-ArtyA7.xdc @@ -97,6 +97,12 @@ set_property PACKAGE_PIN D9 [get_ports {south_reset}] set_property IOSTANDARD LVCMOS33 [get_ports {south_reset}] +##### JTAG Port ##### +set_property -dict {PACKAGE_PIN G13 IOSTANDARD LVCMOS33} [get_ports tck] +set_property -dict {PACKAGE_PIN B11 IOSTANDARD LVCMOS33} [get_ports tdo] +set_property -dict {PACKAGE_PIN A11 IOSTANDARD LVCMOS33} [get_ports tms] +set_property -dict {PACKAGE_PIN D12 IOSTANDARD LVCMOS33} [get_ports tdi] + ##### SD Card I/O ##### #***** may have to switch to Pmod JB or JC. diff --git a/fpga/src/fpgaTopArtyA7.sv b/fpga/src/fpgaTopArtyA7.sv index 827ca1438b..0488eb432e 100644 --- a/fpga/src/fpgaTopArtyA7.sv +++ b/fpga/src/fpgaTopArtyA7.sv @@ -73,7 +73,12 @@ module fpgaTop #(parameter logic RVVI_SYNTH_SUPPORTED = 1) output [0:0] ddr3_cke, output [0:0] ddr3_cs_n, output [1:0] ddr3_dm, - output [0:0] ddr3_odt + output [0:0] ddr3_odt, + // JTAG signals + input tck, + input tdi, + input tms, + output tdo ); wire CPUCLK; @@ -507,7 +512,7 @@ module fpgaTop #(parameter logic RVVI_SYNTH_SUPPORTED = 1) `include "parameter-defs.vh" wallypipelinedsoc #(P) - wallypipelinedsoc(.clk(CPUCLK), .reset_ext(bus_struct_reset), .reset(), + wallypipelinedsoc(.clk(CPUCLK), .reset_ext(bus_struct_reset), .reset(), .tck, .tdi, .tms, .tdo, .HRDATAEXT, .HREADYEXT, .HRESPEXT, .HSELEXT, .HSELEXTSDC, .HCLK(HCLKOpen), .HRESETn(HRESETnOpen), .HADDR, .HWDATA, .HWSTRB, .HWRITE, .HSIZE, .HBURST, .HPROT, @@ -1154,12 +1159,12 @@ module fpgaTop #(parameter logic RVVI_SYNTH_SUPPORTED = 1) assign Minstret = fpgaTop.wallypipelinedsoc.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[2]; assign TrapM = fpgaTop.wallypipelinedsoc.core.TrapM; assign PrivilegeModeW = fpgaTop.wallypipelinedsoc.core.priv.priv.privmode.PrivilegeModeW; - assign GPRAddr = fpgaTop.wallypipelinedsoc.core.ieu.dp.regf.a3; - assign GPRWen = fpgaTop.wallypipelinedsoc.core.ieu.dp.regf.we3; - assign GPRValue = fpgaTop.wallypipelinedsoc.core.ieu.dp.regf.wd3; - assign FPRAddr = fpgaTop.wallypipelinedsoc.core.fpu.fpu.fregfile.a4; - assign FPRWen = fpgaTop.wallypipelinedsoc.core.fpu.fpu.fregfile.we4; - assign FPRValue = fpgaTop.wallypipelinedsoc.core.fpu.fpu.fregfile.wd4; + assign GPRAddr = fpgaTop.wallypipelinedsoc.core.ieu.dp.gpr.regf.a3; + assign GPRWen = fpgaTop.wallypipelinedsoc.core.ieu.dp.gpr.regf.we3; + assign GPRValue = fpgaTop.wallypipelinedsoc.core.ieu.dp.gpr.regf.wd3; + assign FPRAddr = fpgaTop.wallypipelinedsoc.core.fpu.fpu.fpr.fregfile.a4; + assign FPRWen = fpgaTop.wallypipelinedsoc.core.fpu.fpu.fpr.fregfile.we4; + assign FPRValue = fpgaTop.wallypipelinedsoc.core.fpu.fpu.fpr.fregfile.wd4; assign CSRArray[0] = fpgaTop.wallypipelinedsoc.core.priv.priv.csr.csrm.MSTATUS_REGW; // 12'h300 assign CSRArray[1] = fpgaTop.wallypipelinedsoc.core.priv.priv.csr.csrm.MSTATUSH_REGW; // 12'h310 diff --git a/openocd.cfg b/openocd.cfg new file mode 100644 index 0000000000..6ac36ae4a1 --- /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-4 + +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 0x1002ac05 + +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/sim/Makefile b/sim/Makefile index 1197dfc2ef..8961cfb841 100644 --- a/sim/Makefile +++ b/sim/Makefile @@ -4,7 +4,7 @@ SIM = ${WALLY}/sim -all: riscoftests memfiles coveragetests deriv +all: riscoftests memfiles coveragetests deriv jtag wally-riscv-arch-test: wallyriscoftests memfiles @@ -141,6 +141,10 @@ coveragetests: deriv: derivgen.pl +jtag: + mkdir -p ../tests/debug/work + svf_convert.py ../tests/debug/src/ ../tests/debug/work/ + benchmarks: $(MAKE) -C ../benchmarks/embench build $(MAKE) -C ../benchmarks/embench size diff --git a/sim/questa/wally.do b/sim/questa/wally.do index 632e2c156a..648ae7c0df 100644 --- a/sim/questa/wally.do +++ b/sim/questa/wally.do @@ -197,7 +197,7 @@ set temp3 [lindex $PlusArgs 3] # "Extra checking for conflicts with always_comb done at vopt time" # because vsim will run vopt -vlog -lint -work ${WKDIR} +incdir+${CONFIG}/${CFG} +incdir+${CONFIG}/deriv/${CFG} +incdir+${CONFIG}/shared ${lockstepvoptstring} ${FCdefineIDV_INCLUDE_TRACE2COV} ${FCdefineINCLUDE_TRACE2COV} ${ImperasPubInc} ${ImperasPrivInc} ${rvviFiles} ${FCdefineCOVER_BASE_RV64I} ${FCdefineCOVER_LEVEL_DV_PR_EXT} ${FCdefineCOVER_RV64I} ${FCdefineCOVER_RV64M} ${FCdefineCOVER_RV64A} ${FCdefineCOVER_RV64F} ${FCdefineCOVER_RV64D} ${FCdefineCOVER_RV64ZICSR} ${FCdefineCOVER_RV64C} ${idvFiles} ${riscvISACOVsrc} ${SRC}/cvw.sv ${TB}/${TESTBENCH}.sv ${TB}/common/*.sv ${SRC}/*/*.sv ${SRC}/*/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*/*/*.sv -suppress 2583 -suppress 7063,2596,13286 +vlog -lint -work ${WKDIR} +incdir+${CONFIG}/${CFG} +incdir+${CONFIG}/deriv/${CFG} +incdir+${CONFIG}/shared ${lockstepvoptstring} ${FCdefineIDV_INCLUDE_TRACE2COV} ${FCdefineINCLUDE_TRACE2COV} ${ImperasPubInc} ${ImperasPrivInc} ${rvviFiles} ${FCdefineCOVER_BASE_RV64I} ${FCdefineCOVER_LEVEL_DV_PR_EXT} ${FCdefineCOVER_RV64I} ${FCdefineCOVER_RV64M} ${FCdefineCOVER_RV64A} ${FCdefineCOVER_RV64F} ${FCdefineCOVER_RV64D} ${FCdefineCOVER_RV64ZICSR} ${FCdefineCOVER_RV64C} ${idvFiles} ${riscvISACOVsrc} ${SRC}/cvw.sv ${TB}/${TESTBENCH}.sv ${TB}/common/*.sv ${TB}/jtag/*.sv ${SRC}/*/*.sv ${SRC}/*/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*/*/*.sv -suppress 2583 -suppress 7063,2596,13286 # start and run simulation # remove +acc flag for faster sim during regressions if there is no need to access internal signals diff --git a/sim/questa/wave.do b/sim/questa/wave.do index 201002d7d3..5942a2d0df 100644 --- a/sim/questa/wave.do +++ b/sim/questa/wave.do @@ -673,6 +673,31 @@ add wave -noupdate -expand -group eth /testbench/rvvi_synth/ethernet/mii_tx_clk add wave -noupdate -expand -group eth /testbench/rvvi_synth/ethernet/mii_txd add wave -noupdate -expand -group eth /testbench/rvvi_synth/ethernet/mii_tx_en add wave -noupdate -expand -group eth /testbench/rvvi_synth/ethernet/mii_tx_er +add wave -noupdate -group DM /testbench/dut/dm/dm/DmActive +add wave -noupdate -group DM /testbench/dut/dm/dm/ReqAddress +add wave -noupdate -group DM /testbench/dut/dm/dm/ReqData +add wave -noupdate -group DM /testbench/dut/dm/dm/ReqOP +add wave -noupdate -group DM /testbench/dut/dm/dm/State +add wave -noupdate -group DM /testbench/dut/dm/dm/AcState +add wave -noupdate -group DM /testbench/dut/dm/dm/AllHaveReset +add wave -noupdate -group DM /testbench/dut/dm/dm/AllRunning +add wave -noupdate -group DM /testbench/dut/dm/dm/AllHalted +add wave -noupdate -group DM /testbench/dut/dm/dm/Busy +add wave -noupdate -group DM /testbench/dut/dm/dm/CmdErr +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/State +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/DebugMode +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/ProgBufTrap +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/DebugCause +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/Step +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/HaltReq +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/ResumeReq +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/ExecProgBuf +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/DCall +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/DRet +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/ForceBreakPoint +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/ebreakM +add wave -noupdate -group DMC /testbench/dut/core/dmc/debugcontrol/DebugStall +add wave -noupdate /testbench/dut/core/priv/priv/csr/csrd/csrd/DPC TreeUpdate [SetDefaultTree] WaveRestoreCursors {{Cursor 4} {640 ns} 1} {{Cursor 4} {2400 ns} 1} {{Cursor 3} {554 ns} 0} {{Cursor 4} {120089 ns} 0} quietly wave cursor active 4 diff --git a/sim/verilator/Makefile b/sim/verilator/Makefile index a7a57244e3..d31772f973 100644 --- a/sim/verilator/Makefile +++ b/sim/verilator/Makefile @@ -10,7 +10,7 @@ OPT= PARAMS?=-DVERILATOR=1 --no-trace-top NONPROF?=--stats VERILATOR_DIR=${WALLY}/sim/verilator -SOURCE=${WALLY}/config/shared/*.vh ${WALLY}/config/${WALLYCONF} ${WALLY}/config/deriv/${WALLYCONF} ${WALLY}/src/cvw.sv ${WALLY}/testbench/*.sv ${WALLY}/testbench/common/*.sv ${WALLY}/src/*/*.sv ${WALLY}/src/*/*/*.sv +SOURCE=${WALLY}/config/shared/*.vh ${WALLY}/config/${WALLYCONF} ${WALLY}/config/deriv/${WALLYCONF} ${WALLY}/src/cvw.sv ${WALLY}/testbench/*.sv ${WALLY}/testbench/common/*.sv ${WALLY}/testbench/jtag/*.sv ${WALLY}/src/*/*.sv ${WALLY}/src/*/*/*.sv EXTRA_ARGS= @@ -25,7 +25,7 @@ TARGET=$(WORKING_DIR)/target # INCLUDE_PATH are pathes that Verilator should search for files it needs INCLUDE_PATH="-I${WALLY}/config/shared" "-I${WALLY}/config/$(WALLYCONF)" "-I${WALLY}/config/deriv/$(WALLYCONF)" # SOURCES are source files -SOURCES=${WALLY}/src/cvw.sv ${WALLY}/testbench/${TESTBENCH}.sv ${WALLY}/testbench/common/*.sv ${WALLY}/src/*/*.sv ${WALLY}/src/*/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*/*/*.sv +SOURCES=${WALLY}/src/cvw.sv ${WALLY}/testbench/${TESTBENCH}.sv ${WALLY}/testbench/common/*.sv ${WALLY}/testbench/jtag/*.sv ${WALLY}/src/*/*.sv ${WALLY}/src/*/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*/*/*.sv # DEPENDENCIES are configuration files and source files, which leads to recompilation of executables DEPENDENCIES=${WALLY}/config/shared/*.vh $(SOURCES) diff --git a/src/cvw.sv b/src/cvw.sv index ed04934848..b81267f9ce 100644 --- a/src/cvw.sv +++ b/src/cvw.sv @@ -37,12 +37,12 @@ package cvw; `include "BranchPredictorType.vh" typedef struct packed { - int XLEN; // Machine width (32 or 64) - logic IEEE754; // IEEE754 NaN handling (0 = use RISC-V NaN propagation instead) - int MISA; // Machine Instruction Set Architecture - int AHBW; // AHB bus width (usually = XLEN) - int RAM_LATENCY; // Latency to stress AHB - logic BURST_EN; // Support AHB Burst Mode + int XLEN; // Machine width (32 or 64) + logic IEEE754; // IEEE754 NaN handling (0 = use RISC-V NaN propagation instead) + int MISA; // Machine Instruction Set Architecture + int AHBW; // AHB bus width (usually = XLEN) + int RAM_LATENCY; // Latency to stress AHB + logic BURST_EN; // Support AHB Burst Mode // RISC-V Features logic ZICSR_SUPPORTED; @@ -74,12 +74,12 @@ typedef struct packed { logic DCACHE_SUPPORTED; logic ICACHE_SUPPORTED; -// TLB configuration. Entries should be a power of 2 + // TLB configuration. Entries should be a power of 2 int ITLB_ENTRIES; int DTLB_ENTRIES; -// Cache configuration. Sizes should be a power of two -// typical configuration 4 ways, 4096 ints per way, 256 bit or more lines + // Cache configuration. Sizes should be a power of two + // typical configuration 4 ways, 4096 ints per way, 256 bit or more lines int DCACHE_NUMWAYS; int DCACHE_WAYSIZEINBYTES; int DCACHE_LINELENINBITS; @@ -88,23 +88,23 @@ typedef struct packed { int ICACHE_LINELENINBITS; int CACHE_SRAMLEN; -// Integer Divider Configuration -// IDIV_BITSPERCYCLE must be 1, 2, or 4 + // Integer Divider Configuration + // IDIV_BITSPERCYCLE must be 1, 2, or 4 int IDIV_BITSPERCYCLE; logic IDIV_ON_FPU; -// Legal number of PMP entries are 0, 16, or 64 + // Legal number of PMP entries are 0, 16, or 64 int PMP_ENTRIES; -// Address space + // Address space logic [63:0] RESET_VECTOR; -// WFI Timeout Wait + // WFI Timeout Wait int WFI_TIMEOUT_BIT; -// Peripheral Addresses -// Peripheral memory space extends from BASE to BASE+RANGE -// Range should be a thermometer code with 0's in the upper bits and 1s in the lower bits + // Peripheral Addresses + // Peripheral memory space extends from BASE to BASE+RANGE + // Range should be a thermometer code with 0's in the upper bits and 1s in the lower bits logic DTIM_SUPPORTED; logic [63:0] DTIM_BASE; logic [63:0] DTIM_RANGE; @@ -140,17 +140,20 @@ typedef struct packed { logic SPI_SUPPORTED; logic [63:0] SPI_BASE; logic [63:0] SPI_RANGE; + // Debug program buffer support is enabled with DEBUG_SUPPORTED + logic [63:0] PROGBUF_BASE; + logic [63:0] PROGBUF_RANGE; -// Test modes + // Test modes -// Tie GPIO outputs back to inputs + // Tie GPIO outputs back to inputs logic GPIO_LOOPBACK_TEST; logic SPI_LOOPBACK_TEST; - -// Hardware configuration + + // Hardware configuration int UART_PRESCALE ; -// Interrupt configuration + // Interrupt configuration int PLIC_NUM_SRC; logic PLIC_NUM_SRC_LT_32; int PLIC_GPIO_ID; @@ -158,31 +161,31 @@ typedef struct packed { int PLIC_SPI_ID; int PLIC_SDC_ID; - logic BPRED_SUPPORTED; - logic [31:0] BPRED_TYPE; - int BPRED_NUM_LHR; - int BPRED_SIZE; - int BTB_SIZE; - int RAS_SIZE; - logic INSTR_CLASS_PRED; // is class predictor enabled + logic BPRED_SUPPORTED; + logic [31:0] BPRED_TYPE; + int BPRED_NUM_LHR; + int BPRED_SIZE; + int BTB_SIZE; + int RAS_SIZE; + logic INSTR_CLASS_PRED; // is class predictor enabled -// FPU division architecture + // FPU division architecture int RADIX; int DIVCOPIES; -// bit manipulation + // bit manipulation logic ZBA_SUPPORTED; logic ZBB_SUPPORTED; logic ZBC_SUPPORTED; logic ZBS_SUPPORTED; -// compressed + // compressed logic ZCA_SUPPORTED; logic ZCB_SUPPORTED; logic ZCD_SUPPORTED; logic ZCF_SUPPORTED; -// Cryptography + // Cryptography logic ZBKB_SUPPORTED; logic ZBKC_SUPPORTED; logic ZBKX_SUPPORTED; @@ -191,101 +194,101 @@ typedef struct packed { logic ZKNH_SUPPORTED; logic ZKN_SUPPORTED; -// Memory synthesis configuration + // Memory synthesis configuration logic USE_SRAM; -// constants defining different privilege modes -// defined in Table 1.1 of the privileged spec - logic [1:0] M_MODE ; - logic [1:0] S_MODE ; - logic [1:0] U_MODE ; - -// Virtual Memory Constants - int VPN_SEGMENT_BITS; - int VPN_BITS; - int PPN_BITS; - int PA_BITS; - int SVMODE_BITS; - int ASID_BASE; - int ASID_BITS; - -// constants to check SATP_MODE against -// defined in Table 4.3 of the privileged spec - logic [3:0] NO_TRANSLATE; - logic [3:0] SV32; - logic [3:0] SV39; - logic [3:0] SV48; - -// macros to define supported modes - logic A_SUPPORTED; - logic B_SUPPORTED; - logic C_SUPPORTED; - logic D_SUPPORTED; - logic E_SUPPORTED; - logic F_SUPPORTED; - logic I_SUPPORTED; - logic M_SUPPORTED; - logic Q_SUPPORTED; - logic S_SUPPORTED; - logic U_SUPPORTED; + // constants defining different privilege modes + // defined in Table 1.1 of the privileged spec + logic [1:0] M_MODE ; + logic [1:0] S_MODE ; + logic [1:0] U_MODE ; + + // Virtual Memory Constants + int VPN_SEGMENT_BITS; + int VPN_BITS; + int PPN_BITS; + int PA_BITS; + int SVMODE_BITS; + int ASID_BASE; + int ASID_BITS; + + // constants to check SATP_MODE against + // defined in Table 4.3 of the privileged spec + logic [3:0] NO_TRANSLATE; + logic [3:0] SV32; + logic [3:0] SV39; + logic [3:0] SV48; + + // macros to define supported modes + logic A_SUPPORTED; + logic B_SUPPORTED; + logic C_SUPPORTED; + logic D_SUPPORTED; + logic E_SUPPORTED; + logic F_SUPPORTED; + logic I_SUPPORTED; + logic M_SUPPORTED; + logic Q_SUPPORTED; + logic S_SUPPORTED; + logic U_SUPPORTED; -// logarithm of XLEN, used for number of index bits to select - int LOG_XLEN; - -// Number of 64 bit PMP Configuration Register entries (or pairs of 32 bit entries) - int PMPCFG_ENTRIES; - -// Floating point constants for Quad, Double, Single, and Half precisions - int Q_LEN; - int Q_NE; - int Q_NF; - int Q_BIAS; - logic [1:0] Q_FMT; - int D_LEN; - int D_NE; - int D_NF; - int D_BIAS; - logic [1:0] D_FMT; - int S_LEN; - int S_NE; - int S_NF; - int S_BIAS; - logic [1:0] S_FMT; - int H_LEN; - int H_NE; - int H_NF; - int H_BIAS; - logic [1:0] H_FMT; - -// Floating point length FLEN and number of exponent (NE) and fraction (NF) bits - int FLEN; - int LOGFLEN; - int NE ; - int NF ; - logic [1:0] FMT ; - int BIAS; - -// Floating point constants needed for FPU paramerterization - int FPSIZES; - int FMTBITS; - int LEN1 ; - int NE1 ; - int NF1 ; - logic [1:0] FMT1 ; - int BIAS1; - int LEN2 ; - int NE2 ; - int NF2 ; - logic [1:0] FMT2 ; - int BIAS2; - -// largest length in IEU/FPU - int CVTLEN; - int LLEN; - int LOGCVTLEN; - int NORMSHIFTSZ; - int LOGNORMSHIFTSZ; - int FMALEN; + // logarithm of XLEN, used for number of index bits to select + int LOG_XLEN; + + // Number of 64 bit PMP Configuration Register entries (or pairs of 32 bit entries) + int PMPCFG_ENTRIES; + + // Floating point constants for Quad, Double, Single, and Half precisions + int Q_LEN; + int Q_NE; + int Q_NF; + int Q_BIAS; + logic [1:0] Q_FMT; + int D_LEN; + int D_NE; + int D_NF; + int D_BIAS; + logic [1:0] D_FMT; + int S_LEN; + int S_NE; + int S_NF; + int S_BIAS; + logic [1:0] S_FMT; + int H_LEN; + int H_NE; + int H_NF; + int H_BIAS; + logic [1:0] H_FMT; + + // Floating point length FLEN and number of exponent (NE) and fraction (NF) bits + int FLEN; + int LOGFLEN; + int NE; + int NF; + logic [1:0] FMT; + int BIAS; + + // Floating point constants needed for FPU paramerterization + int FPSIZES; + int FMTBITS; + int LEN1; + int NE1; + int NF1; + logic [1:0] FMT1; + int BIAS1; + int LEN2; + int NE2; + int NF2; + logic [1:0] FMT2; + int BIAS2; + + // largest length in IEU/FPU + int CVTLEN; + int LLEN; + int LOGCVTLEN; + int NORMSHIFTSZ; + int LOGNORMSHIFTSZ; + int FMALEN; // division constants int LOGR ; @@ -296,6 +299,10 @@ typedef struct packed { int DIVBLEN ; // integer division/remainder constants int INTDIVb ; + +// 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 0000000000..0d9c92dc0d --- /dev/null +++ b/src/debug/dm.sv @@ -0,0 +1,499 @@ +/////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////////////////////// + +// TODO List: +// Ignore wfi instructions in debug mode (overwrite with NOP?) +// mask all interrupts/ignore all traps (except ebreak) in debug mode +// capture CSR read/write failures as convert them to cmderr + + +module dm import cvw::*; #(parameter cvw_t P) ( + input logic clk, + input logic rst, + + // External JTAG signals + input logic tck, + input logic tdi, + input logic tms, + output logic tdo, + + // Platform reset signal + output logic NdmReset, + // Core control signals + input logic ResumeAck, // Signals Hart has been resumed + input logic HaveReset, // Signals Hart has been reset + input logic DebugStall, // Signals core is halted + output logic HaltReq, // Initiates core halt + output logic ResumeReq, // Initiates core resume + output logic HaltOnReset, // Halts core immediately on hart reset + output logic AckHaveReset, // Clears HaveReset status + + // Scan Chain + output logic DebugScanEn, // puts scannable flops into scan mode + input logic DebugScanIn, // (misc) scan chain data in + input logic GPRScanIn, // (GPR) scan chain data in + input logic FPRScanIn, // (FPR) scan chain data in + input logic CSRScanIn, // (CSR) scan chain data in + output logic DebugScanOut, // scan chain data out + output logic MiscSel, // selects general scan chain + output logic GPRSel, // selects GPR scan chain + output logic FPRSel, // selects FPR scan chain + output logic CSRSel, // selects CSR scan chain + output logic [11:0] RegAddr, // address for scanable regfiles (GPR, FPR, CSR) + output logic DebugCapture, // latches values into scan register before scanning out + output logic DebugRegUpdate, // writes values from scan register after scanning in + + // Program Buffer + output logic [P.XLEN-1:0] ProgBufAddr, + output logic ProgBuffScanEn, + output logic ExecProgBuf +); + `include "debug.vh" + + localparam PROGBUF_SIZE = (P.PROGBUF_RANGE+1)/4; + localparam DATA_COUNT = (P.LLEN/32); + localparam AARSIZE_ENC = $clog2(P.LLEN/8); + + // DMI Signals + logic ReqReady; + logic ReqValid; + logic [`DMI_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; + + // JTAG ID for Wally: + // Version [31:28] = 0x1 : 0001 + // PartNumber [27:12] = 0x2A : Wally (00000000_00101010) + // JEDEC number [11:1] = 0x602 : Bank 13 (1100) Open HW Group (0000010) + // [0] = 1 + localparam JTAG_DEVICE_ID = 32'h1002AC05; + + dtm #(`DMI_ADDR_WIDTH, JTAG_DEVICE_ID) dtm (.clk, .rst, .tck, .tdi, .tms, .tdo, + .ReqReady, .ReqValid, .ReqAddress, .ReqData, .ReqOP, .RspReady, + .RspValid, .RspData, .RspOP); + + enum logic [3:0] {INACTIVE, IDLE, ACK, R_DATA, W_DATA, R_DMSTATUS, W_DMCONTROL, R_DMCONTROL, + W_ABSTRACTCS, R_ABSTRACTCS, ABST_COMMAND, R_SYSBUSCS, W_PROGBUF, READ_ZERO, + INVALID, EXEC_PROGBUF} State; + + enum logic [2:0] {AC_IDLE, AC_UPDATE, AC_SCAN, AC_CAPTURE, PROGBUFF_WRITE} AcState, NewAcState; + + logic dmreset; // Sysreset or not DmActive + const logic [P.XLEN-`DMI_ADDR_WIDTH-1:0] UpperReqAddr = 0; // concat with ReqAddr to make linter happy + logic ActivateReq; + logic WriteDMControl; + logic WriteDMControlBusy; + logic AcceptAbstrCmdReqs; + logic ValAccRegReq; + + //// DM register fields + // DMControl + logic AckUnavail; + logic DmActive; // This bit is used to (de)activate the DM. Toggling off-on acts as reset + + // DMStatus + const logic NdmResetPending = 0; + const logic StickyUnavail = 0; + const logic ImpEBreak = 0; + logic AllHaveReset; + logic AnyHaveReset; + logic AllResumeAck; + logic AnyResumeAck; + const logic AllNonExistent = 0; + const logic AnyNonExistent = 0; + const logic AllUnavail = 0; + const logic AnyUnavail = 0; + logic AllRunning; + logic AnyRunning; + logic AllHalted; + logic AnyHalted; + const logic Authenticated = 1; + const logic AuthBusy = 0; + const logic HasResetHaltReq = 1; + const logic ConfStrPtrValid = 0; // Used with SysBusAccess + const logic [3:0] Version = 3; // DM Version + // AbstractCS + const logic [4:0] ProgBufSize = PROGBUF_SIZE[4:0]; + logic Busy; + const logic RelaxedPriv = 1; + logic [2:0] CmdErr; + const logic [3:0] DataCount = DATA_COUNT[3:0]; + + // AbsCmd internal state + logic AcWrite; // Abstract Command write state + logic [P.LLEN:0] ScanReg; // The part of the debug scan chain located within DM + logic [P.LLEN-1:0] ScanNext; // New ScanReg value + logic [P.LLEN-1:0] ARMask; // Masks which bits of the ScanReg get updated + logic [P.LLEN-1:0] PackedDataReg; // Combines DataX msg registers into a single LLEN wide register + logic [P.LLEN-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 MiscRegNo; // Requested RegNo is on the Misc scan chain + logic GPRegNo; // Requested RegNo is a GPR + logic FPRegNo; // Requested RegNo is a FPR + logic CSRegNo; // Requested RegNo is a CSR + logic StoreScanChain; // Store current value of ScanReg into DataX + logic WriteMsgReg; // Write to DataX + logic WriteScanReg; // Insert data from DataX into ScanReg + logic WriteProgBuff; // Insert data from DMI 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 + + + // Core control signals + assign AllHaveReset = HaveReset; + assign AnyHaveReset = HaveReset; + assign AnyHalted = DebugStall; + assign AllHalted = DebugStall; + assign AnyRunning = ~DebugStall; + assign AllRunning = ~DebugStall; + // I believe resumeack is used to determine when a resume is requested but never completes + // It's pretty worthless in this implementation (complain to the riscv debug working group) + assign AllResumeAck = ResumeAck; + assign AnyResumeAck = ResumeAck; + + assign dmreset = rst | ~DmActive; + assign ActivateReq = (State == INACTIVE) & ReqValid & (ReqAddress == `DMCONTROL) & (ReqOP == `OP_WRITE); + // Transfer set, AARSIZE (encoded) isn't bigger than XLEN, RegNo is valid, not writing to readonly RegNo + assign ValAccRegReq = (AARSIZE_ENC[2:0] >= ReqData[`AARSIZE]) & ~InvalidRegNo & ~(ReqData[`AARWRITE] & RegReadOnly); + assign AcceptAbstrCmdReqs = ~|CmdErr & ~Busy & DebugStall; // No cmderr, not busy (another abstrcmd isn't running), and core is halted + + // DMControl + // 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 + assign WriteDMControlBusy = Busy & (ReqData[`HALTREQ] | ReqData[`RESUMEREQ] | ReqData[`ACKHAVERESET] | ReqData[`SETRESETHALTREQ] | ReqData[`CLRRESETHALTREQ]); + assign WriteDMControl = (State == W_DMCONTROL) & ~WriteDMControlBusy; + + flopenr #(1) DmActiveReg (.clk, .reset(rst), .en(ActivateReq | WriteDMControl), .d(ReqData[`DMACTIVE]), .q(DmActive)); + flopenr #(3) DmControlReg (.clk, .reset(dmreset), .en(WriteDMControl), + .d({ReqData[`HALTREQ], ReqData[`ACKUNAVAIL], ReqData[`NDMRESET]}), + .q({HaltReq, AckUnavail, NdmReset})); + // AckHaveReset automatically deasserts after one cycle + flopr #(1) AckHaveResetReg (.clk, .reset(rst), .d(WriteDMControl & ReqData[`ACKHAVERESET]), .q(AckHaveReset)); + // ResumeReq automatically deasserts after one cycle + flopr #(1) ResumeReqReg (.clk, .reset(rst), .d(WriteDMControl & ~ReqData[`HALTREQ] & ReqData[`RESUMEREQ]), .q(ResumeReq)); + + always_ff @(posedge clk) begin + if (dmreset) + HaltOnReset <= 0; + else if (WriteDMControl) + if (ReqData[`SETRESETHALTREQ]) + HaltOnReset <= 1; + else if (ReqData[`CLRRESETHALTREQ]) + HaltOnReset <= 0; + end + + //// Basic Ready/Valid handshake between DM and DTM: + // DM idles with ReqReady asserted + // When a value is written to DMI register, ReqValid is asserted in DTM + // DTM waits for RspValid + // DM processes request. Moves to ACK, asserts RspValid, deasserts ReqReady + // DM waits for ReqValid to deassert + // DTM stores response to be captured into shift register on next scan + + // DM/DTM might lock up in the incredibly unlikely case that the hardware debugger + // can complete an entire scan faster than the DM can complete a request + assign RspValid = (State == ACK); + assign ReqReady = (State != ACK); + + // BOZO: review DTM/DM interface + always_ff @(posedge clk) begin : DM_state + if (rst) begin + State <= INACTIVE; + NewAcState <= AC_IDLE; + end else begin + case (State) + default : begin // INACTIVE + if (ReqValid) + State <= ACK; + end + + ACK : begin + NewAcState <= AC_IDLE; + 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.LLEN >= 64) ? W_DATA : INVALID; + {`OP_READ,`DATA1} : State <= (P.LLEN >= 64) ? R_DATA : INVALID; + [{`OP_WRITE,`DATA2}:{`OP_WRITE,`DATA3}] : State <= (P.LLEN >= 128) ? W_DATA : INVALID; + [{`OP_READ,`DATA2}:{`OP_READ,`DATA3}] : State <= (P.LLEN >= 128) ? R_DATA : INVALID; + {`OP_WRITE,`DMCONTROL} : State <= W_DMCONTROL; + {`OP_READ,`DMCONTROL} : State <= R_DMCONTROL; + {`OP_READ,`DMSTATUS} : State <= R_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; + [{`OP_WRITE,`PROGBUF0}:{`OP_WRITE,`PROGBUF3}] : State <= W_PROGBUF; // TODO: update decode range dynamically using PROGBUF_RANGE + [{`OP_READ,`PROGBUF0}:{`OP_READ,`PROGBUFF}], + {2'b??,`HARTINFO}, + {2'b??,`ABSTRACTAUTO}, + {2'b??,`NEXTDM} : State <= READ_ZERO; + default : State <= INVALID; + endcase + end + + R_DMCONTROL, + R_DMSTATUS, + R_ABSTRACTCS, + R_SYSBUSCS, + READ_ZERO, + INVALID, + R_DATA, + W_DATA, + W_DMCONTROL, + W_ABSTRACTCS : State <= ACK; + + ABST_COMMAND : begin + State <= ACK; + if (AcceptAbstrCmdReqs) begin + if (ReqData[`CMDTYPE] == `ACCESS_REGISTER) begin + if (~ReqData[`TRANSFER]) + State <= ReqData[`POSTEXEC] ? EXEC_PROGBUF : ACK; + else if (ValAccRegReq) begin + AcWrite <= ReqData[`AARWRITE]; + NewAcState <= ~ReqData[`AARWRITE] ? AC_CAPTURE : AC_SCAN; + State <= ReqData[`POSTEXEC] ? EXEC_PROGBUF : ACK; + end + end + end + end + + W_PROGBUF : begin + if (~Busy) begin + NewAcState <= PROGBUFF_WRITE; + ProgBufAddr <= {UpperReqAddr, ReqAddress}; + end + State <= ACK; + end + + EXEC_PROGBUF : begin + NewAcState <= AC_IDLE; + if (~Busy) + State <= ACK; + end + endcase + end + end + + // DMI response + always_ff @(posedge clk) begin : DMI_response + // RspData + case(State) + R_DATA : begin + case (ReqAddress) + `DATA0 : RspData <= Data0; + `DATA1 : RspData <= Data1; + `DATA2 : RspData <= Data2; + `DATA3 : RspData <= Data3; + default : RspData <= '0; + endcase + end + R_DMCONTROL : RspData <= {2'b0, 1'b0, 2'b0, 1'b0, 10'b0, 10'b0, 4'b0, NdmReset, DmActive}; + R_DMSTATUS : begin + RspData <= {7'b0, NdmResetPending, StickyUnavail, ImpEBreak, 2'b0, + AllHaveReset, AnyHaveReset, AllResumeAck, AnyResumeAck, AllNonExistent, + AnyNonExistent, AllUnavail, AnyUnavail, AllRunning, AnyRunning, AllHalted, + AnyHalted, Authenticated, AuthBusy, HasResetHaltReq, ConfStrPtrValid, Version}; + end + R_ABSTRACTCS : RspData <= {3'b0, ProgBufSize, 11'b0, Busy, RelaxedPriv, CmdErr, 4'b0, DataCount}; + R_SYSBUSCS : RspData <= 32'h20000000; // SBVersion = 1 + READ_ZERO : RspData <= '0; + default: RspData <= '0; + endcase + + // RspOP + case (State) + INVALID : RspOP <= `OP_SUCCESS; // openocd cannot recover from `OP_FAILED; + default : RspOP <= `OP_SUCCESS; + endcase + end + + // Command Error + always_ff @(posedge clk) begin : command_Error + if (dmreset) + CmdErr <= `CMDERR_NONE; + else + case (State) + R_DATA, + W_DATA, + W_PROGBUF : if (~|CmdErr & Busy) CmdErr <= `CMDERR_BUSY; + W_DMCONTROL : if (~|CmdErr & Busy & WriteDMControlBusy) CmdErr <= `CMDERR_BUSY; + W_ABSTRACTCS : if (~|CmdErr & Busy) CmdErr <= `CMDERR_BUSY; + else if (|ReqData[`CMDERR]) CmdErr <= `CMDERR_NONE; + ABST_COMMAND : begin + if (~DebugStall) CmdErr <= `CMDERR_HALTRESUME; + else if ((ReqData[`CMDTYPE] == `ACCESS_REGISTER) & ReqData[`TRANSFER]) // Access register + if (ReqData[`AARSIZE] > AARSIZE_ENC[2:0]) CmdErr <= `CMDERR_BUS; // If AARSIZE (encoded) is greater than P.LLEN + else if (InvalidRegNo) CmdErr <= `CMDERR_EXCEPTION; // If InvalidRegNo + else if (ReqData[`AARWRITE] & RegReadOnly) CmdErr <= `CMDERR_NOT_SUPPORTED; // If writing to a read only register + else if ((ReqData[`CMDTYPE] != `ACCESS_REGISTER)) CmdErr <= `CMDERR_NOT_SUPPORTED; + end + default : CmdErr <= CmdErr; + endcase + 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 : abstrc_cmd_engine + if (rst) + AcState <= AC_IDLE; + else + case (AcState) + AC_IDLE : begin + Cycle <= 0; + AcState <= NewAcState; + end + + AC_CAPTURE : begin + AcState <= AC_SCAN; + end + + AC_SCAN : begin + if (~MiscRegNo & AcWrite & (Cycle == ScanChainLen)) // Writes to CSR/GPR/FPR are shifted in len(CSR/GPR) or len(FPR) cycles + AcState <= AC_UPDATE; + else if (~MiscRegNo & ~AcWrite & (Cycle == P.LLEN[9:0])) // Reads from CSR/GPR/FPR are shifted in len(ScanReg) cycles + AcState <= AC_IDLE; + else if (MiscRegNo & (Cycle == ScanChainLen)) // Misc scanchain must be scanned completely + AcState <= AC_IDLE; + else + Cycle <= Cycle + 1; + end + + AC_UPDATE : begin + AcState <= AC_IDLE; + end + + PROGBUFF_WRITE : begin + if (Cycle == 32) + AcState <= AC_IDLE; + else + Cycle <= Cycle + 1; + end + + default : begin + AcState <= AC_IDLE; + Cycle <= Cycle; + end + endcase + end + + assign Busy = ~(AcState == AC_IDLE); + assign ExecProgBuf = (State == EXEC_PROGBUF) & ~Busy; + + // Program Buffer + assign ProgBuffScanEn = (AcState == PROGBUFF_WRITE); + + // Scan Chain + assign DebugScanOut = ScanReg[0]; + assign DebugScanEn = (AcState == AC_SCAN); + assign DebugCapture = (AcState == AC_CAPTURE); + assign DebugRegUpdate = (AcState == AC_UPDATE); + + assign MiscRegNo = ~(CSRegNo | GPRegNo | FPRegNo); + assign MiscSel = MiscRegNo & (AcState != AC_IDLE); + assign CSRSel = CSRegNo & (AcState != AC_IDLE); + assign GPRSel = GPRegNo & (AcState != AC_IDLE); + assign FPRSel = FPRegNo & (AcState != AC_IDLE); + + + always_comb begin + case ({CSRSel, GPRSel, FPRSel}) + 3'b100 : ScanReg[P.LLEN] = CSRScanIn; + 3'b010 : ScanReg[P.LLEN] = GPRScanIn; + 3'b001 : ScanReg[P.LLEN] = FPRScanIn; + default : ScanReg[P.LLEN] = DebugScanIn; + endcase + end + + if (P.LLEN == 32) + assign PackedDataReg = Data0; + else if (P.LLEN == 64) + assign PackedDataReg = {Data1,Data0}; + else if (P.LLEN == 128) + assign PackedDataReg = {Data3,Data2,Data1,Data0}; + + // Load data from DMI into scan chain + assign WriteProgBuff = (AcState == PROGBUFF_WRITE) & (Cycle == 0); + // Load data from message registers into scan chain + assign WriteScanReg = AcWrite & (MiscRegNo & (Cycle == ShiftCount) | ~MiscRegNo & (Cycle == 0)); + genvar i; + for (i=0; i= 64) begin + assign Data1Wr = WriteMsgReg ? ReqData : MaskedScanReg[63:32]; + flopenr #(32) data1reg (.clk, .reset(rst), .en(StoreScanChain | WriteMsgReg & (ReqAddress == `DATA1)), .d(Data1Wr), .q(Data1)); + end else + assign Data1 = '0; + if (P.LLEN == 128) begin + assign Data2Wr = WriteMsgReg ? ReqData : MaskedScanReg[95:64]; + assign Data3Wr = WriteMsgReg ? ReqData : MaskedScanReg[127:96]; + 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 else + assign {Data3,Data2} = '0; + + rad #(P) regnodecode(.AarSize(ReqData[`AARSIZE]),.Regno(ReqData[`REGNO]),.CSRegNo,.GPRegNo,.FPRegNo,.ScanChainLen,.ShiftCount,.InvalidRegNo,.RegReadOnly,.RegAddr,.ARMask); + +endmodule diff --git a/src/debug/dmc.sv b/src/debug/dmc.sv new file mode 100644 index 0000000000..e26976e2c8 --- /dev/null +++ b/src/debug/dmc.sv @@ -0,0 +1,127 @@ +/////////////////////////////////////////// +// dmc.sv +// +// Written: matthew.n.otto@okstate.edu 10 May 2024 +// Modified: +// +// Purpose: Controls pipeline during Debug Mode +// +// 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 dmc ( + input logic clk, reset, + input logic StallF, StallD, StallE, StallM, StallW, + input logic FlushD, FlushE, FlushM, FlushW, + input logic Step, + input logic ebreakM, // ebreak instruction + input logic ebreakEn, // DCSR: enter debug mode on ebreak + input logic HaltReq, // Initiates core halt + input logic ResumeReq, // Initiates core resume + input logic HaltOnReset, // Halts core immediately on hart reset + input logic AckHaveReset, // Clears HaveReset status + input logic ExecProgBuf, // Updates PC to progbuf and resumes core + + input logic ProgBufTrap, // Trap while executing progbuf + output logic DebugMode, // Sets state in DM and controls masking of interrupts + output logic [2:0] DebugCause, // Reason Hart entered debug mode + output logic ResumeAck, // Signals Hart has been resumed + output logic HaveReset, // Signals Hart has been reset + output logic DebugStall, // Stall signal goes to hazard unit + + output logic DCall, // Store PCNextF in DPC when entering Debug Mode + output logic DRet, // Updates PCNextF with the current value of DPC, Flushes pipe + output logic ForceBreakPoint // Causes artificial ebreak that puts core in debug mode +); + `include "debug.vh" + + logic StepF, StepD, StepE, StepM, StepW; + + enum logic [1:0] {RUNNING, EXECPROGBUF, HALTED, STEP} State; + + always_ff @(posedge clk) begin + if (reset) + HaveReset <= 1; + else if (AckHaveReset) + HaveReset <= 0; + end + + assign ForceBreakPoint = (State == RUNNING) & HaltReq; + + assign DebugMode = (State != RUNNING); + assign DebugStall = (State == HALTED); + + assign DCall = ((State == RUNNING) | (State == EXECPROGBUF)) & ((ebreakM & ebreakEn) | ForceBreakPoint) | StepW; + assign DRet = (State == HALTED) & (ResumeReq | ExecProgBuf); + + always_ff @(posedge clk) begin + if (reset) begin + State <= HaltOnReset ? HALTED : RUNNING; + DebugCause <= HaltOnReset ? `CAUSE_RESETHALTREQ : 0; + end else begin + case (State) + RUNNING : begin + if (HaltReq) begin + State <= HALTED; + DebugCause <= `CAUSE_HALTREQ; + end else if (ebreakM & ebreakEn) begin + State <= HALTED; + DebugCause <= `CAUSE_EBREAK; + end + end + + EXECPROGBUF : begin + if (ProgBufTrap) begin + State <= HALTED; + end + end + + HALTED : begin + if (ResumeReq) begin + if (Step) begin + State <= STEP; + end else begin + State <= RUNNING; + ResumeAck <= 1; + end + end else if (ExecProgBuf) begin + State <= EXECPROGBUF; + ResumeAck <= 1; + end + end + + STEP : begin + if (StepW) + State <= HALTED; + end + default: ; // empty defualt case to make the linter happy + endcase + end + end + + flopenr #(1) StepFReg (.clk, .reset, .en(~StallF), .d((State == HALTED) & ResumeReq & Step), .q(StepF)); + flopenrc #(1) StepDReg (.clk, .reset, .clear(FlushD), .en(~StallD), .d(StepF), .q(StepD)); + flopenrc #(1) StepEReg (.clk, .reset, .clear(FlushE), .en(~StallE), .d(StepD), .q(StepE)); + flopenrc #(1) StepMReg (.clk, .reset, .clear(FlushM), .en(~StallM), .d(StepE), .q(StepM)); + flopenrc #(1) StepWReg (.clk, .reset, .clear(FlushW), .en(~StallM), .d(StepM), .q(StepW)); + +endmodule diff --git a/src/debug/dtm.sv b/src/debug/dtm.sv new file mode 100644 index 0000000000..47fba4fd13 --- /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, rst, + // 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; // TODO: reset DM (but not hart) + 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 @(posedge clk) begin + if (rst | ~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 (rst | ~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/idreg.sv b/src/debug/idreg.sv new file mode 100755 index 0000000000..1fad8139f8 --- /dev/null +++ b/src/debug/idreg.sv @@ -0,0 +1,46 @@ +/////////////////////////////////////////// +// 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 + if (i == 0) + flop #(1) idregi (.clk(clockDR), .d(captureDR ? 1'b1 : ShiftReg[i+1]), .q(ShiftReg[i])); + else + flop #(1) idregi (.clk(clockDR), .d(captureDR ? DEVICE_ID[i] : ShiftReg[i+1]), .q(ShiftReg[i])); + end +endmodule diff --git a/src/debug/ir.sv b/src/debug/ir.sv new file mode 100644 index 0000000000..60fd2365fc --- /dev/null +++ b/src/debug/ir.sv @@ -0,0 +1,78 @@ +/////////////////////////////////////////// +// 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 + flop #(1) shift_regmsb (.clk(clockIR), .d(shift_reg[1] | captureIR), .q(shift_reg[0])); + genvar i; + for (i = INST_REG_WIDTH; i > 1; i = i - 1) + flop #(1) shift_regi (.clk(clockIR), .d(shift_reg[i] & ~captureIR), .q(shift_reg[i-1])); + + // 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 + /* verilator lint_off SYNCASYNCNET */ + always @(posedge updateIR or negedge resetn) begin + if (~resetn) + {BypassInstr, IDCodeInstr, DtmcsIntrs, DmiInstr} <= 4'b0100; + else + {BypassInstr, IDCodeInstr, DtmcsIntrs, DmiInstr} <= decoded; + end + /* verilator lint_on SYNCASYNCNET */ +endmodule diff --git a/src/debug/jtag.sv b/src/debug/jtag.sv new file mode 100644 index 0000000000..6ea994e17c --- /dev/null +++ b/src/debug/jtag.sv @@ -0,0 +1,125 @@ +/////////////////////////////////////////// +// 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 + + flop #(32) dtmcsreg (.clk(UpdateDtmcs), .d(DtmcsShiftReg[31:0]), .q(DtmcsIn)); + flop #(34+ADDR_WIDTH) dmireg (.clk(UpdateDmi), .d(DmiShiftReg[34+ADDR_WIDTH-1:0]), .q(DmiIn)); + + assign DtmcsShiftReg[32] = tdi_dr; + assign tdo_dtmcs = DtmcsShiftReg[0]; + for (i = 0; i < 32; i = i + 1) + flop #(1) dtmcsshiftreg (.clk(clockDR), .d(captureDR ? DtmcsOut[i] : DtmcsShiftReg[i+1]), .q(DtmcsShiftReg[i])); + + assign DmiShiftReg[34+ADDR_WIDTH] = tdi_dr; + assign tdo_dmi = DmiShiftReg[0]; + for (i = 0; i < 34+ADDR_WIDTH; i = i + 1) + flop #(1) dmishiftreg (.clk(clockDR), .d(captureDR ? DmiOut[i] : DmiShiftReg[i+1]), .q(DmiShiftReg[i])); + + // jtag id register + idreg #(DEVICE_ID) id (.tdi(tdi_dr), .clockDR, .captureDR, .tdo(tdo_idcode)); + + // bypass register + flop #(1) bypassreg (.clk(clockDR), .d(tdi_dr & shiftDR), .q(tdo_bypass)); + +endmodule diff --git a/src/debug/notes.txt b/src/debug/notes.txt new file mode 100755 index 0000000000..c191927d2a --- /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 0000000000..ecbd0bd0ea --- /dev/null +++ b/src/debug/rad.sv @@ -0,0 +1,188 @@ +/////////////////////////////////////////// +// rad.sv +// +// Written: matthew.n.otto@okstate.edu +// Created: 28 April 2024 +// +// Purpose: Decodes the register address and generates various control signals +// 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 GPRegNo, + output logic FPRegNo, + output logic CSRegNo, + output logic [9:0] ScanChainLen, + output logic [9:0] ShiftCount, + output logic InvalidRegNo, + output logic RegReadOnly, + output logic [11:0] RegAddr, + output logic [P.LLEN-1:0] ARMask +); + `include "debug.vh" + + 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.LLEN + + TRAPMLEN + PCMLEN + INSTRMLEN + + MEMRWMLEN + INSTRVALIDMLEN + WRITEDATAMLEN + + IEUADRMLEN + READDATAMLEN; + + localparam TRAPM_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.LLEN-1:0] Mask; + + assign RegAddr = Regno[11:0]; + assign ScanChainLen = (CSRegNo | GPRegNo) ? P.XLEN[9:0] : FPRegNo ? P.FLEN[9:0] : SCANCHAINLEN[9:0]; + + // Register decoder + always_comb begin + InvalidRegNo = 0; + RegReadOnly = 0; + CSRegNo = 0; + GPRegNo = 0; + FPRegNo = 0; + case (Regno) inside + [`DCSR_REGNO:`DPC_REGNO] : begin + ShiftCount = P.LLEN[9:0] - 1; + CSRegNo = 1; + end + + [`FFLAGS_REGNO:`FCSR_REGNO], + [`MSTATUS_REGNO:`MCOUNTEREN_REGNO], // InvalidRegNo = ~P.ZICSR_SUPPORTED; + `MENVCFG_REGNO, + `MSTATUSH_REGNO, + `MENVCFGH_REGNO, + `MCOUNTINHIBIT_REGNO, + [`MSCRATCH_REGNO:`MIP_REGNO], + [`PMPCFG0_REGNO:`PMPADDR3F_REGNO], // TODO This is variable len (P.PA_BITS)? + [`TSELECT_REGNO:`TDATA3_REGNO], + `SIP_REGNO, + `MIP_REGNO, + `MHPMEVENTBASE_REGNO, + `MHPMCOUNTERBASE_REGNO, + `MHPMCOUNTERHBASE_REGNO, + [`HPMCOUNTERBASE_REGNO:`TIME_REGNO], + [`HPMCOUNTERHBASE_REGNO:`TIMEH_REGNO], + `SSTATUS_REGNO, + [`SIE_REGNO:`SCOUNTEREN_REGNO], + `SENVCFG_REGNO, + [`SSCRATCH_REGNO:`SIP_REGNO], + `STIMECMP_REGNO, + `STIMECMPH_REGNO, + `SATP_REGNO, + `SIE_REGNO, + `SIP_REGNO, + `MIE_REGNO, + `MIP_REGNO : begin + ShiftCount = P.LLEN[9:0] - 1'b1; + CSRegNo = 1; + // Comment out because gives error on openocd + // This value cause the csrs to all go read-only + // which openocd doesnt like + //RegReadOnly = 1; + end + + [`HPMCOUNTERBASE_REGNO:`TIME_REGNO], + [`HPMCOUNTERHBASE_REGNO:`TIMEH_REGNO], + [`MVENDORID_REGNO:`MCONFIGPTR_REGNO] : begin + ShiftCount = P.LLEN[9:0] - 1; + CSRegNo = 1; + RegReadOnly = 1; + end + + [`X0_REGNO:`X15_REGNO] : begin + ShiftCount = P.LLEN[9:0] - 1; + GPRegNo = 1; + end + [`X16_REGNO:`X31_REGNO] : begin + ShiftCount = P.LLEN[9:0] - 1; + InvalidRegNo = P.E_SUPPORTED; + GPRegNo = 1; + end + [`FP0_REGNO:`FP31_REGNO] : begin + ShiftCount = P.LLEN[9:0] - 1; + InvalidRegNo = ~(P.F_SUPPORTED | P.D_SUPPORTED | P.Q_SUPPORTED); + FPRegNo = 1; + end + `TRAPM_REGNO : begin + ShiftCount = SCANCHAINLEN[9:0] - TRAPM_IDX[9:0]; + InvalidRegNo = ~P.ZICSR_SUPPORTED; + RegReadOnly = 1; + end + `PCM_REGNO : begin + ShiftCount = SCANCHAINLEN[9:0] - PCM_IDX[9:0]; + InvalidRegNo = ~(P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED); + end + `INSTRM_REGNO : begin + ShiftCount = SCANCHAINLEN[9:0] - INSTRM_IDX[9:0]; + InvalidRegNo = ~(P.ZICSR_SUPPORTED | P.A_SUPPORTED); + end + `MEMRWM_REGNO : ShiftCount = SCANCHAINLEN[9:0] - MEMRWM_IDX[9:0]; + `INSTRVALIDM_REGNO : ShiftCount = SCANCHAINLEN[9:0] - INSTRVALIDM_IDX[9:0]; + `WRITEDATAM_REGNO : ShiftCount = SCANCHAINLEN[9:0] - WRITEDATAM_IDX[9:0]; + `IEUADRM_REGNO : ShiftCount = SCANCHAINLEN[9:0] - IEUADRM_IDX[9:0]; + `READDATAM_REGNO : begin + ShiftCount = SCANCHAINLEN[9:0] - READDATAM_IDX[9:0]; + RegReadOnly = 1; + end + default : begin + ShiftCount = 0; + InvalidRegNo = 1; + end + endcase + end + + // Mask calculator + always_comb begin + case (Regno) inside + `TRAPM_REGNO : Mask = {{P.LLEN-1{1'b0}}, 1'b1}; + `INSTRM_REGNO : Mask = {{P.LLEN-32{1'b0}}, {32{1'b1}}}; + `MEMRWM_REGNO : Mask = {{P.LLEN-2{1'b0}}, 2'b11}; + `INSTRVALIDM_REGNO : Mask = {{P.LLEN-1{1'b0}}, 1'b1}; + `READDATAM_REGNO : Mask = {P.LLEN{1'b1}}; + [`FP0_REGNO:`FP31_REGNO] : Mask = {{P.LLEN-P.FLEN{1'b0}}, {P.FLEN{1'b1}}}; + default : Mask = {{P.LLEN-P.XLEN{1'b0}}, {P.XLEN{1'b1}}}; + endcase + end + + assign ARMask[31:0] = Mask[31:0]; + if (P.LLEN >= 64) + assign ARMask[63:32] = (AarSize == 3'b011 | AarSize == 3'b100) ? Mask[63:32] : '0; + if (P.LLEN == 128) + assign ARMask[127:64] = (AarSize == 3'b100) ? Mask[127:64] : '0; + +endmodule diff --git a/src/debug/tap.sv b/src/debug/tap.sv new file mode 100644 index 0000000000..9e09b873e9 --- /dev/null +++ b/src/debug/tap.sv @@ -0,0 +1,100 @@ +/////////////////////////////////////////// +// 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 +); + + logic tckn; + + 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; + default : State <= TLReset; + endcase + end + + assign tckn = ~tck; + + flop #(1) resetnreg (.clk(tckn), .d(~(State == TLReset)), .q(resetn)); + flop #(1) tdo_enreg (.clk(tckn), .d(State == ShiftIR | State == ShiftDR), .q(tdo_en)); + flop #(1) captureIRreg (.clk(tckn), .d(State == CaptureIR), .q(captureIR)); + flop #(1) updateIRreg (.clk(tckn), .d(State == UpdateIR), .q(updateIR)); + flop #(1) shiftDRreg (.clk(tckn), .d(State == ShiftDR), .q(shiftDR)); + flop #(1) captureDRreg (.clk(tckn), .d(State == CaptureDR), .q(captureDR)); + flop #(1) updateDRreg (.clk(tckn), .d(State == UpdateDR), .q(updateDR)); + + 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/fpu/fpu.sv b/src/fpu/fpu.sv index 7619b92af9..354822231e 100755 --- a/src/fpu/fpu.sv +++ b/src/fpu/fpu.sv @@ -61,7 +61,15 @@ module fpu import cvw::*; #(parameter cvw_t P) ( input logic [P.FLEN-1:0] ReadDataW, // Read data (from LSU) output logic [P.XLEN-1:0] FCvtIntResW, // convert result to to be written to integer register (to IEU) output logic FCvtIntW, // select FCvtIntRes (to IEU) - output logic [P.XLEN-1:0] FIntDivResultW // Result from integer division (to IEU) + output logic [P.XLEN-1:0] FIntDivResultW, // Result from integer division (to IEU) + // Debug scan chain + input logic DebugSel, + input logic [4:0] DebugRegAddr, + input logic DebugCapture, + input logic DebugRegUpdate, + input logic DebugScanEn, + input logic DebugScanIn, + output logic DebugScanOut ); // RISC-V FPU specifics: @@ -169,6 +177,13 @@ module fpu import cvw::*; #(parameter cvw_t P) ( logic [P.FLEN-1:0] ZfaResE; // Result of Zfa fli or fround instruction logic FRoundNVE, FRoundNXE; // Zfa fround invalid and inexact flags + // Debug signals + logic FRegWriteWM; + logic [4:0] RA1; + logic [4:0] WA1; + logic [P.FLEN-1:0] FResultWM; + logic [P.FLEN-1:0] DebugFPRWriteD; + ////////////////////////////////////////////////////////////////////////////////////////// // Decode Stage: fctrl decoder, read register file ////////////////////////////////////////////////////////////////////////////////////////// @@ -182,13 +197,27 @@ module fpu import cvw::*; #(parameter cvw_t P) ( .IllegalFPUInstrD, .XEnD, .YEnD, .ZEnD, .XEnE, .YEnE, .ZEnE, .FResSelE, .FResSelM, .FResSelW, .FPUActiveE, .PostProcSelE, .PostProcSelM, .FCvtIntW, .Adr1D, .Adr2D, .Adr3D, .Adr1E, .Adr2E, .Adr3E); - + // FP register file - fregfile #(P.FLEN) fregfile (.clk, .reset, .we4(FRegWriteW), - .a1(InstrD[19:15]), .a2(InstrD[24:20]), .a3(InstrD[31:27]), - .a4(RdW), .wd4(FResultW), - .rd1(FRD1D), .rd2(FRD2D), .rd3(FRD3D)); - + // Access FPRs from Debug Module + if (P.DEBUG_SUPPORTED) begin : fpr + fregfile #(P.FLEN) fregfile (.clk, .reset, .we4(FRegWriteWM), + .a1(RA1), .a2(InstrD[24:20]), .a3(InstrD[31:27]), + .a4(WA1), .wd4(FResultWM), + .rd1(FRD1D), .rd2(FRD2D), .rd3(FRD3D)); + assign FRegWriteWM = DebugSel ? DebugRegUpdate : FRegWriteW; + assign RA1 = DebugSel ? DebugRegAddr : InstrD[19:15]; + assign WA1 = DebugSel ? DebugRegAddr : RdW; + assign FResultWM = DebugSel ? DebugFPRWriteD : FResultW; + flopenrs #(P.FLEN) FPScanReg(.clk, .reset, .en(DebugCapture), .d(FRD1D), .q(DebugFPRWriteD), .scan(DebugScanEn), .scanin(DebugScanIn), .scanout(DebugScanOut)); + end else begin + assign DebugScanOut = '0; + fregfile #(P.FLEN) fregfile (.clk, .reset, .we4(FRegWriteW), + .a1(InstrD[19:15]), .a2(InstrD[24:20]), .a3(InstrD[31:27]), + .a4(RdW), .wd4(FResultW), + .rd1(FRD1D), .rd2(FRD2D), .rd3(FRD3D)); + end + // D/E pipeline registers flopenrc #(P.FLEN) DEReg1(clk, reset, FlushE, ~StallE, FRD1D, FRD1E); flopenrc #(P.FLEN) DEReg2(clk, reset, FlushE, ~StallE, FRD2D, FRD2E); diff --git a/src/fpu/fregfile.sv b/src/fpu/fregfile.sv index 40933de175..b2077829ca 100644 --- a/src/fpu/fregfile.sv +++ b/src/fpu/fregfile.sv @@ -34,21 +34,21 @@ module fregfile #(parameter FLEN) ( input logic [FLEN-1:0] wd4, // write data output logic [FLEN-1:0] rd1, rd2, rd3 // read data ); - - logic [FLEN-1:0] rf[31:0]; - integer i; - - // three ported register file - // read three ports combinationally (A1/RD1, A2/RD2, A3/RD3) - // write fourth port on rising edge of clock (A4/WD4/WE4) - // write occurs on falling edge of clock - - always_ff @(negedge clk) // or posedge reset) - if (reset) for(i=0; i<32; i++) rf[i] <= '0; - else if (we4) rf[a4] <= wd4; - - assign rd1 = rf[a1]; - assign rd2 = rf[a2]; - assign rd3 = rf[a3]; - + + logic [FLEN-1:0] rf[31:0]; + integer i; + + // three ported register file + // read three ports combinationally (A1/RD1, A2/RD2, A3/RD3) + // write fourth port on rising edge of clock (A4/WD4/WE4) + // write occurs on falling edge of clock + + always_ff @(negedge clk) // or posedge reset) + if (reset) for(i=0; i<32; i++) rf[i] <= '0; + else if (we4) rf[a4] <= wd4; + + assign rd1 = rf[a1]; + assign rd2 = rf[a2]; + assign rd3 = rf[a3]; + endmodule // regfile diff --git a/src/generic/flop/flopenrcs.sv b/src/generic/flop/flopenrcs.sv new file mode 100644 index 0000000000..d6b55a5d08 --- /dev/null +++ b/src/generic/flop/flopenrcs.sv @@ -0,0 +1,48 @@ +/////////////////////////////////////////// +// 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; + + mux2 #(1) mux (.y(dmux[WIDTH-1]), .s(scan), .d1(scanin), .d0(d[WIDTH-1])); + assign scanout = q[0]; + + genvar i; + for (i=0; i 0) begin : pmp diff --git a/src/mmu/pmachecker.sv b/src/mmu/pmachecker.sv index f2a2e984b5..4ee7163d5c 100644 --- a/src/mmu/pmachecker.sv +++ b/src/mmu/pmachecker.sv @@ -38,7 +38,7 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( input logic WriteAccessM, // Write access input logic ReadAccessM, // Read access input logic [1:0] PBMemoryType, // PBMT field of PTE during TLB hit, or 00 otherwise - output logic Cacheable, Idempotent, SelTIM, + output logic Cacheable, Idempotent, SelTIM, SelProgBuf, output logic PMAInstrAccessFaultF, output logic PMALoadAccessFaultM, output logic PMAStoreAmoAccessFaultM @@ -46,7 +46,7 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( logic PMAAccessFault; logic AccessRW, AccessRWXC, AccessRX; - logic [11:0] SelRegions; + logic [14:0] SelRegions; logic AtomicAllowed; logic CacheableRegion, IdempotentRegion; @@ -72,6 +72,9 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( // Check if tightly integrated memories are selected assign SelTIM = SelRegions[1] | SelRegions[2]; // exclusion-tag: unused-tim + // Debug program buffer + assign SelProgBuf = SelRegions[14]; + // Detect access faults assign PMAAccessFault = SelRegions[0] & AccessRWXC | AtomicAccessM & ~AtomicAllowed; assign PMAInstrAccessFaultF = ExecuteAccessF & PMAAccessFault; diff --git a/src/privileged/csr.sv b/src/privileged/csr.sv index 13dedffa25..f9ca4522be 100644 --- a/src/privileged/csr.sv +++ b/src/privileged/csr.sv @@ -92,29 +92,49 @@ module csr import cvw::*; #(parameter cvw_t P) ( // output logic [P.XLEN-1:0] CSRReadValW, // value read from CSR output logic IllegalCSRAccessM, // Illegal CSR access: CSR doesn't exist or is inaccessible at this privilege level - output logic BigEndianM // memory access is big-endian based on privilege mode and STATUS register endian fields + output logic BigEndianM, // memory access is big-endian based on privilege mode and STATUS register endian fields + // Debug Mode output + input logic DebugMode, + input logic [2:0] DebugCause, + input logic ebreakM, + output logic ebreakEn, + output logic Step, + output logic DebugStopTime_REGW, + output logic [P.XLEN-1:0] DPC, + input logic DCall, + input logic DRet, + input logic ExecProgBuf, + // Debug scan chain + input logic DebugSel, + input logic [11:0] DebugRegAddr, + input logic DebugCapture, + input logic DebugRegUpdate, + input logic DebugScanEn, + input logic DebugScanIn, + output logic DebugScanOut ); localparam MIP = 12'h344; localparam SIP = 12'h144; - logic [P.XLEN-1:0] CSRMReadValM, CSRSReadValM, CSRUReadValM, CSRCReadValM; + logic [P.XLEN-1:0] CSRMReadValM, CSRSReadValM, CSRUReadValM, CSRCReadValM, CSRDReadValM; logic [P.XLEN-1:0] CSRReadValM; logic [P.XLEN-1:0] CSRSrcM; logic [P.XLEN-1:0] CSRRWM, CSRRSM, CSRRCM; - logic [P.XLEN-1:0] CSRWriteValM; + logic [P.XLEN-1:0] CSRWriteValM, CSRWriteValDM, DebugCSRScanVal; logic [P.XLEN-1:0] MSTATUS_REGW, SSTATUS_REGW, MSTATUSH_REGW; logic [P.XLEN-1:0] STVEC_REGW, MTVEC_REGW; logic [P.XLEN-1:0] MEPC_REGW, SEPC_REGW; logic [31:0] MCOUNTINHIBIT_REGW, MCOUNTEREN_REGW, SCOUNTEREN_REGW; logic WriteMSTATUSM, WriteMSTATUSHM, WriteSSTATUSM; + logic CSRWriteDM; logic CSRMWriteM, CSRSWriteM, CSRUWriteM; logic UngatedCSRMWriteM; logic WriteFRMM, WriteFFLAGSM; logic [P.XLEN-1:0] UnalignedNextEPCM, NextEPCM, NextMtvalM; logic [4:0] NextCauseM; - logic [11:0] CSRAdrM; - logic IllegalCSRCAccessM, IllegalCSRMAccessM, IllegalCSRSAccessM, IllegalCSRUAccessM; + logic [11:0] CSRAdrM, CSRAdrDM; + logic IllegalCSRCAccessM, IllegalCSRMAccessM, IllegalCSRSAccessM, IllegalCSRUAccessM, IllegalCSRDAccessM; logic InsufficientCSRPrivilegeM; logic IllegalCSRMWriteReadonlyM; logic [P.XLEN-1:0] CSRReadVal2M; @@ -129,6 +149,7 @@ module csr import cvw::*; #(parameter cvw_t P) ( logic [P.XLEN-1:0] SENVCFG_REGW; logic ENVCFG_STCE; // supervisor timer counter enable logic ENVCFG_FIOM; // fence implies io (presently not used) + logic DebugStopCount_REGW; // only valid unflushed instructions can access CSRs assign InstrValidNotFlushedM = InstrValidM & ~StallW & ~FlushW; @@ -168,7 +189,16 @@ module csr import cvw::*; #(parameter cvw_t P) ( // Trap Returns // A trap sets the PC to TrapVector // A return sets the PC to MEPC or SEPC - mux2 #(P.XLEN) epcmux(SEPC_REGW, MEPC_REGW, mretM, EPCM); + if (P.DEBUG_SUPPORTED) begin + always_comb + if (ExecProgBuf) EPCM = P.PROGBUF_BASE[P.XLEN-1:0]; + else if (DRet) EPCM = DPC; + else if (mretM) EPCM = MEPC_REGW; + else EPCM = SEPC_REGW; + end else begin + mux2 #(P.XLEN) epcmux(SEPC_REGW, MEPC_REGW, mretM, EPCM); + end + /////////////////////////////////////////// // CSRWriteValM @@ -179,7 +209,7 @@ module csr import cvw::*; #(parameter cvw_t P) ( CSRSrcM = InstrM[14] ? {{(P.XLEN-5){1'b0}}, InstrM[19:15]} : SrcAM; // CSR set and clear for MIP/SIP should only touch internal state, not interrupt inputs - if (CSRAdrM == MIP | CSRAdrM == SIP) CSRReadVal2M = {{(P.XLEN-12){1'b0}}, MIP_REGW_writeable}; + if (CSRAdrDM == MIP | CSRAdrDM == SIP) CSRReadVal2M = {{(P.XLEN-12){1'b0}}, MIP_REGW_writeable}; else CSRReadVal2M = CSRReadValM; // Compute AND/OR modification @@ -203,10 +233,10 @@ module csr import cvw::*; #(parameter cvw_t P) ( assign NextEPCM = P.ZCA_SUPPORTED ? {UnalignedNextEPCM[P.XLEN-1:1], 1'b0} : {UnalignedNextEPCM[P.XLEN-1:2], 2'b00}; // 3.1.15 alignment assign NextCauseM = TrapM ? {InterruptM, CauseM}: {CSRWriteValM[P.XLEN-1], CSRWriteValM[3:0]}; assign NextMtvalM = TrapM ? NextFaultMtvalM : CSRWriteValM; - assign UngatedCSRMWriteM = CSRWriteM & (PrivilegeModeW == P.M_MODE); + assign UngatedCSRMWriteM = CSRWriteDM & (PrivilegeModeW == P.M_MODE); assign CSRMWriteM = UngatedCSRMWriteM & InstrValidNotFlushedM; - assign CSRSWriteM = CSRWriteM & (|PrivilegeModeW) & InstrValidNotFlushedM; - assign CSRUWriteM = CSRWriteM & InstrValidNotFlushedM; + assign CSRSWriteM = CSRWriteDM & (|PrivilegeModeW) & InstrValidNotFlushedM; + assign CSRUWriteM = CSRWriteDM & InstrValidNotFlushedM; assign MTrapM = TrapM & (NextPrivilegeModeM == P.M_MODE); assign STrapM = TrapM & (NextPrivilegeModeM == P.S_MODE) & P.S_SUPPORTED; @@ -215,38 +245,37 @@ module csr import cvw::*; #(parameter cvw_t P) ( /////////////////////////////////////////// csri #(P) csri(.clk, .reset, - .CSRMWriteM, .CSRSWriteM, .CSRWriteValM, .CSRAdrM, + .CSRMWriteM, .CSRSWriteM, .CSRWriteValM(CSRWriteValDM), .CSRAdrM(CSRAdrDM), .MExtInt, .SExtInt, .MTimerInt, .STimerInt, .MSwInt, .MIDELEG_REGW, .ENVCFG_STCE, .MIP_REGW, .MIE_REGW, .MIP_REGW_writeable); csrsr #(P) csrsr(.clk, .reset, .StallW, .WriteMSTATUSM, .WriteMSTATUSHM, .WriteSSTATUSM, .TrapM, .FRegWriteM, .NextPrivilegeModeM, .PrivilegeModeW, - .mretM, .sretM, .WriteFRMM, .WriteFFLAGSM, .CSRWriteValM, .SelHPTW, + .mretM, .sretM, .WriteFRMM, .WriteFFLAGSM, .CSRWriteValM(CSRWriteValDM), .SelHPTW, .MSTATUS_REGW, .SSTATUS_REGW, .MSTATUSH_REGW, .STATUS_MPP, .STATUS_SPP, .STATUS_TSR, .STATUS_TW, .STATUS_MIE, .STATUS_SIE, .STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_TVM, .STATUS_FS, .BigEndianM); csrm #(P) csrm(.clk, .reset, - .UngatedCSRMWriteM, .CSRMWriteM, .MTrapM, .CSRAdrM, + .UngatedCSRMWriteM, .CSRMWriteM, .MTrapM, .CSRAdrM(CSRAdrDM), .NextEPCM, .NextCauseM, .NextMtvalM, .MSTATUS_REGW, .MSTATUSH_REGW, - .CSRWriteValM, .CSRMReadValM, .MTVEC_REGW, + .CSRWriteValM(CSRWriteValDM), .CSRMReadValM, .MTVEC_REGW, .MEPC_REGW, .MCOUNTEREN_REGW, .MCOUNTINHIBIT_REGW, .MEDELEG_REGW, .MIDELEG_REGW,.PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW, .MIP_REGW, .MIE_REGW, .WriteMSTATUSM, .WriteMSTATUSHM, .IllegalCSRMAccessM, .IllegalCSRMWriteReadonlyM, .MENVCFG_REGW); - if (P.S_SUPPORTED) begin:csrs logic STCE; assign STCE = P.SSTC_SUPPORTED & (PrivilegeModeW == P.M_MODE | (MCOUNTEREN_REGW[1] & ENVCFG_STCE)); csrs #(P) csrs(.clk, .reset, - .CSRSWriteM, .STrapM, .CSRAdrM, + .CSRSWriteM, .STrapM, .CSRAdrM(CSRAdrDM), .NextEPCM, .NextCauseM, .NextMtvalM, .SSTATUS_REGW, .STATUS_TVM, - .CSRWriteValM, .PrivilegeModeW, + .CSRWriteValM(CSRWriteValDM), .PrivilegeModeW, .CSRSReadValM, .STVEC_REGW, .SEPC_REGW, .SCOUNTEREN_REGW, .SATP_REGW, .MIP_REGW, .MIE_REGW, .MIDELEG_REGW, .MTIME_CLINT, .STCE, @@ -266,7 +295,7 @@ module csr import cvw::*; #(parameter cvw_t P) ( // Floating Point CSRs in User Mode only needed if Floating Point is supported if (P.F_SUPPORTED) begin:csru csru #(P) csru(.clk, .reset, .InstrValidNotFlushedM, - .CSRUWriteM, .CSRAdrM, .CSRWriteValM, .STATUS_FS, .CSRUReadValM, + .CSRUWriteM, .CSRAdrM(CSRAdrDM), .CSRWriteValM(CSRWriteValDM), .STATUS_FS, .CSRUReadValM, .SetFflagsM, .FRM_REGW, .WriteFRMM, .WriteFFLAGSM, .IllegalCSRUAccessM); end else begin @@ -279,18 +308,33 @@ module csr import cvw::*; #(parameter cvw_t P) ( if (P.ZICNTR_SUPPORTED) begin:counters csrc #(P) counters(.clk, .reset, .StallE, .StallM, .FlushM, - .InstrValidNotFlushedM, .LoadStallD, .StoreStallD, .CSRWriteM, .CSRMWriteM, + .InstrValidNotFlushedM, .LoadStallD, .StoreStallD, .CSRWriteM(CSRWriteDM), .CSRMWriteM, .BPDirWrongM, .BTAWrongM, .RASPredPCWrongM, .IClassWrongM, .BPWrongM, .IClassM, .DCacheMiss, .DCacheAccess, .ICacheMiss, .ICacheAccess, .sfencevmaM, .InterruptM, .ExceptionM, .InvalidateICacheM, .ICacheStallF, .DCacheStallM, .DivBusyE, .FDivBusyE, - .CSRAdrM, .PrivilegeModeW, .CSRWriteValM, + .CSRAdrM(CSRAdrDM), .PrivilegeModeW, .CSRWriteValM(CSRWriteValDM), .MCOUNTINHIBIT_REGW, .MCOUNTEREN_REGW, .SCOUNTEREN_REGW, - .MTIME_CLINT, .CSRCReadValM, .IllegalCSRCAccessM); + .MTIME_CLINT, .DebugStopCount_REGW, .CSRCReadValM, .IllegalCSRCAccessM); end else begin assign CSRCReadValM = '0; assign IllegalCSRCAccessM = 1'b1; // counters aren't enabled end + if (P.DEBUG_SUPPORTED) begin:csrd + csrd #(P) csrd(.clk, .reset, .DebugMode, .PrivilegeModeW, + .CSRWriteDM, .CSRAdrM(CSRAdrDM), .CSRWriteValM(CSRWriteValDM), .CSRDReadValM, .IllegalCSRDAccessM, + .DebugCause, .ebreakM, .ebreakEn, .Step, .DebugStopTime_REGW, .DebugStopCount_REGW, .DPC, .PCM, .DCall); + end else begin + assign DebugStopCount_REGW = '0; + assign DebugStopTime_REGW = '0; + assign Step = '0; + assign DPC = '0; + assign DebugScanOut = '0; + assign ebreakEn = 0; + assign CSRDReadValM = '0; + assign IllegalCSRDAccessM = 1'b1; // Debug isn't supported + end + // Broadcast appropriate environment configuration based on privilege mode assign ENVCFG_STCE = MENVCFG_REGW[63]; // supervisor timer counter enable assign ENVCFG_PBMTE = MENVCFG_REGW[62]; // page-based memory types enable @@ -304,13 +348,27 @@ module csr import cvw::*; #(parameter cvw_t P) ( (MENVCFG_REGW[0] & SENVCFG_REGW[0]); // merge CSR Reads - assign CSRReadValM = CSRUReadValM | CSRSReadValM | CSRMReadValM | CSRCReadValM; + assign CSRReadValM = CSRUReadValM | CSRSReadValM | CSRMReadValM | CSRCReadValM | CSRDReadValM; flopenrc #(P.XLEN) CSRValWReg(clk, reset, FlushW, ~StallW, CSRReadValM, CSRReadValW); // merge illegal accesses: illegal if none of the CSR addresses is legal or privilege is insufficient - assign InsufficientCSRPrivilegeM = (CSRAdrM[9:8] == 2'b11 & PrivilegeModeW != P.M_MODE) | - (CSRAdrM[9:8] == 2'b01 & PrivilegeModeW == P.U_MODE); + // TODO: ignore/modify this check when in debug mode + assign InsufficientCSRPrivilegeM = (CSRAdrDM[9:8] == 2'b11 & PrivilegeModeW != P.M_MODE) | + (CSRAdrDM[9:8] == 2'b01 & PrivilegeModeW == P.U_MODE); assign IllegalCSRAccessM = ((IllegalCSRCAccessM & IllegalCSRMAccessM & - IllegalCSRSAccessM & IllegalCSRUAccessM | + IllegalCSRSAccessM & IllegalCSRUAccessM & IllegalCSRDAccessM | InsufficientCSRPrivilegeM) & CSRReadM) | IllegalCSRMWriteReadonlyM; + + // Debug module CSR access + // TODO: should DM be able to access CSRs when hart isn't in M mode? + if (P.DEBUG_SUPPORTED) begin + assign CSRAdrDM = DebugSel ? DebugRegAddr : CSRAdrM; + assign CSRWriteDM = DebugSel ? DebugRegUpdate : CSRWriteM; // TODO: add write support + assign CSRWriteValDM = DebugSel ? DebugCSRScanVal : CSRWriteValM; + flopenrs #(P.XLEN) GPScanReg(.clk, .reset, .en(DebugCapture), .d(CSRReadValM), .q(DebugCSRScanVal), .scan(DebugScanEn), .scanin(DebugScanIn), .scanout(DebugScanOut)); + end else begin + assign CSRAdrDM = CSRAdrM; + assign CSRWriteDM = CSRWriteM; + assign CSRWriteValDM = CSRWriteValM; + end endmodule diff --git a/src/privileged/csrc.sv b/src/privileged/csrc.sv index d8ce0e709e..be8d6d5cb3 100644 --- a/src/privileged/csrc.sv +++ b/src/privileged/csrc.sv @@ -57,7 +57,8 @@ module csrc import cvw::*; #(parameter cvw_t P) ( input logic [1:0] PrivilegeModeW, input logic [P.XLEN-1:0] CSRWriteValM, input logic [31:0] MCOUNTINHIBIT_REGW, MCOUNTEREN_REGW, SCOUNTEREN_REGW, - input logic [63:0] MTIME_CLINT, + input logic [63:0] MTIME_CLINT, + input logic DebugStopCount_REGW, output logic [P.XLEN-1:0] CSRCReadValM, output logic IllegalCSRCAccessM ); @@ -138,7 +139,7 @@ module csrc import cvw::*; #(parameter cvw_t P) ( if (P.XLEN==32) begin // write high and low separately logic [P.COUNTERS-1:0] WriteHPMCOUNTERHM; logic [P.XLEN-1:0] NextHPMCOUNTERHM[P.COUNTERS-1:0]; - assign HPMCOUNTERPlusM[i] = {HPMCOUNTERH_REGW[i], HPMCOUNTER_REGW[i]} + {63'b0, CounterEvent[i] & ~MCOUNTINHIBIT_REGW[i]}; + assign HPMCOUNTERPlusM[i] = {HPMCOUNTERH_REGW[i], HPMCOUNTER_REGW[i]} + {63'b0, CounterEvent[i] & ~(MCOUNTINHIBIT_REGW[i] | DebugStopCount_REGW)}; assign WriteHPMCOUNTERHM[i] = CSRMWriteM & (CSRAdrM == MHPMCOUNTERHBASE + i); assign NextHPMCOUNTERHM[i] = WriteHPMCOUNTERHM[i] ? CSRWriteValM : HPMCOUNTERPlusM[i][63:32]; always_ff @(posedge clk) //, posedge reset) // ModelSim doesn't like syntax of passing array element to flop diff --git a/src/privileged/csrd.sv b/src/privileged/csrd.sv new file mode 100644 index 0000000000..c05b965f17 --- /dev/null +++ b/src/privileged/csrd.sv @@ -0,0 +1,137 @@ +/////////////////////////////////////////// +// csrd.sv +// +// Written: matthew.n.otto@okstate.edu +// Created: 13 June 2024 +// +// Purpose: Debug Control and Status Registers +// See RISC-V Debug Specification (4.10) +// +// 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 csrd import cvw::*; #(parameter cvw_t P) ( + input logic clk, reset, + input logic DebugMode, + input logic [1:0] PrivilegeModeW, + input logic CSRWriteDM, + input logic [11:0] CSRAdrM, + input logic [P.XLEN-1:0] CSRWriteValM, + output logic [P.XLEN-1:0] CSRDReadValM, + output logic IllegalCSRDAccessM, + input logic [P.XLEN-1:0] PCM, + input logic DCall, + input logic [2:0] DebugCause, + input logic ebreakM, + output logic ebreakEn, + output logic Step, + output logic DebugStopTime_REGW, + output logic DebugStopCount_REGW, + output logic [P.XLEN-1:0] DPC +); + `include "debug.vh" + + localparam DCSR_ADDR = 12'h7B0; // Debug Control and Status Register + localparam DPC_ADDR = 12'h7B1; // Debug PC + + logic CSRDWriteM; + logic [31:0] DCSR; + logic [P.XLEN-1:0] DPCWriteVal; + logic WriteDCSRM; + logic WriteDPCM; + + // DCSR fields + const logic [3:0] DebugVer = 4'h4; + const logic ebreakVS = '0; + const logic ebreakVU = '0; + logic MEbreak; + logic SEbreak; + logic UEbreak; + const logic StepIE = '0; + logic [2:0] Cause; + const logic V = '0; + const logic MPrvEn = '0; + const logic NMIP = '0; // pending non-maskable interrupt TODO: update + logic [1:0] Prv; + + + //assign ebreakEn = ebreakM; // Only support ebreak from M mode + assign ebreakEn = 1'b1; // OpenOCD doesn't set ebreakM???? ebreakM; // Only support ebreak from M mode + assign CSRDWriteM = CSRWriteDM & (PrivilegeModeW == P.M_MODE) & DebugMode; + + assign WriteDCSRM = CSRDWriteM & (CSRAdrM == DCSR_ADDR); + assign WriteDPCM = CSRDWriteM & (CSRAdrM == DPC_ADDR); + + always_ff @(posedge clk) begin + if (reset) begin + Prv <= 2'h3; + Cause <= 3'h0; + end else if (DCall) begin + Prv <= PrivilegeModeW; + Cause <= DebugCause; + end else if (WriteDCSRM) begin + Prv <= CSRWriteValM[`PRV]; + end + end + + always_ff @(posedge clk) begin + MEbreak <= '0; + SEbreak <= '0; + UEbreak <= '0; + if (reset) begin + MEbreak <= '0; + SEbreak <= '0; + UEbreak <= '0; + end else begin + if (ebreakM) begin + if (PrivilegeModeW == P.M_MODE) MEbreak <= 1'b1; + if (PrivilegeModeW == P.S_MODE) SEbreak <= 1'b1; + if (PrivilegeModeW == P.U_MODE) UEbreak <= 1'b1; + end else if (WriteDCSRM) begin + MEbreak <= CSRWriteValM[`EBREAKM]; + SEbreak <= CSRWriteValM[`EBREAKS]; + UEbreak <= CSRWriteValM[`EBREAKU]; + end + end + end + + flopenr #(3) DCSRreg (clk, reset, WriteDCSRM, + {CSRWriteValM[`STEP], CSRWriteValM[`STOPTIME], CSRWriteValM[`STOPCOUNT]}, + {Step, DebugStopTime_REGW, DebugStopCount_REGW}); + + assign DCSR = {DebugVer, 10'b0, ebreakVS, ebreakVU, MEbreak, 1'b0, SEbreak, UEbreak, StepIE, + DebugStopCount_REGW, DebugStopTime_REGW, Cause, V, MPrvEn, NMIP, Step, Prv}; + + assign DPCWriteVal = DCall ? PCM : CSRWriteValM; + flopenr #(P.XLEN) DPCreg (clk, reset, WriteDPCM | DCall, DPCWriteVal, DPC); + + always_comb begin + CSRDReadValM = '0; + IllegalCSRDAccessM = 1'b0; + if (~((PrivilegeModeW == P.M_MODE) & DebugMode)) + IllegalCSRDAccessM = 1'b1; + else + case (CSRAdrM) + DCSR_ADDR : CSRDReadValM = {{(P.XLEN-32){1'b0}},DCSR}; + DPC_ADDR : CSRDReadValM = DPC; + default: IllegalCSRDAccessM = 1'b1; + endcase + end + +endmodule diff --git a/src/privileged/csrm.sv b/src/privileged/csrm.sv index 24132ab939..9b096ed414 100644 --- a/src/privileged/csrm.sv +++ b/src/privileged/csrm.sv @@ -92,10 +92,6 @@ module csrm import cvw::*; #(parameter cvw_t P) ( localparam TDATA1 = 12'h7A1; localparam TDATA2 = 12'h7A2; localparam TDATA3 = 12'h7A3; - localparam DCSR = 12'h7B0; - localparam DPC = 12'h7B1; - localparam DSCRATCH0 = 12'h7B2; - localparam DSCRATCH1 = 12'h7B3; // Constants localparam ZERO = {(P.XLEN){1'b0}}; // when compressed instructions are supported, there can't be misaligned instructions diff --git a/src/privileged/privdec.sv b/src/privileged/privdec.sv index 6321413d44..236db5d302 100644 --- a/src/privileged/privdec.sv +++ b/src/privileged/privdec.sv @@ -30,7 +30,8 @@ module privdec import cvw::*; #(parameter cvw_t P) ( input logic clk, reset, - input logic StallW, FlushW, + input logic StallW, FlushW, + input logic ForceBreakPoint, // Debug Module initiated break to debug mode input logic [31:15] InstrM, // privileged instruction function field input logic PrivilegedM, // is this a privileged instruction (from IEU controller) input logic IllegalIEUFPUInstrM, // Not a legal IEU instruction @@ -40,13 +41,14 @@ module privdec import cvw::*; #(parameter cvw_t P) ( output logic IllegalInstrFaultM, // Illegal instruction output logic EcallFaultM, BreakpointFaultM, // Ecall or breakpoint; must retire, so don't flush it when the trap occurs output logic sretM, mretM, RetM, // return instructions - output logic wfiM, wfiW, sfencevmaM // wfi / sfence.vma / sinval.vma instructions + output logic wfiM, wfiW, sfencevmaM, // wfi / sfence.vma / sinval.vma instructions + output logic ebreakM // ebreak instruction ); logic rs1zeroM; // rs1 field = 0 logic IllegalPrivilegedInstrM; // privileged instruction isn't a legal one or in legal mode logic WFITimeoutM; // WFI reaches timeout threshold - logic ebreakM, ecallM; // ebreak / ecall instructions + logic ecallM; // ecall instructions logic sinvalvmaM; // sinval.vma logic sfencewinvalM, sfenceinvalirM; // sfence.w.inval, sfence.inval.ir logic invalM; // any of the svinval instructions @@ -94,7 +96,10 @@ module privdec import cvw::*; #(parameter cvw_t P) ( // Extract exceptions by name and handle them /////////////////////////////////////////// - assign BreakpointFaultM = ebreakM; // could have other causes from a debugger + if (P.DEBUG_SUPPORTED) + assign BreakpointFaultM = ebreakM | ForceBreakPoint; + else + assign BreakpointFaultM = ebreakM; assign EcallFaultM = ecallM; /////////////////////////////////////////// diff --git a/src/privileged/privileged.sv b/src/privileged/privileged.sv index 197ab23be3..5f9fc01d30 100644 --- a/src/privileged/privileged.sv +++ b/src/privileged/privileged.sv @@ -96,9 +96,30 @@ module privileged import cvw::*; #(parameter cvw_t P) ( input logic InvalidateICacheM, // fence instruction output logic BigEndianM, // Use big endian in current privilege mode // Fault outputs - output logic wfiM, IntPendingM // Stall in Memory stage for WFI until interrupt pending or timeout -); - + output logic wfiM, IntPendingM, // Stall in Memory stage for WFI until interrupt pending or timeout + output logic ebreakM, // Notifies DM to enter debug mode + // Debuge Mode + output logic ebreakEn, + input logic ForceBreakPoint, + input logic DebugMode, + output logic ProgBufTrap, + input logic [2:0] DebugCause, + output logic Step, + output logic DebugStopTime_REGW, + output logic [P.XLEN-1:0] DPC, + input logic DCall, + input logic DRet, + input logic ExecProgBuf, + // Debug scan chain + input logic DebugSel, + input logic [11:0] DebugRegAddr, + input logic DebugCapture, + input logic DebugRegUpdate, + input logic DebugScanEn, + input logic DebugScanIn, + output logic DebugScanOut +); + logic [3:0] CauseM; // trap cause logic [15:0] MEDELEG_REGW; // exception delegation CSR logic [11:0] MIDELEG_REGW; // interrupt delegation CSR @@ -127,9 +148,9 @@ module privileged import cvw::*; #(parameter cvw_t P) ( // decode privileged instructions privdec #(P) pmd(.clk, .reset, .StallW, .FlushW, .InstrM(InstrM[31:15]), - .PrivilegedM, .IllegalIEUFPUInstrM, .IllegalCSRAccessM, + .PrivilegedM, .IllegalIEUFPUInstrM, .IllegalCSRAccessM, .ForceBreakPoint, .PrivilegeModeW, .STATUS_TSR, .STATUS_TVM, .STATUS_TW, .IllegalInstrFaultM, - .EcallFaultM, .BreakpointFaultM, .sretM, .mretM, .RetM, .wfiM, .wfiW, .sfencevmaM); + .EcallFaultM, .BreakpointFaultM, .sretM, .mretM, .RetM, .wfiM, .wfiW, .sfencevmaM, .ebreakM); // Control and Status Registers csr #(P) csr(.clk, .reset, .FlushM, .FlushW, .StallE, .StallM, .StallW, @@ -147,7 +168,9 @@ module privileged import cvw::*; #(parameter cvw_t P) ( .SATP_REGW, .PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW, .SetFflagsM, .FRM_REGW, .ENVCFG_CBE, .ENVCFG_PBMTE, .ENVCFG_ADUE, .EPCM, .TrapVectorM, - .CSRReadValW, .IllegalCSRAccessM, .BigEndianM); + .CSRReadValW, .IllegalCSRAccessM, .BigEndianM, + .DebugMode, .DebugCause, .ebreakM, .ebreakEn, .Step, .DebugStopTime_REGW, .DPC, .DCall, .DRet, .ExecProgBuf, + .DebugSel, .DebugRegAddr, .DebugCapture, .DebugRegUpdate, .DebugScanEn, .DebugScanIn, .DebugScanOut); // pipeline early-arriving trap sources privpiperegs ppr(.clk, .reset, .StallD, .StallE, .StallM, .FlushD, .FlushE, .FlushM, @@ -161,6 +184,6 @@ module privileged import cvw::*; #(parameter cvw_t P) ( .LoadAccessFaultM, .StoreAmoAccessFaultM, .EcallFaultM, .InstrPageFaultM, .LoadPageFaultM, .StoreAmoPageFaultM, .PrivilegeModeW, .MIP_REGW, .MIE_REGW, .MIDELEG_REGW, .MEDELEG_REGW, .STATUS_MIE, .STATUS_SIE, - .InstrValidM, .CommittedM, .CommittedF, + .InstrValidM, .CommittedM, .CommittedF, .DebugMode, .ProgBufTrap, .TrapM, .wfiM, .wfiW, .InterruptM, .ExceptionM, .IntPendingM, .DelegateM, .CauseM); endmodule diff --git a/src/privileged/trap.sv b/src/privileged/trap.sv index 95ae26e1a6..f36f7521d9 100644 --- a/src/privileged/trap.sv +++ b/src/privileged/trap.sv @@ -40,6 +40,8 @@ module trap import cvw::*; #(parameter cvw_t P) ( input logic STATUS_MIE, STATUS_SIE, // machine/supervisor interrupt enables input logic InstrValidM, // current instruction is valid, not flushed input logic CommittedM, CommittedF, // LSU/IFU has committed to a bus operation that can't be interrupted + input logic DebugMode, // Ignore all interrupts in debug mode + output logic ProgBufTrap, // Trap while executing progbuf returns to debug mode output logic TrapM, // Trap is occurring output logic InterruptM, // Interrupt is occurring output logic ExceptionM, // exception is occurring @@ -66,7 +68,7 @@ module trap import cvw::*; #(parameter cvw_t P) ( assign IntPendingM = |PendingIntsM; assign Committed = CommittedM | CommittedF; assign EnabledIntsM = (MIntGlobalEnM ? PendingIntsM & ~MIDELEG_REGW : '0) | (SIntGlobalEnM ? PendingIntsM & MIDELEG_REGW : '0); - assign ValidIntsM = Committed ? '0 : EnabledIntsM; + assign ValidIntsM = (Committed | DebugMode) ? '0 : EnabledIntsM; assign InterruptM = (|ValidIntsM) & InstrValidM & (~wfiM | wfiW); // suppress interrupt if the memory system has partially processed a request. Delay interrupt until wfi is in the W stage. // wfiW is to support possible but unlikely back to back wfi instructions. wfiM would be high in the M stage, while also in the W stage. assign DelegateM = P.S_SUPPORTED & (InterruptM ? MIDELEG_REGW[CauseM] : MEDELEG_REGW[CauseM]) & @@ -88,14 +90,17 @@ module trap import cvw::*; #(parameter cvw_t P) ( BreakpointFaultM | EcallFaultM | LoadAccessFaultM | StoreAmoAccessFaultM; // coverage on - assign TrapM = (ExceptionM & ~CommittedF) | InterruptM; + //assign TrapM = (ExceptionM & ~CommittedF) | InterruptM; + // Debug Test + assign TrapM = ~DebugMode & ((ExceptionM & ~CommittedF) | InterruptM); + assign ProgBufTrap = ExceptionM & ~CommittedF; /////////////////////////////////////////// // Cause priority defined in privileged spec /////////////////////////////////////////// always_comb - if (reset) CauseM = 4'd0; // hard reset 3.3 + if (reset) CauseM = 4'd0; // hard reset 3.3 else if (ValidIntsM[11]) CauseM = 4'd11; // Machine External Int else if (ValidIntsM[3]) CauseM = 4'd3; // Machine Sw Int else if (ValidIntsM[7]) CauseM = 4'd7; // Machine Timer Int diff --git a/src/uncore/clint_apb.sv b/src/uncore/clint_apb.sv index 76735aaa66..d6fca21722 100644 --- a/src/uncore/clint_apb.sv +++ b/src/uncore/clint_apb.sv @@ -36,6 +36,7 @@ module clint_apb import cvw::*; #(parameter cvw_t P) ( input logic [P.XLEN/8-1:0] PSTRB, input logic PWRITE, input logic PENABLE, + input logic DebugStopTime_REGW, output logic [P.XLEN-1:0] PRDATA, output logic PREADY, output logic [63:0] MTIME, @@ -144,7 +145,7 @@ module clint_apb import cvw::*; #(parameter cvw_t P) ( for(i=0;i $(TARGET).objdump + + +$(TARGET): $(TARGET).c Makefile + riscv64-unknown-elf-gcc -o $(TARGET).elf -g\ + -march=rv64gc -mabi=lp64d -mcmodel=medany \ + -nostdlib -static -lm -fno-tree-loop-distribute-patterns \ + -Ttest.ld $(TARGET).c + +clean: + rm -f $(TARGET).elf $(TARGET).objdump diff --git a/tests/debug/simple/make.bak b/tests/debug/simple/make.bak new file mode 100644 index 0000000000..be8214e5e5 --- /dev/null +++ b/tests/debug/simple/make.bak @@ -0,0 +1,15 @@ +TARGET = simple + +$(TARGET).objdump: $(TARGET) + riscv64-unknown-elf-objdump -S -D $(TARGET) > $(TARGET).objdump + spike $(TARGET) + +$(TARGET): $(TARGET).c Makefile + riscv64-unknown-elf-gcc -o $(TARGET) -g\ + -march=rv64gc -mabi=lp64d -mcmodel=medany \ + -nostdlib -static -lm -fno-tree-loop-distribute-patterns \ + -T../../../examples/C/common/test.ld -I../../../examples/C/common/ \ + $(TARGET).c ../../../examples/C/common/crt.S ../../../examples/C/common/syscalls.c + +clean: + rm -f $(TARGET) $(TARGET).objdump diff --git a/tests/debug/simple/openocd.cfg b/tests/debug/simple/openocd.cfg new file mode 100644 index 0000000000..40a9ed4e53 --- /dev/null +++ b/tests/debug/simple/openocd.cfg @@ -0,0 +1,37 @@ +# OpenOCD config file for Core V Wally +# 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-10.2 + +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 +#ftdi layout_init 0x0008 0x001b + +transport select jtag +adapter speed 1000 +#ftdi tdo_sample_edge falling + +set _CHIPNAME cvw +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1002AC05 + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME +$_TARGETNAME configure -work-area-phys 0x8000000 -work-area-size 0x4000 -work-area-backup 0 + +# enable memory access error reports +riscv set_enable_virt2phys off +riscv set_enable_virtual off + +init +halt + diff --git a/tests/debug/simple/simple.c b/tests/debug/simple/simple.c new file mode 100644 index 0000000000..8f734dca41 --- /dev/null +++ b/tests/debug/simple/simple.c @@ -0,0 +1,18 @@ + +int main() { + + // This is just random simple instructions + // to test the RISC-V debug gdb + asm("li a0, 0x1000"); + asm("addi a1, a0, 0x100"); + asm("addi a2, a1, 0x200"); + asm("li a3, 0x4000000"); + asm("sw a0, 0(a3)"); + asm("sw a1, 4(a3)"); + asm("lw a4, 0(a3)"); + asm("lw a5, 4(a3)"); + asm("lw a5, 4(a3)"); + asm("nop"); + while(1); + +} diff --git a/tests/debug/simple/simple.elf b/tests/debug/simple/simple.elf new file mode 100755 index 0000000000..086b418db6 Binary files /dev/null and b/tests/debug/simple/simple.elf differ diff --git a/tests/debug/simple/test.ld b/tests/debug/simple/test.ld new file mode 100644 index 0000000000..3c7091084c --- /dev/null +++ b/tests/debug/simple/test.ld @@ -0,0 +1,25 @@ + +OUTPUT_ARCH( "riscv" ) + +ENTRY(main) + +/*----------------------------------------------------------------------*/ +/* Sections */ +/*----------------------------------------------------------------------*/ + +MEMORY { + + ram(wxa!ri): ORIGIN = 0x80000000, LENGTH = 0x4000 + +} + +SECTIONS { + + .text : + { + *(.text*) + } + > ram + . = ALIGN(4); + +} diff --git a/tests/debug/src/halt_step_resume_test.py b/tests/debug/src/halt_step_resume_test.py new file mode 100644 index 0000000000..c29f41e5f9 --- /dev/null +++ b/tests/debug/src/halt_step_resume_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# Self compiling python -> SVF script +# When you execute this program, openocd_tcl_wrapper.SVF_Generator should generate a basic SVF file +# The output file will have the same path as this file with a ".svf" extension + +# To make a new test, copy this file and write new test logic in main() below "### Write test program here ###" +# For available functions calls, see SVF_Generator in openocd_tcl_wrapper + + +# Hacky way to add $wally/bin to path so we can import openocd_tcl_wrapper from $wally/tests +import sys +import os +sys.path.insert(0, os.path.abspath("bin")) +sys.path.insert(0, os.path.abspath("../../../bin")) +from openocd_tcl_wrapper import SVF_Generator + +def main(): + with SVF_Generator(writeout=True) as svf: + ### Write test program here ### + svf.comment("Check jtag id") + svf.check_jtag_id(0x1002AC05) + svf.comment("Activating DM") + svf.activate_dm() + svf.comment("Read DMStatus, compare bottom 8 bits to 0xa3") + svf.read_dmi(0x11, 0xa3, mask=0xff) + svf.comment("Reset hart") + svf.reset_hart() + svf.comment("Halt hart") + svf.halt() + svf.comment("Step") + svf.step() + svf.comment("Idle for 50 cycles") + svf.spin(50) + svf.comment("Step") + svf.step() + svf.comment("Resume") + svf.resume() + + + +if __name__ == "__main__": + main() diff --git a/tests/debug/src/halt_step_resume_test.svf b/tests/debug/src/halt_step_resume_test.svf new file mode 100644 index 0000000000..250ed2d33e --- /dev/null +++ b/tests/debug/src/halt_step_resume_test.svf @@ -0,0 +1,43 @@ +// Check jtag id +SDR 32 TDO(1002ac05); +// Activating DM +SIR 5 TDI(11); +SDR 41 TDI(4000000006); +// Read DMStatus, compare bottom 8 bits to 0xa3 +SDR 41 TDI(4400000001); +SDR 41 TDO(28c) MASK(3ff); +// Reset hart +SDR 41 TDI(400000000e); +SDR 41 TDI(4000000006); +SDR 41 TDI(4040000006); +// Halt hart +SDR 41 TDI(4200000006); +SDR 41 TDI(4000000006); +SDR 41 TDI(4400000001); +SDR 41 TDO(c00) MASK(c03); +// Step +SDR 41 TDI(1000000012); +SDR 41 TDI(1400000002); +SDR 41 TDI(5c00cc1ec2); +RUNTEST 64; +SDR 41 TDI(4100000006); +SDR 41 TDI(1000000002); +SDR 41 TDI(1400000002); +SDR 41 TDI(5c00cc1ec2); +RUNTEST 64; +// Idle for 50 cycles +RUNTEST 50; +// Step +SDR 41 TDI(1000000012); +SDR 41 TDI(1400000002); +SDR 41 TDI(5c00cc1ec2); +RUNTEST 64; +SDR 41 TDI(4100000006); +SDR 41 TDI(1000000002); +SDR 41 TDI(1400000002); +SDR 41 TDI(5c00cc1ec2); +RUNTEST 64; +// Resume +SDR 41 TDI(4100000006); +SDR 41 TDI(4400000001); +SDR 41 TDO(c0000) MASK(c0003); diff --git a/tests/debug/src/progbuff_test.py b/tests/debug/src/progbuff_test.py new file mode 100644 index 0000000000..b925e430b0 --- /dev/null +++ b/tests/debug/src/progbuff_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +# Self compiling python -> SVF script +# When you execute this program, openocd_tcl_wrapper.SVF_Generator should generate a basic SVF file +# The output file will have the same path as this file with a ".svf" extension + +# To make a new test, copy this file and write new test logic in main() below "### Write test program here ###" +# For available functions calls, see SVF_Generator in openocd_tcl_wrapper + + +# Hacky way to add $wally/bin to path so we can import openocd_tcl_wrapper from $wally/tests +import sys +import os +sys.path.insert(0, os.path.abspath("bin")) +sys.path.insert(0, os.path.abspath("../../../bin")) +from openocd_tcl_wrapper import SVF_Generator + +def main(): + with SVF_Generator(writeout=True) as svf: + ### Write test program here ### + svf.comment("Check jtag id") + svf.check_jtag_id(0x1002AC05) + svf.comment("Activating DM") + svf.activate_dm() + svf.comment("Read DMStatus, compare bottom 8 bits to 0xa3") + svf.read_dmi(0x11, 0xa3, mask=0xff) + svf.comment("Reset hart") + svf.reset_hart() + svf.comment("Halt hart") + svf.halt() + svf.comment("Write \"0x80000000\" to x8") + svf.write_data("X8", 0x80000000) + svf.comment("Write \"0xbadc0ffee0ddf00d\" to x9") + svf.write_data("X9", 0xbadc0ffee0ddf00d) + svf.comment("Write program buffer:") + svf.write_progbuf([0x00943023, 0x00840413, 0x00100073]) + svf.comment("Execute Program Buffer") + svf.exec_progbuf() + svf.resume() + + + +if __name__ == "__main__": + main() diff --git a/tests/debug/src/progbuff_test.svf b/tests/debug/src/progbuff_test.svf new file mode 100644 index 0000000000..4ed2b3dafd --- /dev/null +++ b/tests/debug/src/progbuff_test.svf @@ -0,0 +1,37 @@ +// Check jtag id +SDR 32 TDO(1002ac05); +// Activating DM +SIR 5 TDI(11); +SDR 41 TDI(4000000006); +// Read DMStatus, compare bottom 8 bits to 0xa3 +SDR 41 TDI(4400000001); +SDR 41 TDO(28c) MASK(3ff); +// Reset hart +SDR 41 TDI(400000000e); +SDR 41 TDI(4000000006); +SDR 41 TDI(4040000006); +// Halt hart +SDR 41 TDI(4200000006); +SDR 41 TDI(4000000006); +SDR 41 TDI(4400000001); +SDR 41 TDO(c00) MASK(c03); +// Write "0x80000000" to x8 +SDR 41 TDI(1200000002); +SDR 41 TDI(1400000002); +SDR 41 TDI(5c00cc4022); +RUNTEST 64; +// Write "0xbadc0ffee0ddf00d" to x9 +SDR 41 TDI(138377c036); +SDR 41 TDI(16eb703ffa); +SDR 41 TDI(5c00cc4026); +RUNTEST 64; +// Write program buffer: +SDR 41 TDI(800250c08e); +SDR 41 TDI(840210104e); +SDR 41 TDI(88004001ce); +// Execute Program Buffer +SDR 41 TDI(5c00100002); +RUNTEST 10; +SDR 41 TDI(4100000006); +SDR 41 TDI(4400000001); +SDR 41 TDO(c0000) MASK(c0003);