update submodule usage, tidy logic analyzer config check

This commit is contained in:
Fischer Moseley 2024-01-14 12:51:52 -08:00
parent 970499d137
commit 1528f569ef
11 changed files with 136 additions and 120 deletions

View File

@ -54,9 +54,11 @@ Usage:
def help():
print(usage)
def version():
print(logo)
def wrong_args():
print('Wrong number of arguments, run "manta help" for usage.')

View File

@ -68,98 +68,102 @@ class LogicAnalyzerCore(Elaboratable):
warn(f"Ignoring unrecognized option '{option}' in Logic Analyzer.")
# Check sample depth is provided and positive
if "sample_depth" not in config:
sample_depth = config.get("sample_depth")
if not sample_depth:
raise ValueError("Logic Analyzer must have sample_depth specified.")
if not isinstance(config["sample_depth"], int):
raise ValueError("Logic Analyzer sample_depth must be an integer.")
if config["sample_depth"] <= 0:
raise ValueError("Logic Analyzer sample_depth must be positive.")
if not isinstance(sample_depth, int) or sample_depth <= 0:
raise ValueError("Logic Analyzer sample_depth must be a positive integer.")
# Check probes
if "probes" not in config:
raise ValueError("Logic Analyzer must have at least one probe specified.")
if len(config["probes"]) == 0:
if "probes" not in config or len(config["probes"]) == 0:
raise ValueError("Logic Analyzer must have at least one probe specified.")
for name, width in config["probes"].items():
if width < 0:
raise ValueError(f"Width of probe {name} must be positive.")
# Check triggers
if "triggers" not in config:
raise ValueError("Logic Analyzer must have at least one trigger specified.")
if len(config["triggers"]) == 0:
raise ValueError("Logic Analyzer must have at least one trigger specified.")
# Check trigger location
if "trigger_location" in config:
if not isinstance(config["trigger_location"], int):
raise ValueError("Trigger location must be an integer.")
if config["trigger_location"] < 0:
raise ValueError("Trigger location must be positive.")
if config["trigger_location"] > config["sample_depth"]:
raise ValueError("Trigger location cannot exceed sample depth.")
# Check trigger mode, if provided
if "trigger_mode" in config:
valid_modes = ["single_shot", "incremental", "immediate"]
if config["trigger_mode"] not in valid_modes:
trigger_mode = config.get("trigger_mode")
valid_modes = ["single_shot", "incremental", "immediate"]
if trigger_mode and trigger_mode not in valid_modes:
raise ValueError(
f"Unrecognized trigger mode {config['trigger_mode']} provided."
)
# Check triggers
if (trigger_mode) and (trigger_mode != "immediate"):
if ("triggers" not in config) or (config["triggers"] == 0):
raise ValueError(
f"Unrecognized trigger mode {config['trigger_mode']} provided."
"Logic Analyzer must have at least one trigger specified."
)
if config["trigger_mode"] == "incremental":
if "trigger_location" in config:
warn(
"Ignoring option 'trigger_location', as 'trigger_mode' is set to immediate, and there is no trigger condition to wait for."
)
# Check trigger location
trigger_location = config.get("trigger_location")
if trigger_location:
if not isinstance(trigger_location, int) or trigger_location < 0:
raise ValueError("Trigger location must be a positive integer.")
if trigger_location > config["sample_depth"]:
raise ValueError("Trigger location cannot exceed sample depth.")
if trigger_mode == "immediate":
warn(
"Ignoring option 'trigger_location', as 'trigger_mode' is set to immediate, and there is no trigger condition to wait for."
)
# Check triggers themselves
for trigger in config["triggers"]:
if not isinstance(trigger, str):
raise ValueError("Trigger must be specified with a string.")
if trigger_mode == "immediate":
if "triggers" in config:
warn(
"Ignoring triggers as 'trigger_mode' is set to immediate, and there are no triggers to specify."
)
# Trigger conditions may be composed of either two or three components,
# depending on the operation specified. In the case of operations that
# don't need an argument (like DISABLE, RISING, FALLING, CHANGING) or
# three statements in
else:
if ("triggers" not in config) or (len(config["triggers"]) == 0):
raise ValueError("At least one trigger must be specified.")
# Check the trigger operations
components = trigger.strip().split(" ")
if len(components) == 2:
name, op = components
if op not in ["DISABLE", "RISING", "FALLING", "CHANGING"]:
for trigger in config.get("triggers"):
if not isinstance(trigger, str):
raise ValueError("Trigger must be specified with a string.")
# Trigger conditions may be composed of either two or three components,
# depending on the operation specified. In the case of operations that
# don't need an argument (like DISABLE, RISING, FALLING, CHANGING) or
# three statements in
# Check the trigger operations
components = trigger.strip().split(" ")
if len(components) == 2:
name, op = components
if op not in ["DISABLE", "RISING", "FALLING", "CHANGING"]:
raise ValueError(
f"Unable to interpret trigger condition '{trigger}'."
)
elif len(components) == 3:
name, op, arg = components
if op not in ["GT", "LT", "GEQ", "LEQ", "EQ", "NEQ"]:
raise ValueError(
f"Unable to interpret trigger condition '{trigger}'."
)
else:
raise ValueError(
f"Unable to interpret trigger condition '{trigger}'."
)
elif len(components) == 3:
name, op, arg = components
if op not in ["GT", "LT", "GEQ", "LEQ", "EQ", "NEQ"]:
raise ValueError(
f"Unable to interpret trigger condition '{trigger}'."
)
else:
raise ValueError(f"Unable to interpret trigger condition '{trigger}'.")
# Check probe names
if components[0] not in config["probes"]:
raise ValueError(f"Unknown probe name '{components[0]}' specified.")
# Check probe names
if components[0] not in config["probes"]:
raise ValueError(f"Unknown probe name '{components[0]}' specified.")
def elaborate(self, platform):
m = Module()
# Add submodules
m.submodules["fsm"] = fsm = self.fsm
m.submodules["sample_mem"] = sample_mem = self.sample_mem
m.submodules["trig_blk"] = trig_blk = self.trig_blk
m.submodules.fsm = fsm = self.fsm
m.submodules.sample_mem = sample_mem = self.sample_mem
m.submodules.trig_blk = trig_blk = self.trig_blk
# Concat all the probes together, and feed to input of sample memory
# (it is necessary to reverse the order such that first probe occupies
@ -173,11 +177,10 @@ class LogicAnalyzerCore(Elaboratable):
trig_blk.bus_i.eq(self.fsm.bus_o),
sample_mem.bus_i.eq(trig_blk.bus_o),
self.bus_o.eq(sample_mem.bus_o),
# Non-bus Connections
fsm.trigger.eq(trig_blk.trig),
sample_mem.user_addr.eq(fsm.r.write_pointer),
sample_mem.user_we.eq(fsm.write_enable)
sample_mem.user_we.eq(fsm.write_enable),
]
return m
@ -215,6 +218,7 @@ class LogicAnalyzerCore(Elaboratable):
print_if_verbose(" -> Resetting core...")
state = self.fsm.r.get_probe("state")
if state != self.fsm.states["IDLE"]:
self.fsm.r.set_probe("request_start", 0)
self.fsm.r.set_probe("request_stop", 0)
self.fsm.r.set_probe("request_stop", 1)
self.fsm.r.set_probe("request_stop", 0)
@ -224,12 +228,16 @@ class LogicAnalyzerCore(Elaboratable):
# Set triggers
print_if_verbose(" -> Setting triggers...")
self.trig_blk.set_triggers(self.config)
self.trig_blk.clear_triggers()
if self.config["trigger_mode"] != "immediate":
self.trig_blk.set_triggers(self.config)
# Set trigger mode, default to single-shot if user didn't specify a mode
print_if_verbose(" -> Setting trigger mode...")
if "trigger_mode" in self.config:
self.fsm.r.set_probe("trigger_mode", self.config["trigger_mode"])
mode = self.config["trigger_mode"].upper()
self.fsm.r.set_probe("trigger_mode", self.fsm.trigger_modes[mode])
else:
self.fsm.r.set_probe("trigger_mode", self.fsm.trigger_modes["SINGLE_SHOT"])
@ -244,6 +252,7 @@ class LogicAnalyzerCore(Elaboratable):
# Send a start request to the state machine
print_if_verbose(" -> Starting capture...")
self.fsm.r.set_probe("request_start", 0)
self.fsm.r.set_probe("request_start", 1)
self.fsm.r.set_probe("request_start", 0)
@ -362,16 +371,20 @@ class LogicAnalyzerCapture:
signals.append(signal)
clock = writer.register_var("manta", "clk", "wire", size=1)
trigger = writer.register_var("manta", "trigger", "wire", size=1)
# include a trigger signal such would be meaningful (ie, we didn't trigger immediately)
if self.config["trigger_mode"] != "immediate":
trigger = writer.register_var("manta", "trigger", "wire", size=1)
# add the data to each probe in the vcd file
for timestamp in range(0, 2 * len(self.data)):
# run the clock
writer.change(clock, timestamp, timestamp % 2 == 0)
# set the trigger
triggered = (timestamp // 2) >= self.get_trigger_location()
writer.change(trigger, timestamp, triggered)
# set the trigger (if there is one)
if self.config["trigger_mode"] != "immediate":
triggered = (timestamp // 2) >= self.get_trigger_location()
writer.change(trigger, timestamp, triggered)
# add other signals
for signal in signals:

View File

@ -56,7 +56,7 @@ class LogicAnalyzerFSM(Elaboratable):
def elaborate(self, platform):
m = Module()
m.submodules["registers"] = self.r
m.submodules.registers = self.r
prev_request_start = Signal(1)
prev_request_stop = Signal(1)
@ -74,6 +74,7 @@ class LogicAnalyzerFSM(Elaboratable):
m.d.sync += self.write_enable.eq(1)
with m.If(self.r.trigger_mode == self.trigger_modes["IMMEDIATE"]):
m.d.sync += self.r.state.eq(self.states["CAPTURING"])
m.d.sync += self.r.write_pointer.eq(self.r.write_pointer + 1)
with m.Else():
with m.If(self.r.trigger_location == 0):
@ -82,7 +83,7 @@ class LogicAnalyzerFSM(Elaboratable):
with m.Else():
m.d.sync += self.r.state.eq(self.states["MOVE_TO_POSITION"])
m.d.sync += self.r.state.eq(self.states["MOVE_TO_POSITION"])
# m.d.sync += self.r.state.eq(self.states["MOVE_TO_POSITION"])
with m.Elif(self.r.state == self.states["MOVE_TO_POSITION"]):
m.d.sync += self.r.write_pointer.eq(self.r.write_pointer + 1)

View File

@ -38,7 +38,7 @@ class LogicAnalyzerPlayback(Elaboratable):
def elaborate(self, platform):
m = Module()
m.submodules["mem"] = self.mem
m.submodules.mem = self.mem
m.d.comb += self.read_port.en.eq(1)

View File

@ -28,12 +28,13 @@ class LogicAnalyzerTriggerBlock(Elaboratable):
def get_max_addr(self):
return self.r.get_max_addr()
def set_triggers(self, config):
# reset all triggers to zero
def clear_triggers(self):
# reset all triggers to disabled with no argument
for p in self.probes:
self.r.set_probe(p.name + "_op", 0)
self.r.set_probe(p.name + "_arg", 0)
def set_triggers(self, config):
# set triggers
for trigger in config["triggers"]:
components = trigger.strip().split(" ")
@ -53,7 +54,7 @@ class LogicAnalyzerTriggerBlock(Elaboratable):
m = Module()
# Add IO Core as submodule
m.submodules["registers"] = self.r
m.submodules.registers = self.r
# Add triggers as submodules
for t in self.triggers:

View File

@ -126,7 +126,7 @@ class Manta(Elaboratable):
m = Module()
# Add interface as submodule
m.submodules["interface"] = self.interface
m.submodules.interface = self.interface
# Add all cores as submodules
for name, instance in self.cores.items():

View File

@ -61,7 +61,7 @@ class ReadOnlyMemoryCore(Elaboratable):
m.d.sync += self.bus_pipe[0].eq(self.bus_i)
for i in range(1, 3):
m.d.sync += self.bus_pipe[i].eq(self.bus_pipe[i-1])
m.d.sync += self.bus_pipe[i].eq(self.bus_pipe[i - 1])
m.d.sync += self.bus_o.eq(self.bus_pipe[2])

View File

@ -232,10 +232,10 @@ class UARTInterface(Elaboratable):
# fancy submoduling and such goes in here
m = Module()
m.submodules["uart_rx"] = uart_rx = UARTReceiver(self.clocks_per_baud)
m.submodules["bridge_rx"] = bridge_rx = ReceiveBridge()
m.submodules["bridge_tx"] = bridge_tx = TransmitBridge()
m.submodules["uart_tx"] = uart_tx = UARTTransmitter(self.clocks_per_baud)
m.submodules.uart_rx = uart_rx = UARTReceiver(self.clocks_per_baud)
m.submodules.bridge_rx = bridge_rx = ReceiveBridge()
m.submodules.bridge_tx = bridge_tx = TransmitBridge()
m.submodules.uart_tx = uart_tx = UARTTransmitter(self.clocks_per_baud)
m.d.comb += [
# UART RX -> Internal Bus

View File

@ -13,7 +13,7 @@ class IOCoreLoopbackTest(Elaboratable):
self.port = port
self.config = self.platform_specific_config()
self.m = Manta(self.config)
self.manta = Manta(self.config)
def platform_specific_config(self):
return {
@ -41,17 +41,17 @@ class IOCoreLoopbackTest(Elaboratable):
def elaborate(self, platform):
m = Module()
m.submodules["manta"] = self.m
m.submodules.manta = self.manta
uart_pins = platform.request("uart")
m.d.comb += [
self.m.io_core.probe0.eq(self.m.io_core.probe4),
self.m.io_core.probe1.eq(self.m.io_core.probe5),
self.m.io_core.probe2.eq(self.m.io_core.probe6),
self.m.io_core.probe3.eq(self.m.io_core.probe7),
self.m.interface.rx.eq(uart_pins.rx.i),
uart_pins.tx.o.eq(self.m.interface.tx),
self.manta.io_core.probe0.eq(self.manta.io_core.probe4),
self.manta.io_core.probe1.eq(self.manta.io_core.probe5),
self.manta.io_core.probe2.eq(self.manta.io_core.probe6),
self.manta.io_core.probe3.eq(self.manta.io_core.probe7),
self.manta.interface.rx.eq(uart_pins.rx.i),
uart_pins.tx.o.eq(self.manta.interface.tx),
]
return m
@ -71,7 +71,7 @@ class IOCoreLoopbackTest(Elaboratable):
outputs = self.config["cores"]["io_core"]["outputs"]
for name, attrs in outputs.items():
actual = self.m.io_core.get_probe(name)
actual = self.manta.io_core.get_probe(name)
if isinstance(attrs, dict):
if "initial_value" in attrs:
@ -100,8 +100,8 @@ class IOCoreLoopbackTest(Elaboratable):
width = self.config["cores"]["io_core"]["inputs"][input]
value = randint(0, 2**width - 1)
self.m.io_core.set_probe(output, value)
readback = self.m.io_core.get_probe(input)
self.manta.io_core.set_probe(output, value)
readback = self.manta.io_core.get_probe(input)
if readback != value:
raise ValueError(

View File

@ -12,7 +12,7 @@ class LogicAnalyzerCounterTest(Elaboratable):
self.port = port
self.config = self.platform_specific_config()
self.m = Manta(self.config)
self.manta = Manta(self.config)
def platform_specific_config(self):
return {
@ -20,9 +20,8 @@ class LogicAnalyzerCounterTest(Elaboratable):
"la": {
"type": "logic_analyzer",
"sample_depth": 1024,
"trigger_location": 500,
"trigger_mode": "immediate",
"probes": {"larry": 1, "curly": 3, "moe": 9},
"triggers": ["moe RISING"],
},
},
"uart": {
@ -34,20 +33,20 @@ class LogicAnalyzerCounterTest(Elaboratable):
def elaborate(self, platform):
m = Module()
m.submodules["manta"] = self.m
m.submodules.manta = self.manta
uart_pins = platform.request("uart")
larry = self.m.la.probes[0]
curly = self.m.la.probes[1]
moe = self.m.la.probes[2]
larry = self.manta.la.probes[0]
curly = self.manta.la.probes[1]
moe = self.manta.la.probes[2]
m.d.sync += larry.eq(larry + 1)
m.d.sync += curly.eq(curly + 1)
m.d.sync += moe.eq(moe + 1)
m.d.comb += [
self.m.interface.rx.eq(uart_pins.rx.i),
uart_pins.tx.o.eq(self.m.interface.tx),
self.manta.interface.rx.eq(uart_pins.rx.i),
uart_pins.tx.o.eq(self.manta.interface.tx),
]
return m
@ -57,7 +56,7 @@ class LogicAnalyzerCounterTest(Elaboratable):
def verify(self):
self.build_and_program()
cap = self.m.la.capture()
cap = self.manta.la.capture()
# check that VCD export works
cap.export_vcd("out.vcd")
@ -66,7 +65,7 @@ class LogicAnalyzerCounterTest(Elaboratable):
cap.export_playback_verilog("out.v")
# verify that each signal is just a counter modulo the width of the signal
for name, width in self.m.la.config["probes"].items():
for name, width in self.manta.la.config["probes"].items():
trace = cap.get_trace(name)
for i in range(len(trace) - 1):

View File

@ -22,7 +22,7 @@ class MemoryCoreLoopbackTest(Elaboratable):
self.port = port
self.config = self.platform_specific_config()
self.m = Manta(self.config)
self.manta = Manta(self.config)
def platform_specific_config(self):
return {
@ -50,16 +50,16 @@ class MemoryCoreLoopbackTest(Elaboratable):
def elaborate(self, platform):
m = Module()
m.submodules["manta"] = self.m
m.submodules.manta = self.manta
uart_pins = platform.request("uart")
m.d.comb += [
self.m.mem_core.user_addr.eq(self.m.io_core.addr),
self.m.mem_core.user_data.eq(self.m.io_core.data),
self.m.mem_core.user_we.eq(self.m.io_core.we),
self.m.interface.rx.eq(uart_pins.rx.i),
uart_pins.tx.o.eq(self.m.interface.tx),
self.manta.mem_core.user_addr.eq(self.manta.io_core.addr),
self.manta.mem_core.user_data.eq(self.manta.io_core.data),
self.manta.mem_core.user_we.eq(self.manta.io_core.we),
self.manta.interface.rx.eq(uart_pins.rx.i),
uart_pins.tx.o.eq(self.manta.interface.tx),
]
return m
@ -68,14 +68,14 @@ class MemoryCoreLoopbackTest(Elaboratable):
self.platform.build(self, do_program=True)
def write_user_side(self, addr, data):
self.m.io_core.set_probe("we", 0)
self.m.io_core.set_probe("addr", addr)
self.m.io_core.set_probe("data", data)
self.m.io_core.set_probe("we", 1)
self.m.io_core.set_probe("we", 0)
self.manta.io_core.set_probe("we", 0)
self.manta.io_core.set_probe("addr", addr)
self.manta.io_core.set_probe("data", data)
self.manta.io_core.set_probe("we", 1)
self.manta.io_core.set_probe("we", 0)
def verify_register(self, addr, expected_data):
data = self.m.mem_core.read_from_user_addr(addr)
data = self.manta.mem_core.read_from_user_addr(addr)
if data != expected_data:
raise ValueError(