From 4bdb1ffda20052f60f64530f43b76c6a903eb7e7 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sat, 21 Feb 2015 22:58:42 +0100 Subject: [PATCH 1/5] add README skeleton --- LICENSE | 28 ++++++++++++++++++ README | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 LICENSE create mode 100644 README diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..e0a9ded1b --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +Unless otherwise noted, LiteUSB is copyright (C) 2015 Florent Kermarrec. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Other authors retain ownership of their contributions. If a submission can +reasonably be considered independently copyrightable, it's yours and we +encourage you to claim it with appropriate copyright notices. This submission +then falls under the "otherwise noted" category. All submissions are strongly +encouraged to use the two-clause BSD license reproduced above. diff --git a/README b/README new file mode 100644 index 000000000..f5f69acf6 --- /dev/null +++ b/README @@ -0,0 +1,91 @@ + __ _ __ __ _________ + / / (_) /____ / / / / __/ _ ) + / /__/ / __/ -_) /_/ /\ \/ _ | + /____/_/\__/\__/\____/___/____/ + + Copyright 2015 / EnjoyDigital + florent@enjoy-digital.fr + + A small footprint and configurable USB core + powered by Migen + +[> Doc +--------- +XXX + +[> Intro +--------- +LiteUSB provides a small footprint and configurable USB core. + +LiteUSB is part of LiteX libraries whose aims are to lower entry level of +complex FPGA IP cores by providing simple, elegant and efficient implementations +ofcomponents used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller... + +The core uses simple and specific streaming buses and will provides in the future +adapters to use standardized AXI or Avalon-ST streaming buses. + +Since Python is used to describe the HDL, the core is highly and easily +configurable. + +LiteUSB uses technologies developed in partnership with M-Labs Ltd: + - Migen enables generating HDL with Python in an efficient way. + - MiSoC provides the basic blocks to build a powerful and small footprint SoC. + +LiteUSB can be used as a Migen/MiSoC library (by simply installing it +with the provided setup.py) or can be integrated with your standard design flow +by generating the verilog rtl that you will use as a standard core. + +[> Features +----------- +- FTDI2232 slave fifo core (DMA, virtual TTY) + host software + +[> Possible improvements +------------------------- +- add Cypress FX2 support +- add Cypress FX3 support +- add USB3 transceiver support and use Daisho's USB3 stack? +- ... See below Support and consulting :) + +If you want to support these features, please contact us at florent [AT] +enjoy-digital.fr. You can also contact our partner on the public mailing list +devel [AT] lists.m-labs.hk. + + +[> Getting started +------------------ +XXX + +[> Simulations: +XXX + +[> Tests : +XXX + +[> License +----------- +LiteUSB is released under the very permissive two-clause BSD license. Under +the terms of this license, you are authorized to use LiteUSB for closed-source +proprietary designs. +Even though we do not require you to do so, those things are awesome, so please +do them if possible: + - tell us that you are using LiteUSB + - cite LiteUSB in publications related to research it has helped + - send us feedback and suggestions for improvements + - send us bug reports when something goes wrong + - send us the modifications and improvements you have done to LiteUSB. + +[> Support and consulting +-------------------------- +We love open-source hardware and like sharing our designs with others. + +LiteUSB is developed and maintained by EnjoyDigital. + +If you would like to know more about LiteUSB or if you are already a happy +user and would like to extend it for your needs, EnjoyDigital can provide standard +commercial support as well as consulting services. + +So feel free to contact us, we'd love to work with you! (and eventually shorten +the list of the possible improvements :) + +[> Contact +E-mail: florent [AT] enjoy-digital.fr \ No newline at end of file From 1b0bc5ca44800a6edc1ddda76d5fb46d76ef6923 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sat, 21 Feb 2015 23:06:36 +0100 Subject: [PATCH 2/5] init repo structure --- liteusb/core/.keep_me | 0 liteusb/frontend/.keep_me | 0 liteusb/generic/.keep_me | 0 liteusb/phy/.keep_me | 0 liteusb/test/.keep_me | 0 software/.keep_me | 0 software/ftdi/ft2232h/.keep_me | 0 7 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 liteusb/core/.keep_me create mode 100644 liteusb/frontend/.keep_me create mode 100644 liteusb/generic/.keep_me create mode 100644 liteusb/phy/.keep_me create mode 100644 liteusb/test/.keep_me create mode 100644 software/.keep_me create mode 100644 software/ftdi/ft2232h/.keep_me diff --git a/liteusb/core/.keep_me b/liteusb/core/.keep_me new file mode 100644 index 000000000..e69de29bb diff --git a/liteusb/frontend/.keep_me b/liteusb/frontend/.keep_me new file mode 100644 index 000000000..e69de29bb diff --git a/liteusb/generic/.keep_me b/liteusb/generic/.keep_me new file mode 100644 index 000000000..e69de29bb diff --git a/liteusb/phy/.keep_me b/liteusb/phy/.keep_me new file mode 100644 index 000000000..e69de29bb diff --git a/liteusb/test/.keep_me b/liteusb/test/.keep_me new file mode 100644 index 000000000..e69de29bb diff --git a/software/.keep_me b/software/.keep_me new file mode 100644 index 000000000..e69de29bb diff --git a/software/ftdi/ft2232h/.keep_me b/software/ftdi/ft2232h/.keep_me new file mode 100644 index 000000000..e69de29bb From b59c777cabe089c7d005653894851e40b7ee1542 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sat, 21 Feb 2015 23:13:43 +0100 Subject: [PATCH 3/5] add ft2232h hdl code (will need rework) --- liteusb/common.py | 58 +++++++ liteusb/core/.keep_me | 0 liteusb/core/__init__.py | 4 + liteusb/core/com.py | 28 ++++ liteusb/core/crc.py | 298 ++++++++++++++++++++++++++++++++++ liteusb/core/depacketizer.py | 153 +++++++++++++++++ liteusb/core/packetizer.py | 148 +++++++++++++++++ liteusb/frontend/.keep_me | 0 liteusb/frontend/crossbar.py | 35 ++++ liteusb/frontend/dma.py | 99 +++++++++++ liteusb/frontend/uart.py | 57 +++++++ liteusb/phy/.keep_me | 0 liteusb/phy/ft2232h.py | 307 +++++++++++++++++++++++++++++++++++ 13 files changed, 1187 insertions(+) create mode 100644 liteusb/common.py delete mode 100644 liteusb/core/.keep_me create mode 100644 liteusb/core/__init__.py create mode 100644 liteusb/core/com.py create mode 100644 liteusb/core/crc.py create mode 100644 liteusb/core/depacketizer.py create mode 100644 liteusb/core/packetizer.py delete mode 100644 liteusb/frontend/.keep_me create mode 100644 liteusb/frontend/crossbar.py create mode 100644 liteusb/frontend/dma.py create mode 100644 liteusb/frontend/uart.py delete mode 100644 liteusb/phy/.keep_me create mode 100644 liteusb/phy/ft2232h.py diff --git a/liteusb/common.py b/liteusb/common.py new file mode 100644 index 000000000..e1cf05aa1 --- /dev/null +++ b/liteusb/common.py @@ -0,0 +1,58 @@ +from migen.fhdl.std import * +from migen.genlib.fsm import * +from migen.actorlib.fifo import * +from migen.flow.actor import EndpointDescription + +user_layout = EndpointDescription( + [ ("dst", 8), + ("length", 4*8), + ("error", 1), + ("d", 8) + ], + packetized=True +) + +phy_layout = [ + ("d", 8) +] + +class FtdiPipe: + def __init__(self, layout): + self.sink = Sink(layout) + self.source = Source(layout) + +class FtdiTimeout(Module): + def __init__(self, clk_freq, length): + cnt_max = int(clk_freq*length) + width = bits_for(cnt_max) + + self.clear = Signal() + self.done = Signal() + + cnt = Signal(width) + self.sync += \ + If(self.clear, + cnt.eq(0) + ).Elif(~self.done, + cnt.eq(cnt+1) + ) + self.comb += self.done.eq(cnt == cnt_max) + +# +# TB +# +import random + +def randn(max_n): + return random.randint(0, max_n-1) + +class RandRun: + def __init__(self, level=0): + self.run = True + self.level = level + + def do_simulation(self, selfp): + self.run = True + n = randn(100) + if n < self.level: + self.run = False diff --git a/liteusb/core/.keep_me b/liteusb/core/.keep_me deleted file mode 100644 index e69de29bb..000000000 diff --git a/liteusb/core/__init__.py b/liteusb/core/__init__.py new file mode 100644 index 000000000..a382591ce --- /dev/null +++ b/liteusb/core/__init__.py @@ -0,0 +1,4 @@ +from liteusb.ftdi.uart import FtdiUART +from liteusb.ftdi.dma import FtdiDMA +from liteusb.ftdi.com import FtdiCom +from liteusb.ftdi.crc import FtdiCRC32 \ No newline at end of file diff --git a/liteusb/core/com.py b/liteusb/core/com.py new file mode 100644 index 000000000..86e49099a --- /dev/null +++ b/liteusb/core/com.py @@ -0,0 +1,28 @@ +from migen.fhdl.std import * +from migen.flow.actor import * + +from liteusb.ftdi.std import * +from liteusb.ftdi.crossbar import FtdiCrossbar +from liteusb.ftdi.packetizer import FtdiPacketizer +from liteusb.ftdi.depacketizer import FtdiDepacketizer +from liteusb.ftdi.phy import FtdiPHY + +class FtdiCom(Module): + def __init__(self, pads, *ports): + # crossbar + self.submodules.crossbar = FtdiCrossbar(list(ports)) + + # packetizer / depacketizer + self.submodules.packetizer = FtdiPacketizer() + self.submodules.depacketizer = FtdiDepacketizer() + self.comb += [ + self.crossbar.slave.source.connect(self.packetizer.sink), + self.depacketizer.source.connect(self.crossbar.slave.sink) + ] + + # phy + self.submodules.phy = FtdiPHY(pads) + self.comb += [ + self.packetizer.source.connect(self.phy.sink), + self.phy.source.connect(self.depacketizer.sink) + ] diff --git a/liteusb/core/crc.py b/liteusb/core/crc.py new file mode 100644 index 000000000..df34837c9 --- /dev/null +++ b/liteusb/core/crc.py @@ -0,0 +1,298 @@ + +from collections import OrderedDict +from migen.fhdl.std import * +from migen.genlib.fsm import FSM, NextState +from migen.genlib.record import * +from migen.genlib.misc import chooser, optree +from migen.flow.actor import Sink, Source +from migen.actorlib.fifo import SyncFIFO + +from liteusb.ftdi.std import * + +class CRCEngine(Module): + """Cyclic Redundancy Check Engine + + Compute next CRC value from last CRC value and data input using + an optimized asynchronous LFSR. + + Parameters + ---------- + dat_width : int + Width of the data bus. + width : int + Width of the CRC. + polynom : int + Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC) + + Attributes + ---------- + d : in + Data input. + last : in + last CRC value. + next : + next CRC value. + """ + def __init__(self, dat_width, width, polynom): + self.d = Signal(dat_width) + self.last = Signal(width) + self.next = Signal(width) + + ### + + def _optimize_eq(l): + """ + Replace even numbers of XORs in the equation + with an equivalent XOR + """ + d = OrderedDict() + for e in l: + if e in d: + d[e] += 1 + else: + d[e] = 1 + r = [] + for key, value in d.items(): + if value%2 != 0: + r.append(key) + return r + + # compute and optimize CRC's LFSR + curval = [[("state", i)] for i in range(width)] + for i in range(dat_width): + feedback = curval.pop() + [("din", i)] + for j in range(width-1): + if (polynom & (1<<(j+1))): + curval[j] += feedback + curval[j] = _optimize_eq(curval[j]) + curval.insert(0, feedback) + + # implement logic + for i in range(width): + xors = [] + for t, n in curval[i]: + if t == "state": + xors += [self.last[n]] + elif t == "din": + xors += [self.d[n]] + self.comb += self.next[i].eq(optree("^", xors)) + +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class CRC32(Module): + """IEEE 802.3 CRC + + Implement an IEEE 802.3 CRC generator/checker. + + Parameters + ---------- + dat_width : int + Width of the data bus. + + Attributes + ---------- + d : in + Data input. + value : out + CRC value (used for generator). + error : out + CRC error (used for checker). + """ + width = 32 + polynom = 0x04C11DB7 + init = 2**width-1 + check = 0xC704DD7B + def __init__(self, dat_width): + self.d = Signal(dat_width) + self.value = Signal(self.width) + self.error = Signal() + + ### + + self.submodules.engine = CRCEngine(dat_width, self.width, self.polynom) + reg = Signal(self.width, reset=self.init) + self.sync += reg.eq(self.engine.next) + self.comb += [ + self.engine.d.eq(self.d), + self.engine.last.eq(reg), + + self.value.eq(~reg[::-1]), + self.error.eq(self.engine.next != self.check) + ] + +class CRCInserter(Module): + """CRC Inserter + + Append a CRC at the end of each packet. + + Parameters + ---------- + layout : layout + Layout of the dataflow. + + Attributes + ---------- + sink : in + Packets input without CRC. + source : out + Packets output with CRC. + """ + def __init__(self, crc_class, layout): + self.sink = sink = Sink(layout) + self.source = source = Source(layout) + self.busy = Signal() + + ### + + dw = flen(sink.d) + crc = crc_class(dw) + fsm = FSM(reset_state="IDLE") + self.submodules += crc, fsm + + fsm.act("IDLE", + crc.reset.eq(1), + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + NextState("COPY"), + ) + ) + fsm.act("COPY", + crc.ce.eq(sink.stb & source.ack), + crc.d.eq(sink.d), + Record.connect(sink, source), + source.eop.eq(0), + If(sink.stb & sink.eop & source.ack, + NextState("INSERT"), + ) + ) + ratio = crc.width//dw + if ratio > 1: + cnt = Signal(max=ratio, reset=ratio-1) + cnt_done = Signal() + fsm.act("INSERT", + source.stb.eq(1), + chooser(crc.value, cnt, source.d, reverse=True), + If(cnt_done, + source.eop.eq(1), + If(source.ack, NextState("IDLE")) + ) + ) + self.comb += cnt_done.eq(cnt == 0) + self.sync += \ + If(fsm.ongoing("IDLE"), + cnt.eq(cnt.reset) + ).Elif(fsm.ongoing("INSERT") & ~cnt_done, + cnt.eq(cnt - source.ack) + ) + else: + fsm.act("INSERT", + source.stb.eq(1), + source.eop.eq(1), + source.d.eq(crc.value), + If(source.ack, NextState("IDLE")) + ) + self.comb += self.busy.eq(~fsm.ongoing("IDLE")) + +class CRC32Inserter(CRCInserter): + def __init__(self, layout): + CRCInserter.__init__(self, CRC32, layout) + +class CRCChecker(Module): + """CRC Checker + + Check CRC at the end of each packet. + + Parameters + ---------- + layout : layout + Layout of the dataflow. + + Attributes + ---------- + sink : in + Packets input with CRC. + source : out + Packets output without CRC and "error" set to 0 + on eop when CRC OK / set to 1 when CRC KO. + """ + def __init__(self, crc_class, layout): + self.sink = sink = Sink(layout) + self.source = source = Source(layout) + self.busy = Signal() + + ### + + dw = flen(sink.d) + crc = crc_class(dw) + self.submodules += crc + ratio = crc.width//dw + + error = Signal() + fifo = InsertReset(SyncFIFO(layout, ratio + 1)) + self.submodules += fifo + + fsm = FSM(reset_state="RESET") + self.submodules += fsm + + fifo_in = Signal() + fifo_out = Signal() + fifo_full = Signal() + + self.comb += [ + fifo_full.eq(fifo.fifo.level == ratio), + fifo_in.eq(sink.stb & (~fifo_full | fifo_out)), + fifo_out.eq(source.stb & source.ack), + + Record.connect(sink, fifo.sink), + fifo.sink.stb.eq(fifo_in), + self.sink.ack.eq(fifo_in), + + source.stb.eq(sink.stb & fifo_full), + source.sop.eq(fifo.source.sop), + source.eop.eq(sink.eop), + fifo.source.ack.eq(fifo_out), + source.payload.eq(fifo.source.payload), + + source.error.eq(sink.error | crc.error), + ] + + fsm.act("RESET", + crc.reset.eq(1), + fifo.reset.eq(1), + NextState("IDLE"), + ) + fsm.act("IDLE", + crc.d.eq(sink.d), + If(sink.stb & sink.sop & sink.ack, + crc.ce.eq(1), + NextState("COPY") + ) + ) + fsm.act("COPY", + crc.d.eq(sink.d), + If(sink.stb & sink.ack, + crc.ce.eq(1), + If(sink.eop, + NextState("RESET") + ) + ) + ) + self.comb += self.busy.eq(~fsm.ongoing("IDLE")) + +class CRC32Checker(CRCChecker): + def __init__(self, layout): + CRCChecker.__init__(self, CRC32, layout) + +class FtdiCRC32(Module): + def __init__(self, tag): + self.tag = tag + + self.submodules.inserter = CRC32Inserter(user_layout) + self.submodules.checker = CRC32Checker(user_layout) + + self.dma_sink = self.inserter.sink + self.dma_source = self.checker.source + + self.sink = self.checker.sink + self.source = self.inserter.source diff --git a/liteusb/core/depacketizer.py b/liteusb/core/depacketizer.py new file mode 100644 index 000000000..d1b75a915 --- /dev/null +++ b/liteusb/core/depacketizer.py @@ -0,0 +1,153 @@ +from migen.fhdl.std import * +from migen.actorlib.structuring import * +from migen.genlib.fsm import FSM, NextState + +from liteusb.ftdi.std import * + +class FtdiDepacketizer(Module): + def __init__(self, timeout=10): + self.sink = sink = Sink(phy_layout) + self.source = source = Source(user_layout) + + # Packet description + # - preamble : 4 bytes + # - dst : 1 byte + # - length : 4 bytes + # - payload + preamble = Array(Signal(8) for i in range(4)) + + header = [ + # dst + source.dst, + # length + source.length[24:32], + source.length[16:24], + source.length[8:16], + source.length[0:8], + ] + + header_pack = InsertReset(Pack(phy_layout, len(header))) + self.submodules += header_pack + + for i, byte in enumerate(header): + chunk = getattr(header_pack.source.payload, "chunk" + str(i)) + self.comb += byte.eq(chunk.d) + + fsm = FSM() + self.submodules += fsm + + self.comb += preamble[0].eq(sink.d) + for i in range(1, 4): + self.sync += If(sink.stb & sink.ack, + preamble[i].eq(preamble[i-1]) + ) + fsm.act("WAIT_SOP", + If( (preamble[3] == 0x5A) & + (preamble[2] == 0xA5) & + (preamble[1] == 0x5A) & + (preamble[0] == 0xA5) & + sink.stb, + NextState("RECEIVE_HEADER") + ), + sink.ack.eq(1), + header_pack.source.ack.eq(1), + ) + + self.submodules.timeout = FtdiTimeout(60000000, timeout) + self.comb += self.timeout.clear.eq(fsm.ongoing("WAIT_SOP")) + + fsm.act("RECEIVE_HEADER", + header_pack.sink.stb.eq(sink.stb), + header_pack.sink.payload.eq(sink.payload), + If(self.timeout.done, NextState("WAIT_SOP")) + .Elif(header_pack.source.stb, NextState("RECEIVE_PAYLOAD")) + .Else(sink.ack.eq(1)) + ) + + self.comb += header_pack.reset.eq(self.timeout.done) + + sop = Signal() + eop = Signal() + cnt = Signal(32) + + fsm.act("RECEIVE_PAYLOAD", + source.stb.eq(sink.stb), + source.sop.eq(sop), + source.eop.eq(eop), + source.d.eq(sink.d), + sink.ack.eq(source.ack), + If((eop & sink.stb & source.ack) | self.timeout.done, NextState("WAIT_SOP")) + ) + + self.sync += \ + If(fsm.ongoing("WAIT_SOP"), + cnt.eq(0) + ).Elif(source.stb & source.ack, + cnt.eq(cnt + 1) + ) + self.comb += sop.eq(cnt == 0) + self.comb += eop.eq(cnt == source.length - 1) + +# +# TB +# +src_data = [ + 0x5A, 0xA5, 0x5A, 0xA5, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03, + 0x5A, 0xA5, 0x5A, 0xA5, 0x12, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, +]*4 + +class DepacketizerSourceModel(Module, Source, RandRun): + def __init__(self, data): + Source.__init__(self, phy_layout) + RandRun.__init__(self, 50) + self.data = data + + self._stb = 0 + self._cnt = 0 + + def do_simulation(self, selfp): + RandRun.do_simulation(self, selfp) + + if self.run and not self._stb: + self._stb = 1 + + if selfp.stb and selfp.ack: + self._cnt +=1 + + selfp.stb = self._stb + selfp.d = self.data[self._cnt] + + if self._cnt == len(self.data)-1: + raise StopSimulation + + +class DepacketizerSinkModel(Module, Sink, RandRun): + def __init__(self): + Sink.__init__(self, user_layout, True) + RandRun.__init__(self, 50) + + def do_simulation(self, selfp): + RandRun.do_simulation(self, selfp) + if self.run: + selfp.ack = 1 + else: + selfp.ack = 0 + + +class TB(Module): + def __init__(self): + self.submodules.source = DepacketizerSourceModel(src_data) + self.submodules.dut = FtdiDepacketizer() + self.submodules.sink = DepacketizerSinkModel() + + self.comb += [ + self.source.connect(self.dut.sink), + self.dut.source.connect(self.sink), + ] + +def main(): + from migen.sim.generic import run_simulation + run_simulation(TB(), ncycles=400, vcd_name="tb_depacketizer.vcd") + +if __name__ == "__main__": + main() diff --git a/liteusb/core/packetizer.py b/liteusb/core/packetizer.py new file mode 100644 index 000000000..bba3ab88c --- /dev/null +++ b/liteusb/core/packetizer.py @@ -0,0 +1,148 @@ +from migen.fhdl.std import * +from migen.actorlib.structuring import * +from migen.genlib.fsm import FSM, NextState + +from liteusb.ftdi.std import * + +class FtdiPacketizer(Module): + def __init__(self): + self.sink = sink = Sink(user_layout) + self.source = source = Source(phy_layout) + + # Packet description + # - preamble : 4 bytes + # - dst : 1 byte + # - length : 4 bytes + # - payload + header = [ + # preamble + 0x5A, + 0xA5, + 0x5A, + 0xA5, + # dst + sink.dst, + # length + sink.length[24:32], + sink.length[16:24], + sink.length[8:16], + sink.length[0:8], + ] + + header_unpack = Unpack(len(header), phy_layout) + self.submodules += header_unpack + + for i, byte in enumerate(header): + chunk = getattr(header_unpack.sink.payload, "chunk" + str(i)) + self.comb += chunk.d.eq(byte) + + fsm = FSM() + self.submodules += fsm + + fsm.act("WAIT_SOP", + If(sink.stb & sink.sop, NextState("SEND_HEADER")) + ) + + fsm.act("SEND_HEADER", + header_unpack.sink.stb.eq(1), + source.stb.eq(1), + source.d.eq(header_unpack.source.d), + header_unpack.source.ack.eq(source.ack), + If(header_unpack.sink.ack, NextState("SEND_DATA")) + ) + + fsm.act("SEND_DATA", + source.stb.eq(sink.stb), + source.d.eq(sink.d), + sink.ack.eq(source.ack), + If(source.ack & sink.eop, NextState("WAIT_SOP")) + ) + +# +# TB +# +src_data = [ + (0x01, 4, + [0x0, 0x1, 0x2, 0x3] + ), + (0x16, 8, + [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7] + ), + (0x22, 16, + [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF] + ), +] + +class PacketizerSourceModel(Module, Source, RandRun): + def __init__(self, data): + Source.__init__(self, user_layout, True) + RandRun.__init__(self, 25) + self.data = data + + self._stb = 0 + self._sop = 0 + self._eop = 0 + self._frame_cnt = 0 + self._payload_cnt = 0 + + def do_simulation(self, selfp): + RandRun.do_simulation(self, selfp) + dst, length, payload = self.data[self._frame_cnt] + + if selfp.stb and selfp.ack: + if self._payload_cnt == length-1: + self._frame_cnt += 1 + self._payload_cnt = 0 + else: + self._payload_cnt += 1 + if self.run: + self._stb = 1 + else: + self._stb = 0 + + if self.run and not self._stb: + self._stb = 1 + + self._sop = int((self._payload_cnt == 0)) + self._eop = int((self._payload_cnt == length-1)) + + selfp.stb = self._stb + selfp.sop = self._sop & self._stb + selfp.eop = self._eop & self._stb + selfp.dst = dst + selfp.length = length + selfp.d = payload[self._payload_cnt] + + if self._frame_cnt == len(self.data): + raise StopSimulation + +class PacketizerSinkModel(Module, Sink, RandRun): + def __init__(self): + Sink.__init__(self, phy_layout) + RandRun.__init__(self, 25) + + def do_simulation(self, selfp): + RandRun.do_simulation(self, selfp) + if self.run: + selfp.ack = 1 + else: + selfp.ack = 0 + + +class TB(Module): + def __init__(self): + self.submodules.source = PacketizerSourceModel(src_data) + self.submodules.dut = FtdiPacketizer() + self.submodules.sink = PacketizerSinkModel() + + self.comb +=[ + self.source.connect(self.dut.sink), + self.dut.source.connect(self.sink), + ] + +def main(): + from migen.sim.generic import run_simulation + run_simulation(TB(), ncycles=400, vcd_name="tb_packetizer.vcd") + +if __name__ == "__main__": + main() diff --git a/liteusb/frontend/.keep_me b/liteusb/frontend/.keep_me deleted file mode 100644 index e69de29bb..000000000 diff --git a/liteusb/frontend/crossbar.py b/liteusb/frontend/crossbar.py new file mode 100644 index 000000000..d1771c049 --- /dev/null +++ b/liteusb/frontend/crossbar.py @@ -0,0 +1,35 @@ +from migen.fhdl.std import * +from migen.genlib.roundrobin import * +from migen.genlib.record import Record + +from liteusb.ftdi.std import * + +class FtdiCrossbar(Module): + def __init__(self, masters, slave=None): + if slave is None: + slave = FtdiPipe(user_layout) + self.slave = slave + + # masters --> slave arbitration + self.submodules.rr = RoundRobin(len(masters)) + cases = {} + for i, m in enumerate(masters): + sop = Signal() + eop = Signal() + ongoing = Signal() + self.comb += [ + sop.eq(m.source.stb & m.source.sop), + eop.eq(m.source.stb & m.source.eop & m.source.ack), + ] + self.sync += ongoing.eq((sop | ongoing) & ~eop) + self.comb += self.rr.request[i].eq(sop | ongoing) + + cases[i] = [Record.connect(masters[i].source, slave.source)] + self.comb += Case(self.rr.grant, cases) + + # slave --> master demux + cases = {} + for i, m in enumerate(masters): + cases[m.tag] = [Record.connect(slave.sink, masters[i].sink)] + cases["default"] = [slave.sink.ack.eq(1)] + self.comb += Case(slave.sink.dst, cases) diff --git a/liteusb/frontend/dma.py b/liteusb/frontend/dma.py new file mode 100644 index 000000000..a8e1ef99e --- /dev/null +++ b/liteusb/frontend/dma.py @@ -0,0 +1,99 @@ +from migen.fhdl.std import * +from migen.flow.actor import * +from migen.flow.network import * +from migen.actorlib import dma_lasmi, structuring, spi +from migen.bank.description import * +from migen.bank.eventmanager import * +from migen.genlib.record import Record + +from liteusb.ftdi.std import * + +class FtdiDMAWriter(Module, AutoCSR): + def __init__(self, lasmim): + self.sink = sink = Sink(user_layout) + + # Pack data + pack_factor = lasmim.dw//8 + pack = structuring.Pack(phy_layout, pack_factor, reverse=True) + cast = structuring.Cast(pack.source.payload.layout, lasmim.dw) + + # DMA + writer = dma_lasmi.Writer(lasmim) + self._reset = CSR() + self.dma = InsertReset(spi.DMAWriteController(writer, mode=spi.MODE_SINGLE_SHOT)) + self.comb += self.dma.reset.eq(self._reset.r & self._reset.re) + + # Remove sop/eop/length/dst fields from payload + self.comb += [ + pack.sink.stb.eq(sink.stb), + pack.sink.payload.eq(sink.payload), + sink.ack.eq(pack.sink.ack) + ] + + # Graph + g = DataFlowGraph() + g.add_pipeline(pack, cast, self.dma) + self.submodules += CompositeActor(g) + + # IRQ + self.submodules.ev = EventManager() + self.ev.done = EventSourcePulse() + self.ev.finalize() + self.comb += self.ev.done.trigger.eq(sink.stb & sink.eop) + + # CRC + self._crc_failed = CSRStatus() + self.sync += \ + If(sink.stb & sink.eop, + self._crc_failed.status.eq(sink.error) + ) + +class FtdiDMAReader(Module, AutoCSR): + def __init__(self, lasmim, tag): + self.source = source = Source(user_layout) + + reader = dma_lasmi.Reader(lasmim) + self.dma = spi.DMAReadController(reader, mode=spi.MODE_SINGLE_SHOT) + + pack_factor = lasmim.dw//8 + packed_dat = structuring.pack_layout(8, pack_factor) + cast = structuring.Cast(lasmim.dw, packed_dat) + unpack = structuring.Unpack(pack_factor, phy_layout, reverse=True) + + # Graph + cnt = Signal(32) + self.sync += \ + If(self.dma.generator._r_shoot.re, + cnt.eq(0) + ).Elif(source.stb & source.ack, + cnt.eq(cnt + 1) + ) + g = DataFlowGraph() + g.add_pipeline(self.dma, cast, unpack) + self.submodules += CompositeActor(g) + self.comb += [ + source.stb.eq(unpack.source.stb), + source.sop.eq(cnt == 0), + source.eop.eq(cnt == (self.dma.length*pack_factor-1)), + source.length.eq(self.dma.length*pack_factor+4), + source.d.eq(unpack.source.d), + source.dst.eq(tag), + unpack.source.ack.eq(source.ack) + ] + + # IRQ + self.submodules.ev = EventManager() + self.ev.done = EventSourcePulse() + self.ev.finalize() + self.comb += self.ev.done.trigger.eq(source.stb & source.eop) + +class FtdiDMA(Module, AutoCSR): + def __init__(self, lasmim_ftdi_dma_wr, lasmim_ftdi_dma_rd, tag): + self.tag = tag + + self.submodules.writer = FtdiDMAWriter(lasmim_ftdi_dma_wr) + self.submodules.reader = FtdiDMAReader(lasmim_ftdi_dma_rd, self.tag) + self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev) + + self.sink = self.writer.sink + self.source = self.reader.source diff --git a/liteusb/frontend/uart.py b/liteusb/frontend/uart.py new file mode 100644 index 000000000..64833f4ce --- /dev/null +++ b/liteusb/frontend/uart.py @@ -0,0 +1,57 @@ +from migen.fhdl.std import * +from migen.bank.description import * +from migen.bank.eventmanager import * +from migen.genlib.fifo import SyncFIFOBuffered + +from liteusb.ftdi.std import * + +class FtdiUART(Module, AutoCSR): + def __init__(self, tag, fifo_depth=64): + self.tag = tag + + self._rxtx = CSR(8) + + self.submodules.ev = EventManager() + self.ev.tx = EventSourcePulse() + self.ev.rx = EventSourceLevel() + self.ev.finalize() + + self.source = source = Source(user_layout) + self.sink = sink = Sink(user_layout) + + ### + + # TX + tx_start = self._rxtx.re + tx_done = self.ev.tx.trigger + + self.sync += \ + If(tx_start, + source.stb.eq(1), + source.d.eq(self._rxtx.r), + ).Elif(tx_done, + source.stb.eq(0) + ) + + self.comb += [ + source.sop.eq(1), + source.eop.eq(1), + source.length.eq(1), + source.dst.eq(self.tag), + tx_done.eq(source.stb & source.ack), + ] + + # RX + rx_available = self.ev.rx.trigger + + rx_fifo = SyncFIFOBuffered(8, fifo_depth) + self.submodules += rx_fifo + self.comb += [ + rx_fifo.we.eq(sink.stb), + sink.ack.eq(sink.stb & rx_fifo.writable), + rx_fifo.din.eq(sink.d), + + rx_available.eq(rx_fifo.readable), + rx_fifo.re.eq(self.ev.rx.clear), + self._rxtx.w.eq(rx_fifo.dout) + ] diff --git a/liteusb/phy/.keep_me b/liteusb/phy/.keep_me deleted file mode 100644 index e69de29bb..000000000 diff --git a/liteusb/phy/ft2232h.py b/liteusb/phy/ft2232h.py new file mode 100644 index 000000000..6f5461b0a --- /dev/null +++ b/liteusb/phy/ft2232h.py @@ -0,0 +1,307 @@ +from migen.fhdl.std import * +from migen.flow.actor import * +from migen.actorlib.fifo import AsyncFIFO +from migen.fhdl.specials import * + +from liteusb.ftdi.std import * + +class FtdiPHY(Module): + def __init__(self, pads, fifo_depth=32, read_time=16, write_time=16): + dw = flen(pads.data) + + # + # Read / Write Fifos + # + + # Read Fifo (Ftdi --> SoC) + read_fifo = RenameClockDomains(AsyncFIFO(phy_layout, fifo_depth), + {"write":"ftdi", "read":"sys"}) + read_buffer = RenameClockDomains(SyncFIFO(phy_layout, 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":"sys", "read":"ftdi"}) + + 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), + ] + + 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) + + data_w_accepted = Signal(reset=1) + + fsm = FSM() + self.submodules += RenameClockDomains(fsm, {"sys": "ftdi"}) + + 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") + ) + + # + # Read / Write Actions + # + + data_w = Signal(dw) + data_r = Signal(dw) + data_oe = Signal() + + pads.oe_n.reset = 1 + pads.rd_n.reset = 1 + pads.wr_n.reset = 1 + + self.sync.ftdi += [ + 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.d.eq(data_r), + If(~txe_n & data_w_accepted, + data_w.eq(write_fifo.source.d) + ) + ] + + # + # Databus Tristate + # + self.specials += Tristate(pads.data, data_w, data_oe, data_r) + + self.debug = Signal(8) + self.comb += self.debug.eq(data_r) + + +# +# TB +# +class FtdiModel(Module, RandRun): + def __init__(self, rd_data): + RandRun.__init__(self, 50) + 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 + + def wr_sim(self, selfp): + if not selfp.wr_n and not selfp.txe_n: + self.wr_data.append(selfp.data) + 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 = 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) + +class UserModel(Module, RandRun): + def __init__(self, wr_data): + RandRun.__init__(self, 50) + self.wr_data = wr_data + self.wr_data_idx = 0 + + self.sink = Sink(phy_layout) + self.source = Source(phy_layout) + + self.rd_data = [] + + def wr_sim(self, selfp): + auth = True + if selfp.source.stb and not selfp.source.ack: + auth = False + if auth: + if self.wr_data_idx < len(self.wr_data): + if self.run: + selfp.source.d = self.wr_data[self.wr_data_idx] + selfp.source.stb = 1 + self.wr_data_idx += 1 + else: + selfp.source.stb = 0 + else: + self.source.stb = 0 + + def rd_sim(self, selfp): + if self.run: + selfp.sink.ack = 1 + else: + selfp.sink.ack = 0 + if selfp.sink.stb & selfp.sink.ack: + self.rd_data.append(selfp.sink.d) + + def do_simulation(self, selfp): + RandRun.do_simulation(self, selfp) + self.wr_sim(selfp) + self.rd_sim(selfp) + + +LENGTH = 512 +model_rd_data = [i%256 for i in range(LENGTH)][::-1] +user_wr_data = [i%256 for i in range(LENGTH)] + +class TB(Module): + def __init__(self): + self.submodules.model = FtdiModel(model_rd_data) + self.submodules.phy = FtdiPHY(self.model) + + self.submodules.user = UserModel(user_wr_data) + + self.comb += [ + self.user.source.connect(self.phy.sink), + self.phy.source.connect(self.user.sink) + ] + + # Use sys_clk as ftdi_clk in simulation + self.comb += [ + ClockSignal("ftdi").eq(ClockSignal()), + ResetSignal("ftdi").eq(ResetSignal()) + ] + +def print_results(s, l1, l2): + def comp(l1, l2): + r = True + try: + for i, val in enumerate(l1): + if val != l2[i]: + print(s + " : val : %02X, exp : %02X" %(val, l2[i])) + r = False + except: + return r + return r + + c = comp(l1, l2) + r = s + " " + if c: + r += "[OK]" + else: + r += "[KO]" + print(r) + +def main(): + from migen.sim.generic import run_simulation + tb = TB() + run_simulation(tb, ncycles=8000, vcd_name="tb_phy.vcd") + + ### + #print(tb.user.rd_data) + #print(tb.model.wr_data) + #print(len(tb.user.rd_data)) + #print(len(tb.model.wr_data)) + + print_results("FtdiModel --> UserModel", model_rd_data, tb.user.rd_data) + print_results("UserModel --> FtdiModel", user_wr_data, tb.model.wr_data) + +if __name__ == "__main__": + main() From 7837580020625023e73c3105d7971cfabe89a6bd Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sat, 21 Feb 2015 23:18:10 +0100 Subject: [PATCH 4/5] add ft2232h software code (will need rework) --- software/ftdi/ft2232h/libftdicom/Makefile | 15 + software/ftdi/ft2232h/libftdicom/crc.h | 7 + software/ftdi/ft2232h/libftdicom/crc32.c | 81 +++ software/ftdi/ft2232h/libftdicom/fastftdi.c | 554 ++++++++++++++++++++ software/ftdi/ft2232h/libftdicom/fastftdi.h | 135 +++++ software/ftdi/ft2232h/libftdicom/ftdicom.c | 257 +++++++++ software/ftdi/ft2232h/libftdicom/ftdicom.h | 104 ++++ 7 files changed, 1153 insertions(+) create mode 100644 software/ftdi/ft2232h/libftdicom/Makefile create mode 100644 software/ftdi/ft2232h/libftdicom/crc.h create mode 100644 software/ftdi/ft2232h/libftdicom/crc32.c create mode 100644 software/ftdi/ft2232h/libftdicom/fastftdi.c create mode 100644 software/ftdi/ft2232h/libftdicom/fastftdi.h create mode 100644 software/ftdi/ft2232h/libftdicom/ftdicom.c create mode 100644 software/ftdi/ft2232h/libftdicom/ftdicom.h diff --git a/software/ftdi/ft2232h/libftdicom/Makefile b/software/ftdi/ft2232h/libftdicom/Makefile new file mode 100644 index 000000000..65755af51 --- /dev/null +++ b/software/ftdi/ft2232h/libftdicom/Makefile @@ -0,0 +1,15 @@ +CC=gcc +CFLAGS =-Wall -O0 +CFLAGS_DLL =-Wall -O0 -g -shared -Wl,--out-implib,libftdicom.a +INC=-I. -I../libusb +LIBS_PATHS= -L. -L../libusb +LIBS_DLL= -lusb-1.0 -lpthreadGC2 + +all: libftdicom.dll + +libftdicom.dll: crc32.c fastftdi.c ftdicom.c + $(CC) $(INC) -o $@ $(CFLAGS_DLL) $^ $(LIBS_PATHS) $(LIBS_DLL) + +clean: + rm libftdicom.a + rm libftdicom.dll \ No newline at end of file diff --git a/software/ftdi/ft2232h/libftdicom/crc.h b/software/ftdi/ft2232h/libftdicom/crc.h new file mode 100644 index 000000000..61cc036bd --- /dev/null +++ b/software/ftdi/ft2232h/libftdicom/crc.h @@ -0,0 +1,7 @@ +#ifndef __CRC_H +#define __CRC_H + +unsigned short crc16(const unsigned char *buffer, int len); +unsigned int crc32(const unsigned char *buffer, unsigned int len); + +#endif diff --git a/software/ftdi/ft2232h/libftdicom/crc32.c b/software/ftdi/ft2232h/libftdicom/crc32.c new file mode 100644 index 000000000..29b9b9944 --- /dev/null +++ b/software/ftdi/ft2232h/libftdicom/crc32.c @@ -0,0 +1,81 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include + +static const unsigned int crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); + +unsigned int crc32(const unsigned char *buffer, unsigned int len) +{ + unsigned int crc; + crc = 0; + crc = crc ^ 0xffffffffL; + while(len >= 8) { + DO8(buffer); + len -= 8; + } + if(len) do { + DO1(buffer); + } while(--len); + return crc ^ 0xffffffffL; +} diff --git a/software/ftdi/ft2232h/libftdicom/fastftdi.c b/software/ftdi/ft2232h/libftdicom/fastftdi.c new file mode 100644 index 000000000..e2797c758 --- /dev/null +++ b/software/ftdi/ft2232h/libftdicom/fastftdi.c @@ -0,0 +1,554 @@ +/* + * fastftdi.c - A minimal FTDI FT2232H interface for which supports bit-bang + * mode, but focuses on very high-performance support for + * synchronous FIFO mode. Requires libusb-1.0 + * + * Copyright (C) 2009 Micah Elizabeth Scott + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "fastftdi.h" + +#if defined _WIN32 || defined _WIN64 + #include + #include + int gettimeofday (struct timeval *tp, void *tz) + { + struct _timeb timebuffer; + _ftime (&timebuffer); + tp->tv_sec = timebuffer.time; + tp->tv_usec = timebuffer.millitm * 1000; + return 0; + } +#endif + +typedef struct { + FTDIStreamCallback *callback; + void *userdata; + int result; + FTDIProgressInfo progress; +} FTDIStreamState; + +static int +DeviceInit(FTDIDevice *dev) +{ + int err, interface; + + for (interface = 0; interface < 2; interface++) { + if (libusb_kernel_driver_active(dev->handle, interface) == 1) { + if ((err = libusb_detach_kernel_driver(dev->handle, interface))) { + perror("Error detaching kernel driver"); + return err; + } + } + } + + if ((err = libusb_set_configuration(dev->handle, 1))) { + perror("Error setting configuration"); + return err; + } + + for (interface = 0; interface < 2; interface++) { + if ((err = libusb_claim_interface(dev->handle, interface))) { + perror("Error claiming interface"); + return err; + } + } + + return 0; +} + + +int +FTDIDevice_Open(FTDIDevice *dev) +{ + int err; + + memset(dev, 0, sizeof *dev); + + if ((err = libusb_init(&dev->libusb))) { + return err; + } + + libusb_set_debug(dev->libusb, 0); + + dev->handle = libusb_open_device_with_vid_pid(dev->libusb, + LITEUSB_VENDOR, + LITEUSB_PRODUCT); + + if (!dev->handle) { + dev->handle = libusb_open_device_with_vid_pid(dev->libusb, + FTDI_VENDOR, + FTDI_PRODUCT_FT2232H); + } + + if (!dev->handle) { + return LIBUSB_ERROR_NO_DEVICE; + } + + return DeviceInit(dev); +} + + +void +FTDIDevice_Close(FTDIDevice *dev) +{ + libusb_close(dev->handle); + libusb_exit(dev->libusb); +} + + +int +FTDIDevice_Reset(FTDIDevice *dev) +{ + int err; + + err = libusb_reset_device(dev->handle); + if (err) + return err; + + return DeviceInit(dev); +} + + +int +FTDIDevice_SetMode(FTDIDevice *dev, FTDIInterface interface, + FTDIBitmode mode, uint8_t pinDirections, + int baudRate) +{ + int err; + + err = libusb_control_transfer(dev->handle, + LIBUSB_REQUEST_TYPE_VENDOR + | LIBUSB_RECIPIENT_DEVICE + | LIBUSB_ENDPOINT_OUT, + FTDI_SET_BITMODE_REQUEST, + pinDirections | (mode << 8), + interface, + NULL, 0, + FTDI_COMMAND_TIMEOUT); + if (err) + return err; + + if (baudRate) { + int divisor; + + if (mode == FTDI_BITMODE_BITBANG) + baudRate <<= 2; + + divisor = 240000000 / baudRate; + if (divisor < 1 || divisor > 0xFFFF) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + err = libusb_control_transfer(dev->handle, + LIBUSB_REQUEST_TYPE_VENDOR + | LIBUSB_RECIPIENT_DEVICE + | LIBUSB_ENDPOINT_OUT, + FTDI_SET_BAUD_REQUEST, + divisor, + interface, + NULL, 0, + FTDI_COMMAND_TIMEOUT); + if (err) + return err; + } + + return err; +} + + +/* + * Internal callback for cleaning up async writes. + */ + +static void +WriteAsyncCallback(struct libusb_transfer *transfer) +{ + free(transfer->buffer); + libusb_free_transfer(transfer); +} + + +/* + * Write to an FTDI interface, either synchronously or asynchronously. + * Async writes have no completion callback, they finish 'eventually'. + */ + +int +FTDIDevice_Write(FTDIDevice *dev, FTDIInterface interface, + uint8_t *data, size_t length, bool async) +{ + int err; + + if (async) { + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + + if (!transfer) { + return LIBUSB_ERROR_NO_MEM; + } + + libusb_fill_bulk_transfer(transfer, dev->handle, FTDI_EP_OUT(interface), + malloc(length), length, (libusb_transfer_cb_fn) WriteAsyncCallback, 0, 0); + + if (!transfer->buffer) { + libusb_free_transfer(transfer); + return LIBUSB_ERROR_NO_MEM; + } + + memcpy(transfer->buffer, data, length); + err = libusb_submit_transfer(transfer); + + } else { + int transferred; + err = libusb_bulk_transfer(dev->handle, FTDI_EP_OUT(interface), + data, length, &transferred, + FTDI_COMMAND_TIMEOUT); + } + + if (err < 0) + return err; + else + return 0; +} + + +int +FTDIDevice_WriteByteSync(FTDIDevice *dev, FTDIInterface interface, uint8_t byte) +{ + return FTDIDevice_Write(dev, interface, &byte, sizeof byte, false); +} + + +int +FTDIDevice_ReadByteSync(FTDIDevice *dev, FTDIInterface interface, uint8_t *byte) +{ + /* + * This is a simplified synchronous read, intended for bit-banging mode. + * Ignores the modem/buffer status bytes, returns just the data. + * + */ + + uint8_t packet[3]; + int transferred, err; + + err = libusb_bulk_transfer(dev->handle, FTDI_EP_IN(interface), + packet, sizeof packet, &transferred, + FTDI_COMMAND_TIMEOUT); + if (err < 0) { + return err; + } + if (transferred != sizeof packet) { + return -1; + } + + if (byte) { + *byte = packet[sizeof packet - 1]; + } + + return 0; +} + + +/* + * Internal callback for one transfer's worth of stream data. + * Split it into packets and invoke the callbacks. + */ + +static void +ReadStreamCallback(struct libusb_transfer *transfer) +{ + FTDIStreamState *state = transfer->user_data; + + if (state->result == 0) { + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + + int i; + uint8_t *ptr = transfer->buffer; + int length = transfer->actual_length; + int numPackets = (length + FTDI_PACKET_SIZE - 1) >> FTDI_LOG_PACKET_SIZE; + + for (i = 0; i < numPackets; i++) { + int payloadLen; + int packetLen = length; + + if (packetLen > FTDI_PACKET_SIZE) + packetLen = FTDI_PACKET_SIZE; + + payloadLen = packetLen - FTDI_HEADER_SIZE; + state->progress.current.totalBytes += payloadLen; + + state->result = state->callback(ptr + FTDI_HEADER_SIZE, payloadLen, + NULL, state->userdata); + if (state->result) + break; + + ptr += packetLen; + length -= packetLen; + } + + } else { + state->result = LIBUSB_ERROR_IO; + } + } + + if (state->result == 0) { + transfer->status = -1; + state->result = libusb_submit_transfer(transfer); + } +} + + +static double +TimevalDiff(const struct timeval *a, const struct timeval *b) +{ + return (a->tv_sec - b->tv_sec) + 1e-6 * (a->tv_usec - b->tv_usec); +} + + +/* + * Use asynchronous transfers in libusb-1.0 for high-performance + * streaming of data from a device interface back to the PC. This + * function continuously transfers data until either an error occurs + * or the callback returns a nonzero value. This function returns + * a libusb error code or the callback's return value. + * + * For every contiguous block of received data, the callback will + * be invoked. + */ + +int +FTDIDevice_ReadStream(FTDIDevice *dev, FTDIInterface interface, + FTDIStreamCallback *callback, void *userdata, + int packetsPerTransfer, int numTransfers) +{ + struct libusb_transfer **transfers; + FTDIStreamState state = { callback, userdata }; + int bufferSize = packetsPerTransfer * FTDI_PACKET_SIZE; + int xferIndex; + int err = 0; + + /* + * Set up all transfers + */ + + transfers = calloc(numTransfers, sizeof *transfers); + if (!transfers) { + err = LIBUSB_ERROR_NO_MEM; + goto cleanup; + } + + for (xferIndex = 0; xferIndex < numTransfers; xferIndex++) { + struct libusb_transfer *transfer; + + transfer = libusb_alloc_transfer(0); + transfers[xferIndex] = transfer; + if (!transfer) { + err = LIBUSB_ERROR_NO_MEM; + goto cleanup; + } + + libusb_fill_bulk_transfer(transfer, dev->handle, FTDI_EP_IN(interface), + malloc(bufferSize), bufferSize, (libusb_transfer_cb_fn) ReadStreamCallback, + &state, 0); + + if (!transfer->buffer) { + err = LIBUSB_ERROR_NO_MEM; + goto cleanup; + } + + transfer->status = -1; + err = libusb_submit_transfer(transfer); + if (err) + goto cleanup; + } + + /* + * Run the transfers, and periodically assess progress. + */ + + gettimeofday(&state.progress.first.time, NULL); + + do { + FTDIProgressInfo *progress = &state.progress; + const double progressInterval = 0.1; + struct timeval timeout = { 0, 10000 }; + struct timeval now; + + int err = libusb_handle_events_timeout(dev->libusb, &timeout); + if (!state.result) { + state.result = err; + } + + // If enough time has elapsed, update the progress + gettimeofday(&now, NULL); + if (TimevalDiff(&now, &progress->current.time) >= progressInterval) { + + progress->current.time = now; + + if (progress->prev.totalBytes) { + // We have enough information to calculate rates + + double currentTime; + + progress->totalTime = TimevalDiff(&progress->current.time, + &progress->first.time); + currentTime = TimevalDiff(&progress->current.time, + &progress->prev.time); + + progress->totalRate = progress->current.totalBytes / progress->totalTime; + progress->currentRate = (progress->current.totalBytes - + progress->prev.totalBytes) / currentTime; + } + + state.result = state.callback(NULL, 0, progress, state.userdata); + progress->prev = progress->current; + } + } while (!state.result); + + /* + * Cancel any outstanding transfers, and free memory. + */ + + cleanup: + if (transfers) { + bool done_cleanup = false; + while (!done_cleanup) + { + done_cleanup = true; + + for (xferIndex = 0; xferIndex < numTransfers; xferIndex++) { + struct libusb_transfer *transfer = transfers[xferIndex]; + + if (transfer) { + // If a transfer is in progress, cancel it + if (transfer->status == -1) { + libusb_cancel_transfer(transfer); + + // And we need to wait until we get a clean sweep + done_cleanup = false; + + // If a transfer is complete or cancelled, nuke it + } else if (transfer->status == 0 || + transfer->status == LIBUSB_TRANSFER_CANCELLED) { + free(transfer->buffer); + libusb_free_transfer(transfer); + transfers[xferIndex] = NULL; + } + } + } + + // pump events + struct timeval timeout = { 0, 10000 }; + libusb_handle_events_timeout(dev->libusb, &timeout); + } + free(transfers); + } + + if (err) + return err; + else + return state.result; +} + +/* MPSSE mode support -- see + * http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf + */ + +int +FTDIDevice_MPSSE_Enable(FTDIDevice *dev, FTDIInterface interface) +{ + int err; + + /* Reset interface */ + + err = FTDIDevice_SetMode(dev, interface, FTDI_BITMODE_RESET, 0, 0); + if (err) + return err; + + /* Enable MPSSE mode */ + + err = FTDIDevice_SetMode(dev, interface, FTDI_BITMODE_MPSSE, + FTDI_SET_BITMODE_REQUEST, 0); + + return err; +} + +int +FTDIDevice_MPSSE_SetDivisor(FTDIDevice *dev, FTDIInterface interface, + uint8_t ValueL, uint8_t ValueH) +{ + uint8_t buf[3] = {FTDI_MPSSE_SETDIVISOR, 0, 0}; + + buf[1] = ValueL; + buf[2] = ValueH; + + return FTDIDevice_Write(dev, interface, buf, 3, false); +} + +int +FTDIDevice_MPSSE_SetLowByte(FTDIDevice *dev, FTDIInterface interface, uint8_t data, uint8_t dir) +{ + uint8_t buf[3] = {FTDI_MPSSE_SETLOW, 0, 0}; + + buf[1] = data; + buf[2] = dir; + + return FTDIDevice_Write(dev, interface, buf, 3, false); +} + +int +FTDIDevice_MPSSE_SetHighByte(FTDIDevice *dev, FTDIInterface interface, uint8_t data, uint8_t dir) +{ + uint8_t buf[3] = {FTDI_MPSSE_SETHIGH, 0, 0}; + + buf[1] = data; + buf[2] = dir; + + return FTDIDevice_Write(dev, interface, buf, 3, false); +} + +int +FTDIDevice_MPSSE_GetLowByte(FTDIDevice *dev, FTDIInterface interface, uint8_t *byte) +{ + int err; + + err = FTDIDevice_WriteByteSync(dev, interface, FTDI_MPSSE_GETLOW); + if (err) + return err; + + return FTDIDevice_ReadByteSync(dev, interface, byte); +} + +int +FTDIDevice_MPSSE_GetHighByte(FTDIDevice *dev, FTDIInterface interface, uint8_t *byte) +{ + int err; + + err = FTDIDevice_WriteByteSync(dev, interface, FTDI_MPSSE_GETHIGH); + if (err) + return err; + + return FTDIDevice_ReadByteSync(dev, interface, byte); +} diff --git a/software/ftdi/ft2232h/libftdicom/fastftdi.h b/software/ftdi/ft2232h/libftdicom/fastftdi.h new file mode 100644 index 000000000..c7166dec8 --- /dev/null +++ b/software/ftdi/ft2232h/libftdicom/fastftdi.h @@ -0,0 +1,135 @@ +/* + * fastftdi.h - A minimal FTDI FT232H interface for Linux which supports + * bit-bang mode, but focuses on very high-performance support + * for synchronous FIFO mode. + * + * Copyright (C) 2009 Micah Elizabeth Scott + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __FASTFTDI_H +#define __FASTFTDI_H + +#include +#include +#include + +typedef enum { + FTDI_BITMODE_RESET = 0, + FTDI_BITMODE_BITBANG = 1 << 0, + FTDI_BITMODE_MPSSE = 1 << 1, + FTDI_BITMODE_SYNC_BITBANG = 1 << 2, + FTDI_BITMODE_MCU = 1 << 3, + FTDI_BITMODE_OPTO = 1 << 4, + FTDI_BITMODE_CBUS = 1 << 5, + FTDI_BITMODE_SYNC_FIFO = 1 << 6, +} FTDIBitmode; + +typedef enum { + FTDI_MPSSE_SETLOW = 0x80, + FTDI_MPSSE_SETHIGH = 0x82, + FTDI_MPSSE_GETLOW = 0x81, + FTDI_MPSSE_GETHIGH = 0x83, + FTDI_MPSSE_SETDIVISOR = 0x86, +} FTDIMPSSEOpcode; + +typedef enum { + FTDI_INTERFACE_A = 1, + FTDI_INTERFACE_B = 2, +} FTDIInterface; + +typedef struct { + libusb_context *libusb; + libusb_device_handle *handle; +} FTDIDevice; + +typedef struct { + struct { + uint64_t totalBytes; + struct timeval time; + } first, prev, current; + + double totalTime; + double totalRate; + double currentRate; +} FTDIProgressInfo; + + +/* + * USB Constants + */ + +#define FTDI_VENDOR 0x0403 +#define FTDI_PRODUCT_FT2232H 0x6010 + +#define LITEUSB_VENDOR 0x1d50 +#define LITEUSB_PRODUCT 0x607c + +#define FTDI_COMMAND_TIMEOUT 1000 + +#define FTDI_SET_BAUD_REQUEST 0x03 +#define FTDI_SET_BITMODE_REQUEST 0x0B + +#define FTDI_EP_IN(i) (0x81 + (i-1)*2) +#define FTDI_EP_OUT(i) (0x02 + (i-1)*2) + +#define FTDI_PACKET_SIZE 512 // Specific to FT2232H +#define FTDI_LOG_PACKET_SIZE 9 // 512 == 1 << 9 +#define FTDI_HEADER_SIZE 2 + +typedef int (FTDIStreamCallback)(uint8_t *buffer, int length, + FTDIProgressInfo *progress, void *userdata); + + +/* + * Public Functions + */ + +int FTDIDevice_Open(FTDIDevice *dev); +void FTDIDevice_Close(FTDIDevice *dev); +int FTDIDevice_Reset(FTDIDevice *dev); + +int FTDIDevice_SetMode(FTDIDevice *dev, FTDIInterface interface, + FTDIBitmode mode, uint8_t pinDirections, + int baudRate); + +int FTDIDevice_Write(FTDIDevice *dev, FTDIInterface interface, + uint8_t *data, size_t length, bool async); + +int FTDIDevice_WriteByteSync(FTDIDevice *dev, FTDIInterface interface, uint8_t byte); +int FTDIDevice_ReadByteSync(FTDIDevice *dev, FTDIInterface interface, uint8_t *byte); + +int FTDIDevice_ReadStream(FTDIDevice *dev, FTDIInterface interface, + FTDIStreamCallback *callback, void *userdata, + int packetsPerTransfer, int numTransfers); + +int FTDIDevice_MPSSE_Enable(FTDIDevice *dev, FTDIInterface interface); +int FTDIDevice_MPSSE_SetDivisor(FTDIDevice *dev, FTDIInterface interface, + uint8_t ValueL, uint8_t ValueH); + +int FTDIDevice_MPSSE_SetLowByte(FTDIDevice *dev, FTDIInterface interface, + uint8_t data, uint8_t dir); +int FTDIDevice_MPSSE_SetHighByte(FTDIDevice *dev, FTDIInterface interface, + uint8_t data, uint8_t dir); + +int FTDIDevice_MPSSE_GetLowByte(FTDIDevice *dev, FTDIInterface interface, uint8_t *byte); +int FTDIDevice_MPSSE_GetHighByte(FTDIDevice *dev, FTDIInterface interface, uint8_t *byte); + +#endif /* __FASTFTDI_H */ diff --git a/software/ftdi/ft2232h/libftdicom/ftdicom.c b/software/ftdi/ft2232h/libftdicom/ftdicom.c new file mode 100644 index 000000000..19bacf0d4 --- /dev/null +++ b/software/ftdi/ft2232h/libftdicom/ftdicom.c @@ -0,0 +1,257 @@ +/* + * ftdicom.c - Low Level USB communication interface + * + * Provides UART and DMA low level communication + * functions for FT2232H in slave fifo mode. + * + * Copyright (C) 2014 florent@enjoy-digital.fr + * + */ + +#include +#include +#include +#include +#include "ftdicom.h" +#include "crc.h" + +/* + * Open / close functions + */ +int ftdicom_open(FTDICom *com) +{ + int err = 0; + err = FTDIDevice_Open(com->dev); + if (err) + return err; + + com->raw_tx_buf = malloc(RAW_BUFFER_SIZE); + com->raw_rx_buf = malloc(RAW_BUFFER_SIZE); + com->uart_rx_buf = malloc(UART_RINGBUFFER_SIZE_RX); + + err = FTDIDevice_SetMode(com->dev, FTDI_INTERFACE_A, FTDI_BITMODE_SYNC_FIFO, 0xFF, 0); + if (err) + return err; + + com->raw_rx_buf_length = 0; + + pthread_t thread; + com->thread = &thread; + pthread_create(com->thread, NULL, ftdicom_read_thread, com); + + return 0; +} + +void ftdicom_close(FTDICom *com) +{ + free(com->raw_tx_buf); + free(com->raw_rx_buf); + free(com->uart_rx_buf); + FTDIDevice_Close(com->dev); + free(com->thread); +} + +/* + * Write (Tx) functions + */ +int ftdicom_write(FTDICom *com, uint8_t tag, uint8_t *data, size_t length, uint8_t with_crc) +{ + unsigned int computed_crc; + + com->raw_tx_buf[0] = 0x5A; + com->raw_tx_buf[1] = 0xA5; + com->raw_tx_buf[2] = 0x5A; + com->raw_tx_buf[3] = 0xA5; + com->raw_tx_buf[4] = tag; + if (with_crc) + length += 4; + com->raw_tx_buf[5] = (length >> 24) & 0xff; + com->raw_tx_buf[6] = (length >> 16) & 0xff; + com->raw_tx_buf[7] = (length >> 8) & 0xff; + com->raw_tx_buf[8] = (length >> 0) & 0xff; + + memcpy(com->raw_tx_buf+9, data, length); + if (with_crc) { + computed_crc = crc32(data, length-4); + com->raw_tx_buf[9+length-1] = (computed_crc >> 24) & 0xff; + com->raw_tx_buf[9+length-2] = (computed_crc >> 16) & 0xff; + com->raw_tx_buf[9+length-3] = (computed_crc >> 8) & 0xff; + com->raw_tx_buf[9+length-4] = (computed_crc >> 0) & 0xff; + } + return FTDIDevice_Write(com->dev, FTDI_INTERFACE_A, com->raw_tx_buf, 9+length, false); +} + +/* + * Read (Rx) common functions + */ + +int ftdicom_present_bytes(uint8_t tag, uint8_t *buffer, int length) +{ + if (length < NEEDED_FOR_SIZE) + return INCOMPLETE; + + if (buffer[0] != 0x5A || + buffer[1] != 0xA5 || + buffer[2] != 0x5A || + buffer[3] != 0xA5 || + buffer[4] != tag) + return UNMATCHED; + + int size = NEEDED_FOR_SIZE; + size += buffer[5] << 24; + size += buffer[6] << 16; + size += buffer[7] << 8; + size += buffer[8]; + + if (length < size) + return INCOMPLETE; + + return size; +} + +int ftdicom_uart_present_bytes(uint8_t *buffer, int length) +{ + return ftdicom_present_bytes(UART_TAG, buffer, length); +} + +int ftdicom_dma_present_bytes(uint8_t *buffer, int length) +{ + return ftdicom_present_bytes(DMA_TAG, buffer, length); +} + +int ftdicom_read_callback(uint8_t *buffer, int length, FTDIProgressInfo *progress, void *userdata) +{ + FTDICom *com = (FTDICom *) userdata; + + // Concatenate buffer & raw_rx_buf + memcpy(com->raw_rx_buf + com->raw_rx_buf_length, buffer, length); + com->raw_rx_buf_length += length; + + int code = 0; + int incomplete = 0; + int i = 0; + + // Search frames in raw_rx_buf + while (i != com->raw_rx_buf_length && !incomplete) + { + code = 0; + + // UART + code = ftdicom_uart_present_bytes(com->raw_rx_buf + i, com->raw_rx_buf_length-i); + if (code == INCOMPLETE) + { + incomplete = 1; + break; + } else if (code) + { + ftdicom_uart_read_callback(com, com->raw_rx_buf + i + NEEDED_FOR_SIZE, code-NEEDED_FOR_SIZE); + i += code-1; + } + + // DMA + code = ftdicom_dma_present_bytes(com->raw_rx_buf + i, com->raw_rx_buf_length-i); + if (code == INCOMPLETE) + { + incomplete = 1; + break; + } else if (code) + { + ftdicom_dma_read_callback(com, com->raw_rx_buf + i + NEEDED_FOR_SIZE, code-NEEDED_FOR_SIZE); + i += code; + } + + // Nothing found, increment index + if (code == UNMATCHED) + i=i+1; + + } + + // Prepare raw_rx_buf for next callback + if (incomplete == 1) + { + com->raw_rx_buf_length = com->raw_rx_buf_length - i; + memcpy(com->raw_rx_buf, com->raw_rx_buf + i, com->raw_rx_buf_length); + } else { + com->raw_rx_buf_length = 0; + } + + return 0; +} + +void *ftdicom_read_thread(void *userdata) +{ + FTDICom *com = (FTDICom *) userdata; + FTDIDevice_ReadStream(com->dev, FTDI_INTERFACE_A, ftdicom_read_callback, com, 8, 16); + return 0; +} + +/* + * UART functions + */ + +int ftdicom_uart_write_buffer(FTDICom *com, uint8_t *data, size_t length) +{ + return ftdicom_write(com, UART_TAG, data, length, 0); +} + +int ftdicom_uart_write(FTDICom *com, uint8_t c) +{ + return ftdicom_write(com, UART_TAG, &c, 1, 0); +} + +void ftdicom_uart_read_callback(FTDICom *com, uint8_t *buffer, int length) +{ + while (length > 0) { + com->uart_rx_buf[com->uart_rx_produce] = buffer[0]; + com->uart_rx_produce = (com->uart_rx_produce + 1) & UART_RINGBUFFER_MASK_RX; + length -=1; + buffer +=1; + } +} + +uint8_t ftdicom_uart_read(FTDICom *com) +{ + uint8_t c; + + while(com->uart_rx_consume == com->uart_rx_produce); + c = com->uart_rx_buf[com->uart_rx_consume]; + com->uart_rx_consume = (com->uart_rx_consume + 1) & UART_RINGBUFFER_MASK_RX; + return c; +} + +int ftdicom_uart_read_nonblock(FTDICom *com) +{ + return (com->uart_rx_consume != com->uart_rx_produce); +} + +/* + * DMA functions + */ +int ftdicom_dma_write(FTDICom *com, uint8_t *data, size_t length) +{ + return ftdicom_write(com, DMA_TAG, data, length, 1); +} + +void ftdicom_dma_read_set_callback(FTDICom *com, dma_read_ext_callback_t callback, void *userdata) +{ + com->dma_read_ext_callback = callback; + com->userdata = userdata; +} + +int ftdicom_dma_read_callback(FTDICom *com, uint8_t *buffer, int length) +{ + unsigned int received_crc; + unsigned int computed_crc; + + received_crc = ((unsigned int)buffer[length-1] << 24) + |((unsigned int)buffer[length-2] << 16) + |((unsigned int)buffer[length-3] << 8) + |((unsigned int)buffer[length-4]); + computed_crc = crc32(buffer, length-4); + if(received_crc != computed_crc) return -1; + + if (com->dma_read_ext_callback != NULL) + return com->dma_read_ext_callback(buffer, length-4, com->userdata); + else + return -1; +} \ No newline at end of file diff --git a/software/ftdi/ft2232h/libftdicom/ftdicom.h b/software/ftdi/ft2232h/libftdicom/ftdicom.h new file mode 100644 index 000000000..67ccd45da --- /dev/null +++ b/software/ftdi/ft2232h/libftdicom/ftdicom.h @@ -0,0 +1,104 @@ +/* + * ftdicom.c - Low Level USB communication interface + * + * Provides UART and DMA low level communication + * functions for FT2232H in slave fifo mode. + * + * Copyright (C) 2014 florent@enjoy-digital.fr + * + */ + +#ifndef __FTDICOM_H +#define __FTDICOM_H + +#include +#include +#include + +#include "fastftdi.h" + +/* + * Protocol Constants + */ + +#define UART_TAG 0 +#define DMA_TAG 1 + +#define NEEDED_FOR_SIZE 9 +#define PAYLOAD_OFFSET 10 + +#define INCOMPLETE -1 +#define UNMATCHED 0 + + +/* + * Buffer Constants + * + * Buffer sizes must be a power of 2 so that modulos can be computed + * with logical AND. + */ + +// RAW +#define RAW_BUFFER_SIZE 20*1024*1024 + +// UART +#define UART_RINGBUFFER_SIZE_RX 4096 +#define UART_RINGBUFFER_MASK_RX (UART_RINGBUFFER_SIZE_RX-1) + +// DMA +#define DMA_BUFFER_SIZE_TX 20*1024*1024 +#define DMA_BUFFER_SIZE_RX 20*1024*1024 + + +/* + * Struct + */ +typedef int (*dma_read_ext_callback_t)(uint8_t *buffer, int length, void *userdata); +typedef struct { + FTDIDevice *dev; + + uint8_t *raw_tx_buf; + uint8_t *raw_rx_buf; + unsigned int raw_rx_buf_length; + + char *uart_rx_buf; + volatile unsigned int uart_rx_produce; + volatile unsigned int uart_rx_consume; + + pthread_t *thread; + dma_read_ext_callback_t dma_read_ext_callback; + + void *userdata; +} FTDICom; + +/* + * Public Functions + */ + +int ftdicom_open(FTDICom *com); +void ftdicom_close(FTDICom *com); + +int ftdicom_uart_write_buffer(FTDICom *com, uint8_t *data, size_t length); +int ftdicom_uart_write(FTDICom *com, uint8_t c); +uint8_t ftdicom_uart_read(FTDICom *com); +int ftdicom_uart_read_nonblock(FTDICom *com); + +int ftdicom_dma_write(FTDICom *com, uint8_t *data, size_t length); +void ftdicom_dma_read_set_callback(FTDICom *com, dma_read_ext_callback_t callback, void *userdata); + +/* + * Private Functions + */ +int ftdicom_write(FTDICom *com, uint8_t tag, uint8_t *data, size_t length, uint8_t with_crc); + +int ftdicom_present_bytes(uint8_t tag, uint8_t *buffer, int length); +int ftdicom_uart_present_bytes(uint8_t *buffer, int length); +int ftdicom_dma_present_bytes(uint8_t *buffer, int length); +int ftdicom_read_callback(uint8_t *buffer, int length, FTDIProgressInfo *progress, void *userdata); +void *ftdicom_read_thread(void *userdata); + +void ftdicom_uart_read_callback(FTDICom *com, uint8_t *buffer, int length); +int ftdicom_dma_read_callback(FTDICom *com, uint8_t *buffer, int length); + + +#endif /* __FTDICOM_H */ From a2370388fb60de6f1d1f88b8069bbafa46c92c2a Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sat, 21 Feb 2015 23:34:30 +0100 Subject: [PATCH 5/5] doc: remove IP --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index f5f69acf6..0f5bd8f19 100644 --- a/README +++ b/README @@ -18,7 +18,7 @@ XXX LiteUSB provides a small footprint and configurable USB core. LiteUSB is part of LiteX libraries whose aims are to lower entry level of -complex FPGA IP cores by providing simple, elegant and efficient implementations +complex FPGA cores by providing simple, elegant and efficient implementations ofcomponents used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller... The core uses simple and specific streaming buses and will provides in the future