From: Florent Kermarrec Date: Tue, 28 Apr 2015 17:00:13 +0000 (+0200) Subject: liteusb: add FT2232HPHYAsynchronous PHY (Minispartan6+, Pipistrello), needs more... X-Git-Tag: 24jan2021_ls180~2286 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=28c50112a40707ae3979c112bb6f183bac506163;p=litex.git liteusb: add FT2232HPHYAsynchronous PHY (Minispartan6+, Pipistrello), needs more simulations and on-board tests --- diff --git a/misoclib/com/liteusb/phy/ft2232h.py b/misoclib/com/liteusb/phy/ft2232h.py index 5a325656..92b4219b 100644 --- a/misoclib/com/liteusb/phy/ft2232h.py +++ b/misoclib/com/liteusb/phy/ft2232h.py @@ -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") + ) + ) diff --git a/misoclib/com/liteusb/test/Makefile b/misoclib/com/liteusb/test/Makefile index 6b0ad57b..8224e809 100644 --- a/misoclib/com/liteusb/test/Makefile +++ b/misoclib/com/liteusb/test/Makefile @@ -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 diff --git a/misoclib/com/liteusb/test/ft2232h_async_tb.py b/misoclib/com/liteusb/test/ft2232h_async_tb.py new file mode 100644 index 00000000..4af0a6cf --- /dev/null +++ b/misoclib/com/liteusb/test/ft2232h_async_tb.py @@ -0,0 +1,132 @@ +from migen.fhdl.std import * +from migen.flow.actor import * +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 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 * + +class FT2232HAsynchronousModel(Module, RandRun): + def __init__(self, rd_data): + RandRun.__init__(self, 10) + self.rd_data = [0] + rd_data + self.rd_idx = 0 + + # pads + self.data = Signal(8) + self.rxf_n = Signal(reset=1) + self.txe_n = Signal(reset=1) + self.rd_n = Signal(reset=1) + self.wr_n = Signal(reset=1) + + self.init = True + self.wr_data = [] + self.wait_wr_n = False + self.rd_done = 0 + + + self.data_w = Signal(8) + self.data_r = Signal(8) + + 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 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 self.run: + selfp.txe_n = 0 + + def rd_sim(self, selfp): + 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 + + 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) + if self.init: + selfp.rxf_n = 0 + self.wr_data = [] + self.init = False + self.wr_sim(selfp) + self.rd_sim(selfp) + +test_packet = [i%256 for i in range(128)] + + +class TB(Module): + def __init__(self): + self.submodules.model = FT2232HAsynchronousModel(test_packet) + self.submodules.phy = FT2232HPHYAsynchronous(self.model, 50000000) + + self.submodules.streamer = PacketStreamer(phy_description(8)) + self.submodules.streamer_randomizer = AckRandomizer(phy_description(8), level=10) + + 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), + self.phy.sink.stb.eq(self.streamer_randomizer.source.stb), + self.phy.sink.data.eq(self.streamer_randomizer.source.data), + self.streamer_randomizer.source.ack.eq(self.phy.sink.ack), + + self.logger_randomizer.sink.stb.eq(self.phy.source.stb), + self.logger_randomizer.sink.data.eq(self.phy.source.data), + self.phy.source.ack.eq(self.logger_randomizer.sink.ack), + Record.connect(self.logger_randomizer.source, self.logger.sink) + ] + + def gen_simulation(self, selfp): + yield from self.streamer.send(Packet(test_packet)) + 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) + print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e)) + + +def main(): + run_simulation(TB(), ncycles=8000, vcd_name="my.vcd", keep_files=True) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/misoclib/com/liteusb/test/ft2232h_tb.py b/misoclib/com/liteusb/test/ft2232h_tb.py deleted file mode 100644 index 1dd60292..00000000 --- a/misoclib/com/liteusb/test/ft2232h_tb.py +++ /dev/null @@ -1,133 +0,0 @@ -from migen.fhdl.std import * -from migen.flow.actor import * -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.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): - def __init__(self, rd_data): - RandRun.__init__(self, 10) - self.rd_data = [0] + rd_data - self.rd_idx = 0 - - # pads - self.data = Signal(8) - self.rxf_n = Signal(reset=1) - 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 = [] - self.wait_wr_n = False - self.rd_done = 0 - - - self.data_w = Signal(8) - self.data_r = Signal(8) - - self.specials += Tristate(self.data, self.data_r, ~self.oe_n, self.data_w) - - 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 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 - 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 - - def do_simulation(self, selfp): - RandRun.do_simulation(self, selfp) - if self.init: - selfp.rxf_n = 0 - self.wr_data = [] - self.init = False - self.wr_sim(selfp) - self.rd_sim(selfp) - -test_packet = [i%256 for i in range(512)] - - -class TB(Module): - def __init__(self): - self.submodules.model = FT2232HSynchronousModel(test_packet) - self.submodules.phy = FT2232HPHYSynchronous(self.model) - - self.submodules.streamer = PacketStreamer(phy_description()) - self.submodules.streamer_randomizer = AckRandomizer(phy_description(), level=10) - - self.submodules.logger_randomizer = AckRandomizer(phy_description(), level=10) - self.submodules.logger = PacketLogger(phy_description()) - - self.comb += [ - Record.connect(self.streamer.source, self.streamer_randomizer.sink), - self.phy.sink.stb.eq(self.streamer_randomizer.source.stb), - self.phy.sink.data.eq(self.streamer_randomizer.source.data), - self.streamer_randomizer.source.ack.eq(self.phy.sink.ack), - - self.logger_randomizer.sink.stb.eq(self.phy.source.stb), - self.logger_randomizer.sink.data.eq(self.phy.source.data), - self.phy.source.ack.eq(self.logger_randomizer.sink.ack), - 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): - 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:]) - print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e)) - - -def main(): - run_simulation(TB(), ncycles=8000, vcd_name="my.vcd", keep_files=True) - -if __name__ == "__main__": - main() \ No newline at end of file