Browse Source

Debugger separated

master
kaqu 1 year ago
parent
commit
8ac2ee57e3
  1. 1
      .gitignore
  2. 54
      debugger/dbgeval.py
  3. 272
      debugger/qt5dbg.py
  4. 66
      debugger/risq5dbg.py

1
.gitignore

@ -4,3 +4,4 @@ backup/*
software/build/*
helpers/__pycache__/*
libmodules/__pycache__/*
debugger/__pycache__/*

54
helpers/risq5dbg.py → debugger/dbgeval.py

@ -1,20 +1,9 @@
#!/usr/bin/env python3
#
# risq5dbg.py
# RISQ5 (remote) debugger
# dbgeval.py
# Diverse stati strings
#
# Requires litex gateway server, start like (adjust path):
# ~/fpga/bin/lxserver --udp --udp-ip=192.168.1.20
#
# History:
# --------
# 20.01.21/KQ Initial version
#
import argparse
import time
from litex import RemoteClient
x_description = [ # Pseudo reg. naming ...
" ze", " ra", " sp", " gp", " tp", " t0", " t1", " t2",
@ -23,15 +12,20 @@ x_description = [ # Pseudo reg. naming ...
" s8", " s9", " s10", "s11", " t3", " t4", " t5", " t6"
]
def flag(val, c1, c2):
if val != 0:
return c1
return c2
def disassemble(opcode, pc):
return "unknown?"
def printflag(val, c1, c2):
if val != 0:
print(c1, end="")
else:
print(c2, end="")
def disassemble(i32opcode, i32pc):
return "unknown"
def dumpregs(wb, bWithRegs):
# DRAM loader stati
i32DRAMReadCount = wb.regs.dramtransfer_b32RCount.read()
@ -107,31 +101,3 @@ def dumpregs(wb, bWithRegs):
print("---- [b] set breakpoint (pc+=16) [e] execute 'til breakpoint [h] halt execution")
print("---- [ ] status\t[r] dump regs\t[n] to single step\t[x] to exit")
def risq5dbg(csr_csv):
wb = RemoteClient(csr_csv=csr_csv) # Access wishbone bus
wb.open() # to remote client
print("RISQ5 debugger started ...")
wb.regs.risq5_b32mode.write(0) # Define no-run & single stepping mode
wb.regs.risq5_b32mode.write(1) # Enable run (but will halt before 1. instruction!
dumpregs(wb, 0) # Start w/ current status display
# From build/csr.csv we know:
# csr_register,dma_reader_base,0x82004000,4,rw
print("*** NEW PC SET ***")
wb.regs.risq5_b32_next_pc.write(0x40190000) # Provide a new PC address
mode = wb.regs.risq5_b32mode.read() # Pick up current mode setting
wb.regs.risq5_b32mode.write(mode | 16) # Adjust PC now
dumpregs(wb, 0) # Dump current status
wb.close() # Close wishbone access
print("RISQ5 debugger terminated.")
def main():
parser = argparse.ArgumentParser(description="LiteX SoC on Colorlight 5A-75X Testbench")
parser.add_argument("--csr-csv", default="build/csr.csv", help="CSR list location")
args = parser.parse_args()
risq5dbg(args.csr_csv)
if __name__ == "__main__":
main()

272
debugger/qt5dbg.py

@ -0,0 +1,272 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
pong_viewer.py
Pandemic Pong view client only
"""
import sys, time
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from PyQt5.QtGui import QPainter, QBrush, QPen, QFont, QFontMetrics, QColor
from dbgeval import x_description, flag, disassemble
class dbgWindow(QMainWindow):
"""Qt5 base class"""
def __init__(self, wb):
super().__init__()
self.title = "RISQ5 Visual Debugger"
self.top = 0
self.left = 0
self.win_width = 1920 / 2
self.win_height = 960 / 2
self.wb = wb # Store wishbone access
self.InitWindow() # s.b. !
def InitWindow(self):
"""Prepare window for later drawing"""
self.widget = QWidget() # Central layout widget
# Prepare x<n> regs displays
self.MAXLABEL = 32
self.lblList = list() # All input/output fields
for i in range(self.MAXLABEL):
q = QLabel("{0}/x{1}=".format(x_description[i], i)) # For now, may become entry field later ...
self.lblList.append(q)
self.vbox1 = QVBoxLayout()
self.hbox0 = QHBoxLayout() # L1 & LU cache
self.qpbL1Cache = QLabel("L1")
self.qpbLUCache = QLabel("LU")
self.hbox0.addWidget(self.qpbL1Cache)
self.hbox0.addWidget(self.qpbLUCache)
self.hbox1 = QHBoxLayout() # Row of regs (x0 .. x3)
for i in range(0, 4):
self.hbox1.addWidget(self.lblList[i])
self.hbox2 = QHBoxLayout() # Row of regs (x4 .. x7)
for i in range(4, 8):
self.hbox2.addWidget(self.lblList[i])
self.hbox3 = QHBoxLayout() # Row of regs (x8 .. x11)
for i in range(8, 12):
self.hbox3.addWidget(self.lblList[i])
self.hbox4 = QHBoxLayout() # Row of regs (x12 .. x15)
for i in range(12, 16):
self.hbox4.addWidget(self.lblList[i])
self.hbox5 = QHBoxLayout() # Row of regs (x16 .. x19)
for i in range(16, 20):
self.hbox5.addWidget(self.lblList[i])
self.hbox6 = QHBoxLayout() # Row of regs (x20 .. x23)
for i in range(20, 24):
self.hbox6.addWidget(self.lblList[i])
self.hbox7 = QHBoxLayout() # Row of regs (x24 .. x27)
for i in range(24, 28):
self.hbox7.addWidget(self.lblList[i])
self.hbox8 = QHBoxLayout() # Row of regs (x28 .. x31)
for i in range(28, 32):
self.hbox8.addWidget(self.lblList[i])
self.hbox9 = QHBoxLayout() # Buttons, row #1
self.qpbPC = QLabel("PC=")
self.qpbOpcode = QLabel("OPCODE=")
self.hbox9.addWidget(self.qpbPC)
self.hbox9.addWidget(self.qpbOpcode)
self.hbox10 = QHBoxLayout() # Mode & status
self.qpbMode = QLabel("Mode=")
self.qpbStatus = QLabel("Status=")
self.hbox10.addWidget(self.qpbMode)
self.hbox10.addWidget(self.qpbStatus)
self.hbox11 = QHBoxLayout() # FSMs
self.qpbFSMs = QLabel("FSMs")
self.hbox11.addWidget(self.qpbFSMs)
self.hbox12 = QHBoxLayout() # CNTs
self.qpbCNTs = QLabel("CNTs")
self.hbox12.addWidget(self.qpbCNTs)
self.hbox13 = QHBoxLayout() # BP, Next PC
self.qpbBP = QLabel("BP")
self.qpbNextPC = QLabel("Next PC")
self.hbox13.addWidget(self.qpbBP)
self.hbox13.addWidget(self.qpbNextPC)
self.hbox20 = QHBoxLayout()
self.qpbNextStep = QPushButton("Next step")
self.qpbNextStep.clicked.connect(self.on_clickNextStep)
self.qpbExecute = QPushButton("Execute")
self.qpbExecute.clicked.connect(self.on_clickExecute)
self.qpbHalt = QPushButton("Halt")
self.qpbHalt.clicked.connect(self.on_clickHalt)
self.hbox20.addWidget(self.qpbNextStep)
self.hbox20.addWidget(self.qpbExecute)
self.hbox20.addWidget(self.qpbHalt)
self.vbox1.addLayout(self.hbox0, 1)
self.vbox1.addLayout(self.hbox1, 1)
self.vbox1.addLayout(self.hbox2, 1)
self.vbox1.addLayout(self.hbox3, 1)
self.vbox1.addLayout(self.hbox4, 1)
self.vbox1.addLayout(self.hbox5, 1)
self.vbox1.addLayout(self.hbox6, 1)
self.vbox1.addLayout(self.hbox7, 1)
self.vbox1.addLayout(self.hbox8, 1)
self.vbox1.addLayout(self.hbox10, 2)
self.vbox1.addLayout(self.hbox11, 2)
self.vbox1.addLayout(self.hbox12, 2)
self.vbox1.addLayout(self.hbox13, 2)
self.vbox1.addLayout(self.hbox9, 2)
self.vbox1.addLayout(self.hbox20, 2)
sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
sizePolicy.setVerticalStretch(21) #setHeightForWidth(True)
self.setSizePolicy(sizePolicy)
self.widget.setLayout(self.vbox1)
self.setCentralWidget(self.widget)
self.setWindowTitle(self.title)
self.setGeometry(int(self.top), int(self.left), int(self.win_width), int(self.win_height))
# Modify main window background
pW = self.vbox1.parentWidget()
p = pW.palette()
p.setColor(pW.backgroundRole(), Qt.lightGray) # QColor(8,8,0,255)) #Qt.darkGray)
pW.setPalette(p)
pW.setAutoFillBackground(True)
self.show()
self.timer = QTimer() # Start processing 'loop'
self.timer.setInterval(2000) # 2s for start ...
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.on_timer)
self.timer.start()
def UpdateRISQ5Stati(self):
if self.wb != None: # Valid data avail.?
# DRAM loader stati
i32DRAMReadCount = self.wb.regs.dramtransfer_b32RCount.read()
i32DRAMReadOffset = self.wb.regs.dramtransfer_b9Offset.read()
i32DRAMReadData = self.wb.regs.dramtransfer_b32Data.read()
msg = "L1 cache loads: {0} L1[{1}] -> {2}".format(
i32DRAMReadCount, i32DRAMReadOffset, hex(i32DRAMReadData))
self.qpbL1Cache.setText(msg)
i32DRAMReadCount = self.wb.regs.dramtransfer2_b32RCount.read()
i32DRAMReadOffset = self.wb.regs.dramtransfer2_b9Offset.read()
i32DRAMReadData = self.wb.regs.dramtransfer2_b32Data.read()
msg = "LU cache loads: {0} LU[{1}] -> {2}".format(
i32DRAMReadCount, i32DRAMReadOffset, hex(i32DRAMReadData))
self.qpbLUCache.setText(msg)
i32mode = self.wb.regs.risq5_b32mode.read()
msg = "Mode: {0} (0000 0000 0000 ".format(hex(i32mode))
msg = msg + flag(i32mode & 8, 'B', '-') + flag(i32mode & 4, 'S', '-') + flag(i32mode & 2, 'N', '-') + flag(i32mode & 1, 'R', '-') + ")"
self.qpbMode.setText(msg)
i32status = self.wb.regs.risq5_b32status.read()
msg = "Status:{0} (0000 0000 00".format(hex(i32status))
msg = msg + flag(i32status & 32, 'V', '-') + flag(i32status & 16, '>', '-') + " " + flag(i32status & 8, '<', '-') + flag(i32status & 4, 'I', '-') + flag(i32status & 2, 'B', '-') + flag(i32status & 1, 'V', '-') + ")"
self.qpbStatus.setText(msg)
i32FSMs = self.wb.regs.risq5_b32_FSMs.read()
msg = "FSMs: {0} (L1={1} ALU={2} LU={3} DECODE={4})".format(
hex(i32FSMs),
i32FSMs & 0xF,
(i32FSMs >> 4) & 0xF,
(i32FSMs >> 8) & 0xF,
(i32FSMs >> 12) & 0x1FF
)
self.qpbFSMs.setText(msg)
i32Counters = self.wb.regs.risq5_b32_Counters.read()
msg = "CNTs: {0} (C1={1}[Cache loads] C2={2}[Instruction count] C3={3}[Cache offset] C4={4}[reserved])".format(
hex(i32Counters),
i32Counters & 0xFF,
(i32Counters >> 8) & 0xFF,
(i32Counters >> 16) & 0x1FF, # 9-bit
(i32Counters >> 25) & 0x7F # 7-bit
)
self.qpbCNTs.setText(msg)
i32Breakpoint = self.wb.regs.risq5_b32_breakpoint.read()
self.qpbBP.setText("BP={0}".format(hex(i32Breakpoint)))
i32NextPC = self.wb.regs.risq5_b32_next_pc.read()
self.qpbNextPC.setText("New PC={0}".format(hex(i32NextPC)))
for i in range(32): # Walk x<n> register file
self.wb.regs.risq5_b5_wb_reg_no.write(i) # Select register
val = self.wb.regs.risq5_b32_wb_reg_value_r.read() # Pick actual register risq5_b32_wb_reg_value_r
self.lblList[i].setText("{0}/x{1}={2}".format(x_description[i], i, hex(val)))
pc = self.wb.regs.risq5_b32_PC.read()
self.qpbPC.setText("PC={0}".format(hex(pc)))
opcode = self.wb.regs.risq5_b32_opcode.read()
self.qpbOpcode.setText("OPCODE={0} {1}".format(hex(opcode), disassemble(opcode, pc)))
@pyqtSlot()
def on_timer(self):
"""Qt5 timer event runs the game ..."""
self.timer.stop() # Block overrun
self.UpdateRISQ5Stati() # THE job
#self.update() # Enforce redraw (explicitely)
self.timer.start() # Re-enable timer event
@pyqtSlot()
def on_clickNextStep(self):
self.timer.stop()
print("*** NEXT STEP ***")
self.wb.regs.risq5_b32mode.write(self.wb.regs.risq5_b32mode.read() | (1 + 4)) # Run enable & trigger single step
self.UpdateRISQ5Stati() # THE job
self.timer.start()
@pyqtSlot()
def on_clickExecute(self):
self.timer.stop()
print("*** EXECUTE ***")
self.wb.regs.risq5_b32mode.write(self.wb.regs.risq5_b32mode.read() | (1 + 2)) # Run & 'no single stepping'
self.UpdateRISQ5Stati() # THE job
self.timer.start()
@pyqtSlot()
def on_clickHalt(self):
self.timer.stop()
print("*** HALT ***")
self.wb.regs.risq5_b32mode.write(self.wb.regs.risq5_b32mode.read() & (0xFFFFFFFF - 2)) # Clear 'no signal stepping'
self.UpdateRISQ5Stati() # THE job
self.timer.start()
@pyqtSlot()
def cleanUp(self):
self.timer.stop()
if self.wb != None:
self.wb.close() # Close wishbone access
print("RISQ5 debugger terminated.")
# sys.exit(0)
def run_Qt5_viewer(wb):
"""Qt5 viewer"""
app = QApplication(sys.argv) # Eval command line args
window = dbgWindow(wb) # Create Qt5 GUI
app.aboutToQuit.connect(window.cleanUp) # Install housekeeping handler
return app.exec_() # & run logic
if __name__ == "__main__":
run_Qt5_viewer(None)

66
debugger/risq5dbg.py

@ -0,0 +1,66 @@
#!/usr/bin/env python3
#
# risq5dbg.py
# RISQ5 (remote) debugger
#
# Requires litex gateway server, start like (adjust path):
# ~/fpga/bin/lxserver --udp --udp-ip=192.168.1.20
#
# History:
# --------
# 20.01.21/KQ Initial version
#
import argparse
import time
from litex import RemoteClient
from qt5dbg import run_Qt5_viewer
from dbgeval import x_description, dumpregs
def risq5dbg(csr_csv):
wb = RemoteClient(csr_csv=csr_csv) # Access wishbone bus
wb.open() # to remote client
print("RISQ5 debugger started ...")
print("Entering single step mode")
wb.regs.risq5_b32mode.write(0) # Define no-run & single stepping mode
wb.regs.risq5_b32mode.write(1) # Enable run (but will halt before 1. instruction!
#dumpregs(wb, 0) # Start w/ current status display
print("PC adjusted to 0x40190000")
wb.regs.risq5_b32_next_pc.write(0x40190000) # Provide a new PC address
mode = wb.regs.risq5_b32mode.read() # Pick up current mode setting
wb.regs.risq5_b32mode.write(mode | 16) # Adjust PC now
#dumpregs(wb, 0) # Dump current status
run_Qt5_viewer(wb) # Now switch to GUI
def main():
parser = argparse.ArgumentParser(description="RISQ5 Debugger Parser")
parser.add_argument("--csr-csv", default="build/csr.csv", help="CSR list location")
# Dummy args for not changing calling from VSC
parser.add_argument("--build", action="store_true", help="not used")
parser.add_argument("--load", action="store_true", help="not used")
parser.add_argument("--board", default="5a-75b", help="not used")
parser.add_argument("--revision", default="7.0", type=str, help="not used")
parser.add_argument("--with-ethernet", action="store_true", help="not used")
parser.add_argument("--with-etherbone", action="store_true", help="not used")
parser.add_argument("--eth-phy", default=0, type=int, help="not used")
parser.add_argument("--ip-address", default="192.168.1.50", help="not used")
parser.add_argument("--mac-address", default="0x726b895bc2e2", help="not used")
parser.add_argument("--sys-clk-freq", default=60e6, type=float, help="not used")
parser.add_argument("--use-internal-osc", action="store_true", help="not used")
parser.add_argument("--sdram-rate", default="1:1", help="not used")
parser.add_argument("--csr_csv", default="build/csr.csv", help="not used")
parser.add_argument("--doc", action="store_true", help="not used")
parser.add_argument("--flash", action="store_true", help="not used")
parser.add_argument("--uart-name", default="crossover", help="not used")
args = parser.parse_args()
risq5dbg(args.csr_csv) # Point of no return ...
if __name__ == "__main__":
main()
Loading…
Cancel
Save