riscv-formal-nmigen/rvfi/cores/minerva/units/debug/controller.py

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