diff --git a/litex/soc/cores/cpu/vexriscv/core.py b/litex/soc/cores/cpu/vexriscv/core.py index a2476c74e..b94a346d9 100644 --- a/litex/soc/cores/cpu/vexriscv/core.py +++ b/litex/soc/cores/cpu/vexriscv/core.py @@ -5,12 +5,13 @@ from migen import * from litex.soc.interconnect import wishbone from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage - class VexRiscv(Module, AutoCSR): def __init__(self, platform, cpu_reset_address, variant=None): assert variant in (None, "debug"), "Unsupported variant %s" % variant self.ibus = i = wishbone.Interface() self.dbus = d = wishbone.Interface() + i_err = Signal() + d_err = Signal() self.interrupt = Signal(32) @@ -25,149 +26,89 @@ class VexRiscv(Module, AutoCSR): cpu_reset = Signal() cpu_args = {} cpu_filename = "VexRiscv-Debug.v" - # Create four debug registers: - # DEBUG_CORE: The contents of the debug core register - # DEBUG_DATA: Write an instruction into the pipeline, or read the result. - # DEBUG_REFRESH: Write 0x00 or 0x04 here to update either CORE or DATA - # DEBUG_COUNT: An incrementing value that can be used to detect packet loss. - # Updated on a successful WRITE to CORE, DATA, or REFRESH. - self.debug_core = debug_core = CSRStorage(32, name="debug_core", write_from_dev=True) - self.debug_data = debug_data = CSRStorage(32, name="debug_data", write_from_dev=True) - self.debug_refresh = debug_refresh = CSRStorage(8, name="debug_refresh") - self.debug_counter = debug_counter = CSRStatus(32, name="debug_counter") - # # # - debug_bus_cmd_payload_wr = Signal() - debug_bus_cmd_payload_address = Signal(8) - debug_bus_cmd_payload_data = Signal(32) - debug_bus_cmd_ready = Signal() - debug_bus_rsp_data = Signal(32) - debug_start_cmd = Signal() - debug_update_pending = Signal() - debug_write_pending = Signal() - debug_reset_out = Signal() - debug_reset_counter = Signal(16) + self.i_cmd_valid = Signal() + self.i_cmd_payload_wr = Signal() + self.i_cmd_payload_address = Signal(8) + self.i_cmd_payload_data = Signal(32) + self.o_cmd_ready = Signal() + self.o_rsp_data = Signal(32) + self.o_resetOut = Signal() - # A bit to indicate whether we're REFRESHing the CORE or DATA register - refreshing_data = Signal() + reset_debug_logic = Signal() + + self.transfer_complete = Signal() + self.transfer_in_progress = Signal() + self.transfer_wait_for_ack = Signal() + + self.debug_bus = wishbone.Interface() self.sync += [ - # If the core asserts reset_out, set debug_reset for 2**16 cycles. - If(debug_reset_out, - debug_reset_counter.eq(0), - self.debug_reset.eq(1) - ).Elif(debug_reset_counter != (2**16-1), - debug_reset_counter.eq(debug_reset_counter + 1) - ).Else( - self.debug_reset.eq(0) + self.debug_bus.dat_r.eq(self.o_rsp_data), + cpu_reset.eq(reset_debug_logic | ResetSignal()), + ] + + self.sync += [ + # CYC is held high for the duration of the transfer. + # STB is kept high when the transfer finishes (write) + # or the master is waiting for data (read), and stays + # there until ACK, ERR, or RTY are asserted. + If((self.debug_bus.stb & self.debug_bus.cyc) + & (~self.transfer_in_progress) + & (~self.transfer_complete) + & (~self.transfer_wait_for_ack), + self.i_cmd_payload_data.eq(self.debug_bus.dat_w), + self.i_cmd_payload_address.eq((self.debug_bus.adr[0:6] << 2) | 0), + self.i_cmd_payload_wr.eq(self.debug_bus.we), + self.i_cmd_valid.eq(1), + self.transfer_in_progress.eq(1), + self.transfer_complete.eq(0), + self.debug_bus.ack.eq(0) + ).Elif(self.transfer_in_progress, + If(self.o_cmd_ready, + self.i_cmd_valid.eq(0), + self.i_cmd_payload_wr.eq(0), + self.transfer_complete.eq(1), + self.transfer_in_progress.eq(0) + ) + ).Elif(self.transfer_complete, + self.transfer_complete.eq(0), + self.debug_bus.ack.eq(1), + self.transfer_wait_for_ack.eq(1) + ).Elif(self.transfer_wait_for_ack & ~(self.debug_bus.stb & self.debug_bus.cyc), + self.transfer_wait_for_ack.eq(0), + self.debug_bus.ack.eq(0) ), - - # Reset the CPU if debug_reset is asserted and none of the - # Wishbone buses are in use - cpu_reset.eq((~i.cyc & ~d.cyc & ~d.stb & ~i.stb & - self.debug_reset) | ResetSignal()), - - # If there's a Wishbone write on the CORE register, write to - # debug register address 0. - If(debug_core.re, - debug_bus_cmd_payload_address.eq(0x00), - debug_bus_cmd_payload_data.eq(debug_core.storage), - - debug_bus_cmd_payload_wr.eq(1), - debug_start_cmd.eq(1), - debug_write_pending.eq(1), - - debug_core.we.eq(0), - debug_data.we.eq(0) - # Or, if there's a write to the DATA register, write to - # debug register address 4. - ).Elif(debug_data.re, - debug_bus_cmd_payload_address.eq(0x04), - debug_bus_cmd_payload_data.eq(debug_data.storage), - - debug_bus_cmd_payload_wr.eq(1), - debug_start_cmd.eq(1), - debug_write_pending.eq(1), - - debug_core.we.eq(0), - debug_data.we.eq(0) - # A write to the REFRESH register indicates which register - # (DATA or CORE) we want to update from the CPU. - ).Elif(debug_refresh.re, - If(debug_refresh.storage == 0, - refreshing_data.eq(0), - debug_bus_cmd_payload_address.eq(0) - ).Else( - refreshing_data.eq(1), - debug_bus_cmd_payload_address.eq(4) - ), - # Data can be anything, since it's a "read" - debug_bus_cmd_payload_data.eq(0), - - # Start a "Read" command with the "Write" bit set to 0 - debug_bus_cmd_payload_wr.eq(0), - debug_start_cmd.eq(1), - - # The data will be ready when debug_bus_cmd_ready == 1, - # so set the pending bit to look for it on future cycles. - debug_update_pending.eq(1), - - debug_core.we.eq(0), - debug_data.we.eq(0) - # If the pending bit is set, check to see if the cmd_ready - # bit from the debug bus is 1, indicating the CPU has finished - # its operation and is in the idle state. - ).Elif(debug_update_pending, - If(debug_bus_cmd_ready, - debug_bus_cmd_payload_wr.eq(0), - debug_update_pending.eq(0), - debug_write_pending.eq(0), - debug_start_cmd.eq(0), - debug_counter.status.eq(debug_counter.status + 1), - # Depending on whether we were asked to update the CORE - # or DATA register, copy the response data to the correct CSR. - If(~refreshing_data, - debug_core.dat_w.eq(debug_bus_rsp_data), - debug_core.we.eq(1), - debug_data.we.eq(0) - ).Else( - debug_data.dat_w.eq(debug_bus_rsp_data), - debug_core.we.eq(0), - debug_data.we.eq(1) - ) - ) - # If there's a pending write to CORE or DATA, increment the - # packet counter once the operation has finished. - ).Elif(debug_write_pending, - If(debug_bus_cmd_ready, - # When debug_bus_cmd_ready goes 1, - debug_counter.status.eq(debug_counter.status + 1), - debug_update_pending.eq(0), - debug_write_pending.eq(0), - debug_start_cmd.eq(0), - debug_data.we.eq(0), - debug_core.we.eq(0) - ) - # Otherwise, ensure the Write Enable bits on the registers - # are 0, so we're not constantly loading floating values. - ).Else( - debug_core.we.eq(0), - debug_data.we.eq(0) + # Force a Wishbone error if transferring during a reset sequence. + # Because o_resetOut is multiple cycles and i.stb/d.stb should + # deassert one cycle after i_err/i_ack/d_err/d_ack are asserted, + # this will give i_err and o_err enough time to be reset to 0 + # once the reset cycle finishes. + If(self.o_resetOut, + If(i.cyc & i.stb, i_err.eq(1)).Else(i_err.eq(0)), + If(d.cyc & d.stb, d_err.eq(1)).Else(d_err.eq(0)), + reset_debug_logic.eq(1)) + .Else( + reset_debug_logic.eq(0) ) ] cpu_args.update({ "i_debugReset": ResetSignal(), - "i_debug_bus_cmd_valid": debug_start_cmd, - "i_debug_bus_cmd_payload_wr": debug_bus_cmd_payload_wr, - "i_debug_bus_cmd_payload_address": debug_bus_cmd_payload_address, - "i_debug_bus_cmd_payload_data": debug_bus_cmd_payload_data, - "o_debug_bus_cmd_ready": debug_bus_cmd_ready, - "o_debug_bus_rsp_data": debug_bus_rsp_data, - "o_debug_resetOut": debug_reset_out + "i_debug_bus_cmd_valid": self.i_cmd_valid, + "i_debug_bus_cmd_payload_wr": self.i_cmd_payload_wr, + "i_debug_bus_cmd_payload_address": self.i_cmd_payload_address, + "i_debug_bus_cmd_payload_data": self.i_cmd_payload_data, + "o_debug_bus_cmd_ready": self.o_cmd_ready, + "o_debug_bus_rsp_data": self.o_rsp_data, + "o_debug_resetOut": self.o_resetOut }) + self.comb += [ + i.err.eq(i_err), + d.err.eq(d_err), + ] self.specials += Instance("VexRiscv", **cpu_args, @@ -188,7 +129,7 @@ class VexRiscv(Module, AutoCSR): o_iBusWishbone_BTE=i.bte, i_iBusWishbone_DAT_MISO=i.dat_r, i_iBusWishbone_ACK=i.ack, - i_iBusWishbone_ERR=i.err, + i_iBusWishbone_ERR=i_err, o_dBusWishbone_ADR=d.adr, o_dBusWishbone_DAT_MOSI=d.dat_w, @@ -200,7 +141,7 @@ class VexRiscv(Module, AutoCSR): o_dBusWishbone_BTE=d.bte, i_dBusWishbone_DAT_MISO=d.dat_r, i_dBusWishbone_ACK=d.ack, - i_dBusWishbone_ERR=d.err) + i_dBusWishbone_ERR=d_err) # add verilog sources self.add_sources(platform, cpu_filename) diff --git a/litex/soc/tools/vexriscv_debug.py b/litex/soc/tools/vexriscv_debug.py deleted file mode 100644 index 11276861e..000000000 --- a/litex/soc/tools/vexriscv_debug.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import time -import threading -import argparse -import socket -import struct -from litex.soc.tools.remote import RemoteClient - -class ConnectionClosed(Exception): - pass - -# struct vexriscv_req { -# uint8_t readwrite; -# uint8_t size; -# uint32_t address; -# uint32_t data; -#} __attribute__((packed)); -class VexRiscvDebugPacket(): - def __init__(self, data): - self.is_write, self.size, self.address, self.value = struct.unpack("=?BII", data) - -class VexRiscvDebugBridge(): - def __init__(self): - self._get_args() - - def open(self): - if not hasattr(self, "debugger_socket"): - self.debugger_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.debugger_socket.bind(('',7893)) - self.debugger_socket.listen(0) - - if not hasattr(self, "rc"): - self.rc = RemoteClient(csr_csv=self.args.csr) - self.rc.open() - self.core_reg = self.rc.regs.cpu_or_bridge_debug_core - self.data_reg = self.rc.regs.cpu_or_bridge_debug_data - self.refresh_reg = self.rc.regs.cpu_or_bridge_debug_refresh - - def _get_args(self): - parser = argparse.ArgumentParser() - parser.add_argument("--csr", default="test/csr.csv", help="csr mapping file") - self.args = parser.parse_args() - - def accept(self): - if hasattr(self, "debugger"): - return - print("Waiting for connection from debugger...") - self.debugger, address = self.debugger_socket.accept() - print("Accepted debugger connection from {}".format(address[0])) - - def _refresh_reg(self, reg): - self.refresh_reg.write(reg) - - def read_core(self): - self._refresh_reg(0) - self.write_to_debugger(self.core_reg.read()) - - def read_data(self): - self._refresh_reg(4) - self.write_to_debugger(self.data_reg.read()) - - def write_core(self, value): - self.core_reg.write(value) - - def write_data(self, value): - self.data_reg.write(value) - - def read_from_debugger(self): - data = self.debugger.recv(10) - if len(data) != 10: - self.debugger.close() - del self.debugger - raise ConnectionClosed() - return VexRiscvDebugPacket(data) - - def write_to_debugger(self, data): - self.debugger.send(struct.pack("=I", data)) - -def main(): - vrvb = VexRiscvDebugBridge() - vrvb.open() - - while True: - vrvb.accept() - try: - pkt = vrvb.read_from_debugger() - if pkt.is_write == True: - if pkt.address == 0xf00f0000: - vrvb.write_core(pkt.value) - elif pkt.address == 0xf00f0004: - vrvb.write_data(pkt.value) - else: - raise "Unrecognized address" - else: - if pkt.address == 0xf00f0000: - vrvb.read_core() - elif pkt.address == 0xf00f0004: - vrvb.read_data() - else: - raise "Unrecognized address" - except ConnectionClosed: - print("Debugger connection closed") - -if __name__ == "__main__": - main() diff --git a/setup.py b/setup.py index e16e193f6..d129bde0d 100755 --- a/setup.py +++ b/setup.py @@ -38,8 +38,7 @@ setup( "console_scripts": [ "litex_term=litex.soc.tools.litex_term:main", "mkmscimg=litex.soc.tools.mkmscimg:main", - "litex_server=litex.soc.tools.remote.litex_server:main", - "vexriscv_bridge=litex.soc.tools.vexriscv_debug:main" + "litex_server=litex.soc.tools.remote.litex_server:main" ], }, )