#!/usr/bin/env python3
+# Notes for "Debug" mode:
+# both microwatt and Libre-SOC implement (pretty much) the same DMI
+# interface. TBD: really, there should be an OPF Debug SIG which
+# defines this properly. still, these two are interoperable.
+# https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/debug/dmi.py
+# https://github.com/antonblanchard/microwatt/blob/master/core_debug.vhdl
+
import os
import argparse
from migen import (Signal, FSM, If, Display, Finish, NextValue, NextState)
+from migen import Display as D
from litex.build.generic_platform import Pins, Subsignal
from litex.build.sim import SimPlatform
#ram_fname = "/tmp/test.bin"
#ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
# "micropython/firmware.bin"
- #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
- # "tests/xics/xics.bin"
+ ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
+ "tests/xics/xics.bin"
#ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
# "tests/decrementer/decrementer.bin"
#ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
# "hello_world/hello_world.bin"
- ram_fname = None
+ ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
+ "tests/mmu/mmu.bin"
+ #ram_fname = None
# reserve XICS ICP and XICS memory addresses.
- self.mem_map['icp'] = 0xc0004000
- self.mem_map['ics'] = 0xc0005000
+ self.mem_map['xicsicp'] = 0xc0004000
+ self.mem_map['xicsics'] = 0xc0005000
self.mem_map['gpio'] = 0xc0007000
- #self.csr_map["icp"] = 8 # 8 x 0x800 == 0x4000
- #self.csr_map["ics"] = 10 # 10 x 0x800 == 0x5000
+ #self.csr_map["xicsicp"] = 8 # 8 x 0x800 == 0x4000
+ #self.csr_map["xicsics"] = 10 # 10 x 0x800 == 0x5000
ram_init = []
if ram_fname:
if cpu == "libresoc":
# XICS interrupt devices
- icp_addr = self.mem_map['icp']
+ icp_addr = self.mem_map['xicsicp']
icp_wb = self.cpu.xics_icp
icp_region = SoCRegion(origin=icp_addr, size=0x20, cached=False)
- self.bus.add_slave(name='icp', slave=icp_wb, region=icp_region)
+ self.bus.add_slave(name='xicsicp', slave=icp_wb, region=icp_region)
- ics_addr = self.mem_map['ics']
+ ics_addr = self.mem_map['xicsics']
ics_wb = self.cpu.xics_ics
ics_region = SoCRegion(origin=ics_addr, size=0x1000, cached=False)
- self.bus.add_slave(name='ics', slave=ics_wb, region=ics_region)
+ self.bus.add_slave(name='xicsics', slave=ics_wb, region=ics_region)
if "gpio" in variant:
# Simple GPIO peripheral
self.add_constant("MEMTEST_DATA_DEBUG", 1)
- # add JTAG platform pins
- platform.add_extension([
- ("jtag", 0,
- Subsignal("tck", Pins(1)),
- Subsignal("tms", Pins(1)),
- Subsignal("tdi", Pins(1)),
- Subsignal("tdo", Pins(1)),
- )
- ])
+ if "jtag" in variant:
+ # add JTAG platform pins
+ platform.add_extension([
+ ("jtag", 0,
+ Subsignal("tck", Pins(1)),
+ Subsignal("tms", Pins(1)),
+ Subsignal("tdi", Pins(1)),
+ Subsignal("tdo", Pins(1)),
+ )
+ ])
- jtagpads = platform.request("jtag")
- self.comb += self.cpu.jtag_tck.eq(jtagpads.tck)
- self.comb += self.cpu.jtag_tms.eq(jtagpads.tms)
- self.comb += self.cpu.jtag_tdi.eq(jtagpads.tdi)
- self.comb += jtagpads.tdo.eq(self.cpu.jtag_tdo)
+ jtagpads = platform.request("jtag")
+ self.comb += self.cpu.jtag_tck.eq(jtagpads.tck)
+ self.comb += self.cpu.jtag_tms.eq(jtagpads.tms)
+ self.comb += self.cpu.jtag_tdi.eq(jtagpads.tdi)
+ self.comb += jtagpads.tdo.eq(self.cpu.jtag_tdo)
# Debug ---------------------------------------------------------------
+ # (enable with ./sim.py --debug --variant=standard)
if not debug:
return
+ # In debug mode, the DMI interface is used to perform single-step
+ # and dump of the full register set (MSR, r0-r31, CR, XER, PC).
+ # by running the exact same program with microwatt and libre-soc
+ # a straight "diff -u" of the complete progress dumps can be done
+ # and therefore computation instruction discrepancies found immediately
+ # and easily, running at "verilator" speed.
+ #
+ # the FSM is a bit of a dog's dinner, it relies on the way that DMI
+ # works, sending requests at periodic intervals. needs work. DoesTheJob.
+
# setup running of DMI FSM
dmi_addr = Signal(4)
dmi_din = Signal(64)
# debug log out
dbg_addr = Signal(4)
+ regnum = Signal(6)
dbg_dout = Signal(64)
dbg_msg = Signal(1)
# debug messages out
self.sync += If(dbg_msg,
(If(active_dbg & (dbg_addr == 0b10), # PC
- Display("pc : %016x", dbg_dout),
+ D("pc : %016x", dbg_dout),
),
If(dbg_addr == 0b10, # PC
pc.eq(dbg_dout), # capture PC
),
- #If(dbg_addr == 0b11, # MSR
- # Display(" msr: %016x", dbg_dout),
- #),
+ If(dbg_addr == 0b11, # MSR
+ D(" msr: %016x", dbg_dout),
+ ),
If(dbg_addr == 0b1000, # CR
- Display(" cr : %016x", dbg_dout),
+ D(" cr : %016x", dbg_dout),
),
If(dbg_addr == 0b1001, # XER
- Display(" xer: so %d ca %d 32 %d ov %d 32 %d",
+ D(" xer: so %d ca %d 32 %d ov %d 32 %d",
xer_so, xer_ca, xer_ca32, xer_ov, xer_ov32),
),
- If(dbg_addr == 0b101, # GPR
- Display(" gpr: %016x", dbg_dout),
+ If(dbg_addr == 0b101, # GPRs (and "fast" SPRs)
+ If(regnum <= 31, D(" gpr%02x: %016x",
+ regnum, dbg_dout),), # GPRs
+ If(regnum == 32, D(" LR: %016x", dbg_dout),), # LR
+ If(regnum == 33, D(" CTR: %016x", dbg_dout),), # CTR
+ If(regnum == 34, D(" SRR0: %016x", dbg_dout),), # SRR0
+ If(regnum == 35, D(" SRR1: %016x", dbg_dout),), # SRR1
+ If(regnum == 36, D(" HSRR0: %016x", dbg_dout),), # HSRR0
+ If(regnum == 37, D(" HSRR1: %016x", dbg_dout),), # HSRR1
+ If(regnum == 38, D(" SPRG0: %016x", dbg_dout),), # SPRG0
+ If(regnum == 39, D(" SPRG1: %016x", dbg_dout),), # SPRG1
+ If(regnum == 40, D(" SPRG2: %016x", dbg_dout),), # SPRG2
+ If(regnum == 41, D(" SPRG3: %016x", dbg_dout),), # SPRG3
+ If(regnum == 42, D(" HSPRG0: %016x", dbg_dout),), # HSPRG0
+ If(regnum == 43, D(" HSPRG1: %016x", dbg_dout),), # HSPRG1
+ If(regnum == 44, D(" XER: %016x", dbg_dout),), # XER
+ If(regnum == 45, D(" TAR: %016x", dbg_dout),), # TAR
+ #If(regnum == 46, D(" SVSRR0: %016x", dbg_dout),), # SVSRR0
),
# also check if this is a "stat"
If(dbg_addr == 1, # requested a STAT
- #Display(" stat: %x", dbg_dout),
+ #D(" stat: %x", dbg_dout),
If(dbg_dout & 2, # bit 2 of STAT is "stopped" mode
dmirunning.eq(1), # continue running
dmi_monitor.eq(0), # and stop monitor mode
#self.comb += active_dbg.eq((0x0 < pc) & (pc < 0x58))
self.comb += active_dbg.eq(1)
-
# get the MSR
self.sync += If(active_dbg & (dmicount == 12),
(dmi_addr.eq(0b11), # MSR
)
)
- if cpu == "libresoc":
+ if cpu == "libresoc": # XXX TODO: waiting on microwatt upstream patch
#self.comb += active_dbg_cr.eq((0x10300 <= pc) & (pc <= 0x12600))
self.comb += active_dbg_cr.eq(0)
#self.comb += active_dbg_xer.eq((0x10300 <= pc) & (pc <= 0x1094c))
self.comb += active_dbg_xer.eq(active_dbg_cr)
- # get the CR
+ # get the XER
self.sync += If(active_dbg_xer & (dmicount == 20),
(dmi_addr.eq(0b1001), # XER
dmi_req.eq(1),
)
)
- # read all 32 GPRs
- for i in range(32):
+ # read all 32 GPRs plus the next 16 which in microwatt are
+ # the "fast" SPRs, LR, CTR, SRR0, SRR1, etc.
+ for i in range(48):
self.sync += If(active_dbg & (dmicount == 24+(i*8)),
(dmi_addr.eq(0b100), # GSPR addr
- dmi_din.eq(i), # r1
+ dmi_din.eq(i), # register number (0-31 GPR, 32-48 fast SPRs)
+ regnum.eq(i),
dmi_req.eq(1),
dmi_wen.eq(1),
)
# monitor bbus read/write
self.sync += If(active_dbg & self.cpu.dbus.stb & self.cpu.dbus.ack,
- Display(" [%06x] dadr: %8x, we %d s %01x w %016x r: %016x",
+ D(" [%06x] dadr: %8x, we %d s %01x w %016x r: %016x",
#uptime,
0,
self.cpu.dbus.adr,
# monitor ibus write
self.sync += If(active_dbg & self.cpu.ibus.stb & self.cpu.ibus.ack &
self.cpu.ibus.we,
- Display(" [%06x] iadr: %8x, s %01x w %016x",
+ D(" [%06x] iadr: %8x, s %01x w %016x",
#uptime,
0,
self.cpu.ibus.adr,
# monitor ibus read
self.sync += If(active_dbg & self.cpu.ibus.stb & self.cpu.ibus.ack &
~self.cpu.ibus.we,
- Display(" [%06x] iadr: %8x, s %01x r %016x",
+ D(" [%06x] iadr: %8x, s %01x r %016x",
#uptime,
0,
self.cpu.ibus.adr,