From ecf999b8c717a81b77aff9a8c704c874f90bb02d Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 24 Jun 2019 10:58:36 +0200 Subject: [PATCH] soc/cores: add usb_fifo with FT245 USB FIFO PHY from LiteUSB, deprecate LiteUSB LiteUSB was not up to date was not a real USB PHY but was just providing USB FIFO PHYs. New true USB cores are now available: Daisho, ValentyUSB, so it's better using then for true USB support. We only keep the FT245 FIFO PHY in LiteX that can be useful to interface with USB2/USB3 USB FIFOs. --- litex/soc/cores/usb_fifo.py | 338 ++++++++++++++++++++++++++++++++++++ litex_setup.py | 1 - 2 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 litex/soc/cores/usb_fifo.py diff --git a/litex/soc/cores/usb_fifo.py b/litex/soc/cores/usb_fifo.py new file mode 100644 index 000000000..d818870a6 --- /dev/null +++ b/litex/soc/cores/usb_fifo.py @@ -0,0 +1,338 @@ +# This file is Copyright (c) 2015-2018 Florent Kermarrec +# License: BSD + +import math + +from migen import * +from migen.fhdl.specials import Tristate +from migen.genlib.cdc import MultiReg + +from litex.soc.interconnect import stream + + +def phy_description(dw): + payload_layout = [("data", dw)] + return stream.EndpointDescription(payload_layout) + + +def anti_starvation(module, timeout): + en = Signal() + max_time = Signal() + if timeout: + t = timeout - 1 + time = Signal(max=t+1) + module.comb += max_time.eq(time == 0) + module.sync += If(~en, + time.eq(t) + ).Elif(~max_time, + time.eq(time - 1) + ) + else: + module.comb += max_time.eq(0) + return en, max_time + + +class FT245PHYSynchronous(Module): + def __init__(self, pads, clk_freq, + fifo_depth=32, + read_time=128, + write_time=128): + dw = len(pads.data) + + # read fifo (FTDI --> SoC) + read_fifo = ClockDomainsRenamer({"write": "usb", "read": "sys"})(stream.AsyncFIFO(phy_description(dw), fifo_depth)) + read_buffer = ClockDomainsRenamer("usb")(stream.SyncFIFO(phy_description(dw), 4)) + self.comb += read_buffer.source.connect(read_fifo.sink) + + # write fifo (SoC --> FTDI) + write_fifo = ClockDomainsRenamer({"write": "sys", "read": "usb"})(stream.AsyncFIFO(phy_description(dw), fifo_depth)) + + self.submodules += read_fifo, read_buffer, 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.rd_n.reset = 1 + pads.wr_n.reset = 1 + + self.sync.usb += [ + If(fsm.ongoing("READ"), + data_oe.eq(0), + + pads.oe_n.eq(0), + pads.rd_n.eq(~wants_read), + 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) + ) + ] + + +class FT245PHYAsynchronous(Module): + def __init__(self, pads, clk_freq, + fifo_depth=32, + read_time=128, + write_time=128): + dw = len(pads.data) + self.clk_freq = clk_freq + + # timings + tRD = self.ns(30) # RD# active pulse width (t4) + tRDDataSetup = self.ns(14) # RD# to DATA (t3) + tWRDataSetup = self.ns(5) # DATA to WR# active setup time (t8) + tWR = self.ns(30) # WR# active pulse width (t10) + tMultiReg = 2 + + # read fifo (FTDI --> SoC) + read_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth) + + # write fifo (SoC --> FTDI) + write_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth) + + self.submodules += read_fifo, 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.specials += [ + MultiReg(pads.txe_n, txe_n), + MultiReg(pads.rxf_n, rxf_n) + ] + + self.comb += [ + 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) + + fsm = FSM(reset_state="READ") + self.submodules += fsm + + read_done = Signal() + write_done = Signal() + commuting = Signal() + + fsm.act("READ", + read_time_en.eq(1), + If(wants_write & read_done, + If(~wants_read | max_read_time, + commuting.eq(1), + NextState("RTW") + ) + ) + ) + fsm.act("RTW", + NextState("WRITE") + ) + fsm.act("WRITE", + write_time_en.eq(1), + If(wants_read & write_done, + If(~wants_write | max_write_time, + commuting.eq(1), + NextState("WTR") + ) + ) + ) + fsm.act("WTR", + NextState("READ") + ) + + # databus tristate + data_w = Signal(dw) + data_r_async = Signal(dw) + data_r = Signal(dw) + data_oe = Signal() + self.specials += [ + Tristate(pads.data, data_w, data_oe, data_r_async), + MultiReg(data_r_async, data_r) + ] + + + # read actions + pads.rd_n.reset = 1 + + read_fsm = FSM(reset_state="IDLE") + self.submodules += read_fsm + read_counter = Signal(8) + read_counter_reset = Signal() + read_counter_ce = Signal() + self.sync += \ + If(read_counter_reset, + read_counter.eq(0) + ).Elif(read_counter_ce, + read_counter.eq(read_counter + 1) + ) + + read_fsm.act("IDLE", + read_done.eq(1), + read_counter_reset.eq(1), + If(fsm.ongoing("READ") & wants_read, + If(~commuting, + NextState("PULSE_RD_N") + ) + ) + ) + read_fsm.act("PULSE_RD_N", + pads.rd_n.eq(0), + read_counter_ce.eq(1), + If(read_counter == max((tRD-1), (tRDDataSetup + tMultiReg -1)), + NextState("ACQUIRE_DATA") + ) + ) + read_fsm.act("ACQUIRE_DATA", + read_fifo.sink.valid.eq(1), + read_fifo.sink.data.eq(data_r), + NextState("WAIT_RXF_N") + ) + read_fsm.act("WAIT_RXF_N", + If(rxf_n, + NextState("IDLE") + ) + ) + + # write actions + pads.wr_n.reset = 1 + + write_fsm = FSM(reset_state="IDLE") + self.submodules += write_fsm + write_counter = Signal(8) + write_counter_reset = Signal() + write_counter_ce = Signal() + self.sync += \ + If(write_counter_reset, + write_counter.eq(0) + ).Elif(write_counter_ce, + write_counter.eq(write_counter + 1) + ) + + write_fsm.act("IDLE", + write_done.eq(1), + write_counter_reset.eq(1), + If(fsm.ongoing("WRITE") & wants_write, + If(~commuting, + NextState("SET_DATA") + ) + ) + ) + write_fsm.act("SET_DATA", + data_oe.eq(1), + data_w.eq(write_fifo.source.data), + write_counter_ce.eq(1), + If(write_counter == (tWRDataSetup-1), + write_counter_reset.eq(1), + NextState("PULSE_WR_N") + ) + ) + write_fsm.act("PULSE_WR_N", + data_oe.eq(1), + data_w.eq(write_fifo.source.data), + pads.wr_n.eq(0), + write_counter_ce.eq(1), + If(write_counter == (tWR-1), + NextState("WAIT_TXE_N") + ) + ) + write_fsm.act("WAIT_TXE_N", + If(txe_n, + write_fifo.source.ready.eq(1), + NextState("IDLE") + ) + ) + + def ns(self, t, margin=True): + clk_period_ns = 1000000000/self.clk_freq + if margin: + t += clk_period_ns/2 + return math.ceil(t/clk_period_ns) + + +def FT245PHY(pads, *args, **kwargs): + # autodetect PHY + if hasattr(pads, "oe_n"): + return FT245PHYSynchronous(pads, *args, **kwargs) + else: + return FT245PHYAsynchronous(pads, *args, **kwargs) diff --git a/litex_setup.py b/litex_setup.py index 609aa0f3a..45f0ad47c 100755 --- a/litex_setup.py +++ b/litex_setup.py @@ -12,7 +12,6 @@ repos = [ ("migen", ("http://github.com/m-labs/", True, True)), ("litex", ("http://github.com/enjoy-digital/", True, True)), ("liteeth", ("http://github.com/enjoy-digital/", False, True)), - ("liteusb", ("http://github.com/enjoy-digital/", False, True)), ("litedram", ("http://github.com/enjoy-digital/", False, True)), ("litepcie", ("http://github.com/enjoy-digital/", False, True)), ("litesata", ("http://github.com/enjoy-digital/", False, True)),