move liteusb to a separate repo (https://github.com/enjoy-digital/liteusb)
This commit is contained in:
parent
e49a3c20c8
commit
8e8cc8e5a6
|
@ -1,28 +0,0 @@
|
|||
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.
|
|
@ -1,89 +0,0 @@
|
|||
__ _ __ __ _________
|
||||
/ / (_) /____ / / / / __/ _ )
|
||||
/ /__/ / __/ -_) /_/ /\ \/ _ |
|
||||
/____/_/\__/\__/\____/___/____/
|
||||
|
||||
Copyright 2015 / EnjoyDigital / M-Labs Ltd
|
||||
|
||||
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 MiSoC 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 MiSoC library 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 mainly 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
|
|
@ -1,61 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.genlib.fsm import *
|
||||
from migen.actorlib.fifo import *
|
||||
from migen.flow.actor import EndpointDescription
|
||||
from migen.actorlib.packet import *
|
||||
from migen.actorlib.structuring import Pipeline
|
||||
|
||||
|
||||
packet_header_length = 9
|
||||
packet_header_fields = {
|
||||
"preamble": HeaderField(0, 0, 32),
|
||||
"dst": HeaderField(4, 0, 8),
|
||||
"length": HeaderField(5, 0, 32)
|
||||
}
|
||||
packet_header = Header(packet_header_fields,
|
||||
packet_header_length,
|
||||
swap_field_bytes=True)
|
||||
|
||||
|
||||
def phy_description(dw):
|
||||
payload_layout = [("data", dw)]
|
||||
return EndpointDescription(payload_layout, packetized=False)
|
||||
|
||||
|
||||
def packet_description(dw):
|
||||
param_layout = packet_header.get_layout()
|
||||
payload_layout = [
|
||||
("data", dw),
|
||||
("error", dw//8)
|
||||
]
|
||||
return EndpointDescription(payload_layout, param_layout, packetized=True)
|
||||
|
||||
|
||||
def user_description(dw):
|
||||
param_layout = [
|
||||
("dst", 8),
|
||||
("length", 32)
|
||||
]
|
||||
payload_layout = [
|
||||
("data", dw),
|
||||
("error", dw//8)
|
||||
]
|
||||
return EndpointDescription(payload_layout, param_layout, packetized=True)
|
||||
|
||||
|
||||
class LiteUSBMasterPort:
|
||||
def __init__(self, dw):
|
||||
self.source = Source(user_description(dw))
|
||||
self.sink = Sink(user_description(dw))
|
||||
|
||||
|
||||
class LiteUSBSlavePort:
|
||||
def __init__(self, dw, tag):
|
||||
self.sink = Sink(user_description(dw))
|
||||
self.source = Source(user_description(dw))
|
||||
self.tag = tag
|
||||
|
||||
|
||||
class LiteUSBUserPort(LiteUSBSlavePort):
|
||||
def __init__(self, dw, tag):
|
||||
LiteUSBSlavePort.__init__(self, dw, tag)
|
|
@ -1,31 +0,0 @@
|
|||
from misoclib.com.liteusb.common import *
|
||||
from misoclib.com.liteusb.core.packet import LiteUSBPacketizer, LiteUSBDepacketizer
|
||||
from misoclib.com.liteusb.core.crc import LiteUSBCRC32Inserter, LiteUSBCRC32Checker
|
||||
from misoclib.com.liteusb.core.crossbar import LiteUSBCrossbar
|
||||
|
||||
class LiteUSBCore(Module):
|
||||
def __init__(self, phy, clk_freq, with_crc=True):
|
||||
rx_pipeline = [phy]
|
||||
tx_pipeline = [phy]
|
||||
|
||||
# depacketizer / packetizer
|
||||
self.submodules.depacketizer = LiteUSBDepacketizer(clk_freq)
|
||||
self.submodules.packetizer = LiteUSBPacketizer()
|
||||
rx_pipeline += [self.depacketizer]
|
||||
tx_pipeline += [self.packetizer]
|
||||
|
||||
if with_crc:
|
||||
# crc checker / inserter
|
||||
self.submodules.crc_rx = LiteUSBCRC32Checker()
|
||||
self.submodules.crc_tx = LiteUSBCRC32Inserter()
|
||||
rx_pipeline += [self.crc_rx]
|
||||
tx_pipeline += [self.crc_tx]
|
||||
|
||||
# crossbar
|
||||
self.submodules.crossbar = LiteUSBCrossbar()
|
||||
rx_pipeline += [self.crossbar.master]
|
||||
tx_pipeline += [self.crossbar.master]
|
||||
|
||||
# graph
|
||||
self.submodules.rx_pipeline = Pipeline(*rx_pipeline)
|
||||
self.submodules.tx_pipeline = Pipeline(*reversed(tx_pipeline))
|
|
@ -1,291 +0,0 @@
|
|||
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 misoclib.com.liteusb.common 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.data = 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.data[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.data = 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.data.eq(self.data),
|
||||
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.data)
|
||||
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.data.eq(sink.data),
|
||||
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.data, 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.data.eq(crc.value),
|
||||
If(source.ack, NextState("IDLE"))
|
||||
)
|
||||
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
|
||||
|
||||
|
||||
class LiteUSBCRC32Inserter(CRCInserter):
|
||||
def __init__(self):
|
||||
CRCInserter.__init__(self, CRC32, user_description(8))
|
||||
|
||||
|
||||
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.data)
|
||||
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.data.eq(sink.data),
|
||||
If(sink.stb & sink.sop & sink.ack,
|
||||
crc.ce.eq(1),
|
||||
NextState("COPY")
|
||||
)
|
||||
)
|
||||
fsm.act("COPY",
|
||||
crc.data.eq(sink.data),
|
||||
If(sink.stb & sink.ack,
|
||||
crc.ce.eq(1),
|
||||
If(sink.eop,
|
||||
NextState("RESET")
|
||||
)
|
||||
)
|
||||
)
|
||||
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
|
||||
|
||||
|
||||
class LiteUSBCRC32Checker(CRCChecker):
|
||||
def __init__(self):
|
||||
CRCChecker.__init__(self, CRC32, user_description(8))
|
|
@ -1,33 +0,0 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
|
||||
class LiteUSBCrossbar(Module):
|
||||
def __init__(self):
|
||||
self.users = OrderedDict()
|
||||
self.master = LiteUSBMasterPort(8)
|
||||
self.dispatch_param = "dst"
|
||||
|
||||
def get_port(self, dst):
|
||||
port = LiteUSBUserPort(8, dst)
|
||||
if dst in self.users.keys():
|
||||
raise ValueError("Destination {0:#x} already assigned".format(dst))
|
||||
self.users[dst] = port
|
||||
return port
|
||||
|
||||
def do_finalize(self):
|
||||
# TX arbitrate
|
||||
sinks = [port.sink for port in self.users.values()]
|
||||
self.submodules.arbiter = Arbiter(sinks, self.master.source)
|
||||
|
||||
# RX dispatch
|
||||
sources = [port.source for port in self.users.values()]
|
||||
self.submodules.dispatcher = Dispatcher(self.master.sink,
|
||||
sources,
|
||||
one_hot=True)
|
||||
cases = {}
|
||||
cases["default"] = self.dispatcher.sel.eq(0)
|
||||
for i, (k, v) in enumerate(self.users.items()):
|
||||
cases[k] = self.dispatcher.sel.eq(2**i)
|
||||
self.comb += \
|
||||
Case(getattr(self.master.sink, self.dispatch_param), cases)
|
|
@ -1,158 +0,0 @@
|
|||
from misoclib.com.liteusb.common import *
|
||||
from migen.actorlib.structuring import Pack, Unpack
|
||||
from migen.genlib.misc import WaitTimer
|
||||
|
||||
class LiteUSBPacketizer(Module):
|
||||
def __init__(self):
|
||||
self.sink = sink = Sink(user_description(8))
|
||||
self.source = source = Source(phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
# 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_description(8))
|
||||
self.submodules += header_unpack
|
||||
|
||||
for i, byte in enumerate(header):
|
||||
chunk = getattr(header_unpack.sink.payload, "chunk" + str(i))
|
||||
self.comb += chunk.data.eq(byte)
|
||||
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("IDLE",
|
||||
If(sink.stb & sink.sop,
|
||||
NextState("INSERT_HEADER")
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("INSERT_HEADER",
|
||||
header_unpack.sink.stb.eq(1),
|
||||
source.stb.eq(1),
|
||||
source.data.eq(header_unpack.source.data),
|
||||
header_unpack.source.ack.eq(source.ack),
|
||||
If(header_unpack.sink.ack,
|
||||
NextState("COPY")
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("COPY",
|
||||
source.stb.eq(sink.stb),
|
||||
source.data.eq(sink.data),
|
||||
sink.ack.eq(source.ack),
|
||||
If(source.ack & sink.eop,
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class LiteUSBDepacketizer(Module):
|
||||
def __init__(self, clk_freq, timeout=10):
|
||||
self.sink = sink = Sink(phy_description(8))
|
||||
self.source = source = Source(user_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
# 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_description(8), 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.data)
|
||||
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += fsm
|
||||
|
||||
self.comb += preamble[0].eq(sink.data)
|
||||
for i in range(1, 4):
|
||||
self.sync += If(sink.stb & sink.ack,
|
||||
preamble[i].eq(preamble[i-1])
|
||||
)
|
||||
fsm.act("IDLE",
|
||||
sink.ack.eq(1),
|
||||
If((preamble[3] == 0x5A) &
|
||||
(preamble[2] == 0xA5) &
|
||||
(preamble[1] == 0x5A) &
|
||||
(preamble[0] == 0xA5) &
|
||||
sink.stb,
|
||||
NextState("RECEIVE_HEADER")
|
||||
),
|
||||
header_pack.source.ack.eq(1),
|
||||
)
|
||||
|
||||
self.submodules.timer = WaitTimer(clk_freq*timeout)
|
||||
self.comb += self.timer.wait.eq(~fsm.ongoing("IDLE"))
|
||||
|
||||
fsm.act("RECEIVE_HEADER",
|
||||
header_pack.sink.stb.eq(sink.stb),
|
||||
header_pack.sink.payload.eq(sink.payload),
|
||||
If(self.timer.done,
|
||||
NextState("IDLE")
|
||||
).Elif(header_pack.source.stb,
|
||||
NextState("COPY")
|
||||
).Else(
|
||||
sink.ack.eq(1)
|
||||
)
|
||||
)
|
||||
|
||||
self.comb += header_pack.reset.eq(self.timer.done)
|
||||
|
||||
sop = Signal()
|
||||
eop = Signal()
|
||||
cnt = Signal(32)
|
||||
|
||||
fsm.act("COPY",
|
||||
source.stb.eq(sink.stb),
|
||||
source.sop.eq(sop),
|
||||
source.eop.eq(eop),
|
||||
source.data.eq(sink.data),
|
||||
sink.ack.eq(source.ack),
|
||||
If((source.stb & source.ack & eop) | self.timer.done,
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
self.sync += \
|
||||
If(fsm.ongoing("IDLE"),
|
||||
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)
|
|
@ -1,140 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import subprocess
|
||||
import struct
|
||||
import importlib
|
||||
|
||||
from mibuild.tools import write_to_file
|
||||
from migen.util.misc import autotype
|
||||
from migen.fhdl import verilog, edif
|
||||
from migen.fhdl.structure import _Fragment
|
||||
from migen.bank.description import CSRStatus
|
||||
from mibuild import tools
|
||||
from mibuild.xilinx.common import *
|
||||
|
||||
from misoclib.soc import cpuif
|
||||
#from misoclib.lit.liteusb.common import *
|
||||
|
||||
|
||||
def _import(default, name):
|
||||
return importlib.import_module(default + "." + name)
|
||||
|
||||
|
||||
def _get_args():
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""\
|
||||
LiteUSB - based on Migen.
|
||||
|
||||
This program builds and/or loads LiteUSB components.
|
||||
One or several actions can be specified:
|
||||
|
||||
clean delete previous build(s).
|
||||
build-rtl build verilog rtl.
|
||||
build-bitstream build-bitstream build FPGA bitstream.
|
||||
build-csr-csv save CSR map into CSV file.
|
||||
|
||||
load-bitstream load bitstream into volatile storage.
|
||||
|
||||
all clean, build-csr-csv, build-bitstream, load-bitstream.
|
||||
""")
|
||||
|
||||
parser.add_argument("-t", "--target", default="simple", help="Core type to build")
|
||||
parser.add_argument("-s", "--sub-target", default="", help="variant of the Core type to build")
|
||||
parser.add_argument("-p", "--platform", default=None, help="platform to build for")
|
||||
parser.add_argument("-Ot", "--target-option", default=[], nargs=2, action="append", help="set target-specific option")
|
||||
parser.add_argument("-Op", "--platform-option", default=[], nargs=2, action="append", help="set platform-specific option")
|
||||
parser.add_argument("-Ob", "--build-option", default=[], nargs=2, action="append", help="set build option")
|
||||
parser.add_argument("--csr_csv", default="./test/csr.csv", help="CSV file to save the CSR map into")
|
||||
|
||||
parser.add_argument("action", nargs="+", help="specify an action")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
# Note: misoclib need to be installed as a python library
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = _get_args()
|
||||
|
||||
# create top-level Core object
|
||||
target_module = _import("targets", args.target)
|
||||
if args.sub_target:
|
||||
top_class = getattr(target_module, args.sub_target)
|
||||
else:
|
||||
top_class = target_module.default_subtarget
|
||||
|
||||
if args.platform is None:
|
||||
if hasattr(top_class, "default_platform"):
|
||||
platform_name = top_class.default_platform
|
||||
else:
|
||||
raise ValueError("Target has no default platform, specify a platform with -p your_platform")
|
||||
else:
|
||||
platform_name = args.platform
|
||||
platform_module = _import("mibuild.platforms", platform_name)
|
||||
platform_kwargs = dict((k, autotype(v)) for k, v in args.platform_option)
|
||||
platform = platform_module.Platform(**platform_kwargs)
|
||||
|
||||
build_name = top_class.__name__.lower() + "-" + platform_name
|
||||
top_kwargs = dict((k, autotype(v)) for k, v in args.target_option)
|
||||
soc = top_class(platform, **top_kwargs)
|
||||
soc.finalize()
|
||||
memory_regions = soc.get_memory_regions()
|
||||
csr_regions = soc.get_csr_regions()
|
||||
|
||||
# decode actions
|
||||
action_list = ["clean", "build-csr-csv", "build-bitstream", "load-bitstream", "all"]
|
||||
actions = {k: False for k in action_list}
|
||||
for action in args.action:
|
||||
if action in actions:
|
||||
actions[action] = True
|
||||
else:
|
||||
print("Unknown action: "+action+". Valid actions are:")
|
||||
for a in action_list:
|
||||
print(" "+a)
|
||||
sys.exit(1)
|
||||
|
||||
print("""
|
||||
__ _ __ __ _________
|
||||
/ / (_) /____ / / / / __/ _ )
|
||||
/ /__/ / __/ -_) /_/ /\ \/ _ |
|
||||
/____/_/\__/\__/\____/___/____/
|
||||
|
||||
|
||||
A small footprint and configurable USB core
|
||||
powered by Migen
|
||||
|
||||
====== Building parameters: ======
|
||||
System Clk: {} MHz
|
||||
===============================""".format(
|
||||
soc.clk_freq/1000000))
|
||||
|
||||
# dependencies
|
||||
if actions["all"]:
|
||||
actions["build-csr-csv"] = True
|
||||
actions["build-bitstream"] = True
|
||||
actions["load-bitstream"] = True
|
||||
|
||||
if actions["build-bitstream"]:
|
||||
actions["build-csr-csv"] = True
|
||||
actions["build-bitstream"] = True
|
||||
actions["load-bitstream"] = True
|
||||
|
||||
if actions["clean"]:
|
||||
subprocess.call(["rm", "-rf", "build/*"])
|
||||
|
||||
if actions["build-csr-csv"]:
|
||||
csr_csv = cpuif.get_csr_csv(csr_regions)
|
||||
write_to_file(args.csr_csv, csr_csv)
|
||||
|
||||
if actions["build-bitstream"]:
|
||||
build_kwargs = dict((k, autotype(v)) for k, v in args.build_option)
|
||||
vns = platform.build(soc, build_name=build_name, **build_kwargs)
|
||||
if hasattr(soc, "do_exit") and vns is not None:
|
||||
if hasattr(soc.do_exit, '__call__'):
|
||||
soc.do_exit(vns)
|
||||
|
||||
if actions["load-bitstream"]:
|
||||
prog = platform.create_programmer()
|
||||
prog.load_bitstream("build/" + build_name + platform.bitstream_ext)
|
|
@ -1,45 +0,0 @@
|
|||
from migen.genlib.io import CRG
|
||||
from migen.actorlib.fifo import SyncFIFO
|
||||
|
||||
from misoclib.soc import SoC
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
from misoclib.com.liteusb.phy.ft245 import FT245PHY
|
||||
from misoclib.com.liteusb.core import LiteUSBCore
|
||||
from misoclib.com.liteusb.frontend.wishbone import LiteUSBWishboneBridge
|
||||
|
||||
from misoclib.com.gpio import GPIOOut
|
||||
|
||||
class LiteUSBSoC(SoC):
|
||||
csr_map = {}
|
||||
csr_map.update(SoC.csr_map)
|
||||
|
||||
usb_map = {
|
||||
"bridge": 0
|
||||
}
|
||||
|
||||
def __init__(self, platform):
|
||||
clk_freq = int((1/(platform.default_clk_period))*1000000000)
|
||||
SoC.__init__(self, platform, clk_freq,
|
||||
cpu_type="none",
|
||||
with_csr=True, csr_data_width=32,
|
||||
with_uart=False,
|
||||
with_identifier=True,
|
||||
with_timer=False
|
||||
)
|
||||
self.submodules.crg = CRG(platform.request(platform.default_clk_name))
|
||||
|
||||
self.submodules.usb_phy = FT245PHY(platform.request("usb_fifo"), self.clk_freq)
|
||||
self.submodules.usb_core = LiteUSBCore(self.usb_phy, self.clk_freq, with_crc=False)
|
||||
|
||||
|
||||
# Wishbone Bridge
|
||||
usb_bridge_port = self.usb_core.crossbar.get_port(self.usb_map["bridge"])
|
||||
self.add_cpu_or_bridge(LiteUSBWishboneBridge(usb_bridge_port, self.clk_freq))
|
||||
self.add_wb_master(self.cpu_or_bridge.wishbone)
|
||||
|
||||
# Leds
|
||||
leds = Cat(iter([platform.request("user_led", i) for i in range(8)]))
|
||||
self.submodules.leds = GPIOOut(leds)
|
||||
|
||||
default_subtarget = LiteUSBSoC
|
|
@ -1,8 +0,0 @@
|
|||
LITEUSBDIR=../../software
|
||||
|
||||
dll:
|
||||
cd $(LITEUSBDIR)/ftdi/windows && make all
|
||||
cp $(LITEUSBDIR)/ftdi/libftdicom.dll libftdicom.dll
|
||||
|
||||
clean:
|
||||
rm -f libftdicom.dll
|
|
@ -1,31 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import importlib
|
||||
|
||||
FTDI_INTERFACE_A = 1
|
||||
FTDI_INTERFACE_B = 2
|
||||
|
||||
def _get_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--tag", default=0, help="USB channel tag")
|
||||
parser.add_argument("--busword", default=32, help="CSR busword")
|
||||
|
||||
parser.add_argument("test", nargs="+", help="specify a test")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = _get_args()
|
||||
from misoclib.com.liteusb.software.wishbone import LiteUSBWishboneDriver
|
||||
wb = LiteUSBWishboneDriver("ft2232h", FTDI_INTERFACE_B, "asynchronous",
|
||||
tag=int(args.tag),
|
||||
busword=int(args.busword),
|
||||
addrmap="./csr.csv",
|
||||
debug=False)
|
||||
|
||||
def _import(name):
|
||||
return importlib.import_module(name)
|
||||
|
||||
for test in args.test:
|
||||
t = _import(test)
|
||||
t.main(wb)
|
|
@ -1,11 +0,0 @@
|
|||
def main(wb):
|
||||
wb.open()
|
||||
regs = wb.regs
|
||||
# # #
|
||||
for i in range(64):
|
||||
wb.regs.leds_out.write(i)
|
||||
print("sysid : 0x{:04x}".format(regs.identifier_sysid.read()))
|
||||
print("revision : 0x{:04x}".format(regs.identifier_revision.read()))
|
||||
print("frequency : {}MHz".format(int(regs.identifier_frequency.read()/1000000)))
|
||||
# # #
|
||||
wb.close()
|
|
@ -1,102 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.flow.actor import *
|
||||
from migen.flow.network import *
|
||||
from migen.actorlib import structuring, spi
|
||||
from migen.bank.description import *
|
||||
from migen.bank.eventmanager import *
|
||||
from migen.genlib.record import Record
|
||||
|
||||
from misoclib.mem.sdram.frontend import dma_lasmi
|
||||
from misoclib.com.liteusb.common import *
|
||||
|
||||
|
||||
class LiteUSBDMAWriter(Module, AutoCSR):
|
||||
def __init__(self, lasmim):
|
||||
self.sink = sink = Sink(user_description(8))
|
||||
|
||||
# Pack data
|
||||
pack_factor = lasmim.dw//8
|
||||
pack = structuring.Pack(phy_description(8), 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 LiteUSBDMAReader(Module, AutoCSR):
|
||||
def __init__(self, lasmim, tag):
|
||||
self.source = source = Source(user_description(8))
|
||||
|
||||
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_description(8), reverse=True)
|
||||
|
||||
# Graph
|
||||
cnt = Signal(32)
|
||||
self.sync += \
|
||||
If(self.dma.generator._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),
|
||||
source.data.eq(unpack.source.data),
|
||||
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 LiteUSBDMA(Module, AutoCSR):
|
||||
def __init__(self, port, lasmim_dma_wr, lasmim_dma_rd):
|
||||
self.submodules.writer = LiteUSBDMAWriter(lasmim_dma_wr)
|
||||
self.submodules.reader = LiteUSBDMAReader(lasmim_dma_rd, port.tag)
|
||||
self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev)
|
||||
self.comb += [
|
||||
Record.connect(port.source, self.writer.sink),
|
||||
Record.connect(self.reader.source, port.sink),
|
||||
]
|
|
@ -1,35 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
from misoclib.com.uart import UART
|
||||
|
||||
class LiteUSBUARTPHY:
|
||||
def __init__(self):
|
||||
self.sink = Sink([("data", 8)])
|
||||
self.source = Source([("data", 8)])
|
||||
|
||||
class LiteUSBUART(UART):
|
||||
def __init__(self, port,
|
||||
tx_fifo_depth=16,
|
||||
rx_fifo_depth=16):
|
||||
|
||||
phy = LiteUSBUARTPHY()
|
||||
UART.__init__(self, phy, tx_fifo_depth, rx_fifo_depth)
|
||||
|
||||
# TX
|
||||
self.comb += [
|
||||
port.sink.stb.eq(phy.sink.stb),
|
||||
port.sink.sop.eq(1),
|
||||
port.sink.eop.eq(1),
|
||||
port.sink.length.eq(1),
|
||||
port.sink.dst.eq(port.tag),
|
||||
port.sink.data.eq(phy.sink.data),
|
||||
phy.sink.ack.eq(port.sink.ack)
|
||||
]
|
||||
|
||||
# RX
|
||||
self.comb += [
|
||||
phy.source.stb.eq(port.source.stb),
|
||||
phy.source.data.eq(port.source.data),
|
||||
port.source.ack.eq(phy.source.ack)
|
||||
]
|
|
@ -1,9 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
from misoclib.tools.wishbone import WishboneStreamingBridge
|
||||
|
||||
class LiteUSBWishboneBridge(WishboneStreamingBridge):
|
||||
def __init__(self, port, clk_freq):
|
||||
WishboneStreamingBridge.__init__(self, port, clk_freq)
|
||||
self.comb += port.sink.dst.eq(port.tag)
|
|
@ -1,319 +0,0 @@
|
|||
import math
|
||||
|
||||
from migen.fhdl.std import *
|
||||
from migen.flow.actor import *
|
||||
from migen.actorlib.fifo import SyncFIFO, AsyncFIFO
|
||||
from migen.fhdl.specials import *
|
||||
from migen.genlib.cdc import MultiReg
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
|
||||
|
||||
def anti_starvation(module, timeout):
|
||||
en = Signal()
|
||||
max_time = Signal()
|
||||
if timeout:
|
||||
t = timeout - 1
|
||||
time = Signal(max=t+1)
|
||||
module.comb += max_time.eq(time == 0)
|
||||
module.sync += If(~en,
|
||||
time.eq(t)
|
||||
).Elif(~max_time,
|
||||
time.eq(time - 1)
|
||||
)
|
||||
else:
|
||||
module.comb += max_time.eq(0)
|
||||
return en, max_time
|
||||
|
||||
|
||||
class FT245PHYSynchronous(Module):
|
||||
def __init__(self, pads, clk_freq,
|
||||
fifo_depth=32,
|
||||
read_time=128,
|
||||
write_time=128):
|
||||
dw = flen(pads.data)
|
||||
|
||||
# read fifo (FTDI --> SoC)
|
||||
read_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
|
||||
{"write": "usb", "read": "sys"})
|
||||
read_buffer = RenameClockDomains(SyncFIFO(phy_description(8), 4),
|
||||
{"sys": "usb"})
|
||||
self.comb += read_buffer.source.connect(read_fifo.sink)
|
||||
|
||||
# write fifo (SoC --> FTDI)
|
||||
write_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
|
||||
{"write": "sys", "read": "usb"})
|
||||
|
||||
self.submodules += read_fifo, read_buffer, write_fifo
|
||||
|
||||
# sink / source interfaces
|
||||
self.sink = write_fifo.sink
|
||||
self.source = read_fifo.source
|
||||
|
||||
# read / write arbitration
|
||||
wants_write = Signal()
|
||||
wants_read = Signal()
|
||||
|
||||
txe_n = Signal()
|
||||
rxf_n = Signal()
|
||||
|
||||
self.comb += [
|
||||
txe_n.eq(pads.txe_n),
|
||||
rxf_n.eq(pads.rxf_n),
|
||||
wants_write.eq(~txe_n & write_fifo.source.stb),
|
||||
wants_read.eq(~rxf_n & read_fifo.sink.ack),
|
||||
]
|
||||
|
||||
read_time_en, max_read_time = anti_starvation(self, read_time)
|
||||
write_time_en, max_write_time = anti_starvation(self, write_time)
|
||||
|
||||
data_w_accepted = Signal(reset=1)
|
||||
|
||||
fsm = FSM(reset_state="READ")
|
||||
self.submodules += RenameClockDomains(fsm, {"sys": "usb"})
|
||||
|
||||
fsm.act("READ",
|
||||
read_time_en.eq(1),
|
||||
If(wants_write,
|
||||
If(~wants_read | max_read_time,
|
||||
NextState("RTW")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("RTW",
|
||||
NextState("WRITE")
|
||||
)
|
||||
fsm.act("WRITE",
|
||||
write_time_en.eq(1),
|
||||
If(wants_read,
|
||||
If(~wants_write | max_write_time,
|
||||
NextState("WTR")
|
||||
)
|
||||
),
|
||||
write_fifo.source.ack.eq(wants_write & data_w_accepted)
|
||||
)
|
||||
fsm.act("WTR",
|
||||
NextState("READ")
|
||||
)
|
||||
|
||||
# databus tristate
|
||||
data_w = Signal(dw)
|
||||
data_r = Signal(dw)
|
||||
data_oe = Signal()
|
||||
self.specials += Tristate(pads.data, data_w, data_oe, data_r)
|
||||
|
||||
# read / write actions
|
||||
pads.oe_n.reset = 1
|
||||
pads.rd_n.reset = 1
|
||||
pads.wr_n.reset = 1
|
||||
|
||||
self.sync.usb += [
|
||||
If(fsm.ongoing("READ"),
|
||||
data_oe.eq(0),
|
||||
|
||||
pads.oe_n.eq(0),
|
||||
pads.rd_n.eq(~wants_read),
|
||||
pads.wr_n.eq(1)
|
||||
|
||||
).Elif(fsm.ongoing("WRITE"),
|
||||
data_oe.eq(1),
|
||||
|
||||
pads.oe_n.eq(1),
|
||||
pads.rd_n.eq(1),
|
||||
pads.wr_n.eq(~wants_write),
|
||||
|
||||
data_w_accepted.eq(~txe_n)
|
||||
|
||||
).Else(
|
||||
data_oe.eq(1),
|
||||
|
||||
pads.oe_n.eq(~fsm.ongoing("WTR")),
|
||||
pads.rd_n.eq(1),
|
||||
pads.wr_n.eq(1)
|
||||
),
|
||||
read_buffer.sink.stb.eq(~pads.rd_n & ~rxf_n),
|
||||
read_buffer.sink.data.eq(data_r),
|
||||
If(~txe_n & data_w_accepted,
|
||||
data_w.eq(write_fifo.source.data)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class FT245PHYAsynchronous(Module):
|
||||
def __init__(self, pads, clk_freq,
|
||||
fifo_depth=32,
|
||||
read_time=128,
|
||||
write_time=128):
|
||||
dw = flen(pads.data)
|
||||
self.clk_freq = clk_freq
|
||||
|
||||
# timings
|
||||
tRD = self.ns(30) # RD# active pulse width (t4)
|
||||
tRDDataSetup = self.ns(14) # RD# to DATA (t3)
|
||||
tWRDataSetup = self.ns(5) # DATA to WR# active setup time (t8)
|
||||
tWR = self.ns(30) # WR# active pulse width (t10)
|
||||
tMultiReg = 2
|
||||
|
||||
# read fifo (FTDI --> SoC)
|
||||
read_fifo = SyncFIFO(phy_description(8), fifo_depth)
|
||||
|
||||
# write fifo (SoC --> FTDI)
|
||||
write_fifo = SyncFIFO(phy_description(8), fifo_depth)
|
||||
|
||||
self.submodules += read_fifo, write_fifo
|
||||
|
||||
# sink / source interfaces
|
||||
self.sink = write_fifo.sink
|
||||
self.source = read_fifo.source
|
||||
|
||||
# read / write arbitration
|
||||
wants_write = Signal()
|
||||
wants_read = Signal()
|
||||
|
||||
txe_n = Signal()
|
||||
rxf_n = Signal()
|
||||
|
||||
self.specials += [
|
||||
MultiReg(pads.txe_n, txe_n),
|
||||
MultiReg(pads.rxf_n, rxf_n)
|
||||
]
|
||||
|
||||
self.comb += [
|
||||
wants_write.eq(~txe_n & write_fifo.source.stb),
|
||||
wants_read.eq(~rxf_n & read_fifo.sink.ack),
|
||||
]
|
||||
|
||||
read_time_en, max_read_time = anti_starvation(self, read_time)
|
||||
write_time_en, max_write_time = anti_starvation(self, write_time)
|
||||
|
||||
fsm = FSM(reset_state="READ")
|
||||
self.submodules += fsm
|
||||
|
||||
read_done = Signal()
|
||||
write_done = Signal()
|
||||
commuting = Signal()
|
||||
|
||||
fsm.act("READ",
|
||||
read_time_en.eq(1),
|
||||
If(wants_write & read_done,
|
||||
If(~wants_read | max_read_time,
|
||||
commuting.eq(1),
|
||||
NextState("RTW")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("RTW",
|
||||
NextState("WRITE")
|
||||
)
|
||||
fsm.act("WRITE",
|
||||
write_time_en.eq(1),
|
||||
If(wants_read & write_done,
|
||||
If(~wants_write | max_write_time,
|
||||
commuting.eq(1),
|
||||
NextState("WTR")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("WTR",
|
||||
NextState("READ")
|
||||
)
|
||||
|
||||
# databus tristate
|
||||
data_w = Signal(dw)
|
||||
data_r_async = Signal(dw)
|
||||
data_r = Signal(dw)
|
||||
data_oe = Signal()
|
||||
self.specials += [
|
||||
Tristate(pads.data, data_w, data_oe, data_r_async),
|
||||
MultiReg(data_r_async, data_r)
|
||||
]
|
||||
|
||||
|
||||
# read actions
|
||||
pads.rd_n.reset = 1
|
||||
|
||||
read_fsm = FSM(reset_state="IDLE")
|
||||
read_counter = Counter(8)
|
||||
self.submodules += read_fsm, read_counter
|
||||
|
||||
read_fsm.act("IDLE",
|
||||
read_done.eq(1),
|
||||
read_counter.reset.eq(1),
|
||||
If(fsm.ongoing("READ") & wants_read,
|
||||
If(~commuting,
|
||||
NextState("PULSE_RD_N")
|
||||
)
|
||||
)
|
||||
)
|
||||
read_fsm.act("PULSE_RD_N",
|
||||
pads.rd_n.eq(0),
|
||||
read_counter.ce.eq(1),
|
||||
If(read_counter.value == max((tRD-1), (tRDDataSetup + tMultiReg -1)),
|
||||
NextState("ACQUIRE_DATA")
|
||||
)
|
||||
)
|
||||
read_fsm.act("ACQUIRE_DATA",
|
||||
read_fifo.sink.stb.eq(1),
|
||||
read_fifo.sink.data.eq(data_r),
|
||||
NextState("WAIT_RXF_N")
|
||||
)
|
||||
read_fsm.act("WAIT_RXF_N",
|
||||
If(rxf_n,
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
# write actions
|
||||
pads.wr_n.reset = 1
|
||||
|
||||
write_fsm = FSM(reset_state="IDLE")
|
||||
write_counter = Counter(8)
|
||||
self.submodules += write_fsm, write_counter
|
||||
|
||||
write_fsm.act("IDLE",
|
||||
write_done.eq(1),
|
||||
write_counter.reset.eq(1),
|
||||
If(fsm.ongoing("WRITE") & wants_write,
|
||||
If(~commuting,
|
||||
NextState("SET_DATA")
|
||||
)
|
||||
)
|
||||
)
|
||||
write_fsm.act("SET_DATA",
|
||||
data_oe.eq(1),
|
||||
data_w.eq(write_fifo.source.data),
|
||||
write_counter.ce.eq(1),
|
||||
If(write_counter.value == (tWRDataSetup-1),
|
||||
write_counter.reset.eq(1),
|
||||
NextState("PULSE_WR_N")
|
||||
)
|
||||
)
|
||||
write_fsm.act("PULSE_WR_N",
|
||||
data_oe.eq(1),
|
||||
data_w.eq(write_fifo.source.data),
|
||||
pads.wr_n.eq(0),
|
||||
write_counter.ce.eq(1),
|
||||
If(write_counter.value == (tWR-1),
|
||||
NextState("WAIT_TXE_N")
|
||||
)
|
||||
)
|
||||
write_fsm.act("WAIT_TXE_N",
|
||||
If(txe_n,
|
||||
write_fifo.source.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
def ns(self, t, margin=True):
|
||||
clk_period_ns = 1000000000/self.clk_freq
|
||||
if margin:
|
||||
t += clk_period_ns/2
|
||||
return math.ceil(t/clk_period_ns)
|
||||
|
||||
|
||||
def FT245PHY(pads, *args, **kwargs):
|
||||
# autodetect PHY
|
||||
if hasattr(pads, "oe_n"):
|
||||
return FT245PHYSynchronous(pads, *args, **kwargs)
|
||||
else:
|
||||
return FT245PHYAsynchronous(pads, *args, **kwargs)
|
|
@ -1,14 +0,0 @@
|
|||
[> Libftdicom
|
||||
------------------------------
|
||||
|
||||
[> Windows build
|
||||
--------------------------
|
||||
1. Install MinGW32
|
||||
2. Download libusbx windows binaries (tested version: libusbx-1.0.17-win)
|
||||
3. Put libusb-1.0.dll.a in mingw lib directory
|
||||
4. Download Zadig and use WinUSB driver for Interface A and Interface B
|
||||
5. make all in libftdicom/win
|
||||
|
||||
[> Linux build
|
||||
--------------------------
|
||||
1. make all in libftdicom/linux
|
|
@ -1,364 +0,0 @@
|
|||
import platform
|
||||
import ctypes
|
||||
import os
|
||||
import time
|
||||
import queue
|
||||
import threading
|
||||
|
||||
if platform.system() == "Windows":
|
||||
libftdicom = ctypes.cdll.LoadLibrary("./libftdicom.dll")
|
||||
else:
|
||||
libftdicom = ctypes.cdll.LoadLibrary("./libftdicom.so")
|
||||
|
||||
|
||||
class FTDI_Device(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('_1', ctypes.c_void_p),
|
||||
('_2', ctypes.c_void_p),
|
||||
]
|
||||
|
||||
pFTDI_Device = ctypes.POINTER(FTDI_Device)
|
||||
|
||||
# FTDIDevice_Open
|
||||
FTDIDevice_Open = libftdicom.FTDIDevice_Open
|
||||
FTDIDevice_Open.argtypes = [
|
||||
pFTDI_Device, # Dev
|
||||
ctypes.c_int # Interface
|
||||
]
|
||||
FTDIDevice_Open.restype = ctypes.c_int
|
||||
|
||||
# FTDIDevice_Close
|
||||
FTDIDevice_Close = libftdicom.FTDIDevice_Close
|
||||
FTDIDevice_Close.argtypes = [pFTDI_Device]
|
||||
|
||||
FTDIDevice_SetMode = libftdicom.FTDIDevice_SetMode
|
||||
FTDIDevice_SetMode.argtypes = [
|
||||
pFTDI_Device, # Dev
|
||||
ctypes.c_int, # Interface
|
||||
ctypes.c_int, # Mode
|
||||
ctypes.c_char, # PinDirection
|
||||
ctypes.c_char, # baudrate
|
||||
]
|
||||
|
||||
|
||||
FTDIDevice_Write = libftdicom.FTDIDevice_Write
|
||||
FTDIDevice_Write.argtypes = [
|
||||
pFTDI_Device, # Dev
|
||||
ctypes.c_int, # Interface
|
||||
ctypes.c_char_p, # Buf
|
||||
ctypes.c_size_t, # N
|
||||
ctypes.c_bool, # async
|
||||
]
|
||||
FTDIDevice_Write.restype = ctypes.c_int
|
||||
|
||||
p_cb_StreamCallback = ctypes.CFUNCTYPE(
|
||||
ctypes.c_int, # retval
|
||||
ctypes.POINTER(ctypes.c_uint8), # buf
|
||||
ctypes.c_int, # length
|
||||
ctypes.c_void_p, # progress
|
||||
ctypes.c_void_p) # userdata
|
||||
|
||||
FTDIDevice_ReadStream = libftdicom.FTDIDevice_ReadStream
|
||||
FTDIDevice_ReadStream.argtypes = [
|
||||
pFTDI_Device, # dev
|
||||
ctypes.c_int, # interface
|
||||
p_cb_StreamCallback, # callback
|
||||
ctypes.c_void_p, # userdata
|
||||
ctypes.c_int, # packetsPerTransfer
|
||||
ctypes.c_int, # numTransfers
|
||||
]
|
||||
FTDIDevice_ReadStream.restype = ctypes.c_int
|
||||
|
||||
FTDI_INTERFACE_A = 1
|
||||
FTDI_INTERFACE_B = 2
|
||||
|
||||
FTDI_BITMODE_SYNC_FIFO = (1 << 6)
|
||||
|
||||
|
||||
class FTDIDevice:
|
||||
def __init__(self, interface, mode):
|
||||
self.__is_open = False
|
||||
self._dev = FTDI_Device()
|
||||
self.interface = interface
|
||||
self.mode = mode
|
||||
|
||||
def __del__(self):
|
||||
if self.__is_open:
|
||||
self.__is_open = False
|
||||
FTDIDevice_Close(self._dev)
|
||||
|
||||
def open(self):
|
||||
err = FTDIDevice_Open(self._dev, self.interface)
|
||||
if err:
|
||||
return err
|
||||
else:
|
||||
self.__is_open = True
|
||||
|
||||
if self.mode == "synchronous":
|
||||
err = FTDIDevice_SetMode(self._dev, interface, FTDI_BITMODE_SYNC_FIFO, 0xFF, 0)
|
||||
|
||||
return err
|
||||
|
||||
def write(self, intf, buf, async=False):
|
||||
if not isinstance(buf, bytes):
|
||||
raise TypeError("buf must be bytes")
|
||||
|
||||
return FTDIDevice_Write(self._dev, intf, buf, len(buf), async)
|
||||
|
||||
def read(self, intf, n):
|
||||
buf = []
|
||||
|
||||
def callback(b, prog):
|
||||
buf.extend(b)
|
||||
return int(len(buf) >= n)
|
||||
|
||||
self.read_async(intf, callback, 4, 4)
|
||||
|
||||
return buf
|
||||
|
||||
def read_async(self, intf, callback, packetsPerTransfer, numTransfers):
|
||||
def callback_wrapper(buf, ll, prog, user):
|
||||
if ll:
|
||||
b = ctypes.string_at(buf, ll)
|
||||
else:
|
||||
b = b''
|
||||
return callback(b, prog)
|
||||
|
||||
cb = p_cb_StreamCallback(callback_wrapper)
|
||||
|
||||
return FTDIDevice_ReadStream(self._dev, intf, cb,
|
||||
None, packetsPerTransfer, numTransfers)
|
||||
|
||||
|
||||
class ProtocolError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TimeoutError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
INCOMPLETE = -1
|
||||
UNMATCHED = 0
|
||||
class BaseService:
|
||||
def match_identifier(self, byt):
|
||||
r = True
|
||||
r = r and (byt[0] == 0x5A)
|
||||
r = r and (byt[1] == 0xA5)
|
||||
r = r and (byt[2] == 0x5A)
|
||||
r = r and (byt[3] == 0xA5)
|
||||
r = r and (byt[4] == self.tag)
|
||||
return r
|
||||
|
||||
def get_needed_size_for_identifier(self):
|
||||
return self.NEEDED_FOR_SIZE
|
||||
|
||||
def present_bytes(self, b):
|
||||
if len(b) < self.get_needed_size_for_identifier():
|
||||
return INCOMPLETE
|
||||
|
||||
if not self.match_identifier(b):
|
||||
return UNMATCHED
|
||||
|
||||
size = self.get_packet_size(b)
|
||||
|
||||
if len(b) < size:
|
||||
return INCOMPLETE
|
||||
|
||||
self.consume(b[:size])
|
||||
|
||||
return size
|
||||
|
||||
|
||||
class UART:
|
||||
class __UARTService(BaseService):
|
||||
NEEDED_FOR_SIZE = 9
|
||||
|
||||
def __init__(self, tag):
|
||||
self.tag = tag
|
||||
self.q = queue.Queue()
|
||||
|
||||
def get_packet_size(self, buf):
|
||||
payload_size = buf[5] << 24
|
||||
payload_size |= buf[6] << 16
|
||||
payload_size |= buf[7] << 8
|
||||
payload_size |= buf[8] << 0
|
||||
return 9 + payload_size
|
||||
|
||||
def consume(self, buf):
|
||||
for value in buf[9:]:
|
||||
self.q.put(value)
|
||||
|
||||
def __init__(self, tag):
|
||||
self.tag = tag
|
||||
self.service = UART.__UARTService(self.tag)
|
||||
|
||||
def do_read(self, timeout=None):
|
||||
try:
|
||||
resp = self.service.q.get(True, timeout)
|
||||
except queue.Empty:
|
||||
return -1
|
||||
return resp
|
||||
|
||||
def do_write(self, data):
|
||||
if isinstance(data, int):
|
||||
data = [data]
|
||||
msg = [0x5A, 0xA5, 0x5A, 0xA5]
|
||||
msg.append(self.tag)
|
||||
length = len(data)
|
||||
msg.append((length >> 24) & 0xff)
|
||||
msg.append((length >> 16) & 0xff)
|
||||
msg.append((length >> 8) & 0xff)
|
||||
msg.append((length >> 0) & 0xff)
|
||||
for value in data:
|
||||
msg.append(value&0xff)
|
||||
self.service.write(bytes(msg))
|
||||
|
||||
|
||||
class DMA:
|
||||
class __DMAService(BaseService):
|
||||
NEEDED_FOR_SIZE = 9
|
||||
|
||||
def __init__(self, tag):
|
||||
self.tag = tag
|
||||
self.q = queue.Queue()
|
||||
|
||||
def get_packet_size(self, buf):
|
||||
payload_size = buf[5] << 24
|
||||
payload_size |= buf[6] << 16
|
||||
payload_size |= buf[7] << 8
|
||||
payload_size |= buf[8] << 0
|
||||
return 9 + payload_size
|
||||
|
||||
def consume(self, buf):
|
||||
self.q.put(buf[9:])
|
||||
|
||||
def __init__(self, tag):
|
||||
self.tag = tag
|
||||
self.service = DMA.__DMAService(self.tag)
|
||||
|
||||
def do_read(self, timeout=None):
|
||||
try:
|
||||
resp = list(self.service.q.get(True, timeout))
|
||||
except queue.Empty:
|
||||
raise TimeoutError("DMA read timed out")
|
||||
return resp
|
||||
|
||||
def do_write(self, data):
|
||||
length = len(data)
|
||||
msg = [0x5A, 0xA5, 0x5A, 0xA5, self.tag,
|
||||
(length & 0xff000000) >> 24,
|
||||
(length & 0x00ff0000) >> 16,
|
||||
(length & 0x0000ff00) >> 8,
|
||||
(length & 0x000000ff) >> 0]
|
||||
msg += data
|
||||
self.service.write(bytes(msg))
|
||||
|
||||
|
||||
class FTDIComDevice:
|
||||
def __init__(self, interface, mode, uart_tag=0, dma_tag=1, verbose=False):
|
||||
self.__is_open = False
|
||||
|
||||
self.interface = interface
|
||||
self.mode = mode
|
||||
|
||||
self.dev = FTDIDevice(interface, mode)
|
||||
self.verbose = verbose
|
||||
|
||||
self.uart = UART(uart_tag)
|
||||
self.dma = DMA(dma_tag)
|
||||
|
||||
self.__services = [self.uart.service, self.dma.service]
|
||||
|
||||
# Inject a write function into the services
|
||||
for service in self.__services:
|
||||
def write(msg):
|
||||
if self.verbose:
|
||||
print("< %s" % " ".join("%02x" % i for i in msg))
|
||||
|
||||
self.dev.write(self.interface, msg, async=False)
|
||||
|
||||
service.write = write
|
||||
|
||||
def __comms(self):
|
||||
self.__buf = b""
|
||||
|
||||
def callback(b, prog):
|
||||
try:
|
||||
if self.verbose and b:
|
||||
print("> %s" % " ".join("%02x" % i for i in b))
|
||||
|
||||
self.__buf += b
|
||||
|
||||
incomplete = False
|
||||
|
||||
while self.__buf and not incomplete:
|
||||
for service in self.__services:
|
||||
code = service.present_bytes(self.__buf)
|
||||
if code == INCOMPLETE:
|
||||
incomplete = True
|
||||
break
|
||||
elif code:
|
||||
self.__buf = self.__buf[code:]
|
||||
break
|
||||
else:
|
||||
self.__buf = self.__buf[1:]
|
||||
|
||||
return int(self.__comm_term)
|
||||
except Exception as e:
|
||||
self.__comm_term = True
|
||||
self.__comm_exc = e
|
||||
return 1
|
||||
|
||||
while not self.__comm_term:
|
||||
self.dev.read_async(self.interface, callback, 8, 16)
|
||||
|
||||
if self.__comm_exc:
|
||||
raise self.__comm_exc
|
||||
|
||||
def __del__(self):
|
||||
if self.__is_open:
|
||||
self.close()
|
||||
|
||||
def open(self):
|
||||
if self.__is_open:
|
||||
raise ValueError("FTDICOMDevice doubly opened")
|
||||
|
||||
stat = self.dev.open()
|
||||
if stat:
|
||||
print("USB: Error opening device\n")
|
||||
return stat
|
||||
|
||||
self.commthread = threading.Thread(target=self.__comms, daemon=True)
|
||||
self.__comm_term = False
|
||||
self.__comm_exc = None
|
||||
|
||||
self.commthread.start()
|
||||
|
||||
self.__comm_term = False
|
||||
self.__is_open = True
|
||||
|
||||
def close(self):
|
||||
if not self.__is_open:
|
||||
raise ValueError("FTDICOMDevice doubly closed")
|
||||
|
||||
self.__comm_term = True
|
||||
self.commthread.join()
|
||||
|
||||
self.__is_open = False
|
||||
|
||||
def uartflush(self, timeout=0.25):
|
||||
while (self.uartread(timeout) != -1):
|
||||
pass
|
||||
|
||||
def uartread(self, timeout=None):
|
||||
return self.uart.do_read(timeout)
|
||||
|
||||
def uartwrite(self, data):
|
||||
return self.uart.do_write(data)
|
||||
|
||||
def dmaread(self):
|
||||
return self.dma.do_read()
|
||||
|
||||
def dmawrite(self, data):
|
||||
return self.dma.do_write(data)
|
|
@ -1,81 +0,0 @@
|
|||
import platform
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
|
||||
# XXX FTDI Communication POC
|
||||
|
||||
sys.path.append("../")
|
||||
from ftdi import FTDIComDevice, FTDI_INTERFACE_B
|
||||
|
||||
def uart_console(ftdi_com):
|
||||
def read():
|
||||
while True:
|
||||
print(chr(ftdi_com.uartread()), end="")
|
||||
|
||||
readthread = threading.Thread(target=read, daemon=True)
|
||||
readthread.start()
|
||||
|
||||
def write():
|
||||
while True:
|
||||
for e in input():
|
||||
c = ord(e)
|
||||
ftdi_com.uartwrite(c)
|
||||
ftdi_com.uartwrite(ord("\n"))
|
||||
|
||||
|
||||
writethread = threading.Thread(target=write, daemon=True)
|
||||
writethread.start()
|
||||
|
||||
|
||||
def uart_virtual(ftdi_com):
|
||||
import pty, serial
|
||||
master, slave = pty.openpty()
|
||||
s_name = os.ttyname(slave)
|
||||
ser = serial.Serial(s_name)
|
||||
|
||||
def read():
|
||||
while True:
|
||||
s = ftdi_com.uartread()
|
||||
s = bytes(chr(s).encode('utf-8'))
|
||||
os.write(master, s)
|
||||
|
||||
readthread = threading.Thread(target=read, daemon=True)
|
||||
readthread.start()
|
||||
|
||||
def write():
|
||||
while True:
|
||||
for c in list(os.read(master, 100)):
|
||||
ftdi_com.uartwrite(c)
|
||||
|
||||
writethread = threading.Thread(target=write, daemon=True)
|
||||
writethread.start()
|
||||
|
||||
return s_name
|
||||
|
||||
|
||||
ftdi_map = {
|
||||
"uart": 0,
|
||||
"dma": 1
|
||||
}
|
||||
ftdi_com = FTDIComDevice(FTDI_INTERFACE_B,
|
||||
mode="asynchronous",
|
||||
uart_tag=ftdi_map["uart"],
|
||||
dma_tag=ftdi_map["dma"],
|
||||
verbose=False)
|
||||
ftdi_com.open()
|
||||
# test DMA
|
||||
for i in range(256):
|
||||
ftdi_com.dmawrite([i])
|
||||
print("%02x" %(ftdi_com.dmaread()[0]), end="")
|
||||
sys.stdout.flush()
|
||||
print("")
|
||||
# test UART
|
||||
if platform.system() == "Windows":
|
||||
uart_console(ftdi_com) # redirect uart to console since pty does not exist on Windows platforms
|
||||
else:
|
||||
s_name = uart_virtual(ftdi_com)
|
||||
print(s_name)
|
||||
while True:
|
||||
time.sleep(1)
|
|
@ -1,548 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
* Copyright (C) 2015 Florent Kermarrec
|
||||
*
|
||||
* 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, FTDIInterface interface)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (libusb_kernel_driver_active(dev->handle, (interface-1)) == 1) {
|
||||
if ((err = libusb_detach_kernel_driver(dev->handle, (interface-1)))) {
|
||||
perror("Error detaching kernel driver");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if ((err = libusb_set_configuration(dev->handle, 1))) {
|
||||
perror("Error setting configuration");
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = libusb_claim_interface(dev->handle, (interface-1)))) {
|
||||
perror("Error claiming interface");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
FTDIDevice_Open(FTDIDevice *dev, FTDIInterface interface)
|
||||
{
|
||||
int err;
|
||||
|
||||
memset(dev, 0, sizeof *dev);
|
||||
|
||||
if ((err = libusb_init(&dev->libusb))) {
|
||||
return err;
|
||||
}
|
||||
|
||||
libusb_set_debug(dev->libusb, 0);
|
||||
|
||||
|
||||
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, interface);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FTDIDevice_Close(FTDIDevice *dev)
|
||||
{
|
||||
libusb_close(dev->handle);
|
||||
libusb_exit(dev->libusb);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
FTDIDevice_Reset(FTDIDevice *dev, FTDIInterface interface)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = libusb_reset_device(dev->handle);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return DeviceInit(dev, interface);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
* Copyright (C) 2015 Florent Kermarrec
|
||||
*
|
||||
* 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 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, FTDIInterface interface);
|
||||
void FTDIDevice_Close(FTDIDevice *dev);
|
||||
int FTDIDevice_Reset(FTDIDevice *dev, FTDIInterface interface);
|
||||
|
||||
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 */
|
|
@ -1,35 +0,0 @@
|
|||
UNAME := $(shell uname)
|
||||
|
||||
LIBNAME := libftdicom
|
||||
|
||||
|
||||
# Load libusb via pkg-config
|
||||
PACKAGES := libusb-1.0
|
||||
CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
|
||||
LDFLAGS += $(shell pkg-config --libs $(PACKAGES))
|
||||
|
||||
# Large file support
|
||||
CFLAGS += $(shell getconf LFS_CFLAGS)
|
||||
|
||||
CFLAGS += -fPIC
|
||||
|
||||
SO := $(LIBNAME).so
|
||||
SO_LDFLAGS := $(LDFLAGS) -shared
|
||||
|
||||
# Local headers
|
||||
CFLAGS += -I../include
|
||||
|
||||
SO_OBJS := ../fastftdi.o
|
||||
|
||||
CFLAGS += -O3 -g --std=c99
|
||||
|
||||
all: $(SO)
|
||||
cp libftdicom.so ../libftdicom.so
|
||||
|
||||
$(SO): $(SO_OBJS)
|
||||
cc -o $@ $^ $(SO_LDFLAGS)
|
||||
|
||||
*.o: *.h Makefile
|
||||
|
||||
clean:
|
||||
rm -f $(SO) $(OBJS) $(SO_OBJS)
|
|
@ -1,12 +0,0 @@
|
|||
CC=gcc
|
||||
CFLAGS_DLL =-Wall -O0 -g -shared -Wl,--subsystem,windows -DDLL
|
||||
LIBS= -lusb-1.0
|
||||
|
||||
all: libftdicom.dll
|
||||
cp libftdicom.dll ../libftdicom.dll
|
||||
|
||||
libftdicom.dll: ../fastftdi.c
|
||||
$(CC) -o $@ $(CFLAGS_DLL) $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f libftdicom.dll ../libftdicom.dll
|
|
@ -1,81 +0,0 @@
|
|||
from misoclib.tools.litescope.software.driver.reg import *
|
||||
from misoclib.com.liteusb.software.ftdi import FTDIComDevice
|
||||
|
||||
class LiteUSBWishboneDriverFTDI:
|
||||
cmds = {
|
||||
"write": 0x01,
|
||||
"read": 0x02
|
||||
}
|
||||
def __init__(self, interface, mode, tag, addrmap=None, busword=8, debug=False):
|
||||
self.interface = interface
|
||||
self.mode = mode
|
||||
self.tag = tag
|
||||
self.debug = debug
|
||||
self.com = FTDIComDevice(self.interface,
|
||||
mode=mode,
|
||||
uart_tag=tag,
|
||||
dma_tag=16, # XXX FIXME
|
||||
verbose=debug)
|
||||
if addrmap is not None:
|
||||
self.regs = build_map(addrmap, busword, self.read, self.write)
|
||||
|
||||
def open(self):
|
||||
self.com.open()
|
||||
|
||||
def close(self):
|
||||
self.com.close()
|
||||
|
||||
def read(self, addr, burst_length=1):
|
||||
datas = []
|
||||
msg = []
|
||||
self.com.uartflush()
|
||||
msg.append(self.cmds["read"])
|
||||
msg.append(burst_length)
|
||||
word_addr = addr//4
|
||||
msg.append((word_addr >> 24) & 0xff)
|
||||
msg.append((word_addr >> 16) & 0xff)
|
||||
msg.append((word_addr >> 8) & 0xff)
|
||||
msg.append((word_addr >> 0) & 0xff)
|
||||
self.com.uartwrite(msg)
|
||||
for i in range(burst_length):
|
||||
data = 0
|
||||
for k in range(4):
|
||||
data = data << 8
|
||||
data |= self.com.uartread()
|
||||
if self.debug:
|
||||
print("RD {:08X} @ {:08X}".format(data, addr + 4*i))
|
||||
datas.append(data)
|
||||
if burst_length == 1:
|
||||
return datas[0]
|
||||
else:
|
||||
return datas
|
||||
|
||||
def write(self, addr, data):
|
||||
if isinstance(data, list):
|
||||
burst_length = len(data)
|
||||
else:
|
||||
burst_length = 1
|
||||
data = [data]
|
||||
msg = []
|
||||
msg.append(self.cmds["write"])
|
||||
msg.append(burst_length)
|
||||
word_addr = addr//4
|
||||
msg.append((word_addr >> 24) & 0xff)
|
||||
msg.append((word_addr >> 16) & 0xff)
|
||||
msg.append((word_addr >> 8) & 0xff)
|
||||
msg.append((word_addr >> 0) & 0xff)
|
||||
for i in range(len(data)):
|
||||
dat = data[i]
|
||||
for j in range(4):
|
||||
msg.append((dat >> 24) & 0xff)
|
||||
dat = dat << 8
|
||||
if self.debug:
|
||||
print("WR {:08X} @ {:08X}".format(data[i], addr + 4*i))
|
||||
self.com.uartwrite(msg)
|
||||
|
||||
|
||||
def LiteUSBWishboneDriver(chip="ft2232h", *args, **kwargs):
|
||||
drivers = {
|
||||
"ft2232h": LiteUSBWishboneDriverFTDI
|
||||
}
|
||||
return drivers[chip](*args, **kwargs)
|
|
@ -1,13 +0,0 @@
|
|||
MSCDIR = ../../
|
||||
PYTHON = python3
|
||||
|
||||
CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
|
||||
|
||||
ft245_sync_tb:
|
||||
$(CMD) ft245_sync_tb.py
|
||||
|
||||
ft245_async_tb:
|
||||
$(CMD) ft245_async_tb.py
|
||||
|
||||
core_tb:
|
||||
$(CMD) core_tb.py
|
|
@ -1,16 +0,0 @@
|
|||
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
|
|
@ -1,148 +0,0 @@
|
|||
import binascii
|
||||
|
||||
from migen.fhdl.std import *
|
||||
from migen.flow.actor import *
|
||||
from migen.fhdl.specials import *
|
||||
|
||||
from migen.sim.generic import run_simulation
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
from misoclib.com.liteusb.core import LiteUSBCore
|
||||
from misoclib.com.liteusb.test.common import *
|
||||
|
||||
# XXX for now use it from liteeth to avoid duplication
|
||||
from misoclib.com.liteeth.test.common import *
|
||||
|
||||
def crc32(l):
|
||||
crc = []
|
||||
crc_bytes = split_bytes(binascii.crc32(bytes(l)), 4, "little")
|
||||
for byte in crc_bytes:
|
||||
crc.append(int(byte))
|
||||
return crc
|
||||
|
||||
|
||||
class USBPacket(Packet):
|
||||
def __init__(self, init=[]):
|
||||
Packet.__init__(self, init)
|
||||
self.crc_error = False
|
||||
|
||||
def check_remove_crc(self):
|
||||
if comp(self[-4:], crc32(self[:-4])):
|
||||
for i in range(4):
|
||||
self.pop()
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def decode_remove_header(self):
|
||||
header = []
|
||||
for byte in self[:packet_header.length]:
|
||||
header.append(self.pop(0))
|
||||
for k, v in sorted(packet_header.fields.items()):
|
||||
setattr(self, k, get_field_data(v, header))
|
||||
|
||||
def decode(self):
|
||||
# XXX Header should be protected by CRC
|
||||
self.decode_remove_header()
|
||||
self.crc_error = self.check_remove_crc()
|
||||
if self.crc_error:
|
||||
raise ValueError # XXX handle this properly
|
||||
|
||||
def encode_header(self):
|
||||
header = 0
|
||||
for k, v in sorted(packet_header.fields.items()):
|
||||
value = merge_bytes(split_bytes(getattr(self, k),
|
||||
math.ceil(v.width/8)),
|
||||
"little")
|
||||
header += (value << v.offset+(v.byte*8))
|
||||
for d in split_bytes(header, packet_header.length):
|
||||
self.insert(0, d)
|
||||
|
||||
def insert_crc(self):
|
||||
for d in crc32(self):
|
||||
self.append(d)
|
||||
|
||||
def encode(self):
|
||||
# XXX Header should be protected by CRC
|
||||
self.insert_crc()
|
||||
self.encode_header()
|
||||
|
||||
def __repr__(self):
|
||||
r = "--------\n"
|
||||
for k in sorted(packet_header.fields.keys()):
|
||||
r += k + " : 0x{:0x}\n".format(getattr(self, k))
|
||||
r += "payload: "
|
||||
for d in self:
|
||||
r += "{:02x}".format(d)
|
||||
return r
|
||||
|
||||
|
||||
class PHYModel(Module):
|
||||
def __init__(self):
|
||||
self.sink = Sink(phy_description(8))
|
||||
self.source = Source(phy_description(8))
|
||||
|
||||
class TB(Module):
|
||||
def __init__(self):
|
||||
self.submodules.phy = PHYModel()
|
||||
self.submodules.core = LiteUSBCore(self.phy)
|
||||
|
||||
self.submodules.phy_streamer = PacketStreamer(phy_description(8))
|
||||
self.submodules.phy_streamer_randomizer = AckRandomizer(phy_description(8), level=0)
|
||||
|
||||
self.submodules.phy_logger_randomizer = AckRandomizer(phy_description(8), level=0)
|
||||
self.submodules.phy_logger = PacketLogger(phy_description(8))
|
||||
|
||||
self.submodules.core_streamer = PacketStreamer(user_description(8))
|
||||
self.submodules.core_streamer_randomizer = AckRandomizer(user_description(8), level=10)
|
||||
|
||||
self.submodules.core_logger = PacketLogger(user_description(8))
|
||||
self.submodules.core_logger_randomizer = AckRandomizer(user_description(8), level=10)
|
||||
|
||||
|
||||
user_port = self.core.crossbar.get_port(0x12)
|
||||
|
||||
|
||||
self.comb += [
|
||||
Record.connect(self.phy_streamer.source, self.phy_streamer_randomizer.sink),
|
||||
Record.connect(self.phy_streamer_randomizer.source, self.phy.source),
|
||||
|
||||
Record.connect(self.core_streamer.source, self.core_streamer_randomizer.sink),
|
||||
Record.connect(self.core_streamer_randomizer.source, user_port.sink),
|
||||
|
||||
Record.connect(user_port.source, self.core_logger_randomizer.sink),
|
||||
Record.connect(self.core_logger_randomizer.source, self.core_logger.sink),
|
||||
|
||||
Record.connect(self.phy.sink, self.phy_logger_randomizer.sink),
|
||||
Record.connect(self.phy_logger_randomizer.source, self.phy_logger.sink)
|
||||
]
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
packet = USBPacket([i for i in range(128)])
|
||||
packet.preamble = 0x5AA55AA5
|
||||
packet.dst = 0x12
|
||||
packet.length = 128 + 4
|
||||
packet.encode()
|
||||
yield from self.phy_streamer.send(packet)
|
||||
for i in range(32):
|
||||
yield
|
||||
print(self.core_logger.packet)
|
||||
|
||||
selfp.core_streamer.source.dst = 0x12
|
||||
selfp.core_streamer.source.length = 128 + 4
|
||||
packet = Packet([i for i in range(128)])
|
||||
yield from self.core_streamer.send(packet)
|
||||
for i in range(32):
|
||||
yield
|
||||
for d in self.phy_logger.packet:
|
||||
print("%02x" %d, end="")
|
||||
print("")
|
||||
packet = USBPacket(self.phy_logger.packet)
|
||||
packet.decode()
|
||||
print(packet)
|
||||
|
||||
def main():
|
||||
run_simulation(TB(), ncycles=2000, vcd_name="my.vcd", keep_files=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,139 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.flow.actor import *
|
||||
from migen.fhdl.specials import *
|
||||
|
||||
from migen.sim.generic import run_simulation
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
from misoclib.com.liteusb.phy.ft245 import FT245PHYAsynchronous
|
||||
from misoclib.com.liteusb.test.common import *
|
||||
|
||||
# XXX for now use it from liteeth to avoid duplication
|
||||
from misoclib.com.liteeth.test.common import *
|
||||
|
||||
class FT245AsynchronousModel(Module):
|
||||
def __init__(self, clk_freq, rd_data):
|
||||
self.clk_freq = clk_freq
|
||||
self.rd_data = [0] + rd_data
|
||||
self.rd_idx = 0
|
||||
|
||||
# timings
|
||||
self.tRDInactive = self.ns(49) # RXF# inactive after RD# cycle
|
||||
self.tWRInactive = self.ns(49) # TXE# inactive after WR# cycle
|
||||
|
||||
# pads
|
||||
self.data = Signal(8)
|
||||
self.rxf_n = Signal(reset=1)
|
||||
self.txe_n = Signal(reset=1)
|
||||
self.rd_n = Signal(reset=1)
|
||||
self.wr_n = Signal(reset=1)
|
||||
|
||||
self.init = True
|
||||
self.wr_data = []
|
||||
self.wait_wr_n = False
|
||||
self.rd_done = 0
|
||||
|
||||
|
||||
self.data_w = Signal(8)
|
||||
self.data_r = Signal(8)
|
||||
|
||||
self.specials += Tristate(self.data, self.data_r, ~self.rd_n, self.data_w)
|
||||
|
||||
self.last_wr_n = 1
|
||||
self.last_rd_n = 1
|
||||
|
||||
self.wr_delay = 0
|
||||
self.rd_delay = 0
|
||||
|
||||
def wr_sim(self, selfp):
|
||||
if self.wr_delay:
|
||||
selfp.txe_n = 1
|
||||
self.wr_delay = self.wr_delay - 1
|
||||
else:
|
||||
if (not selfp.wr_n and self.last_wr_n) and not selfp.txe_n:
|
||||
self.wr_data.append(selfp.data_w)
|
||||
self.wr_delay = self.tWRInactive
|
||||
self.last_wr_n = selfp.wr_n
|
||||
|
||||
selfp.txe_n = 0
|
||||
|
||||
def rd_sim(self, selfp):
|
||||
if self.rd_delay:
|
||||
selfp.rxf_n = 1
|
||||
self.rd_delay = self.rd_delay - 1
|
||||
else:
|
||||
rxf_n = selfp.rxf_n
|
||||
if self.rd_idx < len(self.rd_data)-1:
|
||||
self.rd_done = selfp.rxf_n
|
||||
selfp.rxf_n = 0
|
||||
else:
|
||||
selfp.rxf_n = self.rd_done
|
||||
|
||||
if not selfp.rd_n and self.last_rd_n:
|
||||
if self.rd_idx < len(self.rd_data)-1:
|
||||
self.rd_idx += not rxf_n
|
||||
selfp.data_r = self.rd_data[self.rd_idx]
|
||||
self.rd_done = 1
|
||||
if selfp.rd_n and not self.last_rd_n:
|
||||
self.rd_delay = self.tRDInactive
|
||||
|
||||
self.last_rd_n = selfp.rd_n
|
||||
|
||||
def 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)
|
||||
|
||||
def ns(self, t, margin=True):
|
||||
clk_period_ns = 1000000000/self.clk_freq
|
||||
if margin:
|
||||
t += clk_period_ns/2
|
||||
return math.ceil(t/clk_period_ns)
|
||||
|
||||
|
||||
test_packet = [i%256 for i in range(128)]
|
||||
|
||||
|
||||
class TB(Module):
|
||||
def __init__(self):
|
||||
clk_freq = 50*1000000
|
||||
self.submodules.model = FT245AsynchronousModel(clk_freq, test_packet)
|
||||
self.submodules.phy = FT245PHYAsynchronous(self.model, clk_freq)
|
||||
|
||||
self.submodules.streamer = PacketStreamer(phy_description(8))
|
||||
self.submodules.streamer_randomizer = AckRandomizer(phy_description(8), level=10)
|
||||
|
||||
self.submodules.logger_randomizer = AckRandomizer(phy_description(8), level=10)
|
||||
self.submodules.logger = PacketLogger(phy_description(8))
|
||||
|
||||
self.comb += [
|
||||
Record.connect(self.streamer.source, self.streamer_randomizer.sink),
|
||||
self.phy.sink.stb.eq(self.streamer_randomizer.source.stb),
|
||||
self.phy.sink.data.eq(self.streamer_randomizer.source.data),
|
||||
self.streamer_randomizer.source.ack.eq(self.phy.sink.ack),
|
||||
|
||||
self.logger_randomizer.sink.stb.eq(self.phy.source.stb),
|
||||
self.logger_randomizer.sink.data.eq(self.phy.source.data),
|
||||
self.phy.source.ack.eq(self.logger_randomizer.sink.ack),
|
||||
Record.connect(self.logger_randomizer.source, self.logger.sink)
|
||||
]
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
yield from self.streamer.send(Packet(test_packet))
|
||||
for i in range(4000):
|
||||
yield
|
||||
s, l, e = check(test_packet, self.model.wr_data)
|
||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
s, l, e = check(test_packet, self.logger.packet)
|
||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
|
||||
def main():
|
||||
run_simulation(TB(), ncycles=8000, vcd_name="my.vcd", keep_files=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,127 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.flow.actor import *
|
||||
from migen.fhdl.specials import *
|
||||
|
||||
from migen.sim.generic import run_simulation
|
||||
|
||||
from misoclib.com.liteusb.common import *
|
||||
from misoclib.com.liteusb.phy.ft245 import FT245PHYSynchronous
|
||||
from misoclib.com.liteusb.test.common import *
|
||||
|
||||
# XXX for now use it from liteeth to avoid duplication
|
||||
from misoclib.com.liteeth.test.common import *
|
||||
|
||||
class FT245SynchronousModel(Module, RandRun):
|
||||
def __init__(self, rd_data):
|
||||
RandRun.__init__(self, 10)
|
||||
self.rd_data = [0] + rd_data
|
||||
self.rd_idx = 0
|
||||
|
||||
# pads
|
||||
self.data = Signal(8)
|
||||
self.rxf_n = Signal(reset=1)
|
||||
self.txe_n = Signal(reset=1)
|
||||
self.rd_n = Signal(reset=1)
|
||||
self.wr_n = Signal(reset=1)
|
||||
self.oe_n = Signal(reset=1)
|
||||
self.siwua = Signal()
|
||||
self.pwren_n = Signal(reset=1)
|
||||
|
||||
self.init = True
|
||||
self.wr_data = []
|
||||
self.wait_wr_n = False
|
||||
self.rd_done = 0
|
||||
|
||||
|
||||
self.data_w = Signal(8)
|
||||
self.data_r = Signal(8)
|
||||
|
||||
self.specials += Tristate(self.data, self.data_r, ~self.oe_n, self.data_w)
|
||||
|
||||
def wr_sim(self, selfp):
|
||||
if not selfp.wr_n and not selfp.txe_n:
|
||||
self.wr_data.append(selfp.data_w)
|
||||
self.wait_wr_n = False
|
||||
|
||||
if not self.wait_wr_n:
|
||||
if self.run:
|
||||
selfp.txe_n = 1
|
||||
else:
|
||||
if selfp.txe_n:
|
||||
self.wait_wr_n = True
|
||||
selfp.txe_n = 0
|
||||
|
||||
def rd_sim(self, selfp):
|
||||
rxf_n = selfp.rxf_n
|
||||
if self.run:
|
||||
if self.rd_idx < len(self.rd_data)-1:
|
||||
self.rd_done = selfp.rxf_n
|
||||
selfp.rxf_n = 0
|
||||
else:
|
||||
selfp.rxf_n = self.rd_done
|
||||
else:
|
||||
selfp.rxf_n = self.rd_done
|
||||
|
||||
if not selfp.rd_n and not selfp.oe_n:
|
||||
if self.rd_idx < len(self.rd_data)-1:
|
||||
self.rd_idx += not rxf_n
|
||||
selfp.data_r = self.rd_data[self.rd_idx]
|
||||
self.rd_done = 1
|
||||
|
||||
def do_simulation(self, selfp):
|
||||
RandRun.do_simulation(self, selfp)
|
||||
if self.init:
|
||||
selfp.rxf_n = 0
|
||||
self.wr_data = []
|
||||
self.init = False
|
||||
self.wr_sim(selfp)
|
||||
self.rd_sim(selfp)
|
||||
|
||||
test_packet = [i%256 for i in range(512)]
|
||||
|
||||
|
||||
class TB(Module):
|
||||
def __init__(self):
|
||||
self.submodules.model = FT245SynchronousModel(test_packet)
|
||||
self.submodules.phy = FT245PHYSynchronous(self.model)
|
||||
|
||||
self.submodules.streamer = PacketStreamer(phy_description(8))
|
||||
self.submodules.streamer_randomizer = AckRandomizer(phy_description(8), level=10)
|
||||
|
||||
self.submodules.logger_randomizer = AckRandomizer(phy_description(8), level=10)
|
||||
self.submodules.logger = PacketLogger(phy_description(8))
|
||||
|
||||
self.comb += [
|
||||
Record.connect(self.streamer.source, self.streamer_randomizer.sink),
|
||||
self.phy.sink.stb.eq(self.streamer_randomizer.source.stb),
|
||||
self.phy.sink.data.eq(self.streamer_randomizer.source.data),
|
||||
self.streamer_randomizer.source.ack.eq(self.phy.sink.ack),
|
||||
|
||||
self.logger_randomizer.sink.stb.eq(self.phy.source.stb),
|
||||
self.logger_randomizer.sink.data.eq(self.phy.source.data),
|
||||
self.phy.source.ack.eq(self.logger_randomizer.sink.ack),
|
||||
Record.connect(self.logger_randomizer.source, self.logger.sink)
|
||||
]
|
||||
|
||||
# Use sys_clk as ftdi_clk in simulation
|
||||
self.comb += [
|
||||
ClockSignal("ftdi").eq(ClockSignal()),
|
||||
ResetSignal("ftdi").eq(ResetSignal())
|
||||
]
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
yield from self.streamer.send(Packet(test_packet))
|
||||
for i in range(2000):
|
||||
yield
|
||||
s, l, e = check(test_packet, self.model.wr_data)
|
||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
s, l, e = check(test_packet, self.logger.packet[1:])
|
||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
|
||||
def main():
|
||||
run_simulation(TB(), ncycles=8000, vcd_name="my.vcd", keep_files=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue