Commit d8444ebc authored by Bjarne Wintermann's avatar Bjarne Wintermann
Browse files

Added build options for monitor, made queue and gearbox options for output. Closes #32

parent 82adf03a
......@@ -109,6 +109,8 @@ class BusMasterMonitor(Module):
So to monitor a single master on successful transmissions: BusMasterMonitor(masterInterface1, trigger="ack")
To monitor all interfaces of the bus when successful: BusMasterMonitor(list(self.bus.masters.values()), trigger="ack") (this would record anytime one of the masters
received an ack. Which one (or both) got it can later be parsed by reading the records)
To make usage of the data just use self.source (or monitor.source in another module) and use it according to whether you selected queue or gearbox as output.
"""
OR = lambda x,y: _Operator("|", [x,y])
......
......@@ -9,10 +9,18 @@
#include "monitor_component_constants.h"
void start_dma_writing(void) {
// Tell design that addressdata is now valid to write to
monitoringComponent_dma_writeEnable_write(1);
// Tell the DMA itself to start writing
monitoringComponent_writer_enable_write(1);
}
int main(void) {
#ifdef CONFIG_CPU_HAS_INTERRUPT
irq_setmask(0);
irq_setie(1);
irq_setmask(0);
irq_setie(1);
#endif
uart_init();
......@@ -25,20 +33,16 @@ int main(void) {
for (int i = 0; i < NUM_ELEMENTS; i++) {
fifoContents[i] = 0;
}
// Make the writer ready
monitoringComponent_writer_base_write(&fifoContents[0]);
monitoringComponent_writer_length_write(NUM_ELEMENTS);
monitoringComponent_writer_loop_write(0); // TODO: Change from bios?
start_dma_writing();
// Tell design that addressdata is now valid to write to
monitoringComponent_dma_writeEnable_write(1);
// Tell the DMA itself to start writing
monitoringComponent_writer_enable_write(1);
printf("Base-Address: %p\n", (void*) monitoringComponent_writer_base_read());
printf("Elements: %lu\n", (unsigned long) monitoringComponent_writer_length_read());
......
......@@ -28,7 +28,7 @@ from crg import _CRG
#from receiver import LiteBusReceiver
from litex.soc.integration.builder import soc_directory, Builder
#from software.liblitebusmonitor.software import add_litebusmonitor_software
from monitor_component import MonitoringComponent
from monitor_component import DMAMonitoringComponent, GearboxMonitoringComponent
class BaseSoC(SoCCore):
......@@ -47,8 +47,8 @@ class BaseSoC(SoCCore):
self.crg.cd_sys.clk.attr.add("keep")
self.platform.add_period_constraint(self.crg.cd_sys.clk, 1e9 / sys_clk_freq)
self.submodules.monitoringComponent = MonitoringComponent(list(self.bus.masters.values()), dmaBus=self.bus)
def add_monitor(self, fifodepth):
self.submodules.monitoringComponent = DMAMonitoringComponent(list(self.bus.masters.values()), dmaBus=self.bus, numElementsToSave=fifodepth)
self.add_csr("monitoringComponent")
......@@ -65,15 +65,11 @@ def main():
parser.add_argument("--toolchain", default=None, help="FPGA gateware toolchain used for build")
parser.add_argument("--with-monitor", default=True, action="store_true", help="Select this flag to include a monitoring unit on this soc")
parser.add_argument("--monitor-buffer-size", default="5000", action="store", help="How many bus accesses can be saved (accesses over that number get discarded")
parser.add_argument("--with-receiver", default=True, action="store_true", help="Select this flag to include a monitoring unit on this soc")
parser.add_argument("--receiver-buffer-size", default="5000", action="store", help="How many bus accesses can be saved on the receiving unit (accesses over that number get discarded")
parser.add_argument("--with-monitor", action="store_true", default=True, help="Setup a monitoring component that uses a dma to write entries to software")
parser.add_argument("--monitor-elements", action="store", default="100", help="Number of elements to save. Determines queue depth in design AND variable size in firmware")
args = parser.parse_args()
parser.add_argument("--withlbr-dma", action="store_true", default=True, help="Use the DMA and the corresponding BIOS commands to read the data")
args = parser.parse_args()
platform_module = importlib.import_module(args.platform)
if args.toolchain is not None:
......@@ -82,6 +78,9 @@ def main():
platform = platform_module.Platform()
soc = BaseSoC(platform, **soc_core_argdict(args))
builder = Builder(soc, **builder_argdict(args))
if args.with_monitor:
soc.add_monitor(int(args.monitor_elements))
builder.build(run=args.build)
generate_docs(soc, "build/documentation")
......
......@@ -16,12 +16,12 @@ from termcolor import colored
def say(s, error=False):
print(colored(s, ('green' if not error else "red")))
class MonitoringComponent(Module, AutoCSR):
class DMAMonitoringComponent(Module, AutoCSR):
def __init__(self,
interfaces: Union[wishbone.Interface, List[wishbone.Interface]],
trigger:str = "ack",
numElementsToSave:int = 100,
outputMethod:str = "queue",
dmaBus:Union[SoCBusHandler, None]=None,
smartDMA:bool = False
) -> None:
......@@ -32,7 +32,6 @@ class MonitoringComponent(Module, AutoCSR):
interfaces: Bus Interfaces to monitor
trigger: When data gets recorded (default: when any ack is received)
numElementsToSave: Depth of queue. Determines how many bus accesses (across all interfaces) get saved (default: 100)
outputMethod: "queue" -> DMA, "gearbox" -> Pins
dmaBus: BusHandler for the DMA to write data to (default: None)
smartDMA: Whether to only save the bus data from the bus where ack was 1 (only usable if trigger is ack) (default: False)
"""
......@@ -42,78 +41,103 @@ class MonitoringComponent(Module, AutoCSR):
masterInterface=interfaces,
trigger=trigger,
fifoDepth=numElementsToSave,
outputMethod=outputMethod,
outputMethod="queue",
outputQueueDepth=numElementsToSave,
debug=False
)
self.submodules += self.interfaceMonitor
say(f"[MONITOR COMPONENT] Using QUEUE as output method")
# Printing on the BIOS / Firmware
if smartDMA and trigger != "ack":
say("[MONITOR COMPONENT] Design can only filter the DMA data for the correct bus, if ack was the trigger!", error=True)
sys.exit()
# DMA Writer for saving queue contents on unit
if dmaBus is None:
say("[MONITOR COMPONENT] When using a queue for output you must specify a bus for the DMA to write on", error=True)
sys.exit()
# Registers
self.writeEnabled = CSRStorage(size=1, name="dma_writeEnable", description="If set to 1, the DMA assumes that startAddress was correctly set and starts writing data")
# How to use the data
if outputMethod == "queue":
say(f"[MONITOR COMPONENT] Using QUEUE as output method")
# Printing on the BIOS / Firmware
if smartDMA and trigger != "ack":
say("[MONITOR COMPONENT] Design can only filter the DMA data for the correct bus, if ack was the trigger!", error=True)
sys.exit()
# DMA Writer for saving queue contents on unit
if dmaBus is None:
say("[MONITOR COMPONENT] When using a queue for output you must specify a bus for the DMA to write on", error=True)
sys.exit()
# Registers
self.writeEnabled = CSRStorage(size=1, name="dma_writeEnable", description="If set to 1, the DMA assumes that startAddress was correctly set and starts writing data")
# The writer itself manages the address, current address, looping etc.
print(colored("[MONITOR COMPONENT] Adding Bus Master for DMA writer", "green"))
self.dmaInterface = wishbone.Interface(data_width=dmaBus.data_width, adr_width=dmaBus.address_width)
dmaBus.add_master(master=self.dmaInterface, name="MonitorComponentMaster")
self.writer = WishboneDMAWriter(self.dmaInterface, with_csr=True)
self.submodules += self.writer
say(f"[MONITOR COMPONENT] Using smartDMA? {smartDMA}")
# TODO: Layout for Software? How to read from design?
if not smartDMA:
# Record all data from all busses
length = sum([l for _,l in self.interfaceMonitor.mon.fifoLayout])
writerData = Signal(length)
# FIXME: Uncomment next line to properly read data
# self.comb += writerData.eq(Cat([self.interfaceMonitor.source.__getattr__(qsigname) for qsigname,_ in self.interfaceMonitor.mon.fifoLayout]))
self.comb += writerData.eq(self.interfaceMonitor.source.__getattr__("0adr"))
say(f"[MONITOR COMPONENT] [DEBUG] Using adress 0")
else:
# Only record data from the firing bus
# TODO: Annotate which bus fired?
length = sum([l for n,l in self.interfaceMonitor.mon.fifoLayout if n[0] == "0"])
writerData = Signal(length)
# FIXME: self.comb += writerData.eq(Cat([self.interfaceMonitor.source.__getattr__(qsigname) for qsigname,_ in self.interfaceMonitor.mon.fifoLayout if qsigname[0] == "0"]))
# When writing is enabled connect queue to dma writer
self.comb += [
If(self.writeEnabled.storage,
self.writer.sink.valid.eq(self.interfaceMonitor.source.valid),
self.interfaceMonitor.source.ready.eq(self.writer.sink.ready),
self.writer.sink.data.eq(writerData)
).Else(
self.writer.sink.valid.eq(0),
self.interfaceMonitor.source.ready.eq(0)
)
]
say(f"[MONITOR COMPONENT] Writing build constants to firmware header files to {HEADER_PATH}")
# Write layout to C header file
with open(HEADER_PATH, 'w+') as f:
f.write("// Automatically generated by the MonitoringComponent during SoC Building\n")
f.write(f"#define BITS_PER_ENTRY {sum([l for _,l in self.interfaceMonitor.outLayout])}\n")
f.write(f"#define NUM_ELEMENTS {numElementsToSave}\n")
# The writer itself manages the address, current address, looping etc.
print(colored("[MONITOR COMPONENT] Adding Bus Master for DMA writer", "green"))
self.dmaInterface = wishbone.Interface(data_width=dmaBus.data_width, adr_width=dmaBus.address_width)
dmaBus.add_master(master=self.dmaInterface, name="MonitorComponentMaster")
self.writer = WishboneDMAWriter(self.dmaInterface, with_csr=True)
self.submodules += self.writer
say(f"[MONITOR COMPONENT] Using smartDMA? {smartDMA}")
# TODO: Layout for Software? How to read from design?
if not smartDMA:
# Record all data from all busses
length = sum([l for _,l in self.interfaceMonitor.mon.fifoLayout])
writerData = Signal(length)
# FIXME: Uncomment next line to properly read data
# self.comb += writerData.eq(Cat([self.interfaceMonitor.source.__getattr__(qsigname) for qsigname,_ in self.interfaceMonitor.mon.fifoLayout]))
self.comb += writerData.eq(self.interfaceMonitor.source.__getattr__("0adr"))
say(f"[MONITOR COMPONENT] [DEBUG] Using adress 0")
else:
# Only record data from the firing bus
# TODO: Annotate which bus fired?
length = sum([l for n,l in self.interfaceMonitor.mon.fifoLayout if n[0] == "0"])
writerData = Signal(length)
# FIXME: self.comb += writerData.eq(Cat([self.interfaceMonitor.source.__getattr__(qsigname) for qsigname,_ in self.interfaceMonitor.mon.fifoLayout if qsigname[0] == "0"]))
# When writing is enabled connect queue to dma writer
self.comb += [
If(self.writeEnabled.storage,
self.writer.sink.valid.eq(self.interfaceMonitor.source.valid),
self.interfaceMonitor.source.ready.eq(self.writer.sink.ready),
self.writer.sink.data.eq(writerData)
).Else(
self.writer.sink.valid.eq(0),
self.interfaceMonitor.source.ready.eq(0)
)
]
say(f"[MONITOR COMPONENT] Writing build constants to firmware header files to {HEADER_PATH}")
elif outputMethod == "gearbox":
# For transfering to another unit, component, etc.
pass
\ No newline at end of file
# Write layout to C header file
with open(HEADER_PATH, 'w+') as f:
f.write("// Automatically generated by the MonitoringComponent during SoC Building\n")
f.write(f"#define BITS_PER_ENTRY {sum([l for _,l in self.interfaceMonitor.outLayout])}\n")
f.write(f"#define NUM_ELEMENTS {numElementsToSave}\n")
class GearboxMonitoringComponent(Module, AutoCSR):
def __init__(self,
interfaces: Union[wishbone.Interface, List[wishbone.Interface]],
trigger:str = "ack",
numElementsToSave:int = 100,
) -> None:
"""
interfaces: Bus Interfaces to monitor
trigger: When data gets recorded (default: when any ack is received)
numElementsToSave: Depth of queue. Determines how many bus accesses (across all interfaces) get saved (default: 100)
Interface with it using the source member variable. (Like a normal LiteX stream element)
"""
# The monitor itself
self.interfaceMonitor = BusMasterMonitor(
masterInterface=interfaces,
trigger=trigger,
fifoDepth=numElementsToSave,
outputMethod="gearbox",
outputQueueDepth=numElementsToSave,
debug=False
)
self.submodules += self.interfaceMonitor
say(f"[MONITOR COMPONENT] Using GEARBOX as output method")
self.source = self.interfaceMonitor.source
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment