Merge pull request #81 from xobs/vexriscv-to-wishbone
Push Vexriscv debug directly on the Wishbone bus
This commit is contained in:
commit
380f8b96dd
|
@ -5,12 +5,13 @@ from migen import *
|
||||||
from litex.soc.interconnect import wishbone
|
from litex.soc.interconnect import wishbone
|
||||||
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
|
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
|
||||||
|
|
||||||
|
|
||||||
class VexRiscv(Module, AutoCSR):
|
class VexRiscv(Module, AutoCSR):
|
||||||
def __init__(self, platform, cpu_reset_address, variant=None):
|
def __init__(self, platform, cpu_reset_address, variant=None):
|
||||||
assert variant in (None, "debug"), "Unsupported variant %s" % variant
|
assert variant in (None, "debug"), "Unsupported variant %s" % variant
|
||||||
self.ibus = i = wishbone.Interface()
|
self.ibus = i = wishbone.Interface()
|
||||||
self.dbus = d = wishbone.Interface()
|
self.dbus = d = wishbone.Interface()
|
||||||
|
i_err = Signal()
|
||||||
|
d_err = Signal()
|
||||||
|
|
||||||
self.interrupt = Signal(32)
|
self.interrupt = Signal(32)
|
||||||
|
|
||||||
|
@ -25,149 +26,89 @@ class VexRiscv(Module, AutoCSR):
|
||||||
cpu_reset = Signal()
|
cpu_reset = Signal()
|
||||||
cpu_args = {}
|
cpu_args = {}
|
||||||
cpu_filename = "VexRiscv-Debug.v"
|
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()
|
self.i_cmd_valid = Signal()
|
||||||
debug_bus_cmd_payload_address = Signal(8)
|
self.i_cmd_payload_wr = Signal()
|
||||||
debug_bus_cmd_payload_data = Signal(32)
|
self.i_cmd_payload_address = Signal(8)
|
||||||
debug_bus_cmd_ready = Signal()
|
self.i_cmd_payload_data = Signal(32)
|
||||||
debug_bus_rsp_data = Signal(32)
|
self.o_cmd_ready = Signal()
|
||||||
debug_start_cmd = Signal()
|
self.o_rsp_data = Signal(32)
|
||||||
debug_update_pending = Signal()
|
self.o_resetOut = Signal()
|
||||||
debug_write_pending = Signal()
|
|
||||||
debug_reset_out = Signal()
|
|
||||||
debug_reset_counter = Signal(16)
|
|
||||||
|
|
||||||
# A bit to indicate whether we're REFRESHing the CORE or DATA register
|
reset_debug_logic = Signal()
|
||||||
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 += [
|
self.sync += [
|
||||||
# If the core asserts reset_out, set debug_reset for 2**16 cycles.
|
self.debug_bus.dat_r.eq(self.o_rsp_data),
|
||||||
If(debug_reset_out,
|
cpu_reset.eq(reset_debug_logic | ResetSignal()),
|
||||||
debug_reset_counter.eq(0),
|
]
|
||||||
self.debug_reset.eq(1)
|
|
||||||
).Elif(debug_reset_counter != (2**16-1),
|
self.sync += [
|
||||||
debug_reset_counter.eq(debug_reset_counter + 1)
|
# CYC is held high for the duration of the transfer.
|
||||||
).Else(
|
# STB is kept high when the transfer finishes (write)
|
||||||
self.debug_reset.eq(0)
|
# 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)
|
||||||
),
|
),
|
||||||
|
# Force a Wishbone error if transferring during a reset sequence.
|
||||||
# Reset the CPU if debug_reset is asserted and none of the
|
# Because o_resetOut is multiple cycles and i.stb/d.stb should
|
||||||
# Wishbone buses are in use
|
# deassert one cycle after i_err/i_ack/d_err/d_ack are asserted,
|
||||||
cpu_reset.eq((~i.cyc & ~d.cyc & ~d.stb & ~i.stb &
|
# this will give i_err and o_err enough time to be reset to 0
|
||||||
self.debug_reset) | ResetSignal()),
|
# once the reset cycle finishes.
|
||||||
|
If(self.o_resetOut,
|
||||||
# If there's a Wishbone write on the CORE register, write to
|
If(i.cyc & i.stb, i_err.eq(1)).Else(i_err.eq(0)),
|
||||||
# debug register address 0.
|
If(d.cyc & d.stb, d_err.eq(1)).Else(d_err.eq(0)),
|
||||||
If(debug_core.re,
|
reset_debug_logic.eq(1))
|
||||||
debug_bus_cmd_payload_address.eq(0x00),
|
.Else(
|
||||||
debug_bus_cmd_payload_data.eq(debug_core.storage),
|
reset_debug_logic.eq(0)
|
||||||
|
|
||||||
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)
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
cpu_args.update({
|
cpu_args.update({
|
||||||
"i_debugReset": ResetSignal(),
|
"i_debugReset": ResetSignal(),
|
||||||
"i_debug_bus_cmd_valid": debug_start_cmd,
|
"i_debug_bus_cmd_valid": self.i_cmd_valid,
|
||||||
"i_debug_bus_cmd_payload_wr": debug_bus_cmd_payload_wr,
|
"i_debug_bus_cmd_payload_wr": self.i_cmd_payload_wr,
|
||||||
"i_debug_bus_cmd_payload_address": debug_bus_cmd_payload_address,
|
"i_debug_bus_cmd_payload_address": self.i_cmd_payload_address,
|
||||||
"i_debug_bus_cmd_payload_data": debug_bus_cmd_payload_data,
|
"i_debug_bus_cmd_payload_data": self.i_cmd_payload_data,
|
||||||
"o_debug_bus_cmd_ready": debug_bus_cmd_ready,
|
"o_debug_bus_cmd_ready": self.o_cmd_ready,
|
||||||
"o_debug_bus_rsp_data": debug_bus_rsp_data,
|
"o_debug_bus_rsp_data": self.o_rsp_data,
|
||||||
"o_debug_resetOut": debug_reset_out
|
"o_debug_resetOut": self.o_resetOut
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
i.err.eq(i_err),
|
||||||
|
d.err.eq(d_err),
|
||||||
|
]
|
||||||
self.specials += Instance("VexRiscv",
|
self.specials += Instance("VexRiscv",
|
||||||
**cpu_args,
|
**cpu_args,
|
||||||
|
|
||||||
|
@ -188,7 +129,7 @@ class VexRiscv(Module, AutoCSR):
|
||||||
o_iBusWishbone_BTE=i.bte,
|
o_iBusWishbone_BTE=i.bte,
|
||||||
i_iBusWishbone_DAT_MISO=i.dat_r,
|
i_iBusWishbone_DAT_MISO=i.dat_r,
|
||||||
i_iBusWishbone_ACK=i.ack,
|
i_iBusWishbone_ACK=i.ack,
|
||||||
i_iBusWishbone_ERR=i.err,
|
i_iBusWishbone_ERR=i_err,
|
||||||
|
|
||||||
o_dBusWishbone_ADR=d.adr,
|
o_dBusWishbone_ADR=d.adr,
|
||||||
o_dBusWishbone_DAT_MOSI=d.dat_w,
|
o_dBusWishbone_DAT_MOSI=d.dat_w,
|
||||||
|
@ -200,7 +141,7 @@ class VexRiscv(Module, AutoCSR):
|
||||||
o_dBusWishbone_BTE=d.bte,
|
o_dBusWishbone_BTE=d.bte,
|
||||||
i_dBusWishbone_DAT_MISO=d.dat_r,
|
i_dBusWishbone_DAT_MISO=d.dat_r,
|
||||||
i_dBusWishbone_ACK=d.ack,
|
i_dBusWishbone_ACK=d.ack,
|
||||||
i_dBusWishbone_ERR=d.err)
|
i_dBusWishbone_ERR=d_err)
|
||||||
|
|
||||||
# add verilog sources
|
# add verilog sources
|
||||||
self.add_sources(platform, cpu_filename)
|
self.add_sources(platform, cpu_filename)
|
||||||
|
|
|
@ -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()
|
|
3
setup.py
3
setup.py
|
@ -38,8 +38,7 @@ setup(
|
||||||
"console_scripts": [
|
"console_scripts": [
|
||||||
"litex_term=litex.soc.tools.litex_term:main",
|
"litex_term=litex.soc.tools.litex_term:main",
|
||||||
"mkmscimg=litex.soc.tools.mkmscimg:main",
|
"mkmscimg=litex.soc.tools.mkmscimg:main",
|
||||||
"litex_server=litex.soc.tools.remote.litex_server:main",
|
"litex_server=litex.soc.tools.remote.litex_server:main"
|
||||||
"vexriscv_bridge=litex.soc.tools.vexriscv_debug:main"
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue