liteusb: add FT2232HPHYAsynchronous PHY (Minispartan6+, Pipistrello), needs more simulations and on-board tests
This commit is contained in:
parent
30eed19283
commit
28c50112a4
|
@ -1,13 +1,34 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.flow.actor import *
|
||||
from migen.actorlib.fifo import AsyncFIFO
|
||||
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 FT2232HPHYSynchronous(Module):
|
||||
def __init__(self, pads, fifo_depth=32, read_time=16, write_time=16):
|
||||
def __init__(self, pads,
|
||||
fifo_depth=32,
|
||||
read_time=16,
|
||||
write_time=16):
|
||||
dw = flen(pads.data)
|
||||
|
||||
#
|
||||
|
@ -15,14 +36,14 @@ class FT2232HPHYSynchronous(Module):
|
|||
#
|
||||
|
||||
# Read Fifo (Ftdi --> SoC)
|
||||
read_fifo = RenameClockDomains(AsyncFIFO(phy_layout, fifo_depth),
|
||||
read_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
|
||||
{"write": "ftdi", "read": "sys"})
|
||||
read_buffer = RenameClockDomains(SyncFIFO(phy_layout, 4),
|
||||
read_buffer = RenameClockDomains(SyncFIFO(phy_description(8), 4),
|
||||
{"sys": "ftdi"})
|
||||
self.comb += read_buffer.source.connect(read_fifo.sink)
|
||||
|
||||
# Write Fifo (SoC --> Ftdi)
|
||||
write_fifo = RenameClockDomains(AsyncFIFO(phy_layout, fifo_depth),
|
||||
write_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
|
||||
{"write": "sys", "read": "ftdi"})
|
||||
|
||||
self.submodules += read_fifo, read_buffer, write_fifo
|
||||
|
@ -49,24 +70,8 @@ class FT2232HPHYSynchronous(Module):
|
|||
wants_read.eq(~rxf_n & read_fifo.sink.ack),
|
||||
]
|
||||
|
||||
def anti_starvation(timeout):
|
||||
en = Signal()
|
||||
max_time = Signal()
|
||||
if timeout:
|
||||
t = timeout - 1
|
||||
time = Signal(max=t+1)
|
||||
self.comb += max_time.eq(time == 0)
|
||||
self.sync += If(~en,
|
||||
time.eq(t)
|
||||
).Elif(~max_time,
|
||||
time.eq(time - 1)
|
||||
)
|
||||
else:
|
||||
self.comb += max_time.eq(0)
|
||||
return en, max_time
|
||||
|
||||
read_time_en, max_read_time = anti_starvation(read_time)
|
||||
write_time_en, max_write_time = anti_starvation(write_time)
|
||||
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)
|
||||
|
||||
|
@ -76,7 +81,9 @@ class FT2232HPHYSynchronous(Module):
|
|||
fsm.act("READ",
|
||||
read_time_en.eq(1),
|
||||
If(wants_write,
|
||||
If(~wants_read | max_read_time, NextState("RTW"))
|
||||
If(~wants_read | max_read_time,
|
||||
NextState("RTW")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("RTW",
|
||||
|
@ -85,7 +92,9 @@ class FT2232HPHYSynchronous(Module):
|
|||
fsm.act("WRITE",
|
||||
write_time_en.eq(1),
|
||||
If(wants_read,
|
||||
If(~wants_write | max_write_time, NextState("WTR"))
|
||||
If(~wants_write | max_write_time,
|
||||
NextState("WTR")
|
||||
)
|
||||
),
|
||||
write_fifo.source.ack.eq(wants_write & data_w_accepted)
|
||||
)
|
||||
|
@ -101,12 +110,7 @@ class FT2232HPHYSynchronous(Module):
|
|||
data_r = Signal(dw)
|
||||
data_oe = Signal()
|
||||
|
||||
if hasattr(pads, "oe_n"):
|
||||
pads_oe_n = pads.oe_n
|
||||
else:
|
||||
pads_oe_n = Signal()
|
||||
|
||||
pads_oe_n.reset = 1
|
||||
pads.oe_n.reset = 1
|
||||
pads.rd_n.reset = 1
|
||||
pads.wr_n.reset = 1
|
||||
|
||||
|
@ -114,14 +118,14 @@ class FT2232HPHYSynchronous(Module):
|
|||
If(fsm.ongoing("READ"),
|
||||
data_oe.eq(0),
|
||||
|
||||
pads_oe_n.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.oe_n.eq(1),
|
||||
pads.rd_n.eq(1),
|
||||
pads.wr_n.eq(~wants_write),
|
||||
|
||||
|
@ -130,7 +134,7 @@ class FT2232HPHYSynchronous(Module):
|
|||
).Else(
|
||||
data_oe.eq(1),
|
||||
|
||||
pads_oe_n.eq(~fsm.ongoing("WTR")),
|
||||
pads.oe_n.eq(~fsm.ongoing("WTR")),
|
||||
pads.rd_n.eq(1),
|
||||
pads.wr_n.eq(1)
|
||||
),
|
||||
|
@ -148,3 +152,170 @@ class FT2232HPHYSynchronous(Module):
|
|||
|
||||
self.debug = Signal(8)
|
||||
self.comb += self.debug.eq(data_r)
|
||||
|
||||
|
||||
class FT2232HPHYAsynchronous(Module):
|
||||
def __init__(self, pads, clk_freq,
|
||||
fifo_depth=32,
|
||||
read_time=16,
|
||||
write_time=16):
|
||||
dw = flen(pads.data)
|
||||
|
||||
#
|
||||
# Read / Write Fifos
|
||||
#
|
||||
|
||||
# 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 / Write Actions
|
||||
#
|
||||
pads.wr_n.reset = 1
|
||||
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 == 15, # XXX Compute exact value
|
||||
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_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 == 5, # XXX Compute exact value
|
||||
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 == 15, # XXX Compute exact value
|
||||
NextState("WAIT_TXE_N")
|
||||
)
|
||||
)
|
||||
write_fsm.act("WAIT_TXE_N",
|
||||
If(txe_n,
|
||||
write_fifo.source.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
|
|
@ -3,5 +3,11 @@ PYTHON = python3
|
|||
|
||||
CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
|
||||
|
||||
ft2232h_tb:
|
||||
$(CMD) ft2232h_tb.py
|
||||
ft2232h_sync_tb:
|
||||
$(CMD) ft2232h_sync_tb.py
|
||||
|
||||
ft2232h_async_tb:
|
||||
$(CMD) ft2232h_async_tb.py
|
||||
|
||||
core_tb:
|
||||
$(CMD) core_tb.py
|
||||
|
|
|
@ -5,19 +5,13 @@ from migen.fhdl.specials import *
|
|||
from migen.sim.generic import run_simulation
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
from misoclib.com.liteusb.phy.ft2232h import FT2232HPHYSynchronous
|
||||
from misoclib.com.liteusb.phy.ft2232h import FT2232HPHYAsynchronous
|
||||
from misoclib.com.liteusb.test.common import *
|
||||
|
||||
# XXX for now use it from liteeth to avoid duplication
|
||||
from misoclib.com.liteeth.test.common import *
|
||||
|
||||
|
||||
def phy_description():
|
||||
payload_layout = [("data", 8)]
|
||||
return EndpointDescription(payload_layout, packetized=True)
|
||||
|
||||
|
||||
class FT2232HSynchronousModel(Module, RandRun):
|
||||
class FT2232HAsynchronousModel(Module, RandRun):
|
||||
def __init__(self, rd_data):
|
||||
RandRun.__init__(self, 10)
|
||||
self.rd_data = [0] + rd_data
|
||||
|
@ -29,9 +23,6 @@ class FT2232HSynchronousModel(Module, RandRun):
|
|||
self.txe_n = Signal(reset=1)
|
||||
self.rd_n = Signal(reset=1)
|
||||
self.wr_n = Signal(reset=1)
|
||||
self.oe_n = Signal(reset=1)
|
||||
self.siwua = Signal()
|
||||
self.pwren_n = Signal(reset=1)
|
||||
|
||||
self.init = True
|
||||
self.wr_data = []
|
||||
|
@ -42,37 +33,51 @@ class FT2232HSynchronousModel(Module, RandRun):
|
|||
self.data_w = Signal(8)
|
||||
self.data_r = Signal(8)
|
||||
|
||||
self.specials += Tristate(self.data, self.data_r, ~self.oe_n, self.data_w)
|
||||
self.specials += Tristate(self.data, self.data_r, ~self.rd_n, self.data_w)
|
||||
|
||||
self.last_wr_n = 1
|
||||
self.last_rd_n = 1
|
||||
|
||||
self.wr_delay = 0
|
||||
self.rd_delay = 0
|
||||
|
||||
def wr_sim(self, selfp):
|
||||
if not selfp.wr_n and not selfp.txe_n:
|
||||
self.wr_data.append(selfp.data_w)
|
||||
self.wait_wr_n = False
|
||||
if self.wr_delay:
|
||||
selfp.txe_n = 1
|
||||
self.wr_delay = self.wr_delay - 1
|
||||
else:
|
||||
if (not selfp.wr_n and self.last_wr_n) and not selfp.txe_n:
|
||||
self.wr_data.append(selfp.data_w)
|
||||
self.wr_delay = 20 # XXX FIXME
|
||||
self.last_wr_n = selfp.wr_n
|
||||
|
||||
if not self.wait_wr_n:
|
||||
if self.run:
|
||||
selfp.txe_n = 1
|
||||
else:
|
||||
if selfp.txe_n:
|
||||
self.wait_wr_n = True
|
||||
selfp.txe_n = 0
|
||||
|
||||
def rd_sim(self, selfp):
|
||||
rxf_n = selfp.rxf_n
|
||||
if self.run:
|
||||
if self.rd_idx < len(self.rd_data)-1:
|
||||
self.rd_done = selfp.rxf_n
|
||||
selfp.rxf_n = 0
|
||||
if self.rd_delay:
|
||||
selfp.rxf_n = 1
|
||||
self.rd_delay = self.rd_delay - 1
|
||||
else:
|
||||
rxf_n = selfp.rxf_n
|
||||
if self.run:
|
||||
if self.rd_idx < len(self.rd_data)-1:
|
||||
self.rd_done = selfp.rxf_n
|
||||
selfp.rxf_n = 0
|
||||
else:
|
||||
selfp.rxf_n = self.rd_done
|
||||
else:
|
||||
selfp.rxf_n = self.rd_done
|
||||
else:
|
||||
selfp.rxf_n = self.rd_done
|
||||
|
||||
if not selfp.rd_n and not selfp.oe_n:
|
||||
if self.rd_idx < len(self.rd_data)-1:
|
||||
self.rd_idx += not rxf_n
|
||||
selfp.data_r = self.rd_data[self.rd_idx]
|
||||
self.rd_done = 1
|
||||
if not selfp.rd_n and self.last_rd_n:
|
||||
if self.rd_idx < len(self.rd_data)-1:
|
||||
self.rd_idx += not rxf_n
|
||||
selfp.data_r = self.rd_data[self.rd_idx]
|
||||
self.rd_done = 1
|
||||
if selfp.rd_n and not self.last_rd_n:
|
||||
self.rd_delay = 4 # XXX FIXME
|
||||
|
||||
self.last_rd_n = selfp.rd_n
|
||||
|
||||
def do_simulation(self, selfp):
|
||||
RandRun.do_simulation(self, selfp)
|
||||
|
@ -83,19 +88,19 @@ class FT2232HSynchronousModel(Module, RandRun):
|
|||
self.wr_sim(selfp)
|
||||
self.rd_sim(selfp)
|
||||
|
||||
test_packet = [i%256 for i in range(512)]
|
||||
test_packet = [i%256 for i in range(128)]
|
||||
|
||||
|
||||
class TB(Module):
|
||||
def __init__(self):
|
||||
self.submodules.model = FT2232HSynchronousModel(test_packet)
|
||||
self.submodules.phy = FT2232HPHYSynchronous(self.model)
|
||||
self.submodules.model = FT2232HAsynchronousModel(test_packet)
|
||||
self.submodules.phy = FT2232HPHYAsynchronous(self.model, 50000000)
|
||||
|
||||
self.submodules.streamer = PacketStreamer(phy_description())
|
||||
self.submodules.streamer_randomizer = AckRandomizer(phy_description(), level=10)
|
||||
self.submodules.streamer = PacketStreamer(phy_description(8))
|
||||
self.submodules.streamer_randomizer = AckRandomizer(phy_description(8), level=10)
|
||||
|
||||
self.submodules.logger_randomizer = AckRandomizer(phy_description(), level=10)
|
||||
self.submodules.logger = PacketLogger(phy_description())
|
||||
self.submodules.logger_randomizer = AckRandomizer(phy_description(8), level=10)
|
||||
self.submodules.logger = PacketLogger(phy_description(8))
|
||||
|
||||
self.comb += [
|
||||
Record.connect(self.streamer.source, self.streamer_randomizer.sink),
|
||||
|
@ -109,20 +114,14 @@ class TB(Module):
|
|||
Record.connect(self.logger_randomizer.source, self.logger.sink)
|
||||
]
|
||||
|
||||
# Use sys_clk as ftdi_clk in simulation
|
||||
self.comb += [
|
||||
ClockSignal("ftdi").eq(ClockSignal()),
|
||||
ResetSignal("ftdi").eq(ResetSignal())
|
||||
]
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
yield from self.streamer.send(Packet(test_packet))
|
||||
for i in range(2000):
|
||||
for i in range(4000):
|
||||
yield
|
||||
s, l, e = check(test_packet, self.model.wr_data)
|
||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
s, l, e = check(test_packet, self.logger.packet[1:])
|
||||
s, l, e = check(test_packet, self.logger.packet)
|
||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
|
Loading…
Reference in New Issue