mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
move liteusb to a separate repo (https://github.com/enjoy-digital/liteusb)
This commit is contained in:
parent
e49a3c20c8
commit
8e8cc8e5a6
36 changed files with 0 additions and 3102 deletions
|
@ -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 a new issue