mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-11 06:05:49 +00:00
seed debug module for Wally
This commit is contained in:
parent
f73ebc1b45
commit
77ec3d58c6
404
bin/hw_interface.py
Executable file
404
bin/hw_interface.py
Executable file
@ -0,0 +1,404 @@
|
|||||||
|
#########################################################################################
|
||||||
|
# hw_interface.py
|
||||||
|
#
|
||||||
|
# Written: matthew.n.otto@okstate.edu
|
||||||
|
# Created: 19 April 2024
|
||||||
|
#
|
||||||
|
# Purpose: Send debugging commands to OpenOCD via local telnet connection
|
||||||
|
#
|
||||||
|
# A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
# https:#github.com/openhwgroup/cvw
|
||||||
|
#
|
||||||
|
# Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
#
|
||||||
|
# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
# except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||||
|
# may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https:#solderpad.org/licenses/SHL-2.1/
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
# License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
# either express or implied. See the License for the specific language governing permissions
|
||||||
|
# and limitations under the License.
|
||||||
|
#########################################################################################
|
||||||
|
|
||||||
|
# This script uses python to send text commands to OpenOCD via telnet
|
||||||
|
# OpenOCD also supports tcl commands directly
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import time
|
||||||
|
from telnetlib import Telnet
|
||||||
|
|
||||||
|
debug = False
|
||||||
|
XLEN = 64 # TODO: infer this value from the MISA
|
||||||
|
|
||||||
|
tapname = "cvw.cpu" # this is set via the openocd config. It can be found by running `scan_chain`
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: if JTAG clk is fast enough, need to check for busy between absract commands
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global tn
|
||||||
|
with Telnet("127.0.0.1", 4444) as tn:
|
||||||
|
read() # clear welcome message from read buffer
|
||||||
|
activate_dm() # necessary if openocd init is disabled
|
||||||
|
status()
|
||||||
|
halt()
|
||||||
|
GPR = dump_GPR()
|
||||||
|
print(GPR)
|
||||||
|
check_errors()
|
||||||
|
print(f"PCM: '{read_data("PCM")}'")
|
||||||
|
resume()
|
||||||
|
status()
|
||||||
|
#clear_abstrcmd_err()
|
||||||
|
#write_data("READDATAM", "0xAA0987210000FFFF")
|
||||||
|
#print(f"READDATAM'{read_data("READDATAM")}'")
|
||||||
|
#print(f"WRITEDATAM: '{read_data("WRITEDATAM")}'")
|
||||||
|
#print(f"IEUADRM: '{read_data("IEUADRM")}'")
|
||||||
|
#write_data("TRAPM", "0x0")
|
||||||
|
#print(f"INSTRVALIDM: '{read_data("INSTRVALIDM")}'")
|
||||||
|
#print(f"MEMRWM: '{read_data("MEMRWM")}'")
|
||||||
|
#write_data("MEMRWM", "0x3")
|
||||||
|
#write_data("PCM", "0x100000")
|
||||||
|
#dmi_reset()
|
||||||
|
#clear_abstrcmd_err()
|
||||||
|
|
||||||
|
|
||||||
|
def dump_GPR():
|
||||||
|
gpr = {}
|
||||||
|
for i in range(1,32):
|
||||||
|
addr = f"X{i}"
|
||||||
|
gpr[addr] = read_data(addr)
|
||||||
|
# DM will assert Abstract Command Err if GPR X16-X31 isn't implemented (CMDERR_EXCEPTION)
|
||||||
|
# This will clear that error and return early.
|
||||||
|
if i == 16:
|
||||||
|
abstractcs = int(read_dmi("0x16"), 16)
|
||||||
|
cmderr = (abstractcs & 0x700) >> 8
|
||||||
|
if cmderr == 3:
|
||||||
|
clear_abstrcmd_err()
|
||||||
|
break
|
||||||
|
return gpr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def write_data(register, data):
|
||||||
|
"""Writes data of width XLEN to specified register"""
|
||||||
|
# Translate register alias to DM regno
|
||||||
|
regno = int(register_translations[register], 16)
|
||||||
|
# Write data to 32 bit message registers
|
||||||
|
data = int(data, 16)
|
||||||
|
write_dmi("0x4", hex(data & 0xffffffff))
|
||||||
|
if XLEN == 64:
|
||||||
|
write_dmi("0x5", hex((data >> 32) & 0xffffffff))
|
||||||
|
if XLEN == 128:
|
||||||
|
write_dmi("0x6", hex((data >> 64) & 0xffffffff))
|
||||||
|
write_dmi("0x7", hex((data >> 96) & 0xffffffff))
|
||||||
|
# Transfer data from msg registers to target register
|
||||||
|
access_register(write=True, regno=regno, addr_size=XLEN)
|
||||||
|
# Check that operations completed without error
|
||||||
|
if acerr := check_absrtcmderr():
|
||||||
|
raise Exception(acerr)
|
||||||
|
|
||||||
|
|
||||||
|
def read_data(register):
|
||||||
|
"""Read data of width XLEN from specified register"""
|
||||||
|
# Translate register alias to DM regno
|
||||||
|
regno = int(register_translations[register], 16)
|
||||||
|
# Transfer data from target register to msg registers
|
||||||
|
access_register(write=False, regno=regno, addr_size=XLEN)
|
||||||
|
# Read data from 32 bit message registers
|
||||||
|
data = ""
|
||||||
|
data = read_dmi("0x4").replace("0x", "").zfill(8)
|
||||||
|
if XLEN >= 64:
|
||||||
|
data = read_dmi("0x5").replace("0x", "").zfill(8) + data
|
||||||
|
if XLEN == 128:
|
||||||
|
data = read_dmi("0x6").replace("0x", "").zfill(8) + data
|
||||||
|
data = read_dmi("0x7").replace("0x", "").zfill(8) + data
|
||||||
|
# Check that operations completed without error
|
||||||
|
if acerr := check_absrtcmderr():
|
||||||
|
raise Exception(acerr)
|
||||||
|
return f"0x{data}"
|
||||||
|
|
||||||
|
|
||||||
|
def access_register(write, regno, addr_size):
|
||||||
|
"""3.7.1.1
|
||||||
|
Before starting an abstract command, a debugger must ensure that haltreq, resumereq, and
|
||||||
|
ackhavereset are all 0."""
|
||||||
|
addr = "0x17"
|
||||||
|
data = 1 << 17 # transfer bit always set
|
||||||
|
if addr_size == 32:
|
||||||
|
data += 2 << 20
|
||||||
|
elif addr_size == 64:
|
||||||
|
data += 3 << 20
|
||||||
|
elif addr_size == 128:
|
||||||
|
data += 4 << 20
|
||||||
|
else:
|
||||||
|
raise Exception("must provide valid register access size (32, 64, 128). See: 3.7.1.1 aarsize")
|
||||||
|
if write:
|
||||||
|
data += 1<<16
|
||||||
|
data += regno
|
||||||
|
data = hex(data)
|
||||||
|
write_dmi(addr, data)
|
||||||
|
|
||||||
|
|
||||||
|
def halt():
|
||||||
|
write_dmi("0x10", "0x80000001")
|
||||||
|
check_errors()
|
||||||
|
|
||||||
|
|
||||||
|
def resume():
|
||||||
|
write_dmi("0x10", "0x40000001")
|
||||||
|
check_errors()
|
||||||
|
|
||||||
|
|
||||||
|
def step():
|
||||||
|
write_dmi("0x10", "0xC0000001")
|
||||||
|
check_errors()
|
||||||
|
|
||||||
|
|
||||||
|
def set_haltonreset():
|
||||||
|
write_dmi("0x10", "0x9")
|
||||||
|
|
||||||
|
|
||||||
|
def clear_haltonreset():
|
||||||
|
write_dmi("0x10", "0x5")
|
||||||
|
|
||||||
|
|
||||||
|
def reset_hart():
|
||||||
|
write_dmi("0x10", "0x3")
|
||||||
|
write_dmi("0x10", "0x1")
|
||||||
|
|
||||||
|
|
||||||
|
def status():
|
||||||
|
dmstatus = int(read_dmi("0x11"), 16)
|
||||||
|
print("Core status:::")
|
||||||
|
print(f"Running: {bool((dmstatus >> 11) & 0x1)}")
|
||||||
|
print(f"Halted: {bool((dmstatus >> 9) & 0x1)}")
|
||||||
|
|
||||||
|
|
||||||
|
def check_errors():
|
||||||
|
# TODO: update this
|
||||||
|
"""Checks various status bits and reports any potential errors
|
||||||
|
Returns true if any errors are found"""
|
||||||
|
# check dtmcs
|
||||||
|
dtmcs = int(read_dtmcs(), 16)
|
||||||
|
errinfo = (dtmcs & 0x1C0000) >> 18
|
||||||
|
dmistat = (dtmcs & 0xC00) >> 10
|
||||||
|
if errinfo > 0 and errinfo < 4:
|
||||||
|
print(f"DTM Error: {errinfo_translations[errinfo]}")
|
||||||
|
return True
|
||||||
|
if dmistat:
|
||||||
|
print(f"DMI status error: {op_translations[dmistat]}")
|
||||||
|
return True
|
||||||
|
# check if DM is inactive
|
||||||
|
dm_active = int(read_dmi("0x10"), 16) & 0x1
|
||||||
|
if not dm_active:
|
||||||
|
print("DMControl Error: Debug module is not active")
|
||||||
|
return True
|
||||||
|
# check abstract command error
|
||||||
|
abstractcs = int(read_dmi("0x16"), 16)
|
||||||
|
busy = (abstractcs & 0x1000) >> 12
|
||||||
|
cmderr = (abstractcs & 0x700) >> 8
|
||||||
|
if not busy and cmderr:
|
||||||
|
print(f"Abstract Command Error: {cmderr_translations[cmderr]}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def check_busy():
|
||||||
|
"""If an Abstract Command OP is attempted while busy, an abstrcmderr will be asserted"""
|
||||||
|
abstractcs = int(read_dmi("0x16"), 16)
|
||||||
|
return bool((abstractcs & 0x1000) >> 12)
|
||||||
|
|
||||||
|
|
||||||
|
def check_absrtcmderr():
|
||||||
|
"""These errors must be cleared using clear_abstrcmd_err() before another OP can be executed"""
|
||||||
|
abstractcs = int(read_dmi("0x16"), 16)
|
||||||
|
# CmdErr is only valid if Busy is 0
|
||||||
|
busy = bool((abstractcs & 0x1000) >> 12)
|
||||||
|
while busy:
|
||||||
|
time.sleep(0.05)
|
||||||
|
abstractcs = int(read_dmi("0x16"), 16)
|
||||||
|
busy = bool((abstractcs & 0x1000) >> 12)
|
||||||
|
return cmderr_translations[(abstractcs & 0x700) >> 8]
|
||||||
|
|
||||||
|
|
||||||
|
def clear_abstrcmd_err():
|
||||||
|
write_dmi("0x16", "0x700")
|
||||||
|
|
||||||
|
|
||||||
|
def reset_dm():
|
||||||
|
deactivate_dm()
|
||||||
|
activate_dm()
|
||||||
|
|
||||||
|
|
||||||
|
def activate_dm():
|
||||||
|
write_dmi("0x10", "0x1")
|
||||||
|
return int(read_dmi("0x10"), 16) & 0x1
|
||||||
|
|
||||||
|
|
||||||
|
def deactivate_dm():
|
||||||
|
write_dmi("0x10", "0x0")
|
||||||
|
return not int(read_dmi("0x10"), 16) & 0x1
|
||||||
|
|
||||||
|
|
||||||
|
def dmi_reset():
|
||||||
|
"""Reset sticky dmi error status in DTM"""
|
||||||
|
write_dtmcs(dmireset=True)
|
||||||
|
check_errors()
|
||||||
|
|
||||||
|
|
||||||
|
def write_dmi(address, data):
|
||||||
|
cmd = f"riscv dmi_write {address} {data}"
|
||||||
|
rsp = execute(cmd)
|
||||||
|
if "Failed" in rsp:
|
||||||
|
print(rsp)
|
||||||
|
|
||||||
|
|
||||||
|
def read_dmi(address):
|
||||||
|
cmd = f"riscv dmi_read {address}"
|
||||||
|
return execute(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def write_dtmcs(dtmhardreset=False, dmireset=False):
|
||||||
|
data = 0
|
||||||
|
if dtmhardreset:
|
||||||
|
data += 0x1 << 17
|
||||||
|
if dmireset:
|
||||||
|
data += 0x1 << 16
|
||||||
|
execute(f"irscan {tapname} 0x10") # dtmcs instruction
|
||||||
|
execute(f"drscan {tapname} 32 {hex(data)}")
|
||||||
|
|
||||||
|
|
||||||
|
def read_dtmcs():
|
||||||
|
execute(f"irscan {tapname} 0x10") # dtmcs instruction
|
||||||
|
dtmcs = execute(f"drscan {tapname} 32 0x0")
|
||||||
|
return dtmcs
|
||||||
|
|
||||||
|
|
||||||
|
def trst():
|
||||||
|
execute("pathmove RESET IDLE")
|
||||||
|
|
||||||
|
|
||||||
|
def execute(cmd):
|
||||||
|
write(cmd)
|
||||||
|
return read()
|
||||||
|
|
||||||
|
|
||||||
|
def write(cmd):
|
||||||
|
if debug:
|
||||||
|
print(f"Executing command: '{cmd}'")
|
||||||
|
tn.write(cmd.encode('ascii') + b"\n")
|
||||||
|
tn.read_until(b"\n")
|
||||||
|
|
||||||
|
|
||||||
|
def read():
|
||||||
|
data = b""
|
||||||
|
data = tn.read_until(b"> ").decode('ascii')
|
||||||
|
data = data.replace("\r", "").replace("\n", "").replace("> ", "")
|
||||||
|
if debug:
|
||||||
|
print(data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def init():
|
||||||
|
global tn
|
||||||
|
tn = Telnet("127.0.0.1", 4444)
|
||||||
|
atexit.register(cleanup)
|
||||||
|
read() # clear welcome message from read buffer
|
||||||
|
activate_dm()
|
||||||
|
# TODO: query misa and get gpr count
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup():
|
||||||
|
tn.close()
|
||||||
|
|
||||||
|
# 6.1.4 dtmcs errinfo translation table
|
||||||
|
errinfo_translations = {
|
||||||
|
0 : "not implemented",
|
||||||
|
1 : "dmi error",
|
||||||
|
2 : "communication error",
|
||||||
|
3 : "device error",
|
||||||
|
4 : "unknown",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 6.1.5 DMI op translation table
|
||||||
|
op_translations = {
|
||||||
|
0 : "success",
|
||||||
|
1 : "reserved",
|
||||||
|
2 : "failed",
|
||||||
|
3 : "busy",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 3.14.6 Abstract command CmdErr value translation table
|
||||||
|
cmderr_translations = {
|
||||||
|
0 : None,
|
||||||
|
1 : "busy",
|
||||||
|
2 : "not supported",
|
||||||
|
3 : "exception",
|
||||||
|
4 : "halt/resume",
|
||||||
|
5 : "bus",
|
||||||
|
6 : "reserved",
|
||||||
|
7 : "other",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Register alias to regno translation table
|
||||||
|
register_translations = {
|
||||||
|
"MISA" : "0x0301",
|
||||||
|
"TRAPM" : "0xC000",
|
||||||
|
"PCM" : "0xC001",
|
||||||
|
"INSTRM" : "0xC002",
|
||||||
|
"MEMRWM" : "0xC003",
|
||||||
|
"INSTRVALIDM" : "0xC004",
|
||||||
|
"WRITEDATAM" : "0xC005",
|
||||||
|
"IEUADRM" : "0xC006",
|
||||||
|
"READDATAM" : "0xC007",
|
||||||
|
"X0" : "0x1000",
|
||||||
|
"X1" : "0x1001",
|
||||||
|
"X2" : "0x1002",
|
||||||
|
"X3" : "0x1003",
|
||||||
|
"X4" : "0x1004",
|
||||||
|
"X5" : "0x1005",
|
||||||
|
"X6" : "0x1006",
|
||||||
|
"X7" : "0x1007",
|
||||||
|
"X8" : "0x1008",
|
||||||
|
"X9" : "0x1009",
|
||||||
|
"X10" : "0x100A",
|
||||||
|
"X11" : "0x100B",
|
||||||
|
"X12" : "0x100C",
|
||||||
|
"X13" : "0x100D",
|
||||||
|
"X14" : "0x100E",
|
||||||
|
"X15" : "0x100F",
|
||||||
|
"X16" : "0x1010",
|
||||||
|
"X17" : "0x1011",
|
||||||
|
"X18" : "0x1012",
|
||||||
|
"X19" : "0x1013",
|
||||||
|
"X20" : "0x1014",
|
||||||
|
"X21" : "0x1015",
|
||||||
|
"X22" : "0x1016",
|
||||||
|
"X23" : "0x1017",
|
||||||
|
"X24" : "0x1018",
|
||||||
|
"X25" : "0x1019",
|
||||||
|
"X26" : "0x101A",
|
||||||
|
"X27" : "0x101B",
|
||||||
|
"X28" : "0x101C",
|
||||||
|
"X29" : "0x101D",
|
||||||
|
"X30" : "0x101E",
|
||||||
|
"X31" : "0x101F",
|
||||||
|
}
|
||||||
|
|
||||||
|
nonstandard_register_lengths = {
|
||||||
|
"TRAPM" : 1,
|
||||||
|
"INSTRM" : 32,
|
||||||
|
"MEMRWM" : 2,
|
||||||
|
"INSTRVALIDM" : 1,
|
||||||
|
#"READDATAM" : P.LLEN
|
||||||
|
}
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
110
bin/hw_test.py
Normal file
110
bin/hw_test.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
from hw_interface import *
|
||||||
|
|
||||||
|
random_stimulus = False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
registers = dict.fromkeys(register_translations.keys(),[])
|
||||||
|
reg_addrs = list(registers.keys())
|
||||||
|
|
||||||
|
init()
|
||||||
|
reset_dm()
|
||||||
|
reset_hart()
|
||||||
|
|
||||||
|
time.sleep(70) # wait for OpenSBI
|
||||||
|
|
||||||
|
halt()
|
||||||
|
status()
|
||||||
|
|
||||||
|
# dump data in all registers
|
||||||
|
for r in reg_addrs:
|
||||||
|
try:
|
||||||
|
data = read_data(r)
|
||||||
|
registers[r] = data
|
||||||
|
print(f"{r}: {data}")
|
||||||
|
except Exception as e:
|
||||||
|
if e.args[0] == "exception": # Invalid register (not implemented)
|
||||||
|
del registers[r]
|
||||||
|
clear_abstrcmd_err()
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
input("Compare values to ILA, press any key to continue")
|
||||||
|
|
||||||
|
# Write random data to all registers
|
||||||
|
reg_addrs = list(registers.keys())
|
||||||
|
if random_stimulus:
|
||||||
|
random.shuffle(reg_addrs)
|
||||||
|
test_reg_data = {}
|
||||||
|
for r in reg_addrs:
|
||||||
|
test_data = random_hex(r)
|
||||||
|
try:
|
||||||
|
write_data(r, test_data)
|
||||||
|
test_reg_data[r] = test_data
|
||||||
|
print(f"Writing {test_data} to {r}")
|
||||||
|
except Exception as e:
|
||||||
|
if e.args[0] == "not supported": # Register is read only
|
||||||
|
del registers[r]
|
||||||
|
clear_abstrcmd_err()
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
check_errors()
|
||||||
|
|
||||||
|
# GPR X0 is always 0
|
||||||
|
test_reg_data["X0"] = "0x" + "0"*(XLEN//4)
|
||||||
|
|
||||||
|
# Confirm data was written correctly
|
||||||
|
reg_addrs = list(registers.keys())
|
||||||
|
if random_stimulus:
|
||||||
|
random.shuffle(reg_addrs)
|
||||||
|
for r in reg_addrs:
|
||||||
|
try:
|
||||||
|
rdata = read_data(r)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
if rdata != test_reg_data[r]:
|
||||||
|
raise Exception(f"Register {r} read did not return correct data: {rdata} != {test_reg_data[r]}")
|
||||||
|
else:
|
||||||
|
print(f"Read {rdata} from {r}")
|
||||||
|
|
||||||
|
# Return all registers to original state
|
||||||
|
reg_addrs = list(registers.keys())
|
||||||
|
for r in reg_addrs:
|
||||||
|
print(f"Writing {registers[r]} to {r}")
|
||||||
|
try:
|
||||||
|
write_data(r, registers[r])
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# Confirm data was written correctly
|
||||||
|
for r in reg_addrs:
|
||||||
|
try:
|
||||||
|
rdata = read_data(r)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
if rdata != registers[r]:
|
||||||
|
raise Exception(f"Register {r} read did not return correct data: {rdata} != {registers[r]}")
|
||||||
|
print("All writes successful")
|
||||||
|
|
||||||
|
resume()
|
||||||
|
status()
|
||||||
|
|
||||||
|
|
||||||
|
def random_hex(reg_name):
|
||||||
|
if reg_name in nonstandard_register_lengths:
|
||||||
|
size = nonstandard_register_lengths[reg_name]
|
||||||
|
pad = (XLEN-size) // 4
|
||||||
|
else:
|
||||||
|
size = XLEN
|
||||||
|
pad = 0
|
||||||
|
|
||||||
|
if random_stimulus:
|
||||||
|
return f"0x{"0"*pad}{random.getrandbits(size):x}"
|
||||||
|
else:
|
||||||
|
data = 0xa5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5
|
||||||
|
return f"0x{"0"*pad}{(data & (2**size-1)):x}"
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
211
config/shared/debug.vh
Executable file
211
config/shared/debug.vh
Executable file
@ -0,0 +1,211 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// debug.vh
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu
|
||||||
|
// Created: 15 March 2024
|
||||||
|
//
|
||||||
|
// Purpose: debug port definitions
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// DMI op field constants
|
||||||
|
`define OP_NOP 2'b00
|
||||||
|
`define OP_READ 2'b01
|
||||||
|
`define OP_WRITE 2'b10
|
||||||
|
`define OP_SUCCESS 2'b00
|
||||||
|
`define OP_FAILED 2'b10
|
||||||
|
`define OP_BUSY 2'b11
|
||||||
|
|
||||||
|
// Debug Bus Address Width
|
||||||
|
`define ADDR_WIDTH 7
|
||||||
|
|
||||||
|
// Debug Module Debug Bus Register Addresses
|
||||||
|
// DM Internal registers
|
||||||
|
`define DATA0 `ADDR_WIDTH'h04
|
||||||
|
`define DATA1 `ADDR_WIDTH'h05
|
||||||
|
`define DATA2 `ADDR_WIDTH'h06
|
||||||
|
`define DATA3 `ADDR_WIDTH'h07
|
||||||
|
`define DATA4 `ADDR_WIDTH'h08
|
||||||
|
`define DATA5 `ADDR_WIDTH'h09
|
||||||
|
`define DATA6 `ADDR_WIDTH'h0A
|
||||||
|
`define DATA7 `ADDR_WIDTH'h0B
|
||||||
|
`define DATA8 `ADDR_WIDTH'h0C
|
||||||
|
`define DATA9 `ADDR_WIDTH'h0D
|
||||||
|
`define DATA10 `ADDR_WIDTH'h0E
|
||||||
|
`define DATA11 `ADDR_WIDTH'h0F
|
||||||
|
`define DMCONTROL `ADDR_WIDTH'h10
|
||||||
|
`define DMSTATUS `ADDR_WIDTH'h11
|
||||||
|
`define HARTINFO `ADDR_WIDTH'h12
|
||||||
|
`define ABSTRACTCS `ADDR_WIDTH'h16
|
||||||
|
`define COMMAND `ADDR_WIDTH'h17
|
||||||
|
`define ABSTRACTAUTO `ADDR_WIDTH'h18
|
||||||
|
`define NEXTDM `ADDR_WIDTH'h1d
|
||||||
|
//`define dmcs2 `ADDR_WIDTH'h32
|
||||||
|
`define SBCS `ADDR_WIDTH'h38
|
||||||
|
|
||||||
|
|
||||||
|
//// Register field ranges
|
||||||
|
// DMCONTROL 0x10
|
||||||
|
`define HALTREQ 31
|
||||||
|
`define RESUMEREQ 30
|
||||||
|
`define HARTRESET 29
|
||||||
|
`define ACKHAVERESET 28
|
||||||
|
`define ACKUNAVAIL 27
|
||||||
|
`define HASEL 26
|
||||||
|
`define HARTSELLO 25:16
|
||||||
|
`define HARTSELHI 15:6
|
||||||
|
`define SETKEEPALIVE 5
|
||||||
|
`define CLRKEEPALIVE 4
|
||||||
|
`define SETRESETHALTREQ 3
|
||||||
|
`define CLRRESETHALTREQ 2
|
||||||
|
`define NDMRESET 1
|
||||||
|
`define DMACTIVE 0
|
||||||
|
|
||||||
|
// DMSTATUS 0x11
|
||||||
|
`define NDMRESETPENDING 24
|
||||||
|
`define STICKYUNAVAIL 23
|
||||||
|
`define IMPEBREAK 22
|
||||||
|
`define ALLHAVERESET 19
|
||||||
|
`define ANYHAVERESET 18
|
||||||
|
`define ALLRESUMEACK 17
|
||||||
|
`define ANYRESUMEACK 16
|
||||||
|
`define ALLNONEXISTENT 15
|
||||||
|
`define ANYNONEXISTENT 14
|
||||||
|
`define ALLUNAVAIL 13
|
||||||
|
`define ANYUNAVAIL 12
|
||||||
|
`define ALLRUNNING 11
|
||||||
|
`define ANYRUNNING 10
|
||||||
|
`define ALLHALTED 9
|
||||||
|
`define ANYHALTED 8
|
||||||
|
`define AUTHENTICATED 7
|
||||||
|
`define AUTHBUSY 6
|
||||||
|
`define HASRESETHALTREQ 5
|
||||||
|
`define CONFSTRPTRVALID 4
|
||||||
|
`define VERSION 3:0
|
||||||
|
|
||||||
|
// ABSTRACTCS 0x16
|
||||||
|
`define PROGBUFSIZE 28:24
|
||||||
|
`define BUSY 12
|
||||||
|
`define RELAXEDPRIV 11
|
||||||
|
`define CMDERR 10:8
|
||||||
|
`define DATACOUNT 3:0
|
||||||
|
|
||||||
|
// COMMAND 0x17
|
||||||
|
`define CMDTYPE 31:24
|
||||||
|
`define CONTROL 23:0
|
||||||
|
|
||||||
|
//// Abstract Commands
|
||||||
|
// cmderr
|
||||||
|
`define CMDERR_NONE 3'h0
|
||||||
|
`define CMDERR_BUSY 3'h1
|
||||||
|
`define CMDERR_NOT_SUPPORTED 3'h2
|
||||||
|
`define CMDERR_EXCEPTION 3'h3
|
||||||
|
`define CMDERR_HALTRESUME 3'h4
|
||||||
|
`define CMDERR_BUS 3'h5
|
||||||
|
`define CMDERR_OTHER 3'h7
|
||||||
|
|
||||||
|
// Abstract CmdType Constants (3.7.1)
|
||||||
|
`define ACCESS_REGISTER 0
|
||||||
|
`define QUICK_ACCESS 1
|
||||||
|
`define ACCESS_MEMORY 2
|
||||||
|
|
||||||
|
// ACCESS_REGISTER Control ranges
|
||||||
|
`define AARSIZE 22:20
|
||||||
|
`define AARPOSTINCREMENT 19
|
||||||
|
`define POSTEXEC 18
|
||||||
|
`define TRANSFER 17
|
||||||
|
`define AARWRITE 16
|
||||||
|
`define REGNO 15:0
|
||||||
|
|
||||||
|
// aarsize
|
||||||
|
`define AAR32 2
|
||||||
|
`define AAR64 3
|
||||||
|
`define AAR128 4
|
||||||
|
|
||||||
|
// Register Numbers (regno)
|
||||||
|
// (Table 3.3)
|
||||||
|
// 0x0000 – 0x0fff | CSRs. The “PC” can be accessed here through dpc.
|
||||||
|
// 0x1000 – 0x101f | GPRs
|
||||||
|
// 0x1020 – 0x103f | Floating point registers
|
||||||
|
// 0xc000 – 0xffff | Reserved for non-standard extensions and internal use.
|
||||||
|
|
||||||
|
// privileged/csr/csrm
|
||||||
|
`define MISA 16'h0301 // XLEN P.ZICSR_SUPPORTED (Read Only)
|
||||||
|
// wallypipelinedcore
|
||||||
|
`define TRAPM 16'hC000 // 1'b P.ZICSR_SUPPORTED (Read Only)
|
||||||
|
// src/ifu
|
||||||
|
`define PCM 16'hC001 // XLEN P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED
|
||||||
|
`define INSTRM 16'hC002 // 32'b P.ZICSR_SUPPORTED | P.A_SUPPORTED
|
||||||
|
// ieu/controller
|
||||||
|
`define MEMRWM 16'hC003 // 2'b
|
||||||
|
`define INSTRVALIDM 16'hC004 // 1'b
|
||||||
|
// ieu/datapath
|
||||||
|
`define WRITEDATAM 16'hC005 // XLEN
|
||||||
|
// lsu
|
||||||
|
`define IEUADRM 16'hC006 // XLEN
|
||||||
|
`define READDATAM 16'hC007 // LLEN (Read Only)
|
||||||
|
|
||||||
|
// src/ieu/datapath
|
||||||
|
`define X0 16'h1000
|
||||||
|
`define X1 16'h1001
|
||||||
|
`define X2 16'h1002
|
||||||
|
`define X3 16'h1003
|
||||||
|
`define X4 16'h1004
|
||||||
|
`define X5 16'h1005
|
||||||
|
`define X6 16'h1006
|
||||||
|
`define X7 16'h1007
|
||||||
|
`define X8 16'h1008
|
||||||
|
`define X9 16'h1009
|
||||||
|
`define X10 16'h100A
|
||||||
|
`define X11 16'h100B
|
||||||
|
`define X12 16'h100C
|
||||||
|
`define X13 16'h100D
|
||||||
|
`define X14 16'h100E
|
||||||
|
`define X15 16'h100F
|
||||||
|
`define X16 16'h1010 // E_SUPPORTED
|
||||||
|
`define X17 16'h1011 // E_SUPPORTED
|
||||||
|
`define X18 16'h1012 // E_SUPPORTED
|
||||||
|
`define X19 16'h1013 // E_SUPPORTED
|
||||||
|
`define X20 16'h1014 // E_SUPPORTED
|
||||||
|
`define X21 16'h1015 // E_SUPPORTED
|
||||||
|
`define X22 16'h1016 // E_SUPPORTED
|
||||||
|
`define X23 16'h1017 // E_SUPPORTED
|
||||||
|
`define X24 16'h1018 // E_SUPPORTED
|
||||||
|
`define X25 16'h1019 // E_SUPPORTED
|
||||||
|
`define X26 16'h101A // E_SUPPORTED
|
||||||
|
`define X27 16'h101B // E_SUPPORTED
|
||||||
|
`define X28 16'h101C // E_SUPPORTED
|
||||||
|
`define X29 16'h101D // E_SUPPORTED
|
||||||
|
`define X30 16'h101E // E_SUPPORTED
|
||||||
|
`define X31 16'h101F // E_SUPPORTED
|
||||||
|
|
||||||
|
// ACCESS_MEMORY Control ranges (Not implemented)
|
||||||
|
//`define AAMVIRTUAL 23
|
||||||
|
//`define AAMSIZE 22:20
|
||||||
|
//`define AAMPOSTINCREMENT 19
|
||||||
|
//`define AAMWRITE 16
|
||||||
|
//`define TARGET_SPECIFIC 15:14
|
||||||
|
|
||||||
|
// aamsize
|
||||||
|
//`define AAM8 0
|
||||||
|
//`define AAM16 1
|
||||||
|
//`define AAM32 2
|
||||||
|
//`define AAM64 3
|
||||||
|
//`define AAM128 4
|
30
openocd.cfg
Normal file
30
openocd.cfg
Normal file
@ -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" (<bus>-<port>)
|
||||||
|
adapter usb location 1-3
|
||||||
|
|
||||||
|
ftdi vid_pid 0x0403 0x6010
|
||||||
|
ftdi channel 0
|
||||||
|
|
||||||
|
#TODO: figure out which of these bits need to be set
|
||||||
|
# data MSB..LSB direction (1:out) MSB..LSB
|
||||||
|
# 0000'0000'0011'0000 0000'0000'0011'1011
|
||||||
|
ftdi layout_init 0x0030 0x003b
|
||||||
|
|
||||||
|
transport select jtag
|
||||||
|
adapter speed 1000
|
||||||
|
|
||||||
|
set _CHIPNAME cvw
|
||||||
|
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0xDEADBEEF
|
||||||
|
|
||||||
|
set _TARGETNAME $_CHIPNAME.cpu
|
||||||
|
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
|
||||||
|
|
||||||
|
init
|
||||||
|
# this is useful for manual debugging, but breaks gdb
|
||||||
|
poll off
|
@ -294,6 +294,9 @@ typedef struct packed {
|
|||||||
int DURLEN ;
|
int DURLEN ;
|
||||||
int DIVb ;
|
int DIVb ;
|
||||||
int DIVBLEN ;
|
int DIVBLEN ;
|
||||||
|
|
||||||
|
// Debug Module
|
||||||
|
logic DEBUG_SUPPORTED;
|
||||||
} cvw_t;
|
} cvw_t;
|
||||||
|
|
||||||
endpackage
|
endpackage
|
||||||
|
436
src/debug/dm.sv
Normal file
436
src/debug/dm.sv
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// dm.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu
|
||||||
|
// Created: 15 March 2024
|
||||||
|
//
|
||||||
|
// Purpose: Main debug module (dm) for Debug Specification
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License Version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module dm import cvw::*; #(parameter cvw_t P) (
|
||||||
|
input logic clk,
|
||||||
|
input logic rst, // Full hardware reset signal (reset button)
|
||||||
|
|
||||||
|
// External JTAG signals
|
||||||
|
input logic tck,
|
||||||
|
input logic tdi,
|
||||||
|
input logic tms,
|
||||||
|
output logic tdo,
|
||||||
|
|
||||||
|
// Platform reset signal
|
||||||
|
output logic NdmReset,
|
||||||
|
// Core hazard signal
|
||||||
|
output logic DebugStall,
|
||||||
|
|
||||||
|
// Scan Chain
|
||||||
|
output logic ScanEn,
|
||||||
|
input logic ScanIn,
|
||||||
|
output logic ScanOut,
|
||||||
|
output logic GPRSel,
|
||||||
|
output logic DebugCapture,
|
||||||
|
output logic DebugGPRUpdate,
|
||||||
|
output logic [P.E_SUPPORTED+3:0] GPRAddr,
|
||||||
|
output logic GPRScanEn,
|
||||||
|
input logic GPRScanIn,
|
||||||
|
output logic GPRScanOut
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "debug.vh"
|
||||||
|
|
||||||
|
// DMI Signals
|
||||||
|
logic ReqReady;
|
||||||
|
logic ReqValid;
|
||||||
|
logic [`ADDR_WIDTH-1:0] ReqAddress;
|
||||||
|
logic [31:0] ReqData;
|
||||||
|
logic [1:0] ReqOP;
|
||||||
|
logic RspReady;
|
||||||
|
logic RspValid;
|
||||||
|
logic [31:0] RspData;
|
||||||
|
logic [1:0] RspOP;
|
||||||
|
|
||||||
|
localparam JTAG_DEVICE_ID = 32'hdeadbeef; // TODO: put JTAG device ID in parameter struct
|
||||||
|
|
||||||
|
dtm #(`ADDR_WIDTH, JTAG_DEVICE_ID) dtm (.clk, .tck, .tdi, .tms, .tdo,
|
||||||
|
.ReqReady, .ReqValid, .ReqAddress, .ReqData, .ReqOP, .RspReady,
|
||||||
|
.RspValid, .RspData, .RspOP);
|
||||||
|
|
||||||
|
// Core control signals
|
||||||
|
logic HaltReq;
|
||||||
|
logic ResumeReq;
|
||||||
|
logic HaltOnReset;
|
||||||
|
logic Halted;
|
||||||
|
|
||||||
|
hartcontrol hartcontrol(.clk, .rst(rst | ~DmActive), .NdmReset, .HaltReq,
|
||||||
|
.ResumeReq, .HaltOnReset, .DebugStall, .Halted, .AllRunning,
|
||||||
|
.AnyRunning, .AllHalted, .AnyHalted, .AllResumeAck, .AnyResumeAck);
|
||||||
|
|
||||||
|
enum logic [3:0] {INACTIVE, IDLE, ACK, R_DATA, W_DATA, DMSTATUS, W_DMCONTROL, R_DMCONTROL,
|
||||||
|
W_ABSTRACTCS, R_ABSTRACTCS, ABST_COMMAND, R_SYSBUSCS, READ_ZERO,
|
||||||
|
INVALID} State;
|
||||||
|
|
||||||
|
enum logic [1:0] {AC_IDLE, AC_GPRUPDATE, AC_SCAN, AC_CAPTURE} AcState, NewAcState;
|
||||||
|
|
||||||
|
// AbsCmd internal state
|
||||||
|
logic AcWrite; // Abstract Command write state
|
||||||
|
logic [P.XLEN:0] ScanReg; // The part of the debug scan chain located within DM
|
||||||
|
logic [P.XLEN-1:0] ScanNext; // New ScanReg value
|
||||||
|
logic [P.XLEN-1:0] ARMask; // Masks which bits of the ScanReg get updated
|
||||||
|
logic [P.XLEN-1:0] PackedDataReg; // Combines DataX msg registers into a single XLEN wide register
|
||||||
|
logic [P.XLEN-1:0] MaskedScanReg; // Masks which bits of the ScanReg get written to DataX
|
||||||
|
logic [9:0] ShiftCount; // Position of the selected register on the debug scan chain
|
||||||
|
logic [9:0] ScanChainLen; // Total length of currently selected scan chain
|
||||||
|
logic [9:0] Cycle; // DM's current position in the scan chain
|
||||||
|
logic InvalidRegNo; // Requested RegNo is invalid
|
||||||
|
logic RegReadOnly; // Current RegNo points to a readonly register
|
||||||
|
logic GPRRegNo; // Requested RegNo is a GPR
|
||||||
|
logic StoreScanChain; // Store current value of ScanReg into DataX
|
||||||
|
logic WriteMsgReg; // Write to DataX
|
||||||
|
logic WriteScanReg; // Insert data from DataX into ScanReg
|
||||||
|
logic [31:0] Data0Wr; // Muxed inputs to DataX regs
|
||||||
|
logic [31:0] Data1Wr; // Muxed inputs to DataX regs
|
||||||
|
logic [31:0] Data2Wr; // Muxed inputs to DataX regs
|
||||||
|
logic [31:0] Data3Wr; // Muxed inputs to DataX regs
|
||||||
|
|
||||||
|
// message registers
|
||||||
|
logic [31:0] Data0; // 0x04
|
||||||
|
logic [31:0] Data1; // 0x05
|
||||||
|
logic [31:0] Data2; // 0x06
|
||||||
|
logic [31:0] Data3; // 0x07
|
||||||
|
|
||||||
|
// debug module registers
|
||||||
|
logic [31:0] DMControl; // 0x10
|
||||||
|
logic [31:0] DMStatus; // 0x11
|
||||||
|
logic [31:0] AbstractCS; // 0x16
|
||||||
|
logic [31:0] SysBusCS; // 0x38
|
||||||
|
|
||||||
|
// DM register fields
|
||||||
|
|
||||||
|
// DMControl
|
||||||
|
logic AckUnavail;
|
||||||
|
logic DmActive; // This bit is used to (de)activate the DM. Toggling acts as reset
|
||||||
|
// DMStatus
|
||||||
|
logic StickyUnavail;
|
||||||
|
logic ImpEBreak;
|
||||||
|
logic AllResumeAck;
|
||||||
|
logic AnyResumeAck;
|
||||||
|
logic AllNonExistent;
|
||||||
|
logic AnyNonExistent;
|
||||||
|
logic AllUnavail; // TODO
|
||||||
|
logic AnyUnavail;
|
||||||
|
logic AllRunning;
|
||||||
|
logic AnyRunning;
|
||||||
|
logic AllHalted;
|
||||||
|
logic AnyHalted;
|
||||||
|
const logic Authenticated = 1;
|
||||||
|
logic AuthBusy;
|
||||||
|
const logic HasResetHaltReq = 1;
|
||||||
|
logic ConfStrPtrValid;
|
||||||
|
const logic [3:0] Version = 3; // DM Version
|
||||||
|
// AbstractCS
|
||||||
|
const logic [4:0] ProgBufSize = 0;
|
||||||
|
logic Busy;
|
||||||
|
const logic RelaxedPriv = 1;
|
||||||
|
logic [2:0] CmdErr;
|
||||||
|
const logic [3:0] DataCount = (P.XLEN/32);
|
||||||
|
|
||||||
|
// Pack registers
|
||||||
|
assign DMControl = {2'b0, 1'b0, 2'b0, 1'b0, 10'b0,
|
||||||
|
10'b0, 4'b0, NdmReset, DmActive};
|
||||||
|
|
||||||
|
assign DMStatus = {7'b0, 1'b0, StickyUnavail, ImpEBreak, 2'b0,
|
||||||
|
2'b0, AllResumeAck, AnyResumeAck, AllNonExistent,
|
||||||
|
AnyNonExistent, AllUnavail, AnyUnavail, AllRunning, AnyRunning, AllHalted,
|
||||||
|
AnyHalted, Authenticated, AuthBusy, HasResetHaltReq, ConfStrPtrValid, Version};
|
||||||
|
|
||||||
|
assign AbstractCS = {3'b0, ProgBufSize, 11'b0, Busy, RelaxedPriv, CmdErr, 4'b0, DataCount};
|
||||||
|
|
||||||
|
assign SysBusCS = 32'h20000000; // SBVersion = 1
|
||||||
|
|
||||||
|
assign RspValid = (State == ACK);
|
||||||
|
assign ReqReady = (State != ACK);
|
||||||
|
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
if (rst) begin
|
||||||
|
DmActive <= 0;
|
||||||
|
State <= INACTIVE;
|
||||||
|
end else begin
|
||||||
|
case (State)
|
||||||
|
INACTIVE : begin
|
||||||
|
// Reset Values
|
||||||
|
RspData <= 0;
|
||||||
|
HaltReq <= 0;
|
||||||
|
HaltOnReset <= 0;
|
||||||
|
NdmReset <= 0;
|
||||||
|
StickyUnavail <= 0;
|
||||||
|
ImpEBreak <= 0;
|
||||||
|
AuthBusy <= 0;
|
||||||
|
ConfStrPtrValid <= 0;
|
||||||
|
CmdErr <= 0;
|
||||||
|
if (ReqValid) begin
|
||||||
|
if (ReqAddress == `DMCONTROL & ReqOP == `OP_WRITE & ReqData[`DMACTIVE]) begin
|
||||||
|
DmActive <= ReqData[`DMACTIVE];
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
end
|
||||||
|
State <= ACK; // acknowledge all Reqs even if they don't activate DM
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ACK : begin
|
||||||
|
NewAcState <= AC_IDLE;
|
||||||
|
ResumeReq <= 0;
|
||||||
|
if (~ReqValid)
|
||||||
|
State <= ~DmActive ? INACTIVE : IDLE;
|
||||||
|
end
|
||||||
|
|
||||||
|
IDLE : begin
|
||||||
|
if (ReqValid)
|
||||||
|
case ({ReqOP, ReqAddress}) inside
|
||||||
|
{`OP_WRITE,`DATA0} : State <= W_DATA;
|
||||||
|
{`OP_READ,`DATA0} : State <= R_DATA;
|
||||||
|
{`OP_WRITE,`DATA1} : State <= (P.XLEN >= 64) ? W_DATA : INVALID;
|
||||||
|
{`OP_READ,`DATA1} : State <= (P.XLEN >= 64) ? R_DATA : INVALID;
|
||||||
|
[{`OP_WRITE,`DATA2}:{`OP_WRITE,`DATA3}] : State <= (P.XLEN >= 128) ? W_DATA : INVALID;
|
||||||
|
[{`OP_READ,`DATA2}:{`OP_READ,`DATA3}] : State <= (P.XLEN >= 128) ? R_DATA : INVALID;
|
||||||
|
{`OP_WRITE,`DMCONTROL} : State <= W_DMCONTROL;
|
||||||
|
{`OP_READ,`DMCONTROL} : State <= R_DMCONTROL;
|
||||||
|
{`OP_READ,`DMSTATUS} : State <= DMSTATUS;
|
||||||
|
{`OP_WRITE,`ABSTRACTCS} : State <= W_ABSTRACTCS;
|
||||||
|
{`OP_READ,`ABSTRACTCS} : State <= R_ABSTRACTCS;
|
||||||
|
{`OP_WRITE,`COMMAND} : State <= ABST_COMMAND;
|
||||||
|
{`OP_READ,`COMMAND} : State <= READ_ZERO;
|
||||||
|
{`OP_WRITE,`SBCS} : State <= READ_ZERO;
|
||||||
|
{`OP_READ,`SBCS} : State <= R_SYSBUSCS;
|
||||||
|
{2'bx,`HARTINFO},
|
||||||
|
{2'bx,`ABSTRACTAUTO},
|
||||||
|
{2'bx,`NEXTDM} : State <= READ_ZERO;
|
||||||
|
default : State <= INVALID;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
R_DATA : begin
|
||||||
|
if (Busy)
|
||||||
|
CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr;
|
||||||
|
case (ReqAddress)
|
||||||
|
`DATA0 : RspData <= Data0;
|
||||||
|
`DATA1 : RspData <= Data1;
|
||||||
|
`DATA2 : RspData <= Data2;
|
||||||
|
`DATA3 : RspData <= Data3;
|
||||||
|
endcase
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
W_DATA : begin
|
||||||
|
if (Busy)
|
||||||
|
CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr;
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
W_DMCONTROL : begin
|
||||||
|
// While an abstract command is executing (busy in abstractcs is high), a debugger must not change
|
||||||
|
// hartsel, and must not write 1 to haltreq, resumereq, ackhavereset, setresethaltreq, or clrresethaltreq
|
||||||
|
if (Busy & (ReqData[`HALTREQ] | ReqData[`RESUMEREQ] | ReqData[`SETRESETHALTREQ] | ReqData[`CLRRESETHALTREQ]))
|
||||||
|
CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr;
|
||||||
|
else begin
|
||||||
|
HaltReq <= ReqData[`HALTREQ];
|
||||||
|
AckUnavail <= ReqData[`ACKUNAVAIL];
|
||||||
|
NdmReset <= ReqData[`NDMRESET];
|
||||||
|
DmActive <= ReqData[`DMACTIVE]; // Writing 0 here resets the DM
|
||||||
|
|
||||||
|
// On any given write, a debugger may only write 1 to at most one of the following bits: resumereq,
|
||||||
|
// hartreset, ackhavereset, setresethaltreq, and clrresethaltreq. The others must be written 0
|
||||||
|
case ({ReqData[`RESUMEREQ],ReqData[`SETRESETHALTREQ],ReqData[`CLRRESETHALTREQ]})
|
||||||
|
3'b000 :; // None
|
||||||
|
3'b100 : ResumeReq <= 1;
|
||||||
|
3'b010 : HaltOnReset <= 1;
|
||||||
|
3'b001 : HaltOnReset <= 0;
|
||||||
|
default : begin // Invalid (not onehot), dont write any changes
|
||||||
|
HaltReq <= HaltReq;
|
||||||
|
AckUnavail <= AckUnavail;
|
||||||
|
NdmReset <= NdmReset;
|
||||||
|
DmActive <= DmActive;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
R_DMCONTROL : begin
|
||||||
|
RspData <= DMControl;
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
DMSTATUS : begin
|
||||||
|
RspData <= DMStatus;
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
W_ABSTRACTCS : begin
|
||||||
|
if (Busy)
|
||||||
|
CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr;
|
||||||
|
else
|
||||||
|
CmdErr <= |ReqData[`CMDERR] ? `CMDERR_NONE : CmdErr; // clear CmdErr
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
R_ABSTRACTCS : begin
|
||||||
|
RspData <= AbstractCS;
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
ABST_COMMAND : begin
|
||||||
|
if (CmdErr != `CMDERR_NONE); // If CmdErr, do nothing
|
||||||
|
else if (Busy)
|
||||||
|
CmdErr <= `CMDERR_BUSY; // If Busy, set CmdErr, do nothing
|
||||||
|
else if (~Halted)
|
||||||
|
CmdErr <= `CMDERR_HALTRESUME; // If not halted, set CmdErr, do nothing
|
||||||
|
else begin
|
||||||
|
case (ReqData[`CMDTYPE])
|
||||||
|
`ACCESS_REGISTER : begin
|
||||||
|
if (ReqData[`AARSIZE] > $clog2(P.XLEN/8)) // if AARSIZE (encoded) is greater than P.XLEN, set CmdErr, do nothing
|
||||||
|
CmdErr <= `CMDERR_BUS;
|
||||||
|
else if (~ReqData[`TRANSFER]); // If not TRANSFER, do nothing
|
||||||
|
else if (InvalidRegNo)
|
||||||
|
CmdErr <= `CMDERR_EXCEPTION; // If InvalidRegNo, set CmdErr, do nothing
|
||||||
|
else if (ReqData[`AARWRITE] & RegReadOnly)
|
||||||
|
CmdErr <= `CMDERR_NOT_SUPPORTED; // If writing to a read only register, set CmdErr, do nothing
|
||||||
|
else begin
|
||||||
|
AcWrite <= ReqData[`AARWRITE];
|
||||||
|
NewAcState <= AC_SCAN;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
//`QUICK_ACCESS : State <= QUICK_ACCESS;
|
||||||
|
//`ACCESS_MEMORY : State <= ACCESS_MEMORY;
|
||||||
|
default : CmdErr <= `CMDERR_NOT_SUPPORTED;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
R_SYSBUSCS : begin
|
||||||
|
RspData <= SysBusCS;
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
READ_ZERO : begin // Writes ignored, Read Zero
|
||||||
|
RspData <= 0;
|
||||||
|
RspOP <= `OP_SUCCESS;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
INVALID : begin
|
||||||
|
RspOP <= `OP_FAILED;
|
||||||
|
State <= ACK;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Abstract command engine
|
||||||
|
// Due to length of the register scan chain,
|
||||||
|
// abstract commands execute independently of other DM operations
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
if (rst)
|
||||||
|
AcState <= AC_IDLE;
|
||||||
|
else begin
|
||||||
|
case (AcState)
|
||||||
|
AC_IDLE : begin
|
||||||
|
Cycle <= 0;
|
||||||
|
case (NewAcState)
|
||||||
|
AC_SCAN : AcState <= ~AcWrite ? AC_CAPTURE : AC_SCAN;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
AC_CAPTURE : begin
|
||||||
|
AcState <= AC_SCAN;
|
||||||
|
end
|
||||||
|
|
||||||
|
AC_SCAN : begin
|
||||||
|
if (Cycle == ScanChainLen)
|
||||||
|
AcState <= (GPRRegNo & AcWrite) ? AC_GPRUPDATE : AC_IDLE;
|
||||||
|
else
|
||||||
|
Cycle <= Cycle + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
AC_GPRUPDATE : begin
|
||||||
|
AcState <= AC_IDLE;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign Busy = ~(AcState == AC_IDLE);
|
||||||
|
assign DebugCapture = (AcState == AC_CAPTURE);
|
||||||
|
assign DebugGPRUpdate = (AcState == AC_GPRUPDATE);
|
||||||
|
|
||||||
|
// Scan Chain
|
||||||
|
assign GPRSel = GPRRegNo & (AcState != AC_IDLE);
|
||||||
|
assign ScanReg[P.XLEN] = GPRSel ? GPRScanIn : ScanIn;
|
||||||
|
assign ScanOut = GPRSel ? 1'b0 : ScanReg[0];
|
||||||
|
assign GPRScanOut = GPRSel ? ScanReg[0] : 1'b0;
|
||||||
|
assign ScanEn = ~GPRSel & (AcState == AC_SCAN);
|
||||||
|
assign GPRScanEn = GPRSel & (AcState == AC_SCAN);
|
||||||
|
|
||||||
|
// Load data from message registers into scan chain
|
||||||
|
if (P.XLEN == 32)
|
||||||
|
assign PackedDataReg = Data0;
|
||||||
|
else if (P.XLEN == 64)
|
||||||
|
assign PackedDataReg = {Data1,Data0};
|
||||||
|
else if (P.XLEN == 128)
|
||||||
|
assign PackedDataReg = {Data3,Data2,Data1,Data0};
|
||||||
|
|
||||||
|
assign WriteScanReg = AcWrite & (~GPRRegNo & (Cycle == ShiftCount) | GPRRegNo & (Cycle == 0));
|
||||||
|
genvar i;
|
||||||
|
for (i=0; i<P.XLEN; i=i+1) begin
|
||||||
|
// ARMask is used as write enable for subword overwrites (basic mask would overwrite neighbors in the chain)
|
||||||
|
assign ScanNext[i] = WriteScanReg & ARMask[i] ? PackedDataReg[i] : ScanReg[i+1];
|
||||||
|
flopenr #(1) scanreg (.clk, .reset(rst), .en(AcState == AC_SCAN), .d(ScanNext[i]), .q(ScanReg[i]));
|
||||||
|
end
|
||||||
|
|
||||||
|
// Message Registers
|
||||||
|
assign MaskedScanReg = ARMask & ScanReg[P.XLEN:1];
|
||||||
|
assign WriteMsgReg = (State == W_DATA) & ~Busy;
|
||||||
|
assign StoreScanChain = (AcState == AC_SCAN) & (Cycle == ShiftCount) & ~AcWrite;
|
||||||
|
|
||||||
|
assign Data0Wr = StoreScanChain ? MaskedScanReg[31:0] : ReqData;;
|
||||||
|
flopenr #(32) data0reg (.clk, .reset(rst), .en(StoreScanChain | WriteMsgReg & (ReqAddress == `DATA0)), .d(Data0Wr), .q(Data0));
|
||||||
|
if (P.XLEN >= 64) begin
|
||||||
|
assign Data1Wr = StoreScanChain ? MaskedScanReg[63:32] : ReqData;
|
||||||
|
flopenr #(32) data1reg (.clk, .reset(rst), .en(StoreScanChain | WriteMsgReg & (ReqAddress == `DATA1)), .d(Data1Wr), .q(Data1));
|
||||||
|
end
|
||||||
|
if (P.XLEN == 128) begin
|
||||||
|
assign Data2Wr = StoreScanChain ? MaskedScanReg[95:64] : ReqData;
|
||||||
|
assign Data3Wr = StoreScanChain ? MaskedScanReg[127:96] : ReqData;
|
||||||
|
flopenr #(32) data2reg (.clk, .reset(rst), .en(StoreScanChain | WriteMsgReg & (ReqAddress == `DATA2)), .d(Data2Wr), .q(Data2));
|
||||||
|
flopenr #(32) data3reg (.clk, .reset(rst), .en(StoreScanChain | WriteMsgReg & (ReqAddress == `DATA3)), .d(Data3Wr), .q(Data3));
|
||||||
|
end
|
||||||
|
|
||||||
|
rad #(P) regnodecode(.AarSize(ReqData[`AARSIZE]),.Regno(ReqData[`REGNO]),.GPRRegNo,.ScanChainLen,.ShiftCount,.InvalidRegNo,.RegReadOnly,.GPRAddr,.ARMask);
|
||||||
|
endmodule
|
163
src/debug/dtm.sv
Normal file
163
src/debug/dtm.sv
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// dtm.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu
|
||||||
|
// Created: 15 March 2024
|
||||||
|
//
|
||||||
|
// Purpose: debug transport module (dtm) : allows external debugger to communicate with dm
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// To recovert from a core reset, DTM will need to DtmHardReset (or trstn / tms zeroscan).
|
||||||
|
// This is mentioned in spec
|
||||||
|
// To recover from DTM reset, core will probably need to be reset
|
||||||
|
|
||||||
|
module dtm #(parameter ADDR_WIDTH, parameter JTAG_DEVICE_ID) (
|
||||||
|
// System clock
|
||||||
|
input logic clk,
|
||||||
|
// External JTAG signals
|
||||||
|
input logic tck,
|
||||||
|
input logic tdi,
|
||||||
|
input logic tms,
|
||||||
|
output logic tdo,
|
||||||
|
|
||||||
|
// DMI signals
|
||||||
|
input logic ReqReady,
|
||||||
|
output logic ReqValid,
|
||||||
|
output logic [ADDR_WIDTH-1:0] ReqAddress,
|
||||||
|
output logic [31:0] ReqData,
|
||||||
|
output logic [1:0] ReqOP,
|
||||||
|
output logic RspReady,
|
||||||
|
input logic RspValid,
|
||||||
|
input logic [31:0] RspData,
|
||||||
|
input logic [1:0] RspOP
|
||||||
|
);
|
||||||
|
`include "debug.vh"
|
||||||
|
|
||||||
|
enum logic [1:0] {IDLE, START, WAIT, COMPLETE} DMIState;
|
||||||
|
|
||||||
|
// Clock Domain Crossing
|
||||||
|
logic tcks; // Synchronized JTAG clock
|
||||||
|
logic resetn;
|
||||||
|
logic UpdateDtmcs;
|
||||||
|
logic [31:0] DtmcsIn;
|
||||||
|
logic [31:0] DtmcsOut;
|
||||||
|
logic UpdateDmi;
|
||||||
|
logic CaptureDmi;
|
||||||
|
logic [34+ADDR_WIDTH-1:0] DmiIn;
|
||||||
|
logic [34+ADDR_WIDTH-1:0] DmiOut;
|
||||||
|
|
||||||
|
// DTMCS Register
|
||||||
|
const logic [2:0] ErrInfo = 0;
|
||||||
|
logic DtmHardReset;
|
||||||
|
logic DmiReset;
|
||||||
|
const logic [2:0] Idle = 0;
|
||||||
|
logic [1:0] DmiStat;
|
||||||
|
const logic [5:0] ABits = ADDR_WIDTH;
|
||||||
|
const logic [3:0] Version = 1; // DTM spec version 1
|
||||||
|
|
||||||
|
logic [31:0] ValRspData;
|
||||||
|
logic [1:0] ValRspOP;
|
||||||
|
logic Sticky;
|
||||||
|
|
||||||
|
assign DmiOut = {ReqAddress, ValRspData, ValRspOP};
|
||||||
|
assign DmiStat = ValRspOP;
|
||||||
|
|
||||||
|
// Synchronize the edges of tck to the system clock
|
||||||
|
synchronizer clksync (.clk(clk), .d(tck), .q(tcks));
|
||||||
|
|
||||||
|
jtag #(.ADDR_WIDTH(ADDR_WIDTH), .DEVICE_ID(JTAG_DEVICE_ID)) jtag (.tck(tcks), .tdi, .tms, .tdo,
|
||||||
|
.resetn, .UpdateDtmcs, .DtmcsIn, .DtmcsOut, .CaptureDmi, .UpdateDmi, .DmiIn, .DmiOut);
|
||||||
|
|
||||||
|
// DTMCS
|
||||||
|
assign DtmcsOut = {11'b0, ErrInfo, 3'b0, Idle, DmiStat, ABits, Version};
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
if (~resetn | DtmHardReset) begin
|
||||||
|
DtmHardReset <= 0;
|
||||||
|
DmiReset <= 0;
|
||||||
|
end else if (UpdateDtmcs) begin
|
||||||
|
DtmHardReset <= DtmcsIn[17];
|
||||||
|
DmiReset <= DtmcsIn[16];
|
||||||
|
end else if (DmiReset) begin
|
||||||
|
DmiReset <= 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// DMI
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
if (~resetn | DtmHardReset) begin
|
||||||
|
ValRspData <= 0;
|
||||||
|
ValRspOP <= `OP_SUCCESS;
|
||||||
|
//ErrInfo <= 4;
|
||||||
|
Sticky <= 0;
|
||||||
|
DMIState <= IDLE;
|
||||||
|
end else if (DmiReset) begin
|
||||||
|
ValRspOP <= `OP_SUCCESS;
|
||||||
|
//ErrInfo <= 4;
|
||||||
|
Sticky <= 0;
|
||||||
|
end else
|
||||||
|
case (DMIState)
|
||||||
|
IDLE : begin
|
||||||
|
if (UpdateDmi & ~Sticky & DmiIn[1:0] != `OP_NOP) begin
|
||||||
|
{ReqAddress, ReqData, ReqOP} <= DmiIn;
|
||||||
|
ReqValid <= 1;
|
||||||
|
// DmiOut is captured immediately on CaptureDmi
|
||||||
|
// this preemptively sets BUSY for next capture unless overwritten
|
||||||
|
ValRspOP <= `OP_BUSY;
|
||||||
|
DMIState <= START;
|
||||||
|
end else begin
|
||||||
|
ReqValid <= 0;
|
||||||
|
if (~Sticky)
|
||||||
|
ValRspOP <= `OP_SUCCESS;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
START : begin
|
||||||
|
if (ReqReady) begin
|
||||||
|
ReqValid <= 0;
|
||||||
|
RspReady <= 1;
|
||||||
|
DMIState <= WAIT;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
WAIT : begin
|
||||||
|
if (RspValid) begin
|
||||||
|
ValRspData <= RspData;
|
||||||
|
if (~Sticky) // update OP if it isn't currently a sticky value
|
||||||
|
ValRspOP <= RspOP;
|
||||||
|
if (RspOP == `OP_FAILED | RspOP == `OP_BUSY)
|
||||||
|
Sticky <= 1;
|
||||||
|
//if (RspOP == `OP_FAILED)
|
||||||
|
// ErrInfo <= 3;
|
||||||
|
DMIState <= COMPLETE;
|
||||||
|
end else if (CaptureDmi)
|
||||||
|
Sticky <= 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
COMPLETE : begin
|
||||||
|
if (CaptureDmi) begin
|
||||||
|
RspReady <= 0;
|
||||||
|
DMIState <= IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
75
src/debug/hartcontrol.sv
Normal file
75
src/debug/hartcontrol.sv
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// hartcontrol.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu 10 May 2024
|
||||||
|
// Modified:
|
||||||
|
//
|
||||||
|
// Purpose: Controls the state of connected hart
|
||||||
|
//
|
||||||
|
// Documentation: RISC-V System on Chip Design
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module hartcontrol(
|
||||||
|
input logic clk, rst,
|
||||||
|
input logic NdmReset, // Triggers HaltOnReset behavior
|
||||||
|
|
||||||
|
input logic HaltReq, // Initiate core halt
|
||||||
|
input logic ResumeReq, // Initiates core resume
|
||||||
|
input logic HaltOnReset, // Halts core immediately on hart reset
|
||||||
|
|
||||||
|
output logic DebugStall, // Stall signal goes to hazard unit
|
||||||
|
|
||||||
|
// DMStatus bits
|
||||||
|
output logic Halted,
|
||||||
|
output logic AllRunning,
|
||||||
|
output logic AnyRunning,
|
||||||
|
output logic AllHalted,
|
||||||
|
output logic AnyHalted,
|
||||||
|
output logic AllResumeAck,
|
||||||
|
output logic AnyResumeAck
|
||||||
|
);
|
||||||
|
|
||||||
|
assign Halted = DebugStall;
|
||||||
|
assign AllRunning = ~DebugStall;
|
||||||
|
assign AnyRunning = ~DebugStall;
|
||||||
|
assign AllHalted = DebugStall;
|
||||||
|
assign AnyHalted = DebugStall;
|
||||||
|
assign AllResumeAck = ~DebugStall;
|
||||||
|
assign AnyResumeAck = ~DebugStall;
|
||||||
|
|
||||||
|
enum logic {RUNNING, HALTED} State;
|
||||||
|
|
||||||
|
assign DebugStall = (State == HALTED);
|
||||||
|
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
if (rst)
|
||||||
|
State <= RUNNING;
|
||||||
|
else if (NdmReset)
|
||||||
|
State <= HaltOnReset ? HALTED : RUNNING;
|
||||||
|
else begin
|
||||||
|
case (State)
|
||||||
|
RUNNING : State <= HaltReq ? HALTED : RUNNING;
|
||||||
|
|
||||||
|
HALTED : State <= ResumeReq ? RUNNING : HALTED;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
48
src/debug/idreg.sv
Executable file
48
src/debug/idreg.sv
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// idreg.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu
|
||||||
|
// Created: 15 March 2024
|
||||||
|
//
|
||||||
|
// Purpose: JTAG device identification register
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module idreg #(parameter DEVICE_ID) (
|
||||||
|
input logic tdi,
|
||||||
|
input logic clockDR,
|
||||||
|
input logic captureDR,
|
||||||
|
output logic tdo
|
||||||
|
);
|
||||||
|
|
||||||
|
logic [32:0] ShiftReg;
|
||||||
|
assign ShiftReg[32] = tdi;
|
||||||
|
assign tdo = ShiftReg[0];
|
||||||
|
|
||||||
|
genvar i;
|
||||||
|
for (i = 0; i < 32; i = i + 1) begin
|
||||||
|
always @(posedge clockDR) begin
|
||||||
|
if (i > 0)
|
||||||
|
ShiftReg[i] <= captureDR ? DEVICE_ID[i] : ShiftReg[i+1];
|
||||||
|
else
|
||||||
|
ShiftReg[0] <= captureDR ? 1'b1 : ShiftReg[i+1];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
81
src/debug/ir.sv
Normal file
81
src/debug/ir.sv
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// ir.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu
|
||||||
|
// Created: 15 March 2024
|
||||||
|
//
|
||||||
|
// Purpose: JTAG instruction register
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module ir (
|
||||||
|
input logic clockIR,
|
||||||
|
input logic tdi,
|
||||||
|
input logic resetn,
|
||||||
|
input logic captureIR,
|
||||||
|
input logic updateIR,
|
||||||
|
output logic tdo,
|
||||||
|
output logic BypassInstr,
|
||||||
|
output logic IDCodeInstr,
|
||||||
|
output logic DtmcsIntrs,
|
||||||
|
output logic DmiInstr
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam INST_REG_WIDTH = 5;
|
||||||
|
|
||||||
|
logic [INST_REG_WIDTH:0] shift_reg;
|
||||||
|
logic [3:0] decoded;
|
||||||
|
|
||||||
|
|
||||||
|
assign shift_reg[INST_REG_WIDTH] = tdi;
|
||||||
|
assign tdo = shift_reg[0];
|
||||||
|
|
||||||
|
// Shift register
|
||||||
|
always @(posedge clockIR) begin
|
||||||
|
shift_reg[0] <= shift_reg[1] | captureIR;
|
||||||
|
end
|
||||||
|
genvar i;
|
||||||
|
for (i = INST_REG_WIDTH; i > 1; i = i - 1) begin
|
||||||
|
always @(posedge clockIR) begin
|
||||||
|
shift_reg[i-1] <= shift_reg[i] & ~captureIR;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Instruction decoder
|
||||||
|
// 6.1.2
|
||||||
|
always_comb begin
|
||||||
|
unique case (shift_reg[INST_REG_WIDTH-1:0])
|
||||||
|
5'h00 : decoded <= 4'b1000; // bypass
|
||||||
|
5'h01 : decoded <= 4'b0100; // idcode
|
||||||
|
5'h10 : decoded <= 4'b0010; // dtmcs
|
||||||
|
5'h11 : decoded <= 4'b0001; // dmi
|
||||||
|
5'h1F : decoded <= 4'b1000; // bypass
|
||||||
|
default : decoded <= 4'b1000; // bypass
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// Flop decoded instruction to minimizes switching during shiftIR
|
||||||
|
always @(posedge updateIR or negedge resetn) begin
|
||||||
|
if (~resetn)
|
||||||
|
{BypassInstr, IDCodeInstr, DtmcsIntrs, DmiInstr} <= 4'b0100;
|
||||||
|
else if (updateIR)
|
||||||
|
{BypassInstr, IDCodeInstr, DtmcsIntrs, DmiInstr} <= decoded;
|
||||||
|
end
|
||||||
|
endmodule
|
136
src/debug/jtag.sv
Normal file
136
src/debug/jtag.sv
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// jtag.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu
|
||||||
|
// Created: 15 March 2024
|
||||||
|
//
|
||||||
|
// Purpose: JTAG portion of DTM
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module jtag #(parameter ADDR_WIDTH, parameter DEVICE_ID) (
|
||||||
|
// JTAG signals
|
||||||
|
input logic tck,
|
||||||
|
input logic tdi,
|
||||||
|
input logic tms,
|
||||||
|
output logic tdo,
|
||||||
|
output logic resetn,
|
||||||
|
|
||||||
|
// DTM signals
|
||||||
|
output logic UpdateDtmcs,
|
||||||
|
output logic [31:0] DtmcsIn,
|
||||||
|
input logic [31:0] DtmcsOut,
|
||||||
|
|
||||||
|
output logic CaptureDmi,
|
||||||
|
output logic UpdateDmi,
|
||||||
|
output logic [34+ADDR_WIDTH-1:0] DmiIn,
|
||||||
|
input logic [34+ADDR_WIDTH-1:0] DmiOut
|
||||||
|
);
|
||||||
|
|
||||||
|
genvar i;
|
||||||
|
|
||||||
|
// Data signals
|
||||||
|
logic tdi_ir, tdi_dr;
|
||||||
|
logic tdo_ir, tdo_dr;
|
||||||
|
logic tdo_bypass;
|
||||||
|
logic tdo_idcode;
|
||||||
|
logic tdo_dtmcs;
|
||||||
|
logic tdo_dmi;
|
||||||
|
|
||||||
|
// TAP controller logic
|
||||||
|
logic tdo_en;
|
||||||
|
logic captureIR;
|
||||||
|
logic clockIR;
|
||||||
|
logic updateIR;
|
||||||
|
logic shiftDR;
|
||||||
|
logic captureDR;
|
||||||
|
logic clockDR;
|
||||||
|
logic updateDR;
|
||||||
|
logic select;
|
||||||
|
|
||||||
|
// Instruction signals
|
||||||
|
logic BypassInstr;
|
||||||
|
logic IDCodeInstr;
|
||||||
|
logic DtmcsIntrs;
|
||||||
|
logic DmiInstr;
|
||||||
|
|
||||||
|
logic [32:0] DtmcsShiftReg;
|
||||||
|
logic [34+ADDR_WIDTH:0] DmiShiftReg;
|
||||||
|
|
||||||
|
assign UpdateDtmcs = updateDR & DtmcsIntrs;
|
||||||
|
|
||||||
|
assign CaptureDmi = captureDR & DmiInstr;
|
||||||
|
assign UpdateDmi = updateDR & DmiInstr;
|
||||||
|
|
||||||
|
tap tap (.tck, .tms, .resetn, .tdo_en, .captureIR,
|
||||||
|
.clockIR, .updateIR, .shiftDR, .captureDR, .clockDR, .updateDR, .select);
|
||||||
|
|
||||||
|
// IR/DR input demux
|
||||||
|
assign tdi_ir = select ? tdi : 1'bz;
|
||||||
|
assign tdi_dr = select ? 1'bz : tdi;
|
||||||
|
// IR/DR output mux
|
||||||
|
assign tdo = ~tdo_en ? 1'bz :
|
||||||
|
select ? tdo_ir : tdo_dr;
|
||||||
|
|
||||||
|
ir ir (.clockIR, .tdi(tdi_ir), .resetn, .captureIR, .updateIR, .tdo(tdo_ir),
|
||||||
|
.BypassInstr, .IDCodeInstr, .DtmcsIntrs, .DmiInstr);
|
||||||
|
|
||||||
|
// DR demux
|
||||||
|
always_comb begin
|
||||||
|
unique case ({BypassInstr, IDCodeInstr, DtmcsIntrs, DmiInstr})
|
||||||
|
4'b1000 : tdo_dr <= tdo_bypass;
|
||||||
|
4'b0100 : tdo_dr <= tdo_idcode;
|
||||||
|
4'b0010 : tdo_dr <= tdo_dtmcs;
|
||||||
|
4'b0001 : tdo_dr <= tdo_dmi;
|
||||||
|
default : tdo_dr <= tdo_bypass;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
always_ff @(posedge UpdateDtmcs)
|
||||||
|
DtmcsIn <= DtmcsShiftReg[31:0];
|
||||||
|
|
||||||
|
always_ff @(posedge UpdateDmi)
|
||||||
|
DmiIn <= DmiShiftReg[34+ADDR_WIDTH-1:0];
|
||||||
|
|
||||||
|
assign DtmcsShiftReg[32] = tdi_dr;
|
||||||
|
assign tdo_dtmcs = DtmcsShiftReg[0];
|
||||||
|
for (i = 0; i < 32; i = i + 1) begin
|
||||||
|
always_ff @(posedge clockDR) begin
|
||||||
|
DtmcsShiftReg[i] <= captureDR ? DtmcsOut[i] : DtmcsShiftReg[i+1];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign DmiShiftReg[34+ADDR_WIDTH] = tdi_dr;
|
||||||
|
assign tdo_dmi = DmiShiftReg[0];
|
||||||
|
for (i = 0; i < 34+ADDR_WIDTH; i = i + 1) begin
|
||||||
|
always_ff @(posedge clockDR) begin
|
||||||
|
DmiShiftReg[i] <= captureDR ? DmiOut[i] : DmiShiftReg[i+1];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// jtag id register
|
||||||
|
idreg #(DEVICE_ID) id (.tdi(tdi_dr), .clockDR, .captureDR, .tdo(tdo_idcode));
|
||||||
|
|
||||||
|
// bypass register
|
||||||
|
always_ff @(posedge clockDR) begin
|
||||||
|
tdo_bypass <= tdi_dr & shiftDR;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
49
src/debug/notes.txt
Executable file
49
src/debug/notes.txt
Executable file
@ -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 <bus_num>-<port_num>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 <address>
|
||||||
|
riscv dmi_write <address> <value>
|
||||||
|
|
||||||
|
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.
|
142
src/debug/rad.sv
Normal file
142
src/debug/rad.sv
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// rad.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu
|
||||||
|
// Created: 28 April 2024
|
||||||
|
//
|
||||||
|
// Purpose: Calculates the numbers of shifts required to access target register on the debug scan chain
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License Version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module rad import cvw::*; #(parameter cvw_t P) (
|
||||||
|
input logic [2:0] AarSize,
|
||||||
|
input logic [15:0] Regno,
|
||||||
|
output logic GPRRegNo,
|
||||||
|
output logic [9:0] ScanChainLen,
|
||||||
|
output logic [9:0] ShiftCount,
|
||||||
|
output logic InvalidRegNo,
|
||||||
|
output logic RegReadOnly,
|
||||||
|
output logic [P.E_SUPPORTED+3:0] GPRAddr,
|
||||||
|
output logic [P.XLEN-1:0] ARMask
|
||||||
|
);
|
||||||
|
`include "debug.vh"
|
||||||
|
|
||||||
|
localparam MISALEN = P.ZICSR_SUPPORTED ? P.XLEN : 0;
|
||||||
|
localparam TRAPMLEN = P.ZICSR_SUPPORTED ? 1 : 0;
|
||||||
|
localparam PCMLEN = (P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED) ? P.XLEN : 0;
|
||||||
|
localparam INSTRMLEN = (P.ZICSR_SUPPORTED | P.A_SUPPORTED) ? 32 : 0;
|
||||||
|
localparam MEMRWMLEN = 2;
|
||||||
|
localparam INSTRVALIDMLEN = 1;
|
||||||
|
localparam WRITEDATAMLEN = P.XLEN;
|
||||||
|
localparam IEUADRMLEN = P.XLEN;
|
||||||
|
localparam READDATAMLEN = P.LLEN;
|
||||||
|
localparam SCANCHAINLEN = P.XLEN - 1
|
||||||
|
+ MISALEN + TRAPMLEN + PCMLEN + INSTRMLEN
|
||||||
|
+ MEMRWMLEN + INSTRVALIDMLEN + WRITEDATAMLEN
|
||||||
|
+ IEUADRMLEN + READDATAMLEN;
|
||||||
|
localparam GPRCHAINLEN = P.XLEN;
|
||||||
|
|
||||||
|
localparam MISA_IDX = MISALEN;
|
||||||
|
localparam TRAPM_IDX = MISA_IDX + TRAPMLEN;
|
||||||
|
localparam PCM_IDX = TRAPM_IDX + PCMLEN;
|
||||||
|
localparam INSTRM_IDX = PCM_IDX + INSTRMLEN;
|
||||||
|
localparam MEMRWM_IDX = INSTRM_IDX + MEMRWMLEN;
|
||||||
|
localparam INSTRVALIDM_IDX = MEMRWM_IDX + INSTRVALIDMLEN;
|
||||||
|
localparam WRITEDATAM_IDX = INSTRVALIDM_IDX + WRITEDATAMLEN;
|
||||||
|
localparam IEUADRM_IDX = WRITEDATAM_IDX + IEUADRMLEN;
|
||||||
|
localparam READDATAM_IDX = IEUADRM_IDX + READDATAMLEN;
|
||||||
|
|
||||||
|
logic [P.XLEN:0] Mask;
|
||||||
|
|
||||||
|
assign ScanChainLen = GPRRegNo ? GPRCHAINLEN : SCANCHAINLEN;
|
||||||
|
|
||||||
|
if (P.E_SUPPORTED)
|
||||||
|
assign GPRAddr = Regno[4:0];
|
||||||
|
else
|
||||||
|
assign GPRAddr = Regno[3:0];
|
||||||
|
|
||||||
|
// Register decoder
|
||||||
|
always_comb begin
|
||||||
|
InvalidRegNo = 0;
|
||||||
|
RegReadOnly = 0;
|
||||||
|
GPRRegNo = 0;
|
||||||
|
casez (Regno)
|
||||||
|
16'h100? : begin
|
||||||
|
ShiftCount = P.XLEN - 1;
|
||||||
|
GPRRegNo = 1;
|
||||||
|
end
|
||||||
|
16'h101? : begin
|
||||||
|
ShiftCount = P.XLEN - 1;
|
||||||
|
InvalidRegNo = ~P.E_SUPPORTED;
|
||||||
|
GPRRegNo = 1;
|
||||||
|
end
|
||||||
|
`MISA : begin
|
||||||
|
ShiftCount = SCANCHAINLEN - MISA_IDX;
|
||||||
|
InvalidRegNo = ~P.ZICSR_SUPPORTED;
|
||||||
|
RegReadOnly = 1;
|
||||||
|
end
|
||||||
|
`TRAPM : begin
|
||||||
|
ShiftCount = SCANCHAINLEN - TRAPM_IDX;
|
||||||
|
InvalidRegNo = ~P.ZICSR_SUPPORTED;
|
||||||
|
RegReadOnly = 1;
|
||||||
|
end
|
||||||
|
`PCM : begin
|
||||||
|
ShiftCount = SCANCHAINLEN - PCM_IDX;
|
||||||
|
InvalidRegNo = ~(P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED);
|
||||||
|
end
|
||||||
|
`INSTRM : begin
|
||||||
|
ShiftCount = SCANCHAINLEN - INSTRM_IDX;
|
||||||
|
InvalidRegNo = ~(P.ZICSR_SUPPORTED | P.A_SUPPORTED);
|
||||||
|
end
|
||||||
|
`MEMRWM : ShiftCount = SCANCHAINLEN - MEMRWM_IDX;
|
||||||
|
`INSTRVALIDM : ShiftCount = SCANCHAINLEN - INSTRVALIDM_IDX;
|
||||||
|
`WRITEDATAM : ShiftCount = SCANCHAINLEN - WRITEDATAM_IDX;
|
||||||
|
`IEUADRM : ShiftCount = SCANCHAINLEN - IEUADRM_IDX;
|
||||||
|
`READDATAM : begin
|
||||||
|
ShiftCount = SCANCHAINLEN - READDATAM_IDX;
|
||||||
|
RegReadOnly = 1;
|
||||||
|
end
|
||||||
|
default : begin
|
||||||
|
ShiftCount = 0;
|
||||||
|
InvalidRegNo = 1;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// Mask calculator
|
||||||
|
always_comb begin
|
||||||
|
Mask = 0;
|
||||||
|
case(Regno)
|
||||||
|
`TRAPM : Mask = {1{1'b1}};
|
||||||
|
`INSTRM : Mask = {32{1'b1}};
|
||||||
|
`MEMRWM : Mask = {2{1'b1}};
|
||||||
|
`INSTRVALIDM : Mask = {1{1'b1}};
|
||||||
|
`READDATAM : Mask = {P.LLEN{1'b1}};
|
||||||
|
default : Mask = {P.XLEN{1'b1}};
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
assign ARMask[31:0] = Mask[31:0];
|
||||||
|
if (P.XLEN >= 64)
|
||||||
|
assign ARMask[63:32] = (AarSize == 3'b011 || AarSize == 3'b100) ? Mask[63:32] : '0;
|
||||||
|
if (P.XLEN == 128)
|
||||||
|
assign ARMask[127:64] = (AarSize == 3'b100) ? Mask[127:64] : '0;
|
||||||
|
|
||||||
|
endmodule
|
97
src/debug/tap.sv
Normal file
97
src/debug/tap.sv
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// tap.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu, james.stine@okstate.edu
|
||||||
|
// Created: 15 March 2024
|
||||||
|
//
|
||||||
|
// Purpose: JTAG tap controller
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module tap (
|
||||||
|
input logic tck,
|
||||||
|
input logic tms,
|
||||||
|
output logic resetn,
|
||||||
|
output logic tdo_en,
|
||||||
|
output logic captureIR,
|
||||||
|
output logic clockIR,
|
||||||
|
output logic updateIR,
|
||||||
|
output logic shiftDR,
|
||||||
|
output logic captureDR,
|
||||||
|
output logic clockDR,
|
||||||
|
output logic updateDR,
|
||||||
|
output logic select
|
||||||
|
);
|
||||||
|
|
||||||
|
enum logic [3:0] {
|
||||||
|
Exit2DR = 4'h0,
|
||||||
|
Exit1DR = 4'h1,
|
||||||
|
ShiftDR = 4'h2,
|
||||||
|
PauseDR = 4'h3,
|
||||||
|
SelectIR = 4'h4,
|
||||||
|
UpdateDR = 4'h5,
|
||||||
|
CaptureDR = 4'h6,
|
||||||
|
SelectDR = 4'h7,
|
||||||
|
Exit2IR = 4'h8,
|
||||||
|
Exit1IR = 4'h9,
|
||||||
|
ShiftIR = 4'hA,
|
||||||
|
PauseIR = 4'hB,
|
||||||
|
RunTestIdle = 4'hC,
|
||||||
|
UpdateIR = 4'hD,
|
||||||
|
CaptureIR = 4'hE,
|
||||||
|
TLReset = 4'hF
|
||||||
|
} State;
|
||||||
|
|
||||||
|
always @(posedge tck) begin
|
||||||
|
case (State)
|
||||||
|
TLReset : State <= tms ? TLReset : RunTestIdle;
|
||||||
|
RunTestIdle : State <= tms ? SelectDR : RunTestIdle;
|
||||||
|
SelectDR : State <= tms ? SelectIR : CaptureDR;
|
||||||
|
CaptureDR : State <= tms ? Exit1DR : ShiftDR;
|
||||||
|
ShiftDR : State <= tms ? Exit1DR : ShiftDR;
|
||||||
|
Exit1DR : State <= tms ? UpdateDR : PauseDR;
|
||||||
|
PauseDR : State <= tms ? Exit2DR : PauseDR;
|
||||||
|
Exit2DR : State <= tms ? UpdateDR : ShiftDR;
|
||||||
|
UpdateDR : State <= tms ? SelectDR : RunTestIdle;
|
||||||
|
SelectIR : State <= tms ? TLReset : CaptureIR;
|
||||||
|
CaptureIR : State <= tms ? Exit1IR : ShiftIR;
|
||||||
|
ShiftIR : State <= tms ? Exit1IR : ShiftIR;
|
||||||
|
Exit1IR : State <= tms ? UpdateIR : PauseIR;
|
||||||
|
PauseIR : State <= tms ? Exit2IR : PauseIR;
|
||||||
|
Exit2IR : State <= tms ? UpdateIR : ShiftIR;
|
||||||
|
UpdateIR : State <= tms ? SelectDR : RunTestIdle;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(negedge tck) begin
|
||||||
|
resetn <= ~(State == TLReset);
|
||||||
|
tdo_en <= State == ShiftIR || State == ShiftDR;
|
||||||
|
captureIR <= State == CaptureIR;
|
||||||
|
updateIR <= State == UpdateIR;
|
||||||
|
shiftDR <= State == ShiftDR;
|
||||||
|
captureDR <= State == CaptureDR;
|
||||||
|
updateDR <= State == UpdateDR;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign clockIR = tck || State[0] || ~State[1] || ~State[3];
|
||||||
|
assign clockDR = tck || State[0] || ~State[1] || State[3];
|
||||||
|
assign select = State[3];
|
||||||
|
|
||||||
|
endmodule
|
55
src/generic/flop/flopenrcs.sv
Normal file
55
src/generic/flop/flopenrcs.sv
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// flopenrcs.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu 15 April 2024
|
||||||
|
// Modified:
|
||||||
|
//
|
||||||
|
// Purpose: Scannable D flip-flop with enable, synchronous reset, enabled clear
|
||||||
|
//
|
||||||
|
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||||
|
// https://github.com/openhwgroup/cvw
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||||
|
//
|
||||||
|
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||||
|
// except in compliance with the License, or, at your option, the Apache License Version 2.0. You
|
||||||
|
// may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://solderpad.org/licenses/SHL-2.1/
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||||
|
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// either express or implied. See the License for the specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module flopenrcs #(parameter WIDTH = 8) (
|
||||||
|
input logic clk, reset, clear, en,
|
||||||
|
input logic [WIDTH-1:0] d,
|
||||||
|
output logic [WIDTH-1:0] q,
|
||||||
|
input logic scan, // scan enable
|
||||||
|
input logic scanin,
|
||||||
|
output logic scanout
|
||||||
|
);
|
||||||
|
logic [WIDTH-1:0] dmux;
|
||||||
|
|
||||||
|
assign dmux[WIDTH-1] = scan ? scanin : d[WIDTH-1];
|
||||||
|
assign scanout = q[0];
|
||||||
|
|
||||||
|
genvar i;
|
||||||
|
for (i=0; i<WIDTH-1; i=i+1) begin
|
||||||
|
assign dmux[i] = scan ? q[i+1] : d[i];
|
||||||
|
end
|
||||||
|
|
||||||
|
always_ff @(posedge clk)
|
||||||
|
if (reset)
|
||||||
|
q <= '0;
|
||||||
|
else if (en || scan)
|
||||||
|
if (clear)
|
||||||
|
q <= '0;
|
||||||
|
else
|
||||||
|
q <= dmux;
|
||||||
|
|
||||||
|
endmodule
|
65
src/generic/scanreg.sv
Normal file
65
src/generic/scanreg.sv
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
///////////////////////////////////////////
|
||||||
|
// scanreg.sv
|
||||||
|
//
|
||||||
|
// Written: matthew.n.otto@okstate.edu 15 April 2024
|
||||||
|
// Modified:
|
||||||
|
//
|
||||||
|
// Purpose: Scannable register that captures input on first scan cycle
|
||||||
|
//
|
||||||
|
// 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 scanreg #(parameter WIDTH = 8) (
|
||||||
|
input logic clk, reset,
|
||||||
|
input logic [WIDTH-1:0] d,
|
||||||
|
input logic scan, // scan enable
|
||||||
|
input logic scanin,
|
||||||
|
output logic scanout
|
||||||
|
);
|
||||||
|
logic buffer;
|
||||||
|
logic [WIDTH:0] scanreg;
|
||||||
|
logic firstcycle;
|
||||||
|
|
||||||
|
assign scanreg[WIDTH] = buffer;
|
||||||
|
assign scanout = scanreg[0];
|
||||||
|
|
||||||
|
always_ff @(posedge clk)
|
||||||
|
if (reset)
|
||||||
|
buffer <= 0;
|
||||||
|
else
|
||||||
|
buffer <= scanin;
|
||||||
|
|
||||||
|
genvar i;
|
||||||
|
for (i=0; i<WIDTH; i=i+1) begin
|
||||||
|
always_ff @(posedge clk)
|
||||||
|
if (reset)
|
||||||
|
scanreg[i] <= 0;
|
||||||
|
else if (scan)
|
||||||
|
scanreg[i] <= firstcycle ? d[i] : scanreg[i+1];
|
||||||
|
end
|
||||||
|
|
||||||
|
always_ff @(posedge clk)
|
||||||
|
if (reset)
|
||||||
|
firstcycle <= 1;
|
||||||
|
else if (scan && firstcycle)
|
||||||
|
firstcycle <= 0;
|
||||||
|
else if (~scan && ~firstcycle)
|
||||||
|
firstcycle <= 1;
|
||||||
|
|
||||||
|
endmodule
|
@ -442,9 +442,21 @@ module controller import cvw::*; #(parameter cvw_t P) (
|
|||||||
assign IntDivE = MDUE & Funct3E[2]; // Integer division operation
|
assign IntDivE = MDUE & Funct3E[2]; // Integer division operation
|
||||||
|
|
||||||
// Memory stage pipeline control register
|
// Memory stage pipeline control register
|
||||||
flopenrc #(25) controlregM(clk, reset, FlushM, ~StallM,
|
if (P.DEBUG_SUPPORTED) begin
|
||||||
{RegWriteE, ResultSrcE, MemRWE, CSRReadE, CSRWriteE, PrivilegedE, Funct3E, FWriteIntE, AtomicE, InvalidateICacheE, FlushDCacheE, FenceE, InstrValidE, IntDivE, CMOpE, LSUPrefetchE},
|
flopenrc #(22) controlregM(clk, reset, FlushM, ~StallM,
|
||||||
{RegWriteM, ResultSrcM, MemRWM, CSRReadM, CSRWriteM, PrivilegedM, Funct3M, FWriteIntM, AtomicM, InvalidateICacheM, FlushDCacheM, FenceM, InstrValidM, IntDivM, CMOpM, LSUPrefetchM});
|
{RegWriteE, ResultSrcE, CSRReadE, CSRWriteE, PrivilegedE, Funct3E, FWriteIntE, AtomicE, InvalidateICacheE, FlushDCacheE, FenceE, IntDivE, CMOpE, LSUPrefetchE},
|
||||||
|
{RegWriteM, ResultSrcM, CSRReadM, CSRWriteM, PrivilegedM, Funct3M, FWriteIntM, AtomicM, InvalidateICacheM, FlushDCacheM, FenceM, IntDivM, CMOpM, LSUPrefetchM});
|
||||||
|
flopenrcs #(3) controlscanreg(clk, reset, FlushM, ~StallM, {MemRWE,InstrValidE}, {MemRWM,InstrValidM}, DebugScanEn, DebugScanIn, DebugScanOut);
|
||||||
|
end else begin
|
||||||
|
flopenrc #(25) controlregM(clk, reset, FlushM, ~StallM,
|
||||||
|
{RegWriteE, ResultSrcE, MemRWE, CSRReadE, CSRWriteE, PrivilegedE, Funct3E, FWriteIntE, AtomicE, InvalidateICacheE, FlushDCacheE, FenceE, InstrValidE, IntDivE, CMOpE, LSUPrefetchE},
|
||||||
|
{RegWriteM, ResultSrcM, MemRWM, CSRReadM, CSRWriteM, PrivilegedM, Funct3M, FWriteIntM, AtomicM, InvalidateICacheM, FlushDCacheM, FenceM, InstrValidM, IntDivM, CMOpM, LSUPrefetchM});
|
||||||
|
end
|
||||||
|
|
||||||
|
// FIXME: delete once working
|
||||||
|
//flopenrc #(25) controlregM(clk, reset, FlushM, ~StallM,
|
||||||
|
// {RegWriteE, ResultSrcE, MemRWE, CSRReadE, CSRWriteE, PrivilegedE, Funct3E, FWriteIntE, AtomicE, InvalidateICacheE, FlushDCacheE, FenceE, InstrValidE, IntDivE, CMOpE, LSUPrefetchE},
|
||||||
|
// {RegWriteM, ResultSrcM, MemRWM, CSRReadM, CSRWriteM, PrivilegedM, Funct3M, FWriteIntM, AtomicM, InvalidateICacheM, FlushDCacheM, FenceM, InstrValidM, IntDivM, CMOpM, LSUPrefetchM});
|
||||||
flopenrc #(5) RdMReg(clk, reset, FlushM, ~StallM, RdE, RdM);
|
flopenrc #(5) RdMReg(clk, reset, FlushM, ~StallM, RdE, RdM);
|
||||||
|
|
||||||
// Writeback stage pipeline control register
|
// Writeback stage pipeline control register
|
||||||
|
@ -72,12 +72,24 @@ module datapath import cvw::*; #(parameter cvw_t P) (
|
|||||||
input logic [P.XLEN-1:0] CSRReadValW, // CSR read result
|
input logic [P.XLEN-1:0] CSRReadValW, // CSR read result
|
||||||
input logic [P.XLEN-1:0] MDUResultW, // MDU (Multiply/divide unit) result
|
input logic [P.XLEN-1:0] MDUResultW, // MDU (Multiply/divide unit) result
|
||||||
input logic [P.XLEN-1:0] FIntDivResultW, // FPU's integer divide result
|
input logic [P.XLEN-1:0] FIntDivResultW, // FPU's integer divide result
|
||||||
input logic [4:0] RdW // Destination register
|
input logic [4:0] RdW, // Destination register
|
||||||
// Hazard Unit signals
|
// Hazard Unit signals
|
||||||
|
// Debug scan chain
|
||||||
|
input logic DebugScanEn,
|
||||||
|
input logic DebugScanIn,
|
||||||
|
output logic DebugScanOut,
|
||||||
|
input logic GPRSel,
|
||||||
|
input logic DebugCapture,
|
||||||
|
input logic DebugGPRUpdate,
|
||||||
|
input logic [P.E_SUPPORTED+3:0] GPRAddr,
|
||||||
|
input logic GPRScanEn,
|
||||||
|
input logic GPRScanIn,
|
||||||
|
output logic GPRScanOut
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fetch stage signals
|
// Fetch stage signals
|
||||||
// Decode stage signals
|
// Decode stage signals
|
||||||
|
logic [4:0] DB_Rs1D; // (Debug) Muxed source register
|
||||||
logic [P.XLEN-1:0] R1D, R2D; // Read data from Rs1 (RD1), Rs2 (RD2)
|
logic [P.XLEN-1:0] R1D, R2D; // Read data from Rs1 (RD1), Rs2 (RD2)
|
||||||
logic [P.XLEN-1:0] ImmExtD; // Extended immediate in Decode stage
|
logic [P.XLEN-1:0] ImmExtD; // Extended immediate in Decode stage
|
||||||
logic [4:0] RdD; // Destination register in Decode stage
|
logic [4:0] RdD; // Destination register in Decode stage
|
||||||
@ -90,15 +102,29 @@ module datapath import cvw::*; #(parameter cvw_t P) (
|
|||||||
logic [P.XLEN-1:0] IEUResultM; // Result from execution stage
|
logic [P.XLEN-1:0] IEUResultM; // Result from execution stage
|
||||||
logic [P.XLEN-1:0] IFResultM; // Result from either IEU or single-cycle FPU op writing an integer register
|
logic [P.XLEN-1:0] IFResultM; // Result from either IEU or single-cycle FPU op writing an integer register
|
||||||
// Writeback stage signals
|
// Writeback stage signals
|
||||||
|
logic RegWriteWM; // (Debug) Muxed write enable
|
||||||
logic [P.XLEN-1:0] SCResultW; // Store Conditional result
|
logic [P.XLEN-1:0] SCResultW; // Store Conditional result
|
||||||
logic [P.XLEN-1:0] ResultW; // Result to write to register file
|
logic [P.XLEN-1:0] ResultW; // Result to write to register file
|
||||||
logic [P.XLEN-1:0] IFResultW; // Result from either IEU or single-cycle FPU op writing an integer register
|
logic [P.XLEN-1:0] IFResultW; // Result from either IEU or single-cycle FPU op writing an integer register
|
||||||
logic [P.XLEN-1:0] IFCvtResultW; // Result from IEU, signle-cycle FPU op, or 2-cycle FCVT float to int
|
logic [P.XLEN-1:0] IFCvtResultW; // Result from IEU, signle-cycle FPU op, or 2-cycle FCVT float to int
|
||||||
logic [P.XLEN-1:0] MulDivResultW; // Multiply always comes from MDU. Divide could come from MDU or FPU (when using fdivsqrt for integer division)
|
logic [P.XLEN-1:0] MulDivResultW; // Multiply always comes from MDU. Divide could come from MDU or FPU (when using fdivsqrt for integer division)
|
||||||
|
// Debug signals
|
||||||
|
logic [P.XLEN-1:0] DebugGPRWriteD;
|
||||||
|
|
||||||
// Decode stage
|
// Decode stage
|
||||||
regfile #(P.XLEN, P.E_SUPPORTED) regf(clk, reset, RegWriteW, Rs1D, Rs2D, RdW, ResultW, R1D, R2D);
|
|
||||||
extend #(P) ext(.InstrD(InstrD[31:7]), .ImmSrcD, .ImmExtD);
|
extend #(P) ext(.InstrD(InstrD[31:7]), .ImmSrcD, .ImmExtD);
|
||||||
|
if (P.DEBUG_SUPPORTED) begin
|
||||||
|
regfile #(P.XLEN, P.E_SUPPORTED) regf(clk, reset, RegWriteWM, DB_Rs1D, Rs2D, RdWM, ResultWM, R1D, R2D);
|
||||||
|
assign RegWriteWM = GPRSel ? DebugGPRUpdate : RegWriteW;
|
||||||
|
assign DB_Rs1D = GPRSel ? GPRAddr : Rs1D;
|
||||||
|
assign RdWM = GPRSel ? GPRAddr : RdW;
|
||||||
|
assign ResultWM = GPRSel ? DebugGPRWriteD : ResultW;
|
||||||
|
flopenrs #(P.XLEN) GPRScanReg(.clk, .reset, .en(DebugCapture), .d(R1D), .q(DebugGPRWriteD), .scan(GPRScanEn), .scanin(GPRScanIn), .scanout(GPRScanOut));
|
||||||
|
end else begin
|
||||||
|
regfile #(P.XLEN, P.E_SUPPORTED) regf(clk, reset, RegWriteW, Rs1D, Rs2D, RdW, ResultW, R1D, R2D);
|
||||||
|
end
|
||||||
|
// FIXME: Delete once working
|
||||||
|
// regfile #(P.XLEN, P.E_SUPPORTED) regf(clk, reset, RegWriteW, Rs1D, Rs2D, RdW, ResultW, R1D, R2D);
|
||||||
|
|
||||||
// Execute stage pipeline register and logic
|
// Execute stage pipeline register and logic
|
||||||
flopenrc #(P.XLEN) RD1EReg(clk, reset, FlushE, ~StallE, R1D, R1E);
|
flopenrc #(P.XLEN) RD1EReg(clk, reset, FlushE, ~StallE, R1D, R1E);
|
||||||
@ -117,7 +143,12 @@ module datapath import cvw::*; #(parameter cvw_t P) (
|
|||||||
// Memory stage pipeline register
|
// Memory stage pipeline register
|
||||||
flopenrc #(P.XLEN) SrcAMReg(clk, reset, FlushM, ~StallM, SrcAE, SrcAM);
|
flopenrc #(P.XLEN) SrcAMReg(clk, reset, FlushM, ~StallM, SrcAE, SrcAM);
|
||||||
flopenrc #(P.XLEN) IEUResultMReg(clk, reset, FlushM, ~StallM, IEUResultE, IEUResultM);
|
flopenrc #(P.XLEN) IEUResultMReg(clk, reset, FlushM, ~StallM, IEUResultE, IEUResultM);
|
||||||
flopenrc #(P.XLEN) WriteDataMReg(clk, reset, FlushM, ~StallM, ForwardedSrcBE, WriteDataM);
|
if (P.DEBUG_SUPPORTED)
|
||||||
|
flopenrcs #(P.XLEN) WriteDataMReg(clk, reset, FlushM, ~StallM, ForwardedSrcBE, WriteDataM, DebugScanEn, DebugScanIn, DebugScanOut);
|
||||||
|
else
|
||||||
|
flopenrc #(P.XLEN) WriteDataMReg(clk, reset, FlushM, ~StallM, ForwardedSrcBE, WriteDataM);
|
||||||
|
// FIXME: Delete once working
|
||||||
|
// flopenrc #(P.XLEN) WriteDataMReg(clk, reset, FlushM, ~StallM, ForwardedSrcBE, WriteDataM);
|
||||||
|
|
||||||
// Writeback stage pipeline register and logic
|
// Writeback stage pipeline register and logic
|
||||||
flopenrc #(P.XLEN) IFResultWReg(clk, reset, FlushW, ~StallW, IFResultM, IFResultW);
|
flopenrc #(P.XLEN) IFResultWReg(clk, reset, FlushW, ~StallW, IFResultM, IFResultW);
|
||||||
|
@ -96,7 +96,11 @@ module ifu import cvw::*; #(parameter cvw_t P) (
|
|||||||
input var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW[P.PMP_ENTRIES-1:0],// PMP address from privileged unit
|
input var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW[P.PMP_ENTRIES-1:0],// PMP address from privileged unit
|
||||||
output logic InstrAccessFaultF, // Instruction access fault
|
output logic InstrAccessFaultF, // Instruction access fault
|
||||||
output logic ICacheAccess, // Report I$ read to performance counters
|
output logic ICacheAccess, // Report I$ read to performance counters
|
||||||
output logic ICacheMiss // Report I$ miss to performance counters
|
output logic ICacheMiss, // Report I$ miss to performance counters
|
||||||
|
// Debug scan chain
|
||||||
|
input logic DebugScanEn,
|
||||||
|
input logic DebugScanIn,
|
||||||
|
output logic DebugScanOut
|
||||||
);
|
);
|
||||||
|
|
||||||
localparam [31:0] nop = 32'h00000013; // instruction for NOP
|
localparam [31:0] nop = 32'h00000013; // instruction for NOP
|
||||||
@ -140,6 +144,8 @@ module ifu import cvw::*; #(parameter cvw_t P) (
|
|||||||
logic [15:0] InstrRawE, InstrRawM;
|
logic [15:0] InstrRawE, InstrRawM;
|
||||||
logic [LINELEN-1:0] FetchBuffer;
|
logic [LINELEN-1:0] FetchBuffer;
|
||||||
logic [31:0] ShiftUncachedInstr;
|
logic [31:0] ShiftUncachedInstr;
|
||||||
|
// Debug scan chain
|
||||||
|
logic DB_SCR;
|
||||||
|
|
||||||
assign PCFExt = {2'b00, PCSpillF};
|
assign PCFExt = {2'b00, PCSpillF};
|
||||||
|
|
||||||
@ -404,12 +410,33 @@ module ifu import cvw::*; #(parameter cvw_t P) (
|
|||||||
// InstrM is only needed with CSRs or atomic operations
|
// InstrM is only needed with CSRs or atomic operations
|
||||||
if (P.ZICSR_SUPPORTED | P.A_SUPPORTED) begin
|
if (P.ZICSR_SUPPORTED | P.A_SUPPORTED) begin
|
||||||
mux2 #(32) FlushInstrMMux(InstrE, nop, FlushM, NextInstrE);
|
mux2 #(32) FlushInstrMMux(InstrE, nop, FlushM, NextInstrE);
|
||||||
flopenr #(32) InstrMReg(clk, reset, ~StallM, NextInstrE, InstrM);
|
if (P.DEBUG_SUPPORTED)
|
||||||
end else assign InstrM = '0;
|
flopenrs #(32) InstrMReg(clk, reset, ~StallM, NextInstrE, InstrM, DebugScanEn, DB_SCR, DebugScanOut);
|
||||||
|
else
|
||||||
|
flopenr #(32) InstrMReg(clk, reset, ~StallM, NextInstrE, InstrM);
|
||||||
|
end else begin
|
||||||
|
assign InstrM = '0;
|
||||||
|
assign DebugScanOut = DB_SCR;
|
||||||
|
end
|
||||||
|
|
||||||
|
// FIXME: delete once working
|
||||||
|
// flopenr #(32) InstrMReg(clk, reset, ~StallM, NextInstrE, InstrM);
|
||||||
|
//end else assign InstrM = '0;
|
||||||
|
|
||||||
// PCM is only needed with CSRs or branch prediction
|
// PCM is only needed with CSRs or branch prediction
|
||||||
if (P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED)
|
if (P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED)
|
||||||
flopenr #(P.XLEN) PCMReg(clk, reset, ~StallM, PCE, PCM);
|
if (P.DEBUG_SUPPORTED)
|
||||||
else assign PCM = '0;
|
flopenrs #(P.XLEN) PCMReg(clk, reset, ~StallM, PCE, PCM, DebugScanEn, DebugScanIn, DB_SCR);
|
||||||
|
else
|
||||||
|
flopenr #(P.XLEN) PCMReg(clk, reset, ~StallM, PCE, PCM);
|
||||||
|
else begin
|
||||||
|
assign PCM = '0;
|
||||||
|
assign DB_SCR = DebugScanIn;
|
||||||
|
end
|
||||||
|
|
||||||
|
// FIXME: delete once working
|
||||||
|
// flopenr #(P.XLEN) PCMReg(clk, reset, ~StallM, PCE, PCM);
|
||||||
|
//else assign PCM = '0;
|
||||||
|
|
||||||
// If compressed instructions are supported, increment PCLink by 2 or 4 for a jal. Otherwise, just by 4
|
// If compressed instructions are supported, increment PCLink by 2 or 4 for a jal. Otherwise, just by 4
|
||||||
if (P.ZCA_SUPPORTED) begin
|
if (P.ZCA_SUPPORTED) begin
|
||||||
|
@ -154,13 +154,21 @@ module lsu import cvw::*; #(parameter cvw_t P) (
|
|||||||
logic SelDTIM; // Select DTIM rather than bus or D$
|
logic SelDTIM; // Select DTIM rather than bus or D$
|
||||||
logic [P.XLEN-1:0] WriteDataZM;
|
logic [P.XLEN-1:0] WriteDataZM;
|
||||||
logic LSULoadPageFaultM, LSUStoreAmoPageFaultM;
|
logic LSULoadPageFaultM, LSUStoreAmoPageFaultM;
|
||||||
|
|
||||||
|
logic DB_SCR; // Debug Register Scan In
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Pipeline for IEUAdr E to M
|
// Pipeline for IEUAdr E to M
|
||||||
// Zero-extend address to 34 bits for XLEN=32
|
// Zero-extend address to 34 bits for XLEN=32
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
flopenrc #(P.XLEN) AddressMReg(clk, reset, FlushM, ~StallM, IEUAdrE, IEUAdrM);
|
if (P.DEBUG_SUPPORTED)
|
||||||
|
flopenrcs #(P.XLEN) AddressMReg(.clk, .reset, .clear(FlushM), .en(~StallM), .d(IEUAdrE), .q(IEUAdrM), .scan(DebugScanEn), .scanin(DebugScanIn), .scanout(DB_SCR));
|
||||||
|
else
|
||||||
|
flopenrc #(P.XLEN) AddressMReg(.clk, .reset, .clear(FlushM), .en(~StallM), .d(IEUAdrE), .q(IEUAdrM));
|
||||||
|
|
||||||
|
// FIXME: delete once working
|
||||||
|
// flopenrc #(P.XLEN) AddressMReg(clk, reset, FlushM, ~StallM, IEUAdrE, IEUAdrM);
|
||||||
if(MISALIGN_SUPPORT) begin : ziccslm_align
|
if(MISALIGN_SUPPORT) begin : ziccslm_align
|
||||||
logic [P.XLEN-1:0] IEUAdrSpillE, IEUAdrSpillM;
|
logic [P.XLEN-1:0] IEUAdrSpillE, IEUAdrSpillM;
|
||||||
align #(P) align(.clk, .reset, .StallM, .FlushM, .IEUAdrE, .IEUAdrM, .Funct3M, .FpLoadStoreM,
|
align #(P) align(.clk, .reset, .StallM, .FlushM, .IEUAdrE, .IEUAdrM, .Funct3M, .FpLoadStoreM,
|
||||||
@ -423,6 +431,11 @@ module lsu import cvw::*; #(parameter cvw_t P) (
|
|||||||
.FpLoadStoreM, .Funct3M(LSUFunct3M), .ReadDataM);
|
.FpLoadStoreM, .Funct3M(LSUFunct3M), .ReadDataM);
|
||||||
subwordwrite #(P.LLEN) subwordwrite(.LSUFunct3M, .IMAFWriteDataM, .LittleEndianWriteDataM);
|
subwordwrite #(P.LLEN) subwordwrite(.LSUFunct3M, .IMAFWriteDataM, .LittleEndianWriteDataM);
|
||||||
|
|
||||||
|
// Capture ReadDataM
|
||||||
|
if (P.DEBUG_SUPPORTED) begin
|
||||||
|
flopenrs #(P.LLEN) ReadDataMScan (.clk, .reset, .en(DebugCapture), .d(ReadDataM), .q(), .scan(DebugScanEn), .scanin(DB_SCR), .scanout(DebugScanOut));
|
||||||
|
end
|
||||||
|
|
||||||
// Compute byte masks
|
// Compute byte masks
|
||||||
swbytemask #(P.LLEN, P.ZICCLSM_SUPPORTED) swbytemask(.Size(LSUFunct3M), .Adr(PAdrM[$clog2(P.LLEN/8)-1:0]), .ByteMask(ByteMaskM), .ByteMaskExtended(ByteMaskExtendedM));
|
swbytemask #(P.LLEN, P.ZICCLSM_SUPPORTED) swbytemask(.Size(LSUFunct3M), .Adr(PAdrM[$clog2(P.LLEN/8)-1:0]), .ByteMask(ByteMaskM), .ByteMaskExtended(ByteMaskExtendedM));
|
||||||
|
|
||||||
|
@ -49,7 +49,12 @@ module csrm import cvw::*; #(parameter cvw_t P) (
|
|||||||
output var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW [P.PMP_ENTRIES-1:0],
|
output var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW [P.PMP_ENTRIES-1:0],
|
||||||
output logic WriteMSTATUSM, WriteMSTATUSHM,
|
output logic WriteMSTATUSM, WriteMSTATUSHM,
|
||||||
output logic IllegalCSRMAccessM, IllegalCSRMWriteReadonlyM,
|
output logic IllegalCSRMAccessM, IllegalCSRMWriteReadonlyM,
|
||||||
output logic [63:0] MENVCFG_REGW
|
output logic [63:0] MENVCFG_REGW,
|
||||||
|
// Debug scan chain
|
||||||
|
input logic DebugCapture,
|
||||||
|
input logic DebugScanEn,
|
||||||
|
input logic DebugScanIn,
|
||||||
|
output logic DebugScanOut
|
||||||
);
|
);
|
||||||
|
|
||||||
logic [P.XLEN-1:0] MISA_REGW, MHARTID_REGW;
|
logic [P.XLEN-1:0] MISA_REGW, MHARTID_REGW;
|
||||||
@ -131,6 +136,11 @@ module csrm import cvw::*; #(parameter cvw_t P) (
|
|||||||
// MISA is hardwired. Spec says it could be written to disable features, but this is not supported by Wally
|
// MISA is hardwired. Spec says it could be written to disable features, but this is not supported by Wally
|
||||||
assign MISA_REGW = {(P.XLEN == 32 ? 2'b01 : 2'b10), {(P.XLEN-28){1'b0}}, MISA_26[25:0]};
|
assign MISA_REGW = {(P.XLEN == 32 ? 2'b01 : 2'b10), {(P.XLEN-28){1'b0}}, MISA_26[25:0]};
|
||||||
|
|
||||||
|
// Dummy register to provide MISA read access to DM
|
||||||
|
if (P.DEBUG_SUPPORTED) begin
|
||||||
|
flopenrs #(P.XLEN) MISAScanReg (.clk, .reset, .en(DebugCapture), .d(MISA_REGW), .q(), .scan(DebugScanEn), .scanin(DebugScanIn), .scanout(DebugScanOut));
|
||||||
|
end
|
||||||
|
|
||||||
// MHARTID is hardwired. It only exists as a signal so that the testbench can easily see it.
|
// MHARTID is hardwired. It only exists as a signal so that the testbench can easily see it.
|
||||||
assign MHARTID_REGW = '0;
|
assign MHARTID_REGW = '0;
|
||||||
|
|
||||||
|
@ -28,23 +28,35 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
|
module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
|
||||||
input logic clk, reset,
|
input logic clk, reset,
|
||||||
// Privileged
|
// Privileged
|
||||||
input logic MTimerInt, MExtInt, SExtInt, MSwInt,
|
input logic MTimerInt, MExtInt, SExtInt, MSwInt,
|
||||||
input logic [63:0] MTIME_CLINT,
|
input logic [63:0] MTIME_CLINT,
|
||||||
// Bus Interface
|
// Bus Interface
|
||||||
input logic [P.AHBW-1:0] HRDATA,
|
input logic [P.AHBW-1:0] HRDATA,
|
||||||
input logic HREADY, HRESP,
|
input logic HREADY, HRESP,
|
||||||
output logic HCLK, HRESETn,
|
output logic HCLK, HRESETn,
|
||||||
output logic [P.PA_BITS-1:0] HADDR,
|
output logic [P.PA_BITS-1:0] HADDR,
|
||||||
output logic [P.AHBW-1:0] HWDATA,
|
output logic [P.AHBW-1:0] HWDATA,
|
||||||
output logic [P.XLEN/8-1:0] HWSTRB,
|
output logic [P.XLEN/8-1:0] HWSTRB,
|
||||||
output logic HWRITE,
|
output logic HWRITE,
|
||||||
output logic [2:0] HSIZE,
|
output logic [2:0] HSIZE,
|
||||||
output logic [2:0] HBURST,
|
output logic [2:0] HBURST,
|
||||||
output logic [3:0] HPROT,
|
output logic [3:0] HPROT,
|
||||||
output logic [1:0] HTRANS,
|
output logic [1:0] HTRANS,
|
||||||
output logic HMASTLOCK
|
output logic HMASTLOCK,
|
||||||
|
input logic DebugStall,
|
||||||
|
// Debug scan chain
|
||||||
|
input logic DebugScanEn,
|
||||||
|
input logic DebugScanIn,
|
||||||
|
output logic DebugScanOut,
|
||||||
|
input logic GPRSel,
|
||||||
|
input logic DebugCapture,
|
||||||
|
input logic DebugGPRUpdate,
|
||||||
|
input logic [P.E_SUPPORTED+3:0] GPRAddr,
|
||||||
|
input logic GPRScanEn,
|
||||||
|
input logic GPRScanIn,
|
||||||
|
output logic GPRScanOut
|
||||||
);
|
);
|
||||||
|
|
||||||
logic StallF, StallD, StallE, StallM, StallW;
|
logic StallF, StallD, StallE, StallM, StallW;
|
||||||
@ -168,6 +180,9 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
|
|||||||
logic DCacheStallM, ICacheStallF;
|
logic DCacheStallM, ICacheStallF;
|
||||||
logic wfiM, IntPendingM;
|
logic wfiM, IntPendingM;
|
||||||
|
|
||||||
|
// Debug register scan chain interconnects
|
||||||
|
logic [3:0] ScanReg;
|
||||||
|
|
||||||
// instruction fetch unit: PC, branch prediction, instruction cache
|
// instruction fetch unit: PC, branch prediction, instruction cache
|
||||||
ifu #(P) ifu(.clk, .reset,
|
ifu #(P) ifu(.clk, .reset,
|
||||||
.StallF, .StallD, .StallE, .StallM, .StallW, .FlushD, .FlushE, .FlushM, .FlushW,
|
.StallF, .StallD, .StallE, .StallM, .StallW, .FlushD, .FlushE, .FlushM, .FlushW,
|
||||||
@ -189,7 +204,8 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
|
|||||||
.PrivilegeModeW, .PTE, .PageType, .SATP_REGW, .STATUS_MXR, .STATUS_SUM, .STATUS_MPRV,
|
.PrivilegeModeW, .PTE, .PageType, .SATP_REGW, .STATUS_MXR, .STATUS_SUM, .STATUS_MPRV,
|
||||||
.STATUS_MPP, .ENVCFG_PBMTE, .ENVCFG_ADUE, .ITLBWriteF, .sfencevmaM, .ITLBMissF,
|
.STATUS_MPP, .ENVCFG_PBMTE, .ENVCFG_ADUE, .ITLBWriteF, .sfencevmaM, .ITLBMissF,
|
||||||
// pmp/pma (inside mmu) signals.
|
// pmp/pma (inside mmu) signals.
|
||||||
.PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW, .InstrAccessFaultF, .InstrUpdateDAF);
|
.PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW, .InstrAccessFaultF, .InstrUpdateDAF,
|
||||||
|
.DebugScanEn, .DebugScanIn(ScanReg[1]), .DebugScanOut(ScanReg[2]));
|
||||||
|
|
||||||
// integer execution unit: integer register file, datapath and controller
|
// integer execution unit: integer register file, datapath and controller
|
||||||
ieu #(P) ieu(.clk, .reset,
|
ieu #(P) ieu(.clk, .reset,
|
||||||
@ -214,7 +230,9 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
|
|||||||
// hazards
|
// hazards
|
||||||
.StallD, .StallE, .StallM, .StallW, .FlushD, .FlushE, .FlushM, .FlushW,
|
.StallD, .StallE, .StallM, .StallW, .FlushD, .FlushE, .FlushM, .FlushW,
|
||||||
.StructuralStallD, .LoadStallD, .StoreStallD, .PCSrcE,
|
.StructuralStallD, .LoadStallD, .StoreStallD, .PCSrcE,
|
||||||
.CSRReadM, .CSRWriteM, .PrivilegedM, .CSRWriteFenceM, .InvalidateICacheM);
|
.CSRReadM, .CSRWriteM, .PrivilegedM, .CSRWriteFenceM, .InvalidateICacheM,
|
||||||
|
.DebugScanEn, .DebugScanIn(ScanReg[2]), .DebugScanOut(ScanReg[3]),
|
||||||
|
.GPRSel, .DebugCapture, .DebugGPRUpdate, .GPRAddr, .GPRScanEn, .GPRScanIn, .GPRScanOut);
|
||||||
|
|
||||||
lsu #(P) lsu(
|
lsu #(P) lsu(
|
||||||
.clk, .reset, .StallM, .FlushM, .StallW, .FlushW,
|
.clk, .reset, .StallM, .FlushM, .StallW, .FlushW,
|
||||||
@ -250,7 +268,7 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
|
|||||||
.StoreAmoAccessFaultM, // connects to privilege
|
.StoreAmoAccessFaultM, // connects to privilege
|
||||||
.InstrUpdateDAF,
|
.InstrUpdateDAF,
|
||||||
.PCSpillF, .ITLBMissF, .PTE, .PageType, .ITLBWriteF, .SelHPTW,
|
.PCSpillF, .ITLBMissF, .PTE, .PageType, .ITLBWriteF, .SelHPTW,
|
||||||
.LSUStallM);
|
.LSUStallM, .DebugCapture, .DebugScanEn, .DebugScanIn(ScanReg[3]), .DebugScanOut);
|
||||||
|
|
||||||
if(P.BUS_SUPPORTED) begin : ebu
|
if(P.BUS_SUPPORTED) begin : ebu
|
||||||
ebu #(P) ebu(// IFU connections
|
ebu #(P) ebu(// IFU connections
|
||||||
@ -276,7 +294,7 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
|
|||||||
.LSUStallM, .IFUStallF,
|
.LSUStallM, .IFUStallF,
|
||||||
.FPUStallD,
|
.FPUStallD,
|
||||||
.DivBusyE, .FDivBusyE,
|
.DivBusyE, .FDivBusyE,
|
||||||
.wfiM, .IntPendingM,
|
.wfiM, .IntPendingM, .DebugStall,
|
||||||
// Stall & flush outputs
|
// Stall & flush outputs
|
||||||
.StallF, .StallD, .StallE, .StallM, .StallW,
|
.StallF, .StallD, .StallE, .StallM, .StallW,
|
||||||
.FlushD, .FlushE, .FlushM, .FlushW);
|
.FlushD, .FlushE, .FlushM, .FlushW);
|
||||||
@ -303,7 +321,21 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
|
|||||||
.PrivilegeModeW, .SATP_REGW,
|
.PrivilegeModeW, .SATP_REGW,
|
||||||
.STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_MPP, .STATUS_FS,
|
.STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_MPP, .STATUS_FS,
|
||||||
.PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW,
|
.PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW,
|
||||||
.FRM_REGW, .ENVCFG_CBE, .ENVCFG_PBMTE, .ENVCFG_ADUE, .wfiM, .IntPendingM, .BigEndianM);
|
.FRM_REGW, .ENVCFG_CBE, .ENVCFG_PBMTE, .ENVCFG_ADUE, .wfiM, .IntPendingM, .BigEndianM,
|
||||||
|
.DebugCapture, .DebugScanEn, .DebugScanIn, .DebugScanOut(ScanReg[0]));
|
||||||
|
if (P.DEBUG_SUPPORTED) begin
|
||||||
|
flopenrs #(1) scantrapm (.clk, .reset, .en(DebugCapture), .d(TrapM), .q(), .scan(DebugScanEn), .scanin(ScanReg[0]), .scanout(ScanReg[1]));
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
assign {CSRReadValW, PrivilegeModeW,
|
||||||
|
SATP_REGW, STATUS_MXR, STATUS_SUM, STATUS_MPRV, STATUS_MPP, STATUS_FS, FRM_REGW,
|
||||||
|
// PMPCFG_ARRAY_REGW, PMPADDR_ARRAY_REGW,
|
||||||
|
ENVCFG_CBE, ENVCFG_PBMTE, ENVCFG_ADUE,
|
||||||
|
EPCM, TrapVectorM, RetM, TrapM,
|
||||||
|
sfencevmaM, BigEndianM, wfiM, IntPendingM} = '0;
|
||||||
|
assign ScanReg[1] = ScanReg[0];
|
||||||
|
end
|
||||||
|
|
||||||
end else begin
|
end else begin
|
||||||
assign {CSRReadValW, PrivilegeModeW,
|
assign {CSRReadValW, PrivilegeModeW,
|
||||||
SATP_REGW, STATUS_MXR, STATUS_SUM, STATUS_MPRV, STATUS_MPP, STATUS_FS, FRM_REGW,
|
SATP_REGW, STATUS_MXR, STATUS_SUM, STATUS_MPRV, STATUS_MPP, STATUS_FS, FRM_REGW,
|
||||||
|
@ -28,45 +28,64 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
module wallypipelinedsoc import cvw::*; #(parameter cvw_t P) (
|
module wallypipelinedsoc import cvw::*; #(parameter cvw_t P) (
|
||||||
input logic clk,
|
input logic clk,
|
||||||
input logic reset_ext, // external asynchronous reset pin
|
input logic reset_ext, // external asynchronous reset pin
|
||||||
output logic reset, // reset synchronized to clk to prevent races on release
|
output logic reset, // reset synchronized to clk to prevent races on release
|
||||||
|
// JTAG signals
|
||||||
|
input logic tck,
|
||||||
|
input logic tdi,
|
||||||
|
input logic tms,
|
||||||
|
output logic tdo,
|
||||||
// AHB Interface
|
// AHB Interface
|
||||||
input logic [P.AHBW-1:0] HRDATAEXT,
|
input logic [P.AHBW-1:0] HRDATAEXT,
|
||||||
input logic HREADYEXT, HRESPEXT,
|
input logic HREADYEXT, HRESPEXT,
|
||||||
output logic HSELEXT,
|
output logic HSELEXT,
|
||||||
output logic HSELEXTSDC,
|
output logic HSELEXTSDC,
|
||||||
// outputs to external memory, shared with uncore memory
|
// outputs to external memory, shared with uncore memory
|
||||||
output logic HCLK, HRESETn,
|
output logic HCLK, HRESETn,
|
||||||
output logic [P.PA_BITS-1:0] HADDR,
|
output logic [P.PA_BITS-1:0] HADDR,
|
||||||
output logic [P.AHBW-1:0] HWDATA,
|
output logic [P.AHBW-1:0] HWDATA,
|
||||||
output logic [P.XLEN/8-1:0] HWSTRB,
|
output logic [P.XLEN/8-1:0] HWSTRB,
|
||||||
output logic HWRITE,
|
output logic HWRITE,
|
||||||
output logic [2:0] HSIZE,
|
output logic [2:0] HSIZE,
|
||||||
output logic [2:0] HBURST,
|
output logic [2:0] HBURST,
|
||||||
output logic [3:0] HPROT,
|
output logic [3:0] HPROT,
|
||||||
output logic [1:0] HTRANS,
|
output logic [1:0] HTRANS,
|
||||||
output logic HMASTLOCK,
|
output logic HMASTLOCK,
|
||||||
output logic HREADY,
|
output logic HREADY,
|
||||||
// I/O Interface
|
// I/O Interface
|
||||||
input logic TIMECLK, // optional for CLINT MTIME counter
|
input logic TIMECLK, // optional for CLINT MTIME counter
|
||||||
input logic [31:0] GPIOIN, // inputs from GPIO
|
input logic [31:0] GPIOIN, // inputs from GPIO
|
||||||
output logic [31:0] GPIOOUT, // output values for GPIO
|
output logic [31:0] GPIOOUT, // output values for GPIO
|
||||||
output logic [31:0] GPIOEN, // output enables for GPIO
|
output logic [31:0] GPIOEN, // output enables for GPIO
|
||||||
input logic UARTSin, // UART serial data input
|
input logic UARTSin, // UART serial data input
|
||||||
output logic UARTSout, // UART serial data output
|
output logic UARTSout, // UART serial data output
|
||||||
input logic SDCIntr,
|
input logic SDCIntr,
|
||||||
input logic SPIIn, // SPI pins in
|
input logic SPIIn, // SPI pins in
|
||||||
output logic SPIOut, // SPI pins out
|
output logic SPIOut, // SPI pins out
|
||||||
output logic [3:0] SPICS // SPI chip select pins
|
output logic [3:0] SPICS // SPI chip select pins
|
||||||
);
|
);
|
||||||
|
|
||||||
// Uncore signals
|
// Uncore signals
|
||||||
logic [P.AHBW-1:0] HRDATA; // from AHB mux in uncore
|
logic [P.AHBW-1:0] HRDATA; // from AHB mux in uncore
|
||||||
logic HRESP; // response from AHB
|
logic HRESP; // response from AHB
|
||||||
logic MTimerInt, MSwInt;// timer and software interrupts from CLINT
|
logic MTimerInt, MSwInt; // timer and software interrupts from CLINT
|
||||||
logic [63:0] MTIME_CLINT; // from CLINT to CSRs
|
logic [63:0] MTIME_CLINT; // from CLINT to CSRs
|
||||||
logic MExtInt,SExtInt; // from PLIC
|
logic MExtInt,SExtInt; // from PLIC
|
||||||
|
|
||||||
|
// Debug Module signals
|
||||||
|
logic NdmReset;
|
||||||
|
logic DebugStall;
|
||||||
|
logic ScanEn;
|
||||||
|
logic ScanIn;
|
||||||
|
logic ScanOut;
|
||||||
|
logic GPRSel;
|
||||||
|
logic DebugCapture;
|
||||||
|
logic DebugGPRUpdate;
|
||||||
|
logic [P.E_SUPPORTED+3:0] GPRAddr;
|
||||||
|
logic GPRScanEn;
|
||||||
|
logic GPRScanIn;
|
||||||
|
logic GPRScanOut;
|
||||||
|
|
||||||
// synchronize reset to SOC clock domain
|
// synchronize reset to SOC clock domain
|
||||||
synchronizer resetsync(.clk, .d(reset_ext), .q(reset));
|
synchronizer resetsync(.clk, .d(reset_ext), .q(reset));
|
||||||
@ -75,7 +94,9 @@ module wallypipelinedsoc import cvw::*; #(parameter cvw_t P) (
|
|||||||
wallypipelinedcore #(P) core(.clk, .reset,
|
wallypipelinedcore #(P) core(.clk, .reset,
|
||||||
.MTimerInt, .MExtInt, .SExtInt, .MSwInt, .MTIME_CLINT,
|
.MTimerInt, .MExtInt, .SExtInt, .MSwInt, .MTIME_CLINT,
|
||||||
.HRDATA, .HREADY, .HRESP, .HCLK, .HRESETn, .HADDR, .HWDATA, .HWSTRB,
|
.HRDATA, .HREADY, .HRESP, .HCLK, .HRESETn, .HADDR, .HWDATA, .HWSTRB,
|
||||||
.HWRITE, .HSIZE, .HBURST, .HPROT, .HTRANS, .HMASTLOCK
|
.HWRITE, .HSIZE, .HBURST, .HPROT, .HTRANS, .HMASTLOCK,
|
||||||
|
.DebugStall, .DebugScanEn(ScanEn), .DebugScanIn(ScanOut), .DebugScanOut(ScanIn),
|
||||||
|
.GPRSel, .DebugCapture, .DebugGPRUpdate, .GPRAddr, .GPRScanEn, .GPRScanIn(GPRScanOut), .GPRScanOut(GPRScanIn)
|
||||||
);
|
);
|
||||||
|
|
||||||
// instantiate uncore if a bus interface exists
|
// instantiate uncore if a bus interface exists
|
||||||
@ -90,4 +111,12 @@ module wallypipelinedsoc import cvw::*; #(parameter cvw_t P) (
|
|||||||
MTIME_CLINT, GPIOOUT, GPIOEN, UARTSout, SPIOut, SPICS} = '0;
|
MTIME_CLINT, GPIOOUT, GPIOEN, UARTSout, SPIOut, SPICS} = '0;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// instantiate debug module (dm)
|
||||||
|
if (P.DEBUG_SUPPORTED) begin
|
||||||
|
dm #(P) dm (.clk, .rst(reset), .NdmReset, .tck, .tdi, .tms, .tdo,
|
||||||
|
.DebugStall, .ScanEn, .ScanIn, .ScanOut, .GPRSel, .DebugCapture, .DebugGPRUpdate,
|
||||||
|
.GPRAddr, .GPRScanEn, .GPRScanIn, .GPRScanOut);
|
||||||
|
end else begin
|
||||||
|
assign {NdmReset, DebugStall, ScanOut, GPRSel, DebugCapture, DebugGPRUpdate, GPRAddr, GPRScanEn, GPRScanOut} = '0;
|
||||||
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
Loading…
Reference in New Issue
Block a user