From 7416943f9c083ae2f3845ae82957a80e9e21c49c Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 11 Apr 2022 19:23:58 +0200 Subject: [PATCH] cores/usb_fifo: Re-implement FT245PHYSynchronous, passing simple tests on FT601/LimeSDRMini-V2.0. --- litex/soc/cores/usb_fifo.py | 233 +++++++++++++++++++++--------------- 1 file changed, 137 insertions(+), 96 deletions(-) diff --git a/litex/soc/cores/usb_fifo.py b/litex/soc/cores/usb_fifo.py index ed4724137..7a6b37f65 100644 --- a/litex/soc/cores/usb_fifo.py +++ b/litex/soc/cores/usb_fifo.py @@ -1,7 +1,7 @@ # # This file is part of LiteX. # -# Copyright (c) 2015-2018 Florent Kermarrec +# Copyright (c) 2015-2022 Florent Kermarrec # SPDX-License-Identifier: BSD-2-Clause import math @@ -12,6 +12,8 @@ from migen.genlib.cdc import MultiReg from litex.soc.interconnect import stream +from litex.build.io import SDRTristate + # Layout/Helpers ----------------------------------------------------------------------------------- def phy_description(dw): @@ -38,113 +40,152 @@ def anti_starvation(module, timeout): # FT245 Synchronous FIFO Mode ---------------------------------------------------------------------- class FT245PHYSynchronous(Module): + # FIXME: Check/Improve sampling timings. def __init__(self, pads, clk_freq, - fifo_depth = 8, - read_time = 128, - write_time = 128): - dw = len(pads.data) + fifo_depth = 64, + read_time = 128, + write_time = 128): + 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) - 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 Reset. + # ----------- pads.oe_n.reset = 1 pads.rd_n.reset = 1 pads.wr_n.reset = 1 - self.sync.usb += [ - If(fsm.ongoing("READ"), - data_oe.eq(0), + # Read CDC/FIFO (FTDI --> SoC). + # ----------------------------- + self.submodules.read_cdc = stream.ClockDomainCrossing(phy_description(dw), + cd_from = "usb", + cd_to = "sys", + with_common_rst = True + ) + 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) - pads.oe_n.eq(0), - pads.rd_n.eq(~wants_read), - pads.wr_n.eq(1) - ).Elif(fsm.ongoing("WRITE"), - data_oe.eq(1), + # 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) - pads.oe_n.eq(1), - pads.rd_n.eq(1), - pads.wr_n.eq(~wants_write), + # 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) - data_w_accepted.eq(~txe_n) - ).Else( - data_oe.eq(1), + # 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)), + ] - 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) + # 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 ---------------------------------------------------------------------