merge liteusb

This commit is contained in:
Florent Kermarrec 2015-02-28 11:16:16 +01:00
commit b647fe5823
23 changed files with 2459 additions and 0 deletions

28
misoclib/com/LICENSE Normal file
View File

@ -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.

91
misoclib/com/README Normal file
View File

@ -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 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

View File

@ -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

View File

@ -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

View File

@ -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)
]

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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)
]

View File

View File

@ -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()

View File

View File

View File

@ -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

View File

@ -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

View File

@ -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 <crc.h>
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;
}

View File

@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "fastftdi.h"
#if defined _WIN32 || defined _WIN64
#include <time.h>
#include <sys/timeb.h>
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);
}

View File

@ -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 <libusb.h>
#include <stdint.h>
#include <stdbool.h>
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 */

View File

@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#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;
}

View File

@ -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 <stdint.h>
#include <stdbool.h>
#include <pthread.h>
#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 */