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.
This commit is contained in:
parent
e667d5ae53
commit
ecf999b8c7
|
@ -0,0 +1,338 @@
|
|||
# This file is Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||
# 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)
|
|
@ -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)),
|
||||
|
|
Loading…
Reference in New Issue