litex/misoclib/com/liteusb/phy/ft245.py

320 lines
9.0 KiB
Python

import math
from migen.fhdl.std import *
from migen.flow.actor import *
from migen.actorlib.fifo import SyncFIFO, AsyncFIFO
from migen.fhdl.specials import *
from migen.genlib.cdc import MultiReg
from misoclib.com.liteusb.common import *
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 = flen(pads.data)
# read fifo (FTDI --> SoC)
read_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
{"write": "usb", "read": "sys"})
read_buffer = RenameClockDomains(SyncFIFO(phy_description(8), 4),
{"sys": "usb"})
self.comb += read_buffer.source.connect(read_fifo.sink)
# write fifo (SoC --> FTDI)
write_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
{"write": "sys", "read": "usb"})
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.stb),
wants_read.eq(~rxf_n & read_fifo.sink.ack),
]
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 += RenameClockDomains(fsm, {"sys": "usb"})
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.ack.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.stb.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 = flen(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 = SyncFIFO(phy_description(8), fifo_depth)
# write fifo (SoC --> FTDI)
write_fifo = SyncFIFO(phy_description(8), 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.stb),
wants_read.eq(~rxf_n & read_fifo.sink.ack),
]
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")
read_counter = Counter(8)
self.submodules += read_fsm, read_counter
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.value == max((tRD-1), (tRDDataSetup + tMultiReg -1)),
NextState("ACQUIRE_DATA")
)
)
read_fsm.act("ACQUIRE_DATA",
read_fifo.sink.stb.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")
write_counter = Counter(8)
self.submodules += write_fsm, write_counter
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.value == (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.value == (tWR-1),
NextState("WAIT_TXE_N")
)
)
write_fsm.act("WAIT_TXE_N",
If(txe_n,
write_fifo.source.ack.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)