mirror of
				https://github.com/openhwgroup/cvw
				synced 2025-02-11 06:05:49 +00:00 
			
		
		
		
	convert debug script to TCL interface, remove telnetlib dependency
This commit is contained in:
		
							parent
							
								
									c3243caacf
								
							
						
					
					
						commit
						7f63daa49c
					
				@ -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
 | 
			
		||||
}
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
# Written: matthew.n.otto@okstate.edu
 | 
			
		||||
# 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.
 | 
			
		||||
# https:#github.com/openhwgroup/cvw
 | 
			
		||||
@ -30,98 +30,96 @@
 | 
			
		||||
import random
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import hw_debug_interface
 | 
			
		||||
from hw_debug_interface import *
 | 
			
		||||
from openocd_tcl_wrapper import OpenOCD
 | 
			
		||||
 | 
			
		||||
random_stimulus = False
 | 
			
		||||
random_stimulus = True
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    registers = dict.fromkeys(register_translations.keys(),[])
 | 
			
		||||
    reg_addrs = list(registers.keys())
 | 
			
		||||
    with OpenOCD() as cvw:
 | 
			
		||||
        registers = dict.fromkeys(cvw.register_translations.keys(),[])
 | 
			
		||||
        reg_addrs = list(registers.keys())
 | 
			
		||||
 | 
			
		||||
    init()
 | 
			
		||||
    global XLEN
 | 
			
		||||
    XLEN = hw_debug_interface.XLEN
 | 
			
		||||
    reset_dm()
 | 
			
		||||
    reset_hart()
 | 
			
		||||
        global XLEN
 | 
			
		||||
        XLEN = cvw.LLEN
 | 
			
		||||
        global nonstandard_register_lengths
 | 
			
		||||
        nonstandard_register_lengths = cvw.nonstandard_register_lengths
 | 
			
		||||
 | 
			
		||||
    time.sleep(70) # wait for OpenSBI
 | 
			
		||||
        cvw.reset_dm()
 | 
			
		||||
        cvw.reset_hart()
 | 
			
		||||
 | 
			
		||||
    halt()
 | 
			
		||||
    status()
 | 
			
		||||
        time.sleep(70)  # wait for OpenSBI
 | 
			
		||||
 | 
			
		||||
    # 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:
 | 
			
		||||
        cvw.halt()
 | 
			
		||||
 | 
			
		||||
        # dump data in all registers
 | 
			
		||||
        for r in reg_addrs:
 | 
			
		||||
            try:
 | 
			
		||||
                data = cvw.read_data(r)
 | 
			
		||||
                registers[r] = data
 | 
			
		||||
                print(f"{r}: {data}")
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                if e.args[0] == "exception":  # Invalid register (not implemented)
 | 
			
		||||
                    del registers[r]
 | 
			
		||||
                    cvw.clear_abstrcmd_err()
 | 
			
		||||
                else:
 | 
			
		||||
                    raise e
 | 
			
		||||
        input("Compare values to ILA, press any key to continue")
 | 
			
		||||
 | 
			
		||||
        # Write random data to all registers
 | 
			
		||||
        reg_addrs = list(registers.keys())
 | 
			
		||||
        if random_stimulus:
 | 
			
		||||
            random.shuffle(reg_addrs)
 | 
			
		||||
        test_reg_data = {}
 | 
			
		||||
        for r in reg_addrs:
 | 
			
		||||
            test_data = random_hex(r)
 | 
			
		||||
            try:
 | 
			
		||||
                cvw.write_data(r, test_data)
 | 
			
		||||
                test_reg_data[r] = test_data
 | 
			
		||||
                print(f"Writing {test_data} to {r}")
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                if e.args[0] == "not supported":  # Register is read only
 | 
			
		||||
                    del registers[r]
 | 
			
		||||
                    cvw.clear_abstrcmd_err()
 | 
			
		||||
                else:
 | 
			
		||||
                    raise e
 | 
			
		||||
 | 
			
		||||
        # GPR X0 is always 0
 | 
			
		||||
        test_reg_data["x0"] = "0x" + "0"*(cvw.LLEN//4)
 | 
			
		||||
 | 
			
		||||
        # Confirm data was written correctly
 | 
			
		||||
        reg_addrs = list(registers.keys())
 | 
			
		||||
        if random_stimulus:
 | 
			
		||||
            random.shuffle(reg_addrs)
 | 
			
		||||
        for r in reg_addrs:
 | 
			
		||||
            try:
 | 
			
		||||
                rdata = cvw.read_data(r)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                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()
 | 
			
		||||
            if rdata != test_reg_data[r]:
 | 
			
		||||
                print(f"Error: register {r} read did not return correct data: {rdata} != {test_reg_data[r]}")
 | 
			
		||||
            else:
 | 
			
		||||
                print(f"Reading {rdata} from {r}")
 | 
			
		||||
 | 
			
		||||
        # Return all registers to original state
 | 
			
		||||
        reg_addrs = list(registers.keys())
 | 
			
		||||
        for r in reg_addrs:
 | 
			
		||||
            print(f"Writing {registers[r]} to {r}")
 | 
			
		||||
            try:
 | 
			
		||||
                cvw.write_data(r, registers[r])
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                raise e
 | 
			
		||||
 | 
			
		||||
    check_errors()
 | 
			
		||||
        # Confirm data was written correctly
 | 
			
		||||
        for r in reg_addrs:
 | 
			
		||||
            try:
 | 
			
		||||
                rdata = cvw.read_data(r)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                raise e
 | 
			
		||||
            if rdata != registers[r]:
 | 
			
		||||
                raise Exception(f"Register {r} read did not return correct data: {rdata} != {registers[r]}")
 | 
			
		||||
        print("All writes successful")
 | 
			
		||||
 | 
			
		||||
    # 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]:
 | 
			
		||||
            print(f"Error: register {r} read did not return correct data: {rdata} != {test_reg_data[r]}")
 | 
			
		||||
        else:
 | 
			
		||||
            print(f"Reading {rdata} from {r}")
 | 
			
		||||
 | 
			
		||||
    # Return all registers to original state
 | 
			
		||||
    reg_addrs = list(registers.keys())
 | 
			
		||||
    for r in reg_addrs:
 | 
			
		||||
        print(f"Writing {registers[r]} to {r}")
 | 
			
		||||
        try:
 | 
			
		||||
            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()
 | 
			
		||||
        cvw.resume()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def random_hex(reg_name):
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										392
									
								
								bin/openocd_tcl_wrapper.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								bin/openocd_tcl_wrapper.py
									
									
									
									
									
										Normal 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
 | 
			
		||||
    }
 | 
			
		||||
@ -6,7 +6,7 @@ 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
 | 
			
		||||
adapter usb location 1-4
 | 
			
		||||
 | 
			
		||||
ftdi vid_pid 0x0403 0x6010
 | 
			
		||||
ftdi channel 0
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user