cores/usb_fifo: Re-implement FT245PHYSynchronous, passing simple tests on FT601/LimeSDRMini-V2.0.
This commit is contained in:
parent
7187ac22e4
commit
7416943f9c
|
@ -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 ---------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue