seed debug module for Wally

This commit is contained in:
James Stine 2024-06-03 16:37:13 -05:00
parent f73ebc1b45
commit 77ec3d58c6
23 changed files with 2329 additions and 70 deletions

404
bin/hw_interface.py Executable file
View 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
View 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
View 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
View 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

View File

@ -294,6 +294,9 @@ typedef struct packed {
int DURLEN ;
int DIVb ;
int DIVBLEN ;
// Debug Module
logic DEBUG_SUPPORTED;
} cvw_t;
endpackage

436
src/debug/dm.sv Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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

View File

@ -442,9 +442,21 @@ module controller import cvw::*; #(parameter cvw_t P) (
assign IntDivE = MDUE & Funct3E[2]; // Integer division operation
// Memory stage pipeline control register
if (P.DEBUG_SUPPORTED) begin
flopenrc #(22) controlregM(clk, reset, FlushM, ~StallM,
{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);
// Writeback stage pipeline control register

View File

@ -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] MDUResultW, // MDU (Multiply/divide unit) 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
// 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
// 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] ImmExtD; // Extended immediate 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] IFResultM; // Result from either IEU or single-cycle FPU op writing an integer register
// Writeback stage signals
logic RegWriteWM; // (Debug) Muxed write enable
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] 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] 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
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);
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
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
flopenrc #(P.XLEN) SrcAMReg(clk, reset, FlushM, ~StallM, SrcAE, SrcAM);
flopenrc #(P.XLEN) IEUResultMReg(clk, reset, FlushM, ~StallM, IEUResultE, IEUResultM);
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
flopenrc #(P.XLEN) IFResultWReg(clk, reset, FlushW, ~StallW, IFResultM, IFResultW);

View File

@ -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
output logic InstrAccessFaultF, // Instruction access fault
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
@ -140,6 +144,8 @@ module ifu import cvw::*; #(parameter cvw_t P) (
logic [15:0] InstrRawE, InstrRawM;
logic [LINELEN-1:0] FetchBuffer;
logic [31:0] ShiftUncachedInstr;
// Debug scan chain
logic DB_SCR;
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
if (P.ZICSR_SUPPORTED | P.A_SUPPORTED) begin
mux2 #(32) FlushInstrMMux(InstrE, nop, FlushM, NextInstrE);
if (P.DEBUG_SUPPORTED)
flopenrs #(32) InstrMReg(clk, reset, ~StallM, NextInstrE, InstrM, DebugScanEn, DB_SCR, DebugScanOut);
else
flopenr #(32) InstrMReg(clk, reset, ~StallM, NextInstrE, InstrM);
end else assign InstrM = '0;
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
if (P.ZICSR_SUPPORTED | P.BPRED_SUPPORTED)
if (P.DEBUG_SUPPORTED)
flopenrs #(P.XLEN) PCMReg(clk, reset, ~StallM, PCE, PCM, DebugScanEn, DebugScanIn, DB_SCR);
else
flopenr #(P.XLEN) PCMReg(clk, reset, ~StallM, PCE, PCM);
else assign PCM = '0;
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 (P.ZCA_SUPPORTED) begin

View File

@ -155,12 +155,20 @@ module lsu import cvw::*; #(parameter cvw_t P) (
logic [P.XLEN-1:0] WriteDataZM;
logic LSULoadPageFaultM, LSUStoreAmoPageFaultM;
logic DB_SCR; // Debug Register Scan In
/////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline for IEUAdr E to M
// 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
logic [P.XLEN-1:0] IEUAdrSpillE, IEUAdrSpillM;
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);
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
swbytemask #(P.LLEN, P.ZICCLSM_SUPPORTED) swbytemask(.Size(LSUFunct3M), .Adr(PAdrM[$clog2(P.LLEN/8)-1:0]), .ByteMask(ByteMaskM), .ByteMaskExtended(ByteMaskExtendedM));

View File

@ -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 logic WriteMSTATUSM, WriteMSTATUSHM,
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;
@ -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
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.
assign MHARTID_REGW = '0;

View File

@ -44,7 +44,19 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
output logic [2:0] HBURST,
output logic [3:0] HPROT,
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;
@ -168,6 +180,9 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
logic DCacheStallM, ICacheStallF;
logic wfiM, IntPendingM;
// Debug register scan chain interconnects
logic [3:0] ScanReg;
// instruction fetch unit: PC, branch prediction, instruction cache
ifu #(P) ifu(.clk, .reset,
.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,
.STATUS_MPP, .ENVCFG_PBMTE, .ENVCFG_ADUE, .ITLBWriteF, .sfencevmaM, .ITLBMissF,
// 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
ieu #(P) ieu(.clk, .reset,
@ -214,7 +230,9 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
// hazards
.StallD, .StallE, .StallM, .StallW, .FlushD, .FlushE, .FlushM, .FlushW,
.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(
.clk, .reset, .StallM, .FlushM, .StallW, .FlushW,
@ -250,7 +268,7 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
.StoreAmoAccessFaultM, // connects to privilege
.InstrUpdateDAF,
.PCSpillF, .ITLBMissF, .PTE, .PageType, .ITLBWriteF, .SelHPTW,
.LSUStallM);
.LSUStallM, .DebugCapture, .DebugScanEn, .DebugScanIn(ScanReg[3]), .DebugScanOut);
if(P.BUS_SUPPORTED) begin : ebu
ebu #(P) ebu(// IFU connections
@ -276,7 +294,7 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
.LSUStallM, .IFUStallF,
.FPUStallD,
.DivBusyE, .FDivBusyE,
.wfiM, .IntPendingM,
.wfiM, .IntPendingM, .DebugStall,
// Stall & flush outputs
.StallF, .StallD, .StallE, .StallM, .StallW,
.FlushD, .FlushE, .FlushM, .FlushW);
@ -303,7 +321,21 @@ module wallypipelinedcore import cvw::*; #(parameter cvw_t P) (
.PrivilegeModeW, .SATP_REGW,
.STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_MPP, .STATUS_FS,
.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
assign {CSRReadValW, PrivilegeModeW,
SATP_REGW, STATUS_MXR, STATUS_SUM, STATUS_MPRV, STATUS_MPP, STATUS_FS, FRM_REGW,

View File

@ -31,6 +31,11 @@ module wallypipelinedsoc import cvw::*; #(parameter cvw_t P) (
input logic clk,
input logic reset_ext, // external asynchronous reset pin
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
input logic [P.AHBW-1:0] HRDATAEXT,
input logic HREADYEXT, HRESPEXT,
@ -64,10 +69,24 @@ module wallypipelinedsoc import cvw::*; #(parameter cvw_t P) (
// Uncore signals
logic [P.AHBW-1:0] HRDATA; // from AHB mux in uncore
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 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
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,
.MTimerInt, .MExtInt, .SExtInt, .MSwInt, .MTIME_CLINT,
.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
@ -90,4 +111,12 @@ module wallypipelinedsoc import cvw::*; #(parameter cvw_t P) (
MTIME_CLINT, GPIOOUT, GPIOEN, UARTSout, SPIOut, SPICS} = '0;
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