cores/usb_fifo: Re-implement FT245PHYSynchronous, passing simple tests on FT601/LimeSDRMini-V2.0.

This commit is contained in:
Florent Kermarrec 2022-04-11 19:23:58 +02:00
parent 7187ac22e4
commit 7416943f9c
1 changed files with 137 additions and 96 deletions

View File

@ -1,7 +1,7 @@
# #
# This file is part of LiteX. # This file is part of LiteX.
# #
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr> # Copyright (c) 2015-2022 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause # SPDX-License-Identifier: BSD-2-Clause
import math import math
@ -12,6 +12,8 @@ from migen.genlib.cdc import MultiReg
from litex.soc.interconnect import stream from litex.soc.interconnect import stream
from litex.build.io import SDRTristate
# Layout/Helpers ----------------------------------------------------------------------------------- # Layout/Helpers -----------------------------------------------------------------------------------
def phy_description(dw): def phy_description(dw):
@ -38,113 +40,152 @@ def anti_starvation(module, timeout):
# FT245 Synchronous FIFO Mode ---------------------------------------------------------------------- # FT245 Synchronous FIFO Mode ----------------------------------------------------------------------
class FT245PHYSynchronous(Module): class FT245PHYSynchronous(Module):
# FIXME: Check/Improve sampling timings.
def __init__(self, pads, clk_freq, def __init__(self, pads, clk_freq,
fifo_depth = 8, fifo_depth = 64,
read_time = 128, read_time = 128,
write_time = 128): write_time = 128):
dw = len(pads.data) self.dw = dw = len(pads.data)
self.pads = pads
self.sink = stream.Endpoint(phy_description(dw))
self.source = stream.Endpoint(phy_description(dw))
# read fifo (FTDI --> SoC) # # #
read_fifo = stream.AsyncFIFO(phy_description(dw), fifo_depth)
read_fifo = ClockDomainsRenamer({"write": "usb", "read": "sys"})(read_fifo)
read_buffer = stream.SyncFIFO(phy_description(dw), 4)
read_buffer = ClockDomainsRenamer("usb")(read_buffer)
self.comb += read_buffer.source.connect(read_fifo.sink)
self.submodules += read_fifo, read_buffer
# write fifo (SoC --> FTDI) # Pads Reset.
write_fifo = stream.AsyncFIFO(phy_description(dw), fifo_depth) # -----------
write_fifo = ClockDomainsRenamer({"write": "sys", "read": "usb"})(write_fifo)
self.submodules += write_fifo
# sink / source interfaces
self.sink = write_fifo.sink
self.source = read_fifo.source
# read / write arbitration
wants_write = Signal()
wants_read = Signal()
txe_n = Signal()
rxf_n = Signal()
self.comb += [
txe_n.eq(pads.txe_n),
rxf_n.eq(pads.rxf_n),
wants_write.eq(~txe_n & write_fifo.source.valid),
wants_read.eq(~rxf_n & read_fifo.sink.ready),
]
read_time_en, max_read_time = anti_starvation(self, read_time)
write_time_en, max_write_time = anti_starvation(self, write_time)
data_w_accepted = Signal(reset=1)
fsm = FSM(reset_state="READ")
self.submodules += ClockDomainsRenamer("usb")(fsm)
fsm.act("READ",
read_time_en.eq(1),
If(wants_write,
If(~wants_read | max_read_time,
NextState("RTW")
)
)
)
fsm.act("RTW",
NextState("WRITE")
)
fsm.act("WRITE",
write_time_en.eq(1),
If(wants_read,
If(~wants_write | max_write_time,
NextState("WTR")
)
),
write_fifo.source.ready.eq(wants_write & data_w_accepted)
)
fsm.act("WTR",
NextState("READ")
)
# databus tristate
data_w = Signal(dw)
data_r = Signal(dw)
data_oe = Signal()
self.specials += Tristate(pads.data, data_w, data_oe, data_r)
# read / write actions
pads.oe_n.reset = 1 pads.oe_n.reset = 1
pads.rd_n.reset = 1 pads.rd_n.reset = 1
pads.wr_n.reset = 1 pads.wr_n.reset = 1
self.sync.usb += [ # Read CDC/FIFO (FTDI --> SoC).
If(fsm.ongoing("READ"), # -----------------------------
data_oe.eq(0), self.submodules.read_cdc = stream.ClockDomainCrossing(phy_description(dw),
cd_from = "usb",
pads.oe_n.eq(0), cd_to = "sys",
pads.rd_n.eq(~wants_read), with_common_rst = True
pads.wr_n.eq(1)
).Elif(fsm.ongoing("WRITE"),
data_oe.eq(1),
pads.oe_n.eq(1),
pads.rd_n.eq(1),
pads.wr_n.eq(~wants_write),
data_w_accepted.eq(~txe_n)
).Else(
data_oe.eq(1),
pads.oe_n.eq(~fsm.ongoing("WTR")),
pads.rd_n.eq(1),
pads.wr_n.eq(1)
),
read_buffer.sink.valid.eq(~pads.rd_n & ~rxf_n),
read_buffer.sink.data.eq(data_r),
If(~txe_n & data_w_accepted,
data_w.eq(write_fifo.source.data)
) )
self.submodules.read_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth)
self.comb += self.read_cdc.source.connect(self.read_fifo.sink)
self.comb += self.read_fifo.source.connect(self.source)
read_fifo_almost_full = (self.read_fifo.level > (fifo_depth - 4))
read_fifo_almost_full_usb = Signal()
self.specials += MultiReg(read_fifo_almost_full, read_fifo_almost_full_usb)
# Write FIFO/CDC (SoC --> FTDI).
# ------------------------------
self.submodules.write_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth)
self.submodules.write_cdc = stream.ClockDomainCrossing(phy_description(dw),
cd_from = "sys",
cd_to = "usb",
with_common_rst = True
)
self.comb += self.sink.connect(self.write_fifo.sink)
self.comb += self.write_fifo.source.connect(self.write_cdc.sink)
# Read / Write Anti-Starvation.
# -----------------------------
read_time_en, max_read_time = anti_starvation(self, read_time)
write_time_en, max_write_time = anti_starvation(self, write_time)
# Read / Write Detection.
# -----------------------
self.wants_write = wants_write = Signal()
self.wants_read = wants_read = Signal()
self.comb += [
wants_write.eq(~pads.txe_n & self.write_cdc.source.valid),
wants_read.eq( ~pads.rxf_n & (self.read_cdc.sink.ready & ~read_fifo_almost_full_usb)),
]
# Data Bus Tristate.
# ------------------
self.data_w = data_w = Signal(dw)
self.data_r = data_r = Signal(dw)
self.data_oe = data_oe = Signal()
for i in range(dw):
self.specials += SDRTristate(
io = pads.data[i],
o = data_w[i],
oe = data_oe,
i = data_r[i],
clk = ClockSignal("usb")
)
if hasattr(pads, "be"):
for i in range(dw//8):
self.specials += SDRTristate(
io = pads.be[i],
o = Signal(reset=0b1),
oe = data_oe,
i = Signal(),
clk = ClockSignal("usb")
)
# Read / Write FSM.
# -----------------
fsm = FSM(reset_state="READ")
fsm = ClockDomainsRenamer("usb")(fsm)
self.submodules.fsm = fsm
fsm.act("READ",
# Arbitration.
read_time_en.eq(1),
If(wants_write,
If(~wants_read | max_read_time,
NextState("READ-TO-WRITE")
)
),
# Control/Data-Path.
data_oe.eq(0),
NextValue(pads.oe_n, ~wants_read),
NextValue(pads.rd_n, pads.oe_n | ~wants_read),
NextValue(pads.wr_n, 1),
)
self.comb += self.read_cdc.sink.data.eq(data_r)
self.sync.usb += self.read_cdc.sink.valid.eq(~pads.rd_n & ~pads.rxf_n)
fsm.act("READ-TO-WRITE",
NextState("WRITE")
)
fsm.act("WRITE",
# Arbitration.
write_time_en.eq(1),
If(wants_read,
If(~wants_write | max_write_time,
NextState("WRITE-TO-READ")
)
),
# Control/Data-Path.
data_oe.eq(1),
NextValue(pads.oe_n, 1),
NextValue(pads.rd_n, 1),
NextValue(pads.wr_n, ~wants_write),
#data_w.eq(write_fifo.source.data),
NextValue(data_w, self.write_cdc.source.data), # FIXME: Add 1 cycle delay.
self.write_cdc.source.ready.eq(wants_write),
)
fsm.act("WRITE-TO-READ",
NextState("READ")
)
def get_litescope_probes(self):
return [
# Physical.
self.pads.oe_n,
self.pads.rd_n,
self.pads.wr_n,
self.pads.txe_n,
self.pads.rxf_n,
self.data_w,
self.data_r,
self.data_oe,
# Core.
self.wants_write,
self.wants_read,
self.fsm,
# FIFOs.
self.write_fifo.source,
self.read_cdc.sink,
] ]
# FT245 Asynchronous FIFO Mode --------------------------------------------------------------------- # FT245 Asynchronous FIFO Mode ---------------------------------------------------------------------