From c87ca4f1c313895f49bee6afb6a7f60df959832a Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Thu, 19 Jul 2018 17:47:28 +0800 Subject: [PATCH 1/3] vexriscv: put debug bus directly on wishbone bus By placing the VexRiscv debug bus on the Wishbone bus, the Etherbone core can access 32-bit values directly from the core. Additionally, both reading and writing are supported without the need to do a SYNC register as before. Additionally, the address of the Wishbone bus won't move around anymore, as it's fixed when doing `self.register_mem()`. Signed-off-by: Sean Cross --- litex/soc/cores/cpu/vexriscv/core.py | 183 ++++++++------------------- 1 file changed, 53 insertions(+), 130 deletions(-) diff --git a/litex/soc/cores/cpu/vexriscv/core.py b/litex/soc/cores/cpu/vexriscv/core.py index a2476c74e..b407d6fea 100644 --- a/litex/soc/cores/cpu/vexriscv/core.py +++ b/litex/soc/cores/cpu/vexriscv/core.py @@ -5,7 +5,6 @@ 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 @@ -25,147 +24,71 @@ 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() + 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), + ] - # 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) - ) + 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) ) - # 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) + ).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) ) ] + cpu_reset.eq((~i.cyc & ~d.cyc & ~d.stb & ~i.stb & + self.o_resetOut) | ResetSignal()), + 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.specials += Instance("VexRiscv", From f17b8324d4838807eb00a358de210a62db5fc087 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 27 Jul 2018 15:02:31 +0800 Subject: [PATCH 2/3] vexriscv: reset wishbone bus on CPU reset If the CPU is resetting during a Wishbone transfer, assert the ERR line. Because the resetOut line is likely multiple cycles long, this should give Wishbone enough time to finish its transfer, which will cause d.stb and i.stb to go to 0, which will return d_err and i_err to 0. Signed-off-by: Sean Cross --- litex/soc/cores/cpu/vexriscv/core.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/litex/soc/cores/cpu/vexriscv/core.py b/litex/soc/cores/cpu/vexriscv/core.py index b407d6fea..b94a346d9 100644 --- a/litex/soc/cores/cpu/vexriscv/core.py +++ b/litex/soc/cores/cpu/vexriscv/core.py @@ -10,6 +10,8 @@ class VexRiscv(Module, AutoCSR): 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) @@ -34,6 +36,8 @@ class VexRiscv(Module, AutoCSR): self.o_rsp_data = Signal(32) self.o_resetOut = Signal() + reset_debug_logic = Signal() + self.transfer_complete = Signal() self.transfer_in_progress = Signal() self.transfer_wait_for_ack = Signal() @@ -42,6 +46,7 @@ class VexRiscv(Module, AutoCSR): self.sync += [ self.debug_bus.dat_r.eq(self.o_rsp_data), + cpu_reset.eq(reset_debug_logic | ResetSignal()), ] self.sync += [ @@ -74,12 +79,21 @@ class VexRiscv(Module, AutoCSR): ).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) + ), + # 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_reset.eq((~i.cyc & ~d.cyc & ~d.stb & ~i.stb & - self.o_resetOut) | ResetSignal()), - cpu_args.update({ "i_debugReset": ResetSignal(), "i_debug_bus_cmd_valid": self.i_cmd_valid, @@ -91,6 +105,10 @@ class VexRiscv(Module, AutoCSR): "o_debug_resetOut": self.o_resetOut }) + self.comb += [ + i.err.eq(i_err), + d.err.eq(d_err), + ] self.specials += Instance("VexRiscv", **cpu_args, @@ -111,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, @@ -123,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) From fb145dacedbd76235792b056140a4f9885eba534 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 27 Jul 2018 15:21:19 +0800 Subject: [PATCH 3/3] tools: remove vexriscv_debug This program is no longer needed. The `openocd_vexriscv` package natively supports `etherbone`, and now that the vexriscv debug module is available on Wishbone instead of as a CSR, this module no longer works. This change simplifies both tooling (because there is one fewer program to run) and integration (because you don't need to modify your CSRs anymore, just `register_mem()`.) Signed-off-by: Sean Cross --- litex/soc/tools/vexriscv_debug.py | 108 ------------------------------ setup.py | 3 +- 2 files changed, 1 insertion(+), 110 deletions(-) delete mode 100644 litex/soc/tools/vexriscv_debug.py 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" ], }, )