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