Skip to content

Commit

Permalink
Improve LA pipeline
Browse files Browse the repository at this point in the history
* Add converter and hspi packet buffer
  This allows to have smaller probe number than
  HSPI bus width.
  This also allows to run sampling and HSPI at same
  speed, because otherwise since HSPI has approx 10 bytes
  overhead (4 bytes header + 2 or 4 bytes CRC + negociation)
  storage buffer would quickly fill up so we in fact need
  probe number to be less than HSPI bus width if we want continuous
  sampling.
* Add a testing mode where LA generates dummy values
* Retime storage fsm_state CSRStatus with regard to sys clock domain
  • Loading branch information
fallen committed Apr 13, 2024
1 parent dd13ddd commit 7d2074c
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 42 deletions.
123 changes: 82 additions & 41 deletions modules/la.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,86 @@

class LA(Module, AutoCSR):
def __init__(self, ios, hspi_pads, depth, samplerate=1e12, clock_domain="sys", trigger_depth=16, register=False,
csr_csv="analyzer.csv"):
csr_csv="analyzer.csv", testing=False):
self.ios = ios
self.depth = depth
self.samplerate = int(samplerate)
self.data_width = data_width = len(ios)
self.fake_data = Signal(data_width)
self.csr_csv = csr_csv

self.hspi_enable = CSRStorage(1, reset=0)
self.hspi_test_pattern = CSRStorage(data_width)
assert len(ios) in [4, 8, 16]
hspi_data_width = {4: 8,
8: 16,
16: 32}[len(ios)]

# Frontend
self.submodules.trigger = ClockDomainsRenamer({"scope": "sys"})(Trigger(data_width, depth=trigger_depth))
self.submodules.subsampler = ClockDomainsRenamer({"scope": "sys"})(SubSampler(data_width))
self.submodules.trigger = ClockDomainsRenamer({"scope": clock_domain})(Trigger(data_width, depth=trigger_depth))
self.submodules.subsampler = ClockDomainsRenamer({"scope": clock_domain})(SubSampler(data_width))

# Storage buffer
self.submodules.storage = ClockDomainsRenamer({"scope": "sys"})(Storage(data_width, depth))
self.submodules.storage = ClockDomainsRenamer({"scope": clock_domain})(Storage(data_width, depth))

# HSPI TX
self.submodules.hspi_tx = HSPITransmitter(hspi_pads, data_width)
# Upconverter
self.submodules.converter = ClockDomainsRenamer(clock_domain)(stream.Converter(len(ios), hspi_data_width))

self.comb += [
self.trigger.sink.valid.eq(1),
self.trigger.sink.data.eq(ios)
# SyncFIFO to buffer a HSPI packet
self.submodules.hspi_packet_buffer = ClockDomainsRenamer(clock_domain)(stream.SyncFIFO([("data",
hspi_data_width)],
depth=0x2000))
# HSPI TX
self.submodules.hspi_tx = ClockDomainsRenamer({"scope": clock_domain})(HSPITransmitter(hspi_pads, hspi_data_width))

scope_sync = getattr(self.sync, clock_domain)
scope_sync += [
If(~self.hspi_tx.enable.storage,
self.fake_data.eq(0)
).Else(
self.fake_data.eq(self.fake_data + 1)
)
]

self.comb += self.trigger.sink.valid.eq(1)

if testing:
self.comb += self.trigger.sink.data.eq(self.fake_data)
else:
self.comb += self.trigger.sink.data.eq(ios)

# Pipeline
self.submodules.pipeline = stream.Pipeline(
self.trigger.source,
self.subsampler,
self.storage,
self.hspi_tx.sink)
self.converter,
self.hspi_packet_buffer,
)

self.packet_is_buffered = Signal()

self.comb += [
self.packet_is_buffered.eq(~self.hspi_tx.fsm.ongoing("WAIT_INPUT") |
(self.hspi_packet_buffer.level >= self.hspi_tx.max_packet_size.storage)),
If(self.packet_is_buffered,
self.pipeline.source.connect(self.hspi_tx.sink)
).Else(
self.hspi_tx.sink.valid.eq(0)
)
]


class LATest(Module):
def __init__(self):
def __init__(self, stop_via="max_packet_size", inject_not_ready=False):
hd_layout = [("oe", 1), ("o", 8), ("i", 8)]
self.pads = Record([("hd", hd_layout),
("clk", 1),
("ready", 1),
("valid", 1),
("req", 1),
("hract", 1),
("htack", 1)
])
self.ios = Signal(4)
self.submodules.la = LA(self.ios, self.pads, depth=64, clock_domain="sys", samplerate=48e6)
self.submodules.la = LA(self.ios, self.pads, depth=64, clock_domain="sys", samplerate=48e6, testing=True)

def handle_data(data, buffer):
assert data in range(256)
Expand All @@ -65,59 +101,64 @@ def wait_for_tx_req():
if tx_req:
print("Detected TX_REQ")
break
yield self.ios.eq(self.ios + 1)
yield

def simulate():
yield
yield from self.la.storage.length.write(64)
yield
yield self.ios.eq(0)
yield self.la.hspi_test_pattern.storage.eq(0x3)
yield self.la.hspi_enable.storage.eq(1)
yield self.la.storage.length.storage.eq(64)
yield from self.la.hspi_tx.enable.write(1)
yield
yield from self.la.subsampler.value.write(0)
yield
yield from self.la.hspi_tx.max_packet_size.write(50)
yield
yield
yield self.la.storage.enable.storage.eq(1)
yield
yield
yield from self.la.storage.enable.write(1)
yield from wait_for_tx_req()

yield self.pads.ready.eq(1)
for i in range(100):
yield self.ios.eq(self.ios + 1)
tx_valid = yield self.pads.valid
if tx_valid == 1:
print("Detected TX_VALID")
break
yield

data_buffer = bytearray()
for i in range(4096):
for i in range(200):
if i == 40:
yield self.la.storage.enable.storage.eq(0)
if stop_via == "hspi":
yield self.la.hspi_tx.enable.storage.eq(0)
elif stop_via == "storage":
yield self.la.storage.enable.storage.eq(0)
tx_valid = yield self.pads.valid
tx_req = yield self.pads.req
hd_oe = yield self.pads.hd.oe
if (i == 42) and inject_not_ready:
yield self.pads.ready.eq(0)
if (i == 47) and inject_not_ready:
yield self.pads.ready.eq(1)
ready = yield self.pads.ready
if tx_valid == 0:
print("TX_VALID is gone (i={})!".format(i))
print("total buffer: {}".format(data_buffer))
if ready == 0:
print("RX_READY is gone (i={})!".format(i))
if hd_oe == 0:
print("hd.oe is gone (i={})!".format(i))
if tx_valid & ready & hd_oe:
data = yield self.pads.hd.o
handle_data(data, data_buffer)
print("{}: Transmitted 0x{:02x}".format(i, data))
if tx_req == 0:
print("TX_REQ is gone (i={})!".format(i))
print("total buffer (len {}): {}".format(len(data_buffer), data_buffer))
break
data = yield self.pads.hd.o
handle_data(data, data_buffer)
yield self.ios.eq(self.ios + 1)
yield
print("Transmitted 0x{:02x}".format(data))

p = HSPIPacket(data_buffer)

#yield self.la.storage.enable.storage.eq(0)
for i in range(100):
yield self.ios.eq(self.ios + 1)
yield
yield self.la.hspi_enable.storage.eq(0)
yield self.la.hspi_tx.sink.last.eq(1)
for i in range(20):
yield self.ios.eq(self.ios + 1)
yield
HSPIPacket(data_buffer)

print("Running sim!")
run_simulation(self, simulate(), vcd_name="sim.vcd")
run_simulation(self, simulate(), vcd_name="sim.vcd")
4 changes: 3 additions & 1 deletion modules/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self, data_width, depth):

self.enable = CSRStorage()
self.done = CSRStatus()
self.fsm_state_r = CSRStatus()

self.length = CSRStorage(bits_for(depth))
self.offset = CSRStorage(bits_for(depth))
Expand Down Expand Up @@ -54,10 +55,11 @@ def __init__(self, data_width, depth):
self.submodules += mem_flush

self.fsm_state = Signal(max=3)
self.specials += MultiReg(self.fsm_state, self.fsm_state_r.status)

# FSM
fsm = FSM(reset_state="IDLE")
fsm = ClockDomainsRenamer("scope")(fsm)
self.fsm = fsm = ClockDomainsRenamer("scope")(fsm)
self.submodules += fsm
fsm.act("IDLE",
self.fsm_state.eq(0),
Expand Down

0 comments on commit 7d2074c

Please sign in to comment.