213 lines
8.5 KiB
Python
213 lines
8.5 KiB
Python
from nmigen import *
|
|
from nmigen.lib.coding import PriorityEncoder
|
|
|
|
from ...csr import *
|
|
from ...isa import *
|
|
from ...wishbone import wishbone_layout
|
|
from .dmi import DebugReg, Command, Error, Version, cmd_access_reg_layout
|
|
|
|
|
|
__all__ = ["DebugController"]
|
|
|
|
|
|
class HaltCause:
|
|
EBREAK = 1
|
|
TRIGGER = 0
|
|
HALTREQ = 3
|
|
STEP = 4
|
|
RESET_HALTREQ = 2
|
|
|
|
|
|
cause_map = [2, 1, 5, 3, 4]
|
|
|
|
|
|
class DebugController(Elaboratable):
|
|
def __init__(self, debugrf):
|
|
self.dcsr_we = Signal()
|
|
self.dcsr_w = Record((fname, shape) for (fname, shape, mode) in dcsr_layout)
|
|
self.dcsr_r = Record.like(self.dcsr_w)
|
|
self.dpc_we = Signal()
|
|
self.dpc_w = Signal(32)
|
|
|
|
self.dmstatus = debugrf.reg_port(DebugReg.DMSTATUS)
|
|
self.dmcontrol = debugrf.reg_port(DebugReg.DMCONTROL)
|
|
self.hartinfo = debugrf.reg_port(DebugReg.HARTINFO)
|
|
self.abstractcs = debugrf.reg_port(DebugReg.ABSTRACTCS)
|
|
self.command = debugrf.reg_port(DebugReg.COMMAND)
|
|
self.data0 = debugrf.reg_port(DebugReg.DATA0)
|
|
self.haltsum0 = debugrf.reg_port(DebugReg.HALTSUM0)
|
|
|
|
self.trigger_haltreq = Signal()
|
|
|
|
self.x_pc = Signal(32)
|
|
self.x_ebreak = Signal()
|
|
self.x_stall = Signal()
|
|
|
|
self.m_branch_taken = Signal()
|
|
self.m_branch_target = Signal(32)
|
|
self.m_mret = Signal()
|
|
self.m_exception = Signal()
|
|
self.m_pc = Signal(32)
|
|
self.m_valid = Signal()
|
|
self.mepc_r_base = Signal(30)
|
|
self.mtvec_r_base = Signal(30)
|
|
|
|
self.halt = Signal()
|
|
self.halted = Signal()
|
|
self.killall = Signal()
|
|
self.resumereq = Signal()
|
|
self.resumeack = Signal()
|
|
|
|
self.gprf_addr = Signal(5)
|
|
self.gprf_re = Signal()
|
|
self.gprf_dat_r = Signal(32)
|
|
self.gprf_we = Signal()
|
|
self.gprf_dat_w = Signal(32)
|
|
|
|
self.csrf_addr = Signal(12)
|
|
self.csrf_re = Signal()
|
|
self.csrf_dat_r = Signal(32)
|
|
self.csrf_we = Signal()
|
|
self.csrf_dat_w = Signal(32)
|
|
|
|
def elaborate(self, platform):
|
|
m = Module()
|
|
|
|
with m.If(self.dmcontrol.update):
|
|
m.d.sync += [
|
|
self.dmcontrol.w.dmactive.eq(self.dmcontrol.r.dmactive),
|
|
self.dmcontrol.w.ndmreset.eq(self.dmcontrol.r.ndmreset),
|
|
self.dmcontrol.w.hartselhi.eq(self.dmcontrol.r.hartselhi),
|
|
self.dmcontrol.w.hartsello.eq(self.dmcontrol.r.hartsello),
|
|
self.dmcontrol.w.hasel.eq(self.dmcontrol.r.hasel),
|
|
self.dmcontrol.w.hartreset.eq(self.dmcontrol.r.hartreset),
|
|
self.dmcontrol.w.resumereq.eq(self.dmcontrol.r.resumereq),
|
|
]
|
|
|
|
with m.If(self.abstractcs.update):
|
|
m.d.sync += self.abstractcs.w.cmderr.eq(self.abstractcs.r.cmderr)
|
|
|
|
with m.If(self.data0.update):
|
|
m.d.sync += self.data0.w.eq(self.data0.r)
|
|
|
|
m.d.comb += [
|
|
self.dmstatus.w.version.eq(Version.V013),
|
|
self.dmstatus.w.authenticated.eq(1),
|
|
self.resumereq.eq(self.dmcontrol.w.resumereq),
|
|
self.haltsum0.w[0].eq(self.dmstatus.w.allhalted),
|
|
]
|
|
|
|
m_breakpoint = Signal()
|
|
with m.If(~self.x_stall):
|
|
m.d.comb += m_breakpoint.eq(self.x_ebreak & self.dcsr_r.ebreakm)
|
|
|
|
halt_pe = m.submodules.halt_pe = PriorityEncoder(5)
|
|
m.d.comb += [
|
|
halt_pe.i[HaltCause.EBREAK].eq(m_breakpoint & self.m_valid),
|
|
halt_pe.i[HaltCause.TRIGGER].eq(self.trigger_haltreq),
|
|
halt_pe.i[HaltCause.HALTREQ].eq(self.dmcontrol.r.haltreq),
|
|
halt_pe.i[HaltCause.STEP].eq(self.dcsr_r.step & self.m_valid),
|
|
]
|
|
|
|
with m.FSM():
|
|
with m.State("RUN"):
|
|
m.d.comb += self.dmstatus.w.allrunning.eq(1)
|
|
m.d.comb += self.dmstatus.w.anyrunning.eq(1)
|
|
with m.If(~halt_pe.n):
|
|
m.d.sync += self.halt.eq(1)
|
|
m.d.comb += [
|
|
self.dcsr_we.eq(1),
|
|
self.dcsr_w.eq(self.dcsr_r),
|
|
self.dcsr_w.cause.eq(Array(cause_map)[halt_pe.o]),
|
|
self.dcsr_w.stepie.eq(1)
|
|
]
|
|
m.d.comb += self.dpc_we.eq(1)
|
|
with m.If(halt_pe.o == HaltCause.EBREAK):
|
|
m.d.comb += self.dpc_w.eq(self.m_pc)
|
|
with m.Elif(self.m_exception & self.m_valid):
|
|
m.d.comb += self.dpc_w.eq(self.mtvec_r_base << 2)
|
|
with m.Elif(self.m_mret & self.m_valid):
|
|
m.d.comb += self.dpc_w.eq(self.mepc_r_base << 2)
|
|
with m.Elif(self.m_branch_taken & self.m_valid):
|
|
m.d.comb += self.dpc_w.eq(self.m_branch_target)
|
|
with m.Else():
|
|
m.d.comb += self.dpc_w.eq(self.x_pc)
|
|
m.next = "HALTING"
|
|
|
|
with m.State("HALTING"):
|
|
with m.If(self.halted):
|
|
m.d.comb += self.killall.eq(1)
|
|
m.d.sync += self.dmstatus.w.allhalted.eq(1)
|
|
m.d.sync += self.dmstatus.w.anyhalted.eq(1)
|
|
m.next = "WAIT"
|
|
|
|
with m.State("WAIT"):
|
|
with m.If(self.dmcontrol.w.resumereq):
|
|
m.next = "RESUME"
|
|
with m.Elif(self.command.update):
|
|
m.d.sync += self.abstractcs.w.busy.eq(1)
|
|
m.next = "COMMAND:START"
|
|
|
|
with m.State("RESUME"):
|
|
with m.If(self.resumeack):
|
|
m.d.sync += [
|
|
self.dmcontrol.w.resumereq.eq(0),
|
|
self.dmstatus.w.allresumeack.eq(1),
|
|
self.dmstatus.w.anyresumeack.eq(1),
|
|
self.halt.eq(0),
|
|
self.dmstatus.w.allhalted.eq(0),
|
|
self.dmstatus.w.anyhalted.eq(0),
|
|
]
|
|
m.next = "RUN"
|
|
|
|
with m.State("COMMAND:START"):
|
|
with m.Switch(self.command.r.cmdtype):
|
|
with m.Case(Command.ACCESS_REG):
|
|
control = Record(cmd_access_reg_layout)
|
|
m.d.comb += control.eq(self.command.r.control)
|
|
m.d.comb += self.gprf_addr.eq(control.regno)
|
|
m.next = "COMMAND:ACCESS-REG"
|
|
with m.Case():
|
|
m.d.sync += self.abstractcs.w.cmderr.eq(Error.UNSUPPORTED)
|
|
m.next = "COMMAND:DONE"
|
|
|
|
with m.State("COMMAND:ACCESS-REG"):
|
|
control = Record(cmd_access_reg_layout)
|
|
m.d.comb += control.eq(self.command.r.control)
|
|
with m.If(control.postexec | (control.aarsize != 2) | control.aarpostincrement):
|
|
# Unsupported parameters.
|
|
m.d.sync += self.abstractcs.w.cmderr.eq(Error.EXCEPTION)
|
|
with m.Elif(control.regno.matches("0000------------")):
|
|
with m.If(control.transfer):
|
|
m.d.comb += self.csrf_addr.eq(control.regno)
|
|
with m.If(control.write):
|
|
m.d.comb += [
|
|
self.csrf_we.eq(1),
|
|
self.csrf_dat_w.eq(self.data0.r)
|
|
]
|
|
with m.Else():
|
|
m.d.comb += self.csrf_re.eq(1)
|
|
m.d.sync += self.data0.w.eq(self.csrf_dat_r)
|
|
m.d.sync += self.abstractcs.w.cmderr.eq(Error.NONE)
|
|
with m.Elif(control.regno.matches("00010000000-----")):
|
|
with m.If(control.transfer):
|
|
m.d.comb += self.gprf_addr.eq(control.regno)
|
|
with m.If(control.write):
|
|
m.d.comb += [
|
|
self.gprf_we.eq(1),
|
|
self.gprf_dat_w.eq(self.data0.r)
|
|
]
|
|
with m.Else():
|
|
m.d.sync += self.data0.w.eq(self.gprf_dat_r)
|
|
m.d.sync += self.abstractcs.w.cmderr.eq(Error.NONE)
|
|
with m.Else():
|
|
# Unknown register number.
|
|
m.d.sync += self.abstractcs.w.cmderr.eq(Error.EXCEPTION)
|
|
m.next = "COMMAND:DONE"
|
|
|
|
with m.State("COMMAND:DONE"):
|
|
m.d.sync += self.abstractcs.w.busy.eq(0)
|
|
m.next = "WAIT"
|
|
|
|
return m
|