From d766c0eab41bd7fc856de7af00c41711c5827047 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sun, 2 Aug 2020 16:21:56 +0100 Subject: [PATCH] convert microwatt core_debug.vhdl to nmigen --- src/soc/debug/dmi.py | 335 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 src/soc/debug/dmi.py diff --git a/src/soc/debug/dmi.py b/src/soc/debug/dmi.py new file mode 100644 index 00000000..102afab2 --- /dev/null +++ b/src/soc/debug/dmi.py @@ -0,0 +1,335 @@ +"""Converted from microwatt core_debug.vhdl to nmigen + +Provides a DMI (Debug Module Interface) for accessing a Libre-SOC core, +compatible with microwatt's same interface. + +See constants below for addresses and register formats +""" + +from nmigen import Elaboratable, Module, Signal, Cat, Const, Record, Array, Mux +from nmutil.iocontrol import RecordObject +from nmigen.utils import log2_int +from nmigen.cli import rtlil + + +# DMI register addresses +class DBGCore: + CTRL = 0b0000 + STAT = 0b0001 + NIA = 0b0010 # NIA register (read only for now) + MSR = 0b0011 # MSR (read only) + GSPR_INDEX = 0b0100 # GSPR register index + GSPR_DATA = 0b0101 # GSPR register data + LOG_ADDR = 0b0110 # Log buffer address register + LOG_DATA = 0b0111 # Log buffer data register + + +# CTRL register (direct actions, write 1 to act, read back 0) +# bit 0 : Core stop +# bit 1 : Core reset (doesn't clear stop) +# bit 2 : Icache reset +# bit 3 : Single step +# bit 4 : Core start +class DBGCtrl: + STOP = 0 + RESET = 1 + ICRESET = 2 + STEP = 3 + START = 4 + + +# STAT register (read only) +# bit 0 : Core stopping (wait til bit 1 set) +# bit 1 : Core stopped +# bit 2 : Core terminated (clears with start or reset) +class DBGStat: + STOPPING = 0 + STOPPED = 1 + TERM = 2 + + +class DMIInterface(RecordObject): + def __init__(self, name): + super().__init__(name=name) + self.addr_i = Signal(4) # DMI register address + self.din = Signal(64) # DMI data in (if we=1) + self.dout = Signal(64) # DMI data out (if we=0) + self.req_i = Signal() # DMI request valid (stb) + self.we_i = Signal() # DMI write-enable + self.ack_o = Signal() # DMI ack request + + +class CoreDebug(Elaboratable): + def __init__(self, LOG_LENGTH=0): # TODO - debug log 512): + # Length of log buffer + self.LOG_LENGTH = LOG_LENGTH + self.dmi = DMIInterface("dmi") + + # Debug actions + self.core_stop_o = Signal() + self.core_rst_o = Signal() + self.icache_rst_o = Signal() + + # Core status inputs + self.terminate_i = Signal() + self.core_stopped_i = Signal() + self.nia = Signal(64) + self.msr = Signal(64) + + # GSPR register read port + self.dbg_gpr_req_o = Signal() + self.dbg_gpr_ack_i = Signal() + self.dbg_gpr_addr_o = Signal(7) # includes fast SPRs, others? + self.dbg_gpr_data_i = Signal(64) + + # Core logging data + self.log_data_i = Signal(256) + self.log_read_addr_i = Signal(32) + self.log_read_data_o = Signal(64) + self.log_write_addr_o = Signal(32) + + # Misc + self.terminated_o = Signal() + + def elaborate(self, platform): + + m = Module() + comb, sync = m.d.comb, m.d.sync + + # DMI needs fixing... make a one clock pulse + dmi_req_i_1 = Signal() + + # Some internal wires + stat_reg = Signal(64) + + # Some internal latches + stopping = Signal() + do_step = Signal() + do_reset = Signal() + do_icreset = Signal() + terminated = Signal() + do_gspr_rd = Signal() + gspr_index = Signal.like(self.dbg_gpr_addr_o) + + log_dmi_addr = Signal(32) + log_dmi_data = Signal(64) + do_dmi_log_rd = Signal() + dmi_read_log_data = Signal() + dmi_read_log_data_1 = Signal() + + LOG_INDEX_BITS = log2_int(self.LOG_LENGTH) + + # Single cycle register accesses on DMI except for GSPR data + comb += self.dmi.ack_o.eq(Mux(self.dmi.addr_i == DBGCore.GSPR_DATA, + self.dbg_gpr_ack_i, self.dmi.req_i)) + comb += self.dbg_gpr_req_o.eq(Mux(self.dmi.addr_i == DBGCore.GSPR_DATA, + self.dmi.req_i, 0)) + + # Status register read composition (DBUG_CORE_STAT_xxx) + comb += stat_reg.eq(Cat(stopping, # bit 0 + self.core_stopped_i, # bit 1 + terminated)) # bit 2 + + # DMI read data mux + with m.Switch(self.dmi.addr_i): + with m.Case( DBGCore.STAT): + comb += self.dmi.dout.eq(stat_reg) + with m.Case( DBGCore.NIA): + comb += self.dmi.dout.eq(self.nia) + with m.Case( DBGCore.MSR): + comb += self.dmi.dout.eq(self.msr) + with m.Case( DBGCore.GSPR_DATA): + comb += self.dmi.dout.eq(self.dbg_gpr_data_i) + with m.Case( DBGCore.LOG_ADDR): + comb += self.dmi.dout.eq(Cat(log_dmi_addr, + self.log_write_addr_o)) + with m.Case( DBGCore.LOG_DATA): + comb += self.dmi.dout.eq(log_dmi_data) + + # DMI writes + # Reset the 1-cycle "do" signals + sync += do_step.eq(0) + sync += do_reset.eq(0) + sync += do_icreset.eq(0) + sync += do_dmi_log_rd.eq(0) + + # Edge detect on dmi_req_i for 1-shot pulses + sync += dmi_req_i_1.eq(self.dmi.req_i) + with m.If(self.dmi.req_i & ~dmi_req_i_1): + with m.If(self.dmi.we_i): + #sync += Display("DMI write to " & to_hstring(dmi_addr)) + + # Control register actions + + # Core control + with m.If(self.dmi.addr_i == DBGCore.CTRL): + with m.If(self.dmi.din[DBGCtrl.RESET]): + sync += do_reset.eq(1) + sync += terminated.eq(0) + with m.If(self.dmi.din[DBGCtrl.STOP]): + sync += stopping.eq(1) + with m.If(self.dmi.din[DBGCtrl.STEP]): + sync += do_step.eq(1) + sync += terminated.eq(0) + with m.If(self.dmi.din[DBGCtrl.ICRESET]): + sync += do_icreset.eq(1) + with m.If(self.dmi.din[DBGCtrl.START]): + sync += stopping.eq(0) + sync += terminated.eq(0) + + # GSPR address + with m.Elif(self.dmi.addr_i == DBGCore.GSPR_INDEX): + sync += gspr_index.eq(self.dmi.din) + + # Log address + with m.Elif(self.dmi.addr_i == DBGCore.LOG_ADDR): + sync += log_dmi_addr.eq(self.dmi.din) + sync += do_dmi_log_rd.eq(1) + with m.Else(): + # sync += Display("DMI read from " & to_string(dmi_addr)) + pass + + with m.Elif(dmi_read_log_data_1 & ~dmi_read_log_data): + # Increment log_dmi_addr after end of read from DBGCore.LOG_DATA + lds = log_dmi_addr[:LOG_INDEX_BITS+2] + sync += lds.eq(lds + 1) + sync += do_dmi_log_rd.eq(1) + + sync += dmi_read_log_data_1.eq(dmi_read_log_data) + sync += dmi_read_log_data.eq(self.dmi.req_i & + (self.dmi.addr_i == DBGCore.LOG_DATA)) + + # Set core stop on terminate. We'll be stopping some time *after* + # the offending instruction, at least until we can do back flushes + # that preserve NIA which we can't just yet. + with m.If(self.terminate_i): + sync += stopping.eq(1) + sync += terminated.eq(1) + + comb += self.dbg_gpr_addr_o.eq(gspr_index) + + # Core control signals generated by the debug module + comb += self.core_stop_o.eq(stopping & ~do_step) + comb += self.core_rst_o.eq(do_reset) + comb += self.icache_rst_o.eq(do_icreset) + comb += self.terminated_o.eq(terminated) + + # Logging RAM (none) + + if self.LOG_LENGTH == 0: + self.log_read_data_o.eq(0) + self.log_write_addr_o.eq(0x00000001) + + return m + + # TODO: debug logging + """ + maybe_log: with m.If(LOG_LENGTH > 0 generate + subtype log_ptr_t is unsigned(LOG_INDEX_BITS - 1 downto 0) + type log_array_t is array(0 to LOG_LENGTH - 1) of std_ulogic_vector(255 downto 0) + signal log_array : log_array_t + signal log_rd_ptr : log_ptr_t + signal log_wr_ptr : log_ptr_t + signal log_toggle = Signal() + signal log_wr_enable = Signal() + signal log_rd_ptr_latched : log_ptr_t + signal log_rd = Signal()_vector(255 downto 0) + signal log_dmi_reading = Signal() + signal log_dmi_read_done = Signal() + + function select_dword(data = Signal()_vector(255 downto 0) + addr = Signal()_vector(31 downto 0)) return std_ulogic_vector is + variable firstbit : integer + begin + firstbit := to_integer(unsigned(addr(1 downto 0))) * 64 + return data(firstbit + 63 downto firstbit) + end + + attribute ram_style : string + attribute ram_style of log_array : signal is "block" + attribute ram_decomp : string + attribute ram_decomp of log_array : signal is "power" + + begin + # Use MSB of read addresses to stop the logging + log_wr_enable.eq(not (self.log_read_addr(31) or log_dmi_addr(31)) + + log_ram: process(clk) + begin + with m.If(rising_edge(clk)): + with m.If(log_wr_enable = '1'): + log_array(to_integer(log_wr_ptr)).eq(self.log_data + end if + log_rd.eq(log_array(to_integer(log_rd_ptr_latched)) + end if + end process + + + log_buffer: process(clk) + variable b : integer + variable data = Signal()_vector(255 downto 0) + begin + with m.If(rising_edge(clk)): + with m.If(rst = '1'): + log_wr_ptr.eq((others => '0') + log_toggle.eq('0' + with m.Elif(log_wr_enable = '1'): + with m.If(log_wr_ptr = to_unsigned(LOG_LENGTH - 1, LOG_INDEX_BITS)): + log_toggle.eq(not log_toggle + end if + log_wr_ptr.eq(log_wr_ptr + 1 + end if + with m.If(do_dmi_log_rd = '1'): + log_rd_ptr_latched.eq(unsigned(log_dmi_addr(LOG_INDEX_BITS + 1 downto 2)) + else + log_rd_ptr_latched.eq(unsigned(self.log_read_addr(LOG_INDEX_BITS + 1 downto 2)) + end if + with m.If(log_dmi_read_done = '1'): + log_dmi_data.eq(select_dword(log_rd, log_dmi_addr) + else + self.log_read_data.eq(select_dword(log_rd, self.log_read_addr) + end if + log_dmi_read_done.eq(log_dmi_reading + log_dmi_reading.eq(do_dmi_log_rd + end if + end process + self.log_write_addr(LOG_INDEX_BITS - 1 downto 0).eq(std_ulogic_vector(log_wr_ptr) + self.log_write_addr(LOG_INDEX_BITS).eq('1' + self.log_write_addr(31 downto LOG_INDEX_BITS + 1).eq((others => '0') + end generate + + """ + + def __iter__(self): + yield from self.dmi + yield self.core_stop_o + yield self.core_rst_o + yield self.icache_rst_o + yield self.terminate_i + yield self.core_stopped_i + yield self.nia + yield self.msr + yield self.dbg_gpr_req_o + yield self.dbg_gpr_ack_i + yield self.dbg_gpr_addr_o + yield self.dbg_gpr_data_i + yield self.log_data_i + yield self.log_read_addr_i + yield self.log_read_data_o + yield self.log_write_addr_o + yield self.terminated_o + + def ports(self): + return list(self) + + +def test_debug(): + + dut = CoreDebug() + vl = rtlil.convert(dut, ports=dut.ports()) + with open("test_core_debug.il", "w") as f: + f.write(vl) + +if __name__ == '__main__': + test_debug() + -- 2.30.2