Merge branch 'main' of github.com:stineje/cvw

This commit is contained in:
James Stine 2024-06-09 12:06:28 -05:00
commit 74a7f47085
7 changed files with 536 additions and 542 deletions

View File

@ -1,429 +0,0 @@
#!/usr/bin/env python3
#########################################################################################
# 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 re
import time
from telnetlib import Telnet
debug = False
# TODO: if JTAG clk is fast enough, need to check for busy between absract commands
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 interrogate():
global XLEN
global tapname
write("scan_chain")
raw = tn.read_until(b"> ").decode('ascii')
scan_chain = raw.replace("\r", "").replace("> ", "")
scan_chain = [tap for tap in scan_chain.split("\n")[2:] if tap]
if len(scan_chain) > 1:
print(f"Found multiple taps. Selecting tap #0\n{raw}")
scan_chain = scan_chain[0]
tapname = re.search("\d\s+(.+?)\s+", scan_chain).group(1)
print(f"DM tapname: {tapname}")
write("riscv info")
info = tn.read_until(b"> ").decode('ascii').replace("\r", "").replace("> ", "").split("\n")
for line in info:
if XLEN := re.search("hart.xlen\s+(\d+)", line).group(1):
XLEN = int(XLEN)
break
print(f"XLEN: {XLEN}")
def init():
global tn
tn = Telnet("127.0.0.1", 4444)
atexit.register(cleanup)
read() # clear welcome message from read buffer
interrogate()
activate_dm()
# TODO: query 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 (zero)" : "0x1000",
"x1 (ra)" : "0x1001",
"x2 (sp)" : "0x1002",
"x3 (gp)" : "0x1003",
"x4 (tp)" : "0x1004",
"x5 (t0)" : "0x1005",
"x6 (t1)" : "0x1006",
"x7 (t2)" : "0x1007",
"x8 (s0/fp)" : "0x1008",
"x9 (s1)" : "0x1009",
"x10 (a0)" : "0x100A",
"x11 (a1)" : "0x100B",
"x12 (a2)" : "0x100C",
"x13 (a3)" : "0x100D",
"x14 (a4)" : "0x100E",
"x15 (a5)" : "0x100F",
"x16 (a6)" : "0x1010",
"x17 (a7)" : "0x1011",
"x18 (s2)" : "0x1012",
"x19 (s3)" : "0x1013",
"x20 (s4)" : "0x1014",
"x21 (s5)" : "0x1015",
"x22 (s6)" : "0x1016",
"x23 (s7)" : "0x1017",
"x24 (s8)" : "0x1018",
"x25 (s9)" : "0x1019",
"x26 (s10)" : "0x101A",
"x27 (s11)" : "0x101B",
"x28 (t3)" : "0x101C",
"x29 (t4)" : "0x101D",
"x30 (t5)" : "0x101E",
"x31 (t6)" : "0x101F",
"f0 (ft0)" : "0x1020",
"f1 (ft1)" : "0x1021",
"f2 (ft2)" : "0x1022",
"f3 (ft3)" : "0x1023",
"f4 (ft4)" : "0x1024",
"f5 (ft5)" : "0x1025",
"f6 (ft6)" : "0x1026",
"f7 (ft7)" : "0x1027",
"f8 (fs0)" : "0x1028",
"f9 (fs1)" : "0x1029",
"f10 (fa0)" : "0x102A",
"f11 (fa1)" : "0x102B",
"f12 (fa2)" : "0x102C",
"f13 (fa3)" : "0x102D",
"f14 (fa4)" : "0x102E",
"f15 (fa5)" : "0x102F",
"f16 (fa6)" : "0x1030",
"f17 (fa7)" : "0x1031",
"f18 (fs2)" : "0x1032",
"f19 (fs3)" : "0x1033",
"f20 (fs4)" : "0x1034",
"f21 (fs5)" : "0x1035",
"f22 (fs6)" : "0x1036",
"f23 (fs7)" : "0x1037",
"f24 (fs8)" : "0x1038",
"f25 (fs9)" : "0x1039",
"f26 (fs10)" : "0x103A",
"f27 (fs11)" : "0x103B",
"f28 (ft8)" : "0x103C",
"f29 (ft9)" : "0x103D",
"f30 (ft10)" : "0x103E",
"f31 (ft11)" : "0x103F",
}
nonstandard_register_lengths = {
"TRAPM" : 1,
"INSTRM" : 32,
"MEMRWM" : 2,
"INSTRVALIDM" : 1,
"READDATAM" : 64
}

View File

@ -6,7 +6,7 @@
# Written: matthew.n.otto@okstate.edu # Written: matthew.n.otto@okstate.edu
# Created: 19 April 2024 # Created: 19 April 2024
# #
# Purpose: Send test commands to OpenOCD via local telnet connection # Purpose: script to automate testing of hardware debug interface
# #
# A component of the CORE-V-WALLY configurable RISC-V project. # A component of the CORE-V-WALLY configurable RISC-V project.
# https:#github.com/openhwgroup/cvw # https:#github.com/openhwgroup/cvw
@ -30,36 +30,37 @@
import random import random
import time import time
import hw_debug_interface from openocd_tcl_wrapper import OpenOCD
from hw_debug_interface import *
random_stimulus = False random_stimulus = True
def main(): def main():
registers = dict.fromkeys(register_translations.keys(),[]) with OpenOCD() as cvw:
registers = dict.fromkeys(cvw.register_translations.keys(),[])
reg_addrs = list(registers.keys()) reg_addrs = list(registers.keys())
init()
global XLEN global XLEN
XLEN = hw_debug_interface.XLEN XLEN = cvw.LLEN
reset_dm() global nonstandard_register_lengths
reset_hart() nonstandard_register_lengths = cvw.nonstandard_register_lengths
cvw.reset_dm()
cvw.reset_hart()
time.sleep(70) # wait for OpenSBI time.sleep(70) # wait for OpenSBI
halt() cvw.halt()
status()
# dump data in all registers # dump data in all registers
for r in reg_addrs: for r in reg_addrs:
try: try:
data = read_data(r) data = cvw.read_data(r)
registers[r] = data registers[r] = data
print(f"{r}: {data}") print(f"{r}: {data}")
except Exception as e: except Exception as e:
if e.args[0] == "exception": # Invalid register (not implemented) if e.args[0] == "exception": # Invalid register (not implemented)
del registers[r] del registers[r]
clear_abstrcmd_err() cvw.clear_abstrcmd_err()
else: else:
raise e raise e
input("Compare values to ILA, press any key to continue") input("Compare values to ILA, press any key to continue")
@ -72,20 +73,18 @@ def main():
for r in reg_addrs: for r in reg_addrs:
test_data = random_hex(r) test_data = random_hex(r)
try: try:
write_data(r, test_data) cvw.write_data(r, test_data)
test_reg_data[r] = test_data test_reg_data[r] = test_data
print(f"Writing {test_data} to {r}") print(f"Writing {test_data} to {r}")
except Exception as e: except Exception as e:
if e.args[0] == "not supported": # Register is read only if e.args[0] == "not supported": # Register is read only
del registers[r] del registers[r]
clear_abstrcmd_err() cvw.clear_abstrcmd_err()
else: else:
raise e raise e
check_errors()
# GPR X0 is always 0 # GPR X0 is always 0
test_reg_data["X0"] = "0x" + "0"*(XLEN//4) test_reg_data["x0"] = "0x" + "0"*(cvw.LLEN//4)
# Confirm data was written correctly # Confirm data was written correctly
reg_addrs = list(registers.keys()) reg_addrs = list(registers.keys())
@ -93,7 +92,7 @@ def main():
random.shuffle(reg_addrs) random.shuffle(reg_addrs)
for r in reg_addrs: for r in reg_addrs:
try: try:
rdata = read_data(r) rdata = cvw.read_data(r)
except Exception as e: except Exception as e:
raise e raise e
if rdata != test_reg_data[r]: if rdata != test_reg_data[r]:
@ -106,22 +105,21 @@ def main():
for r in reg_addrs: for r in reg_addrs:
print(f"Writing {registers[r]} to {r}") print(f"Writing {registers[r]} to {r}")
try: try:
write_data(r, registers[r]) cvw.write_data(r, registers[r])
except Exception as e: except Exception as e:
raise e raise e
# Confirm data was written correctly # Confirm data was written correctly
for r in reg_addrs: for r in reg_addrs:
try: try:
rdata = read_data(r) rdata = cvw.read_data(r)
except Exception as e: except Exception as e:
raise e raise e
if rdata != registers[r]: if rdata != registers[r]:
raise Exception(f"Register {r} read did not return correct data: {rdata} != {registers[r]}") raise Exception(f"Register {r} read did not return correct data: {rdata} != {registers[r]}")
print("All writes successful") print("All writes successful")
resume() cvw.resume()
status()
def random_hex(reg_name): def random_hex(reg_name):

392
bin/openocd_tcl_wrapper.py Normal file
View File

@ -0,0 +1,392 @@
#########################################################################################
# openocd_tcl_wrapper.py
#
# Written: matthew.n.otto@okstate.edu
# Created: 8 June 2024
#
# Purpose: Python wrapper library used to send debug commands to OpenOCD
#
# A component of the CORE-V-WALLY configurable RISC-V project.
# https://github.com/openhwgroup/cvw
#
# Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
#
# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
#
# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
# except in compliance with the License, or, at your option, the Apache License version 2.0. You
# may obtain a copy of the License at
#
# https://solderpad.org/licenses/SHL-2.1/
#
# Unless required by applicable law or agreed to in writing, any work distributed under the
# License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions
# and limitations under the License.
#########################################################################################
import math
import socket
import time
ENDMSG = b'\x1a'
class OpenOCD:
def __init__(self):
self.tcl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def __enter__(self):
self.tcl.connect(("127.0.0.1", 6666))
self.LLEN = 64 #TODO: find this
return self
def __exit__(self, type, value, traceback):
try:
self.send("exit")
finally:
self.tcl.close()
def capture(self, cmd):
return self.send(f"capture \"{cmd}\"")
def send(self, cmd):
data = cmd.encode("ascii") + ENDMSG
self.tcl.send(data)
return self.receive()
def receive(self):
data = bytes()
while True:
byte = self.tcl.recv(1)
if byte == ENDMSG:
break
else:
data += byte
data = data.decode("ascii").rstrip()
return data
def trst(self):
self.send("pathmove RESET IDLE")
def write_dtmcs(self, dtmhardreset=False, dmireset=False):
"""Send reset commands to DTMCS. Used to clear sticky DMI OP error status"""
data = 0
data |= dtmhardreset << 17
data |= dmireset << 16
if not data:
print("Warning: not writing DTMCS (dtmhardreset and dmireset are both false)")
return
tapname = "cvw.cpu"
self.send(f"irscan {tapname} 0x10") # dtmcs instruction
self.send(f"drscan {tapname} 32 {hex(data)}")
op = self.capture(f"drscan {tapname} 32 0x0")
if (int(op) >> 10) & 0x3:
raise Exception("Error: failed to reset DTMCS (nonzero dmistat)")
def write_dmi(self, address, data):
cmd = f"riscv dmi_write {address} {data}"
rsp = self.capture(cmd)
if "Failed" in rsp:
raise Exception(rsp)
def read_dmi(self, address):
cmd = f"riscv dmi_read {address}"
return self.capture(cmd)
def activate_dm(self):
self.write_dmi("0x10", "0x1")
dmstat = int(self.read_dmi("0x10"), 16)
if not dmstat & 0x1:
raise Exception("Error: failed to activate debug module")
def reset_dm(self):
self.write_dmi("0x10", "0x0")
dmstat = int(self.read_dmi("0x10"), 16)
if dmstat & 0x1:
raise Exception("Error: failed to deactivate debug module")
self.activate_dm()
def reset_hart(self):
self.write_dmi("0x10", "0x3")
self.write_dmi("0x10", "0x1")
dmstat = int(self.read_dmi("0x11"), 16) # check HaveReset
if not ((dmstat >> 18) & 0x3):
raise Exception("Error: Hart failed to reset")
self.write_dmi("0x10", "0x10000001") # ack HaveReset
def set_haltonreset(self):
self.write_dmi("0x10", "0x9")
def clear_haltonreset(self):
self.write_dmi("0x10", "0x5")
def halt(self):
self.write_dmi("0x10", "0x80000001")
dmstat = int(self.read_dmi("0x11"), 16) # Check halted bit
if not ((dmstat >> 8) & 0x3):
raise Exception("Error: Hart failed to halt")
def resume(self):
self.write_dmi("0x10", "0x40000001") # Send resume command
dmstat = int(self.read_dmi("0x11"), 16) # Check resumeack bit
if not ((dmstat >> 16) & 0x3):
raise Exception("Error: Hart failed to resume")
self.write_dmi("0x10", "0x40000001") # Clear resumeack bit
def step(self):
self.write_dmi("0x10", "0xC0000001")
# BOZO: checking resumeack after halt is pointless until sdext halt method is added
dmstat = int(self.read_dmi("0x11"), 16)
if not ((dmstat >> 16) & 0x3):
raise Exception("Error: Hart failed to resume")
def access_register(self, write, regno, addr_size=None):
data = 1 << 17 # transfer bit always set
if not addr_size:
addr_size = self.LLEN
elif addr_size not in (32, 64, 128):
raise Exception("must provide valid register access size (32, 64, 128). See: 3.7.1.1 aarsize")
data += int(math.log2(addr_size // 8)) << 20
data += write << 16
data += regno
self.write_dmi("0x17", hex(data))
def write_data(self, register, data):
"""Write data to specified register"""
# Write data to 32 bit message registers
data = int(data, 16)
self.write_dmi("0x4", hex(data & 0xffffffff))
if self.LLEN >= 64:
self.write_dmi("0x5", hex((data >> 32) & 0xffffffff))
if self.LLEN == 128:
self.write_dmi("0x6", hex((data >> 64) & 0xffffffff))
self.write_dmi("0x7", hex((data >> 96) & 0xffffffff))
# Translate register alias to DM regno
regno = self.translate_regno(register)
# Transfer data from msg registers to target register
self.access_register(write=True, regno=regno)
# Check that operations completed without error
if acerr := self.check_abstrcmderr():
raise Exception(acerr)
def read_data(self, register):
"""Read data from specified register"""
# Translate register alias to DM regno
regno = self.translate_regno(register)
# Transfer data from target register to msg registers
self.access_register(write=False, regno=regno)
# Read data from 32 bit message registers
data = ""
data = self.read_dmi("0x4").replace("0x", "").zfill(8)
if self.LLEN >= 64:
data = self.read_dmi("0x5").replace("0x", "").zfill(8) + data
if self.LLEN == 128:
data = self.read_dmi("0x6").replace("0x", "").zfill(8) + data
data = self.read_dmi("0x7").replace("0x", "").zfill(8) + data
# Check that operations completed without error
if acerr := self.check_abstrcmderr():
raise Exception(acerr)
return f"0x{data}"
def translate_regno(self, register):
if register not in self.register_translations:
register = self.abi_translations[register]
return int(self.register_translations[register], 16)
def check_abstrcmderr(self):
"""These errors must be cleared using clear_abstrcmd_err() before another OP can be executed"""
abstractcs = int(self.read_dmi("0x16"), 16)
# CmdErr is only valid if Busy is 0
while True:
if not bool((abstractcs & 0x1000) >> 12): # if not Busy
break
time.sleep(0.05)
abstractcs = int(self.read_dmi("0x16"), 16)
return self.cmderr_translations[(abstractcs & 0x700) >> 8]
def clear_abstrcmd_err(self):
self.write_dmi("0x16", "0x700")
if self.check_abstrcmderr():
raise Exception("Error: failed to clear AbstrCmdErr")
# 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",
"f0" : "0x1020",
"f1" : "0x1021",
"f2" : "0x1022",
"f3" : "0x1023",
"f4" : "0x1024",
"f5" : "0x1025",
"f6" : "0x1026",
"f7" : "0x1027",
"f8" : "0x1028",
"f9" : "0x1029",
"f10" : "0x102A",
"f11" : "0x102B",
"f12" : "0x102C",
"f13" : "0x102D",
"f14" : "0x102E",
"f15" : "0x102F",
"f16" : "0x1030",
"f17" : "0x1031",
"f18" : "0x1032",
"f19" : "0x1033",
"f20" : "0x1034",
"f21" : "0x1035",
"f22" : "0x1036",
"f23" : "0x1037",
"f24" : "0x1038",
"f25" : "0x1039",
"f26" : "0x103A",
"f27" : "0x103B",
"f28" : "0x103C",
"f29" : "0x103D",
"f30" : "0x103E",
"f31" : "0x103F",
}
abi_translations = {
"x0" : "zero",
"x1" : "ra",
"x2" : "sp",
"x3" : "gp",
"x4" : "tp",
"x5" : "t0",
"x6" : "t1",
"x7" : "t2",
"x8" : "s0/fp",
"x9" : "s1",
"x10" : "a0",
"x11" : "a1",
"x12" : "a2",
"x13" : "a3",
"x14" : "a4",
"x15" : "a5",
"x16" : "a6",
"x17" : "a7",
"x18" : "s2",
"x19" : "s3",
"x20" : "s4",
"x21" : "s5",
"x22" : "s6",
"x23" : "s7",
"x24" : "s8",
"x25" : "s9",
"x26" : "s10",
"x27" : "s11",
"x28" : "t3",
"x29" : "t4",
"x30" : "t5",
"x31" : "t6",
"f0" : "ft0",
"f1" : "ft1",
"f2" : "ft2",
"f3" : "ft3",
"f4" : "ft4",
"f5" : "ft5",
"f6" : "ft6",
"f7" : "ft7",
"f8" : "fs0",
"f9" : "fs1",
"f10" : "fa0",
"f11" : "fa1",
"f12" : "fa2",
"f13" : "fa3",
"f14" : "fa4",
"f15" : "fa5",
"f16" : "fa6",
"f17" : "fa7",
"f18" : "fs2",
"f19" : "fs3",
"f20" : "fs4",
"f21" : "fs5",
"f22" : "fs6",
"f23" : "fs7",
"f24" : "fs8",
"f25" : "fs9",
"f26" : "fs10",
"f27" : "fs11",
"f28" : "ft8",
"f29" : "ft9",
"f30" : "ft10",
"f31" : "ft11",
}
abi_translations |= dict(map(reversed, abi_translations.items())) # two way translations
nonstandard_register_lengths = {
"TRAPM" : 1,
"INSTRM" : 32,
"MEMRWM" : 2,
"INSTRVALIDM" : 1,
"READDATAM" : 64
}

View File

@ -6,7 +6,7 @@ adapter driver ftdi
# when multiple adapters with the same vid_pid are connected (ex: arty-a7 and usb-jtag) # when multiple adapters with the same vid_pid are connected (ex: arty-a7 and usb-jtag)
# need to specify which usb port to drive # need to specify which usb port to drive
# find numerical path using command "lsusb -t" (<bus>-<port>) # find numerical path using command "lsusb -t" (<bus>-<port>)
adapter usb location 1-3 adapter usb location 1-4
ftdi vid_pid 0x0403 0x6010 ftdi vid_pid 0x0403 0x6010
ftdi channel 0 ftdi channel 0

View File

@ -85,10 +85,11 @@ module dm import cvw::*; #(parameter cvw_t P) (
logic ResumeReq; logic ResumeReq;
logic HaltOnReset; logic HaltOnReset;
logic Halted; logic Halted;
logic AckHaveReset;
hartcontrol hartcontrol(.clk, .rst(rst | ~DmActive), .NdmReset, .HaltReq, hartcontrol hartcontrol(.clk, .rst(rst | ~DmActive), .NdmReset, .AckHaveReset, .HaltReq,
.ResumeReq, .HaltOnReset, .DebugStall, .Halted, .AllRunning, .ResumeReq, .HaltOnReset, .DebugStall, .Halted, .AllRunning, .AnyRunning,
.AnyRunning, .AllHalted, .AnyHalted, .AllResumeAck, .AnyResumeAck); .AllHalted, .AnyHalted, .AllResumeAck, .AnyResumeAck, .AllHaveReset, .AnyHaveReset);
enum logic [3:0] {INACTIVE, IDLE, ACK, R_DATA, W_DATA, DMSTATUS, W_DMCONTROL, R_DMCONTROL, enum logic [3:0] {INACTIVE, IDLE, ACK, R_DATA, W_DATA, DMSTATUS, W_DMCONTROL, R_DMCONTROL,
@ -137,9 +138,11 @@ module dm import cvw::*; #(parameter cvw_t P) (
// DMStatus // DMStatus
logic StickyUnavail; logic StickyUnavail;
logic ImpEBreak; logic ImpEBreak;
logic AllHaveReset;
logic AnyHaveReset;
logic AllResumeAck; logic AllResumeAck;
logic AnyResumeAck; logic AnyResumeAck;
logic AllNonExistent; logic AllNonExistent; // TODO
logic AnyNonExistent; logic AnyNonExistent;
logic AllUnavail; // TODO logic AllUnavail; // TODO
logic AnyUnavail; logic AnyUnavail;
@ -164,7 +167,7 @@ module dm import cvw::*; #(parameter cvw_t P) (
10'b0, 4'b0, NdmReset, DmActive}; 10'b0, 4'b0, NdmReset, DmActive};
assign DMStatus = {7'b0, 1'b0, StickyUnavail, ImpEBreak, 2'b0, assign DMStatus = {7'b0, 1'b0, StickyUnavail, ImpEBreak, 2'b0,
2'b0, AllResumeAck, AnyResumeAck, AllNonExistent, AllHaveReset, AnyHaveReset, AllResumeAck, AnyResumeAck, AllNonExistent,
AnyNonExistent, AllUnavail, AnyUnavail, AllRunning, AnyRunning, AllHalted, AnyNonExistent, AllUnavail, AnyUnavail, AllRunning, AnyRunning, AllHalted,
AnyHalted, Authenticated, AuthBusy, HasResetHaltReq, ConfStrPtrValid, Version}; AnyHalted, Authenticated, AuthBusy, HasResetHaltReq, ConfStrPtrValid, Version};
@ -183,8 +186,11 @@ module dm import cvw::*; #(parameter cvw_t P) (
case (State) case (State)
INACTIVE : begin INACTIVE : begin
// Reset Values // Reset Values
// TODO: one-line these
RspData <= 0; RspData <= 0;
HaltReq <= 0; HaltReq <= 0;
ResumeReq <= 0;
AckHaveReset <= 0;
HaltOnReset <= 0; HaltOnReset <= 0;
NdmReset <= 0; NdmReset <= 0;
StickyUnavail <= 0; StickyUnavail <= 0;
@ -204,6 +210,7 @@ module dm import cvw::*; #(parameter cvw_t P) (
ACK : begin ACK : begin
NewAcState <= AC_IDLE; NewAcState <= AC_IDLE;
ResumeReq <= 0; ResumeReq <= 0;
AckHaveReset <= 0;
if (~ReqValid) if (~ReqValid)
State <= ~DmActive ? INACTIVE : IDLE; State <= ~DmActive ? INACTIVE : IDLE;
end end
@ -256,7 +263,7 @@ module dm import cvw::*; #(parameter cvw_t P) (
W_DMCONTROL : begin W_DMCONTROL : begin
// While an abstract command is executing (busy in abstractcs is high), a debugger must not change // 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 // hartsel, and must not write 1 to haltreq, resumereq, ackhavereset, setresethaltreq, or clrresethaltreq
if (Busy & (ReqData[`HALTREQ] | ReqData[`RESUMEREQ] | ReqData[`SETRESETHALTREQ] | ReqData[`CLRRESETHALTREQ])) if (Busy & (ReqData[`HALTREQ] | ReqData[`RESUMEREQ] | ReqData[`ACKHAVERESET] | ReqData[`SETRESETHALTREQ] | ReqData[`CLRRESETHALTREQ]))
CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr; CmdErr <= ~|CmdErr ? `CMDERR_BUSY : CmdErr;
else begin else begin
HaltReq <= ReqData[`HALTREQ]; HaltReq <= ReqData[`HALTREQ];
@ -266,11 +273,12 @@ module dm import cvw::*; #(parameter cvw_t P) (
// On any given write, a debugger may only write 1 to at most one of the following bits: resumereq, // 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 // hartreset, ackhavereset, setresethaltreq, and clrresethaltreq. The others must be written 0
case ({ReqData[`RESUMEREQ],ReqData[`SETRESETHALTREQ],ReqData[`CLRRESETHALTREQ]}) case ({ReqData[`RESUMEREQ],ReqData[`ACKHAVERESET],ReqData[`SETRESETHALTREQ],ReqData[`CLRRESETHALTREQ]})
3'b000 :; // None 4'b0000 :; // None
3'b100 : ResumeReq <= 1; 4'b1000 : ResumeReq <= 1;
3'b010 : HaltOnReset <= 1; 4'b0100 : AckHaveReset <= 1;
3'b001 : HaltOnReset <= 0; 4'b0010 : HaltOnReset <= 1;
4'b0001 : HaltOnReset <= 0;
default : begin // Invalid (not onehot), dont write any changes default : begin // Invalid (not onehot), dont write any changes
HaltReq <= HaltReq; HaltReq <= HaltReq;
AckUnavail <= AckUnavail; AckUnavail <= AckUnavail;

View File

@ -55,7 +55,7 @@ module dtm #(parameter ADDR_WIDTH, parameter JTAG_DEVICE_ID) (
// Clock Domain Crossing // Clock Domain Crossing
logic tcks; // Synchronized JTAG clock logic tcks; // Synchronized JTAG clock
logic resetn; logic resetn; // TODO: reset DM (but not hart)
logic UpdateDtmcs; logic UpdateDtmcs;
logic [31:0] DtmcsIn; logic [31:0] DtmcsIn;
logic [31:0] DtmcsOut; logic [31:0] DtmcsOut;

View File

@ -30,6 +30,7 @@
module hartcontrol( module hartcontrol(
input logic clk, rst, input logic clk, rst,
input logic NdmReset, // Triggers HaltOnReset behavior input logic NdmReset, // Triggers HaltOnReset behavior
input logic AckHaveReset, // Clears *HaveReset status
input logic HaltReq, // Initiate core halt input logic HaltReq, // Initiate core halt
input logic ResumeReq, // Initiates core resume input logic ResumeReq, // Initiates core resume
@ -44,18 +45,31 @@ module hartcontrol(
output logic AllHalted, output logic AllHalted,
output logic AnyHalted, output logic AnyHalted,
output logic AllResumeAck, output logic AllResumeAck,
output logic AnyResumeAck output logic AnyResumeAck,
output logic AllHaveReset,
output logic AnyHaveReset
); );
enum logic {RUNNING, HALTED} State;
assign AnyHaveReset = AllHaveReset;
always_ff @(posedge clk) begin
if (NdmReset)
AllHaveReset <= 1;
else if (AckHaveReset)
AllHaveReset <= 0;
end
assign Halted = DebugStall; assign Halted = DebugStall;
assign AllRunning = ~DebugStall; assign AllRunning = ~DebugStall;
assign AnyRunning = ~DebugStall; assign AnyRunning = ~DebugStall;
assign AllHalted = DebugStall; assign AllHalted = DebugStall;
assign AnyHalted = DebugStall; assign AnyHalted = DebugStall;
assign AllResumeAck = ~DebugStall; // BOZO: when sdext is implemented (proper step support is added)
assign AnyResumeAck = ~DebugStall; // change ResumeReq to be ignored when HaltReq
// but ResumeReq should still always clear *ResumeAck
enum logic {RUNNING, HALTED} State; assign AnyResumeAck = AllResumeAck;
assign DebugStall = (State == HALTED); assign DebugStall = (State == HALTED);
@ -66,9 +80,20 @@ module hartcontrol(
State <= HaltOnReset ? HALTED : RUNNING; State <= HaltOnReset ? HALTED : RUNNING;
else begin else begin
case (State) case (State)
RUNNING : State <= HaltReq ? HALTED : RUNNING; RUNNING : begin
if (HaltReq) begin
State <= HALTED;
end else if (ResumeReq) begin
AllResumeAck <= 0;
end
end
HALTED : State <= ResumeReq ? RUNNING : HALTED; HALTED : begin
if (ResumeReq) begin
State <= RUNNING;
AllResumeAck <= 1;
end
end
endcase endcase
end end
end end