mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
move litepcie to a separate repo (https://github.com/enjoy-digital/litepcie)
This commit is contained in:
parent
bedf3ed9a6
commit
35e3853f6e
56 changed files with 0 additions and 4124 deletions
1
README
1
README
|
@ -29,7 +29,6 @@
|
|||
and logic, etc.
|
||||
* Possibility to encapsulate legacy Verilog/VHDL code.
|
||||
* Complex FPGA cores that can be used integrated in MiSoC or standalone:
|
||||
- LitePcie: a small footprint and configuragle PCIe core
|
||||
- LiteEth: a small footprint and configurable Ethernet core
|
||||
- LiteSATA: a small footprint and configurable SATA core
|
||||
- LiteScope: a small footprint and configurable logic analyzer core
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
Unless otherwise noted, LitePCIe 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,123 +0,0 @@
|
|||
__ _ __ ___ _________
|
||||
/ / (_) /____ / _ \/ ___/ _/__
|
||||
/ /__/ / __/ -_) ___/ /___/ // -_)
|
||||
/____/_/\__/\__/_/ \___/___/\__/
|
||||
|
||||
Copyright 2015 / EnjoyDigital / M-Labs Ltd
|
||||
|
||||
A small footprint and configurable PCIe core
|
||||
with MMAP interface and scatter-gather DMA
|
||||
developed by EnjoyDigital
|
||||
|
||||
[> Doc
|
||||
---------
|
||||
XXX
|
||||
|
||||
[> Intro
|
||||
---------
|
||||
LitePCIe provides a small footprint and configurable PCIe gen1/2 core.
|
||||
|
||||
LitePCIe 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.
|
||||
|
||||
LitePCIe 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.
|
||||
|
||||
LitePCIe 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
|
||||
-----------
|
||||
- 7-Series Artix7/Kintex7 PHY (up to PCIe Gen2 X2)
|
||||
- Scatter-gather DMA
|
||||
- Wishbone bridge
|
||||
- Linux driver with DMA loopback demo and Sysfs
|
||||
|
||||
|
||||
[> Possibles improvements
|
||||
-------------------------
|
||||
- add standardized interfaces (AXI, Avalon-ST)
|
||||
- add support for PCIe Gen2 X4 and X8 on 7-Series
|
||||
- clean up 7-Series wrappers
|
||||
- add Altera/Lattice support
|
||||
- ... 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
|
||||
------------------
|
||||
1. Install Python3 and your vendor's software
|
||||
|
||||
2. Obtain Migen and install it:
|
||||
git clone https://github.com/m-labs/migen
|
||||
cd migen
|
||||
python3 setup.py install
|
||||
cd ..
|
||||
|
||||
3. Obtain MiSoC:
|
||||
git clone https://github.com/m-labs/misoc --recursive
|
||||
|
||||
4. Build and load PCIe DMA loopback design (only for KC705 for now):
|
||||
go to misoclib/com/litepcie/example_designs/
|
||||
run ./make.py all load-bitstream
|
||||
|
||||
5. Build and load Linux Driver:
|
||||
go to misoclib/com/litepcie/software/linux/kernel
|
||||
make all
|
||||
./init.sh
|
||||
|
||||
5. Build and load Linux utilities:
|
||||
go to misoclib/com/litepcie/software/linux/user
|
||||
make all
|
||||
./litepcie_util dma_loopback_test
|
||||
|
||||
[> Simulations:
|
||||
Simulations are available in misoclib/com/litepcie/test:
|
||||
- wishbone_tb
|
||||
- dma_tb
|
||||
To run a simulation, move to misoclib/com/litepcie/test/ and run:
|
||||
make simulation_name
|
||||
|
||||
[> Tests :
|
||||
A DMA loopback example with Wishbone over Sysfs is provided.
|
||||
Please go to Getting Started section to see how to run the tests.
|
||||
|
||||
[> License
|
||||
-----------
|
||||
LitePCIe is released under the very permissive two-clause BSD license. Under
|
||||
the terms of this license, you are authorized to use LiteEth 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 LitePCIe
|
||||
- cite LitePCIe 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 LitePCIe.
|
||||
|
||||
[> Support and consulting
|
||||
--------------------------
|
||||
We love open-source hardware and like sharing our designs with others.
|
||||
|
||||
LitePCIe is mainly developed and maintained by EnjoyDigital.
|
||||
|
||||
If you would like to know more about LitePCIe 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,67 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.genlib.record import *
|
||||
from migen.genlib.misc import reverse_bytes
|
||||
from migen.flow.actor import *
|
||||
from migen.actorlib.packet import Arbiter, Dispatcher
|
||||
|
||||
KB = 1024
|
||||
MB = 1024*KB
|
||||
GB = 1024*MB
|
||||
|
||||
|
||||
def get_bar_mask(size):
|
||||
mask = 0
|
||||
found = 0
|
||||
for i in range(32):
|
||||
if size%2:
|
||||
found = 1
|
||||
if found:
|
||||
mask |= (1 << i)
|
||||
size = size >> 1
|
||||
return mask
|
||||
|
||||
def phy_layout(dw):
|
||||
layout = [
|
||||
("dat", dw),
|
||||
("be", dw//8)
|
||||
]
|
||||
return EndpointDescription(layout, packetized=True)
|
||||
|
||||
|
||||
def request_layout(dw):
|
||||
layout = [
|
||||
("we", 1),
|
||||
("adr", 32),
|
||||
("len", 10),
|
||||
("req_id", 16),
|
||||
("tag", 8),
|
||||
("dat", dw),
|
||||
("channel", 8), # for routing
|
||||
("user_id", 8) # for packet identification
|
||||
]
|
||||
return EndpointDescription(layout, packetized=True)
|
||||
|
||||
|
||||
def completion_layout(dw):
|
||||
layout = [
|
||||
("adr", 32),
|
||||
("len", 10),
|
||||
("last", 1),
|
||||
("req_id", 16),
|
||||
("cmp_id", 16),
|
||||
("err", 1),
|
||||
("tag", 8),
|
||||
("dat", dw),
|
||||
("channel", 8), # for routing
|
||||
("user_id", 8) # for packet identification
|
||||
]
|
||||
return EndpointDescription(layout, packetized=True)
|
||||
|
||||
|
||||
def interrupt_layout():
|
||||
return [("dat", 8)]
|
||||
|
||||
|
||||
def dma_layout(dw):
|
||||
layout = [("data", dw)]
|
||||
return EndpointDescription(layout, packetized=True)
|
|
@ -1,40 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
from migen.genlib.record import *
|
||||
|
||||
from misoclib.com.litepcie.core.packet.depacketizer import Depacketizer
|
||||
from misoclib.com.litepcie.core.packet.packetizer import Packetizer
|
||||
from misoclib.com.litepcie.core.switch.crossbar import Crossbar
|
||||
|
||||
|
||||
class Endpoint(Module):
|
||||
def __init__(self, phy, max_pending_requests=4, with_reordering=False):
|
||||
self.phy = phy
|
||||
self.max_pending_requests = max_pending_requests
|
||||
|
||||
# # #
|
||||
|
||||
# TLP Packetizer / Depacketizer
|
||||
depacketizer = Depacketizer(phy.dw, phy.bar0_mask)
|
||||
packetizer = Packetizer(phy.dw)
|
||||
self.submodules += depacketizer, packetizer
|
||||
self.comb += [
|
||||
phy.source.connect(depacketizer.sink),
|
||||
packetizer.source.connect(phy.sink)
|
||||
]
|
||||
|
||||
# Crossbar
|
||||
self.crossbar = crossbar = Crossbar(phy.dw, max_pending_requests, with_reordering)
|
||||
self.submodules += crossbar
|
||||
|
||||
# (Slave) HOST initiates the transactions
|
||||
self.comb += [
|
||||
Record.connect(depacketizer.req_source, crossbar.phy_slave.sink),
|
||||
Record.connect(crossbar.phy_slave.source, packetizer.cmp_sink)
|
||||
]
|
||||
|
||||
# (Master) FPGA initiates the transactions
|
||||
self.comb += [
|
||||
Record.connect(crossbar.phy_master.source, packetizer.req_sink),
|
||||
Record.connect(depacketizer.cmp_source, crossbar.phy_master.sink)
|
||||
]
|
|
@ -1,26 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
|
||||
|
||||
class InterruptController(Module, AutoCSR):
|
||||
def __init__(self, n_irqs=32):
|
||||
self.irqs = Signal(n_irqs)
|
||||
self.source = Source(interrupt_layout())
|
||||
|
||||
self._enable = CSRStorage(n_irqs)
|
||||
self._clear = CSR(n_irqs)
|
||||
self._vector = CSRStatus(n_irqs)
|
||||
|
||||
# # #
|
||||
|
||||
enable = self._enable.storage
|
||||
clear = Signal(n_irqs)
|
||||
self.comb += If(self._clear.re, clear.eq(self._clear.r))
|
||||
|
||||
# memorize and clear irqs
|
||||
vector = self._vector.status
|
||||
self.sync += vector.eq(~clear & (vector | self.irqs))
|
||||
|
||||
self.comb += self.source.stb.eq((vector & enable) != 0)
|
|
@ -1,117 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.genlib.record import *
|
||||
from migen.flow.actor import EndpointDescription, Sink, Source
|
||||
from migen.actorlib.packet import HeaderField, Header
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
|
||||
# constants
|
||||
fmt_type_dict = {
|
||||
"mem_rd32": 0b0000000,
|
||||
"mem_wr32": 0b1000000,
|
||||
"mem_rd64": 0b0100000,
|
||||
"mem_wr64": 0b1100000,
|
||||
|
||||
"cpld": 0b1001010,
|
||||
"cpl": 0b0001010
|
||||
}
|
||||
|
||||
cpl_dict = {
|
||||
"sc": 0b000,
|
||||
"ur": 0b001,
|
||||
"crs": 0b010,
|
||||
"ca": 0b011
|
||||
}
|
||||
|
||||
max_request_size = 512
|
||||
|
||||
|
||||
# headers
|
||||
tlp_common_header_length = 16
|
||||
tlp_common_header_fields = {
|
||||
"fmt": HeaderField(0*4, 29, 2),
|
||||
"type": HeaderField(0*4, 24, 5),
|
||||
}
|
||||
tlp_common_header = Header(tlp_common_header_fields,
|
||||
tlp_common_header_length,
|
||||
swap_field_bytes=False)
|
||||
|
||||
|
||||
tlp_request_header_length = 16
|
||||
tlp_request_header_fields = {
|
||||
"fmt": HeaderField(0*4, 29, 2),
|
||||
"type": HeaderField(0*4, 24, 5),
|
||||
"tc": HeaderField(0*4, 20, 3),
|
||||
"td": HeaderField(0*4, 15, 1),
|
||||
"ep": HeaderField(0*4, 14, 1),
|
||||
"attr": HeaderField(0*4, 12, 2),
|
||||
"length": HeaderField(0*4, 0, 10),
|
||||
|
||||
"requester_id": HeaderField(1*4, 16, 16),
|
||||
"tag": HeaderField(1*4, 8, 8),
|
||||
"last_be": HeaderField(1*4, 4, 4),
|
||||
"first_be": HeaderField(1*4, 0, 4),
|
||||
|
||||
"address": HeaderField(2*4, 2, 30),
|
||||
}
|
||||
tlp_request_header = Header(tlp_request_header_fields,
|
||||
tlp_request_header_length,
|
||||
swap_field_bytes=False)
|
||||
|
||||
|
||||
tlp_completion_header_length = 16
|
||||
tlp_completion_header_fields = {
|
||||
"fmt": HeaderField(0*4, 29, 2),
|
||||
"type": HeaderField(0*4, 24, 5),
|
||||
"tc": HeaderField(0*4, 20, 3),
|
||||
"td": HeaderField(0*4, 15, 1),
|
||||
"ep": HeaderField(0*4, 14, 1),
|
||||
"attr": HeaderField(0*4, 12, 2),
|
||||
"length": HeaderField(0*4, 0, 10),
|
||||
|
||||
"completer_id": HeaderField(1*4, 16, 16),
|
||||
"status": HeaderField(1*4, 13, 3),
|
||||
"bcm": HeaderField(1*4, 12, 1),
|
||||
"byte_count": HeaderField(1*4, 0, 12),
|
||||
|
||||
"requester_id": HeaderField(2*4, 16, 16),
|
||||
"tag": HeaderField(2*4, 8, 8),
|
||||
"lower_address": HeaderField(2*4, 0, 7),
|
||||
}
|
||||
tlp_completion_header = Header(tlp_completion_header_fields,
|
||||
tlp_completion_header_length,
|
||||
swap_field_bytes=False)
|
||||
|
||||
|
||||
# layouts
|
||||
def tlp_raw_layout(dw):
|
||||
layout = [
|
||||
("header", 4*32),
|
||||
("dat", dw),
|
||||
("be", dw//8)
|
||||
]
|
||||
return EndpointDescription(layout, packetized=True)
|
||||
|
||||
|
||||
def tlp_common_layout(dw):
|
||||
layout = tlp_common_header.get_layout() + [
|
||||
("dat", dw),
|
||||
("be", dw//8)
|
||||
]
|
||||
return EndpointDescription(layout, packetized=True)
|
||||
|
||||
|
||||
def tlp_request_layout(dw):
|
||||
layout = tlp_request_header.get_layout() + [
|
||||
("dat", dw),
|
||||
("be", dw//8)
|
||||
]
|
||||
return EndpointDescription(layout, packetized=True)
|
||||
|
||||
|
||||
def tlp_completion_layout(dw):
|
||||
layout = tlp_completion_header.get_layout() + [
|
||||
("dat", dw),
|
||||
("be", dw//8)
|
||||
]
|
||||
return EndpointDescription(layout, packetized=True)
|
|
@ -1,160 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.actorlib.structuring import *
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
|
||||
|
||||
class HeaderExtracter(Module):
|
||||
def __init__(self, dw):
|
||||
self.sink = Sink(phy_layout(dw))
|
||||
self.source = Source(tlp_raw_layout(dw))
|
||||
|
||||
###
|
||||
|
||||
sink, source = self.sink, self.source
|
||||
|
||||
sop = Signal()
|
||||
shift = Signal()
|
||||
|
||||
sink_dat_r = Signal(dw)
|
||||
sink_be_r = Signal(dw//8)
|
||||
|
||||
fsm = FSM(reset_state="HEADER1")
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("HEADER1",
|
||||
sink.ack.eq(1),
|
||||
If(sink.stb,
|
||||
shift.eq(1),
|
||||
NextState("HEADER2")
|
||||
)
|
||||
)
|
||||
fsm.act("HEADER2",
|
||||
sink.ack.eq(1),
|
||||
If(sink.stb,
|
||||
shift.eq(1),
|
||||
If(sink.eop,
|
||||
sink.ack.eq(0),
|
||||
NextState("TERMINATE"),
|
||||
).Else(
|
||||
NextState("COPY")
|
||||
)
|
||||
)
|
||||
)
|
||||
self.sync += [
|
||||
If(shift, self.source.header.eq(Cat(self.source.header[64:], sink.dat))),
|
||||
If(sink.stb & sink.ack,
|
||||
sink_dat_r.eq(sink.dat),
|
||||
sink_be_r.eq(sink.be)
|
||||
)
|
||||
]
|
||||
fsm.act("COPY",
|
||||
sink.ack.eq(source.ack),
|
||||
source.stb.eq(sink.stb),
|
||||
source.sop.eq(sop),
|
||||
source.eop.eq(sink.eop),
|
||||
source.dat.eq(Cat(reverse_bytes(sink_dat_r[32:]), reverse_bytes(sink.dat[:32]))),
|
||||
source.be.eq(Cat(freversed(sink_be_r[4:]), freversed(sink.be[:4]))),
|
||||
If(source.stb & source.ack & source.eop,
|
||||
NextState("HEADER1")
|
||||
)
|
||||
)
|
||||
self.sync += \
|
||||
If(fsm.before_entering("COPY"),
|
||||
sop.eq(1)
|
||||
).Elif(source.stb & source.ack,
|
||||
sop.eq(0)
|
||||
)
|
||||
fsm.act("TERMINATE",
|
||||
sink.ack.eq(source.ack),
|
||||
source.stb.eq(1),
|
||||
source.sop.eq(1),
|
||||
source.eop.eq(1),
|
||||
source.dat.eq(reverse_bytes(sink.dat[32:])),
|
||||
source.be.eq(freversed(sink.be[4:])),
|
||||
If(source.stb & source.ack & source.eop,
|
||||
NextState("HEADER1")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Depacketizer(Module):
|
||||
def __init__(self, dw, address_mask=0):
|
||||
self.sink = Sink(phy_layout(dw))
|
||||
|
||||
self.req_source = Source(request_layout(dw))
|
||||
self.cmp_source = Source(completion_layout(dw))
|
||||
|
||||
###
|
||||
|
||||
# extract raw header
|
||||
header_extracter = HeaderExtracter(dw)
|
||||
self.submodules += header_extracter
|
||||
self.comb += Record.connect(self.sink, header_extracter.sink)
|
||||
header = header_extracter.source.header
|
||||
|
||||
|
||||
# dispatch data according to fmt/type
|
||||
dispatch_source = Source(tlp_common_layout(dw))
|
||||
dispatch_sinks = [Sink(tlp_common_layout(dw)) for i in range(2)]
|
||||
|
||||
self.comb += [
|
||||
dispatch_source.stb.eq(header_extracter.source.stb),
|
||||
header_extracter.source.ack.eq(dispatch_source.ack),
|
||||
dispatch_source.sop.eq(header_extracter.source.sop),
|
||||
dispatch_source.eop.eq(header_extracter.source.eop),
|
||||
dispatch_source.dat.eq(header_extracter.source.dat),
|
||||
dispatch_source.be.eq(header_extracter.source.be),
|
||||
tlp_common_header.decode(header, dispatch_source)
|
||||
]
|
||||
|
||||
self.submodules.dispatcher = Dispatcher(dispatch_source, dispatch_sinks)
|
||||
|
||||
fmt_type = Cat(dispatch_source.type, dispatch_source.fmt)
|
||||
self.comb += \
|
||||
If((fmt_type == fmt_type_dict["mem_rd32"]) | (fmt_type == fmt_type_dict["mem_wr32"]),
|
||||
self.dispatcher.sel.eq(0),
|
||||
).Elif((fmt_type == fmt_type_dict["cpld"]) | (fmt_type == fmt_type_dict["cpl"]),
|
||||
self.dispatcher.sel.eq(1),
|
||||
)
|
||||
|
||||
# decode TLP request and format local request
|
||||
tlp_req = Source(tlp_request_layout(dw))
|
||||
self.comb += Record.connect(dispatch_sinks[0], tlp_req)
|
||||
self.comb += tlp_request_header.decode(header, tlp_req)
|
||||
|
||||
req_source = self.req_source
|
||||
self.comb += [
|
||||
req_source.stb.eq(tlp_req.stb),
|
||||
req_source.we.eq(tlp_req.stb & (Cat(tlp_req.type, tlp_req.fmt) == fmt_type_dict["mem_wr32"])),
|
||||
tlp_req.ack.eq(req_source.ack),
|
||||
req_source.sop.eq(tlp_req.sop),
|
||||
req_source.eop.eq(tlp_req.eop),
|
||||
req_source.adr.eq(Cat(Signal(2), tlp_req.address & (~address_mask))),
|
||||
req_source.len.eq(tlp_req.length),
|
||||
req_source.req_id.eq(tlp_req.requester_id),
|
||||
req_source.tag.eq(tlp_req.tag),
|
||||
req_source.dat.eq(tlp_req.dat),
|
||||
]
|
||||
|
||||
# decode TLP completion and format local completion
|
||||
tlp_cmp = Source(tlp_completion_layout(dw))
|
||||
self.comb += Record.connect(dispatch_sinks[1], tlp_cmp)
|
||||
self.comb += tlp_completion_header.decode(header, tlp_cmp)
|
||||
|
||||
cmp_source = self.cmp_source
|
||||
self.comb += [
|
||||
cmp_source.stb.eq(tlp_cmp.stb),
|
||||
tlp_cmp.ack.eq(cmp_source.ack),
|
||||
cmp_source.sop.eq(tlp_cmp.sop),
|
||||
cmp_source.eop.eq(tlp_cmp.eop),
|
||||
cmp_source.len.eq(tlp_cmp.length),
|
||||
cmp_source.last.eq(tlp_cmp.length == (tlp_cmp.byte_count[2:])),
|
||||
cmp_source.adr.eq(tlp_cmp.lower_address),
|
||||
cmp_source.req_id.eq(tlp_cmp.requester_id),
|
||||
cmp_source.cmp_id.eq(tlp_cmp.completer_id),
|
||||
cmp_source.err.eq(tlp_cmp.status != 0),
|
||||
cmp_source.tag.eq(tlp_cmp.tag),
|
||||
cmp_source.dat.eq(tlp_cmp.dat)
|
||||
]
|
|
@ -1,187 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.actorlib.structuring import *
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
from migen.genlib.misc import chooser
|
||||
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
|
||||
|
||||
class HeaderInserter(Module):
|
||||
def __init__(self, dw):
|
||||
self.sink = sink = Sink(tlp_raw_layout(dw))
|
||||
self.source = source = Source(phy_layout(dw))
|
||||
|
||||
###
|
||||
|
||||
fsm = FSM(reset_state="HEADER1")
|
||||
self.submodules += fsm
|
||||
|
||||
sink_dat_r = Signal(dw)
|
||||
sink_eop_r = Signal()
|
||||
self.sync += \
|
||||
If(sink.stb & sink.ack,
|
||||
sink_dat_r.eq(sink.dat),
|
||||
sink_eop_r.eq(sink.eop)
|
||||
)
|
||||
|
||||
fsm.act("HEADER1",
|
||||
sink.ack.eq(1),
|
||||
If(sink.stb & sink.sop,
|
||||
sink.ack.eq(0),
|
||||
source.stb.eq(1),
|
||||
source.sop.eq(1),
|
||||
source.eop.eq(0),
|
||||
source.dat.eq(sink.header[:64]),
|
||||
source.be.eq(0xff),
|
||||
If(source.stb & source.ack,
|
||||
NextState("HEADER2"),
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("HEADER2",
|
||||
source.stb.eq(1),
|
||||
source.sop.eq(0),
|
||||
source.eop.eq(sink.eop),
|
||||
source.dat.eq(Cat(sink.header[64:96], reverse_bytes(sink.dat[:32]))),
|
||||
source.be.eq(Cat(Signal(4, reset=0xf), freversed(sink.be[:4]))),
|
||||
If(source.stb & source.ack,
|
||||
sink.ack.eq(1),
|
||||
If(source.eop,
|
||||
NextState("HEADER1")
|
||||
).Else(
|
||||
NextState("COPY")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("COPY",
|
||||
source.stb.eq(sink.stb | sink_eop_r),
|
||||
source.sop.eq(0),
|
||||
source.eop.eq(sink_eop_r),
|
||||
source.dat.eq(Cat(reverse_bytes(sink_dat_r[32:64]), reverse_bytes(sink.dat[:32]))),
|
||||
If(sink_eop_r,
|
||||
source.be.eq(0x0f)
|
||||
).Else(
|
||||
source.be.eq(0xff)
|
||||
),
|
||||
If(source.stb & source.ack,
|
||||
sink.ack.eq(~sink_eop_r),
|
||||
If(source.eop,
|
||||
NextState("HEADER1")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Packetizer(Module):
|
||||
def __init__(self, dw):
|
||||
self.req_sink = req_sink = Sink(request_layout(dw))
|
||||
self.cmp_sink = cmp_sink = Sink(completion_layout(dw))
|
||||
|
||||
self.source = Source(phy_layout(dw))
|
||||
|
||||
###
|
||||
|
||||
# format TLP request and encode it
|
||||
tlp_req = Sink(tlp_request_layout(dw))
|
||||
self.comb += [
|
||||
tlp_req.stb.eq(req_sink.stb),
|
||||
req_sink.ack.eq(tlp_req.ack),
|
||||
tlp_req.sop.eq(req_sink.sop),
|
||||
tlp_req.eop.eq(req_sink.eop),
|
||||
|
||||
If(req_sink.we,
|
||||
Cat(tlp_req.type, tlp_req.fmt).eq(fmt_type_dict["mem_wr32"])
|
||||
).Else(
|
||||
Cat(tlp_req.type, tlp_req.fmt).eq(fmt_type_dict["mem_rd32"])
|
||||
),
|
||||
|
||||
tlp_req.tc.eq(0),
|
||||
tlp_req.td.eq(0),
|
||||
tlp_req.ep.eq(0),
|
||||
tlp_req.attr.eq(0),
|
||||
tlp_req.length.eq(req_sink.len),
|
||||
|
||||
tlp_req.requester_id.eq(req_sink.req_id),
|
||||
tlp_req.tag.eq(req_sink.tag),
|
||||
If(req_sink.len > 1,
|
||||
tlp_req.last_be.eq(0xf)
|
||||
).Else(
|
||||
tlp_req.last_be.eq(0x0)
|
||||
),
|
||||
tlp_req.first_be.eq(0xf),
|
||||
tlp_req.address.eq(req_sink.adr[2:]),
|
||||
|
||||
tlp_req.dat.eq(req_sink.dat),
|
||||
If(req_sink.we,
|
||||
tlp_req.be.eq(0xff)
|
||||
).Else(
|
||||
tlp_req.be.eq(0x00)
|
||||
),
|
||||
]
|
||||
|
||||
tlp_raw_req = Sink(tlp_raw_layout(dw))
|
||||
self.comb += [
|
||||
tlp_raw_req.stb.eq(tlp_req.stb),
|
||||
tlp_req.ack.eq(tlp_raw_req.ack),
|
||||
tlp_raw_req.sop.eq(tlp_req.sop),
|
||||
tlp_raw_req.eop.eq(tlp_req.eop),
|
||||
tlp_request_header.encode(tlp_req, tlp_raw_req.header),
|
||||
tlp_raw_req.dat.eq(tlp_req.dat),
|
||||
tlp_raw_req.be.eq(tlp_req.be),
|
||||
]
|
||||
|
||||
# format TLP completion and encode it
|
||||
tlp_cmp = Sink(tlp_completion_layout(dw))
|
||||
self.comb += [
|
||||
tlp_cmp.stb.eq(cmp_sink.stb),
|
||||
cmp_sink.ack.eq(tlp_cmp.ack),
|
||||
tlp_cmp.sop.eq(cmp_sink.sop),
|
||||
tlp_cmp.eop.eq(cmp_sink.eop),
|
||||
|
||||
tlp_cmp.tc.eq(0),
|
||||
tlp_cmp.td.eq(0),
|
||||
tlp_cmp.ep.eq(0),
|
||||
tlp_cmp.attr.eq(0),
|
||||
tlp_cmp.length.eq(cmp_sink.len),
|
||||
|
||||
tlp_cmp.completer_id.eq(cmp_sink.cmp_id),
|
||||
If(cmp_sink.err,
|
||||
Cat(tlp_cmp.type, tlp_cmp.fmt).eq(fmt_type_dict["cpl"]),
|
||||
tlp_cmp.status.eq(cpl_dict["ur"])
|
||||
).Else(
|
||||
Cat(tlp_cmp.type, tlp_cmp.fmt).eq(fmt_type_dict["cpld"]),
|
||||
tlp_cmp.status.eq(cpl_dict["sc"])
|
||||
),
|
||||
tlp_cmp.bcm.eq(0),
|
||||
tlp_cmp.byte_count.eq(cmp_sink.len*4),
|
||||
|
||||
tlp_cmp.requester_id.eq(cmp_sink.req_id),
|
||||
tlp_cmp.tag.eq(cmp_sink.tag),
|
||||
tlp_cmp.lower_address.eq(cmp_sink.adr),
|
||||
|
||||
tlp_cmp.dat.eq(cmp_sink.dat),
|
||||
tlp_cmp.be.eq(0xff)
|
||||
]
|
||||
|
||||
tlp_raw_cmp = Sink(tlp_raw_layout(dw))
|
||||
self.comb += [
|
||||
tlp_raw_cmp.stb.eq(tlp_cmp.stb),
|
||||
tlp_cmp.ack.eq(tlp_raw_cmp.ack),
|
||||
tlp_raw_cmp.sop.eq(tlp_cmp.sop),
|
||||
tlp_raw_cmp.eop.eq(tlp_cmp.eop),
|
||||
tlp_completion_header.encode(tlp_cmp, tlp_raw_cmp.header),
|
||||
tlp_raw_cmp.dat.eq(tlp_cmp.dat),
|
||||
tlp_raw_cmp.be.eq(tlp_cmp.be),
|
||||
]
|
||||
|
||||
# arbitrate
|
||||
tlp_raw = Sink(tlp_raw_layout(dw))
|
||||
self.submodules.arbitrer = Arbiter([tlp_raw_req, tlp_raw_cmp], tlp_raw)
|
||||
|
||||
# insert header
|
||||
header_inserter = HeaderInserter(dw)
|
||||
self.submodules += header_inserter
|
||||
self.comb += [
|
||||
Record.connect(tlp_raw, header_inserter.sink),
|
||||
Record.connect(header_inserter.source, self.source)
|
||||
]
|
|
@ -1,34 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
|
||||
|
||||
class SlaveInternalPort:
|
||||
def __init__(self, dw, address_decoder=None):
|
||||
self.address_decoder = address_decoder
|
||||
self.sink = Sink(completion_layout(dw))
|
||||
self.source = Source(request_layout(dw))
|
||||
|
||||
|
||||
class MasterInternalPort:
|
||||
def __init__(self, dw, channel=None, write_only=False, read_only=False):
|
||||
self.channel = channel
|
||||
self.write_only = write_only
|
||||
self.read_only = read_only
|
||||
self.sink = Sink(request_layout(dw))
|
||||
self.source = Source(completion_layout(dw))
|
||||
|
||||
|
||||
class SlavePort:
|
||||
def __init__(self, port):
|
||||
self.address_decoder = port.address_decoder
|
||||
self.sink = port.source
|
||||
self.source = port.sink
|
||||
|
||||
|
||||
class MasterPort:
|
||||
def __init__(self, port):
|
||||
self.channel = port.channel
|
||||
self.sink = port.source
|
||||
self.source = port.sink
|
|
@ -1,120 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core.switch.common import *
|
||||
from misoclib.com.litepcie.core.switch.request_controller import RequestController
|
||||
|
||||
|
||||
class Crossbar(Module, AutoCSR):
|
||||
def __init__(self, dw, max_pending_requests, with_reordering=False):
|
||||
self.dw = dw
|
||||
self.max_pending_requests = max_pending_requests
|
||||
self.with_reordering = with_reordering
|
||||
|
||||
self.master = MasterInternalPort(dw)
|
||||
self.slave = SlaveInternalPort(dw)
|
||||
self.phy_master = MasterPort(self.master)
|
||||
self.phy_slave = SlavePort(self.slave)
|
||||
|
||||
self.user_masters = []
|
||||
self.user_masters_channel = 0
|
||||
self.user_slaves = []
|
||||
|
||||
def get_slave_port(self, address_decoder):
|
||||
s = SlaveInternalPort(self.dw, address_decoder)
|
||||
self.user_slaves.append(s)
|
||||
return SlavePort(s)
|
||||
|
||||
def get_master_port(self, write_only=False, read_only=False):
|
||||
m = MasterInternalPort(self.dw, self.user_masters_channel, write_only, read_only)
|
||||
self.user_masters_channel += 1
|
||||
self.user_masters.append(m)
|
||||
return MasterPort(m)
|
||||
|
||||
def filter_masters(self, write_only, read_only):
|
||||
masters = []
|
||||
for m in self.user_masters:
|
||||
if m.write_only == write_only and m.read_only == read_only:
|
||||
masters.append(m)
|
||||
return masters
|
||||
|
||||
def slave_dispatch_arbitrate(self, slaves, slave):
|
||||
# dispatch
|
||||
s_sources = [s.source for s in slaves]
|
||||
s_dispatcher = Dispatcher(slave.source, s_sources, one_hot=True)
|
||||
self.submodules += s_dispatcher
|
||||
for i, s in enumerate(slaves):
|
||||
self.comb += s_dispatcher.sel[i].eq(s.address_decoder(slave.source.adr))
|
||||
|
||||
# arbitrate
|
||||
s_sinks = [s.sink for s in slaves]
|
||||
s_arbiter = Arbiter(s_sinks, slave.sink)
|
||||
self.submodules += s_arbiter
|
||||
|
||||
def master_arbitrate_dispatch(self, masters, master):
|
||||
# arbitrate
|
||||
m_sinks = [m.sink for m in masters]
|
||||
m_arbiter = Arbiter(m_sinks, master.sink)
|
||||
self.submodules += m_arbiter
|
||||
|
||||
# dispatch
|
||||
m_sources = [m.source for m in masters]
|
||||
m_dispatcher = Dispatcher(master.source, m_sources)
|
||||
self.submodules += m_dispatcher
|
||||
self.comb += m_dispatcher.sel.eq(master.source.channel)
|
||||
|
||||
def do_finalize(self):
|
||||
# Slave path
|
||||
# Dispatch request to user sources (according to address decoder)
|
||||
# Arbitrate completion from user sinks
|
||||
if self.user_slaves != []:
|
||||
self.slave_dispatch_arbitrate(self.user_slaves, self.slave)
|
||||
|
||||
# Master path
|
||||
# Abritrate requests from user sinks
|
||||
# Dispatch completion to user sources (according to channel)
|
||||
|
||||
# +-------+
|
||||
# reqs---> | RD |
|
||||
# cmps<--- | PORTS |---------+
|
||||
# +-------+ +---+----+ +----------+
|
||||
# |Arb/Disp|-->|Controller|--+
|
||||
# +-------+ +---+----+ +----------+ |
|
||||
# reqs---> | RW | | |
|
||||
# cmps<--- | PORTS |---------+ |
|
||||
# +-------+ +---+----+
|
||||
# |Arb/Disp|<--> to/from Packetizer/
|
||||
# +-------+ +---+----+ Depacketizer
|
||||
# reqs---> | WR | +--------+ |
|
||||
# cmps<--- | PORTS |-----|Arb/Disp|-----------------+
|
||||
# +-------+ +--------+
|
||||
#
|
||||
# The controller blocks RD requests when the max number of pending
|
||||
# requests have been sent (max_pending_requests parameters).
|
||||
# To avoid blocking write_only ports when RD requests are blocked,
|
||||
# a separate arbitration stage is used.
|
||||
|
||||
if self.user_masters != []:
|
||||
masters = []
|
||||
|
||||
# Arbitrate / dispatch read_only / read_write ports
|
||||
# and insert controller
|
||||
rd_rw_masters = self.filter_masters(False, True)
|
||||
rd_rw_masters += self.filter_masters(False, False)
|
||||
if rd_rw_masters != []:
|
||||
rd_rw_master = MasterInternalPort(self.dw)
|
||||
controller = RequestController(self.dw, self.max_pending_requests, self.with_reordering)
|
||||
self.submodules += controller
|
||||
self.master_arbitrate_dispatch(rd_rw_masters, controller.master_in)
|
||||
masters.append(controller.master_out)
|
||||
|
||||
# Arbitrate / dispatch write_only ports
|
||||
wr_masters = self.filter_masters(True, False)
|
||||
if wr_masters != []:
|
||||
wr_master = MasterInternalPort(self.dw)
|
||||
self.master_arbitrate_dispatch(wr_masters, wr_master)
|
||||
masters.append(wr_master)
|
||||
|
||||
# Final Arbitrate / dispatch stage
|
||||
self.master_arbitrate_dispatch(masters, self.master)
|
|
@ -1,175 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.actorlib.structuring import *
|
||||
from migen.genlib.fifo import SyncFIFO
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
from migen.actorlib.fifo import SyncFIFO as SyncFlowFIFO
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
from misoclib.com.litepcie.core.switch.common import *
|
||||
|
||||
|
||||
class Reordering(Module):
|
||||
def __init__(self, dw, max_pending_requests):
|
||||
self.sink = Sink(completion_layout(dw))
|
||||
self.source = Source(completion_layout(dw))
|
||||
|
||||
self.req_we = Signal()
|
||||
self.req_tag = Signal(log2_int(max_pending_requests))
|
||||
|
||||
# # #
|
||||
|
||||
tag_buffer = SyncFIFO(log2_int(max_pending_requests), 2*max_pending_requests)
|
||||
self.submodules += tag_buffer
|
||||
self.comb += [
|
||||
tag_buffer.we.eq(self.req_we),
|
||||
tag_buffer.din.eq(self.req_tag)
|
||||
]
|
||||
|
||||
reorder_buffers = [SyncFlowFIFO(completion_layout(dw), 2*max_request_size//(dw//8), buffered=True)
|
||||
for i in range(max_pending_requests)]
|
||||
self.submodules += iter(reorder_buffers)
|
||||
|
||||
# store incoming completion in "sink.tag" buffer
|
||||
cases = {}
|
||||
for i in range(max_pending_requests):
|
||||
cases[i] = [Record.connect(self.sink, reorder_buffers[i].sink)]
|
||||
cases["default"] = [self.sink.ack.eq(1)]
|
||||
self.comb += Case(self.sink.tag, cases)
|
||||
|
||||
# read buffer according to tag_buffer order
|
||||
cases = {}
|
||||
for i in range(max_pending_requests):
|
||||
cases[i] = [Record.connect(reorder_buffers[i].source, self.source)]
|
||||
cases["default"] = []
|
||||
self.comb += [
|
||||
Case(tag_buffer.dout, cases),
|
||||
If(self.source.stb & self.source.eop & self.source.last,
|
||||
tag_buffer.re.eq(self.source.ack)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class RequestController(Module):
|
||||
def __init__(self, dw, max_pending_requests, with_reordering=False):
|
||||
self.master_in = MasterInternalPort(dw)
|
||||
self.master_out = MasterInternalPort(dw)
|
||||
|
||||
# # #
|
||||
|
||||
req_sink, req_source = self.master_in.sink, self.master_out.sink
|
||||
cmp_sink, cmp_source = self.master_out.source, self.master_in.source
|
||||
|
||||
tag_fifo = SyncFIFO(log2_int(max_pending_requests), max_pending_requests)
|
||||
self.submodules += tag_fifo
|
||||
|
||||
info_mem = Memory(16, max_pending_requests)
|
||||
info_mem_wr_port = info_mem.get_port(write_capable=True)
|
||||
info_mem_rd_port = info_mem.get_port(async_read=False)
|
||||
self.specials += info_mem, info_mem_wr_port, info_mem_rd_port
|
||||
|
||||
req_tag = Signal(max=max_pending_requests)
|
||||
self.sync += \
|
||||
If(tag_fifo.re,
|
||||
req_tag.eq(tag_fifo.dout)
|
||||
)
|
||||
|
||||
# requests mgt
|
||||
req_fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += req_fsm
|
||||
|
||||
req_fsm.act("IDLE",
|
||||
req_sink.ack.eq(0),
|
||||
If(req_sink.stb & req_sink.sop & ~req_sink.we & tag_fifo.readable,
|
||||
tag_fifo.re.eq(1),
|
||||
NextState("SEND_READ")
|
||||
).Elif(req_sink.stb & req_sink.sop & req_sink.we,
|
||||
NextState("SEND_WRITE")
|
||||
)
|
||||
)
|
||||
req_fsm.act("SEND_READ",
|
||||
Record.connect(req_sink, req_source),
|
||||
req_sink.ack.eq(0),
|
||||
req_source.tag.eq(req_tag),
|
||||
If(req_source.stb & req_source.eop & req_source.ack,
|
||||
NextState("UPDATE_INFO_MEM")
|
||||
)
|
||||
)
|
||||
req_fsm.act("SEND_WRITE",
|
||||
Record.connect(req_sink, req_source),
|
||||
req_source.tag.eq(32),
|
||||
If(req_source.stb & req_source.eop & req_source.ack,
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
req_fsm.act("UPDATE_INFO_MEM",
|
||||
info_mem_wr_port.we.eq(1),
|
||||
info_mem_wr_port.adr.eq(req_tag),
|
||||
info_mem_wr_port.dat_w[0:8].eq(req_sink.channel),
|
||||
info_mem_wr_port.dat_w[8:16].eq(req_sink.user_id),
|
||||
req_sink.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
|
||||
|
||||
# completions mgt
|
||||
if with_reordering:
|
||||
self.submodules.reordering = Reordering(dw, max_pending_requests)
|
||||
self.comb += [
|
||||
self.reordering.req_we.eq(info_mem_wr_port.we),
|
||||
self.reordering.req_tag.eq(info_mem_wr_port.adr),
|
||||
Record.connect(self.reordering.source, cmp_source)
|
||||
]
|
||||
cmp_source = self.reordering.sink
|
||||
|
||||
cmp_fsm = FSM(reset_state="INIT")
|
||||
self.submodules += cmp_fsm
|
||||
|
||||
tag_cnt = Signal(max=max_pending_requests)
|
||||
inc_tag_cnt = Signal()
|
||||
self.sync += \
|
||||
If(inc_tag_cnt,
|
||||
tag_cnt.eq(tag_cnt+1)
|
||||
)
|
||||
|
||||
cmp_fsm.act("INIT",
|
||||
inc_tag_cnt.eq(1),
|
||||
tag_fifo.we.eq(1),
|
||||
tag_fifo.din.eq(tag_cnt),
|
||||
If(tag_cnt == (max_pending_requests-1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
cmp_fsm.act("IDLE",
|
||||
cmp_sink.ack.eq(1),
|
||||
info_mem_rd_port.adr.eq(cmp_sink.tag),
|
||||
If(cmp_sink.stb & cmp_sink.sop,
|
||||
cmp_sink.ack.eq(0),
|
||||
NextState("COPY"),
|
||||
)
|
||||
)
|
||||
cmp_fsm.act("COPY",
|
||||
info_mem_rd_port.adr.eq(cmp_sink.tag),
|
||||
If(cmp_sink.stb & cmp_sink.eop & cmp_sink.last,
|
||||
cmp_sink.ack.eq(0),
|
||||
NextState("UPDATE_TAG_FIFO"),
|
||||
).Else(
|
||||
Record.connect(cmp_sink, cmp_source),
|
||||
If(cmp_sink.stb & cmp_sink.eop & cmp_sink.ack,
|
||||
NextState("IDLE")
|
||||
)
|
||||
),
|
||||
cmp_source.channel.eq(info_mem_rd_port.dat_r[0:8]),
|
||||
cmp_source.user_id.eq(info_mem_rd_port.dat_r[8:16]),
|
||||
)
|
||||
cmp_fsm.act("UPDATE_TAG_FIFO",
|
||||
tag_fifo.we.eq(1),
|
||||
tag_fifo.din.eq(cmp_sink.tag),
|
||||
info_mem_rd_port.adr.eq(cmp_sink.tag),
|
||||
Record.connect(cmp_sink, cmp_source),
|
||||
If(cmp_sink.stb & cmp_sink.ack,
|
||||
NextState("IDLE")
|
||||
),
|
||||
cmp_source.channel.eq(info_mem_rd_port.dat_r[0:8]),
|
||||
cmp_source.user_id.eq(info_mem_rd_port.dat_r[8:16]),
|
||||
)
|
|
@ -1,148 +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.com.litepcie.common import *
|
||||
|
||||
|
||||
def _import(default, name):
|
||||
return importlib.import_module(default + "." + name)
|
||||
|
||||
|
||||
def _get_args():
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""\
|
||||
LitePCIe - based on Migen.
|
||||
|
||||
This program builds and/or loads LitePCIe 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.
|
||||
build-csr-header save CSR map into C header file.
|
||||
|
||||
load-bitstream load bitstream into volatile storage.
|
||||
|
||||
all clean, build-csr-csv, build-bitstream, load-bitstream.
|
||||
""")
|
||||
|
||||
parser.add_argument("-t", "--target", default="dma", 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("--csr_csv", default="./test/csr.csv", help="CSV file to save the CSR map into")
|
||||
parser.add_argument("--csr_header", default="../software/linux/kernel/csr.h", help="C header 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:
|
||||
platform_name = top_class.default_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-csr-header", "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 PCIe
|
||||
core powered by Migen
|
||||
====== Building options: ======
|
||||
Platform: {}
|
||||
Target: {}
|
||||
Subtarget: {}
|
||||
System Clk: {} MHz
|
||||
===============================""".format(
|
||||
platform_name,
|
||||
args.target,
|
||||
top_class.__name__,
|
||||
soc.clk_freq/1000000
|
||||
)
|
||||
)
|
||||
|
||||
# dependencies
|
||||
if actions["all"]:
|
||||
actions["build-csr-csv"] = True
|
||||
actions["build-csr-header"] = True
|
||||
actions["build-bitstream"] = True
|
||||
actions["load-bitstream"] = True
|
||||
|
||||
if actions["build-bitstream"]:
|
||||
actions["build-csr-csv"] = True
|
||||
actions["build-csr-header"] = 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-csr-header"]:
|
||||
csr_header = cpuif.get_csr_header(csr_regions, soc.get_constants(), with_access_functions=False)
|
||||
write_to_file(args.csr_header, csr_header)
|
||||
|
||||
if actions["build-bitstream"]:
|
||||
vns = platform.build(soc, build_name=build_name)
|
||||
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,99 +0,0 @@
|
|||
from migen.bus import wishbone
|
||||
from migen.genlib.io import CRG
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from migen.genlib.misc import timeline
|
||||
|
||||
from misoclib.soc import SoC
|
||||
from misoclib.tools.litescope.common import *
|
||||
|
||||
from misoclib.com.uart.bridge import UARTWishboneBridge
|
||||
|
||||
from misoclib.com.litepcie.phy.s7pciephy import S7PCIEPHY
|
||||
from misoclib.com.litepcie.core import Endpoint
|
||||
from misoclib.com.litepcie.core.irq.interrupt_controller import InterruptController
|
||||
from misoclib.com.litepcie.frontend.dma import DMA
|
||||
from misoclib.com.litepcie.frontend.wishbone import LitePCIeWishboneBridge
|
||||
|
||||
|
||||
class _CRG(Module, AutoCSR):
|
||||
def __init__(self, platform):
|
||||
self.clock_domains.cd_sys = ClockDomain("sys")
|
||||
self.clock_domains.cd_clk125 = ClockDomain("clk125")
|
||||
|
||||
# soft reset generaton
|
||||
self._soft_rst = CSR()
|
||||
soft_rst = Signal()
|
||||
# trigger soft reset 1us after CSR access to terminate
|
||||
# Wishbone access when reseting from PCIe
|
||||
self.sync += [
|
||||
timeline(self._soft_rst.re & self._soft_rst.r, [(125, [soft_rst.eq(1)])]),
|
||||
]
|
||||
|
||||
# sys_clk / sys_rst (from PCIe)
|
||||
self.comb += self.cd_sys.clk.eq(self.cd_clk125.clk)
|
||||
self.specials += AsyncResetSynchronizer(self.cd_sys, self.cd_clk125.rst | soft_rst)
|
||||
|
||||
# scratch register
|
||||
self._scratch = CSR(32)
|
||||
self.sync += If(self._scratch.re, self._scratch.w.eq(self._scratch.r))
|
||||
|
||||
|
||||
class PCIeDMASoC(SoC):
|
||||
default_platform = "kc705"
|
||||
csr_map = {
|
||||
"crg": 16,
|
||||
"pcie_phy": 17,
|
||||
"dma": 18,
|
||||
"irq_controller": 19
|
||||
}
|
||||
csr_map.update(SoC.csr_map)
|
||||
interrupt_map = {
|
||||
"dma_writer": 0,
|
||||
"dma_reader": 1
|
||||
}
|
||||
interrupt_map.update(SoC.interrupt_map)
|
||||
mem_map = {
|
||||
"csr": 0x00000000, # (shadow @0x80000000)
|
||||
}
|
||||
mem_map.update(SoC.csr_map)
|
||||
|
||||
def __init__(self, platform, with_uart_bridge=True):
|
||||
clk_freq = 125*1000000
|
||||
SoC.__init__(self, platform, clk_freq,
|
||||
cpu_type="none",
|
||||
shadow_base=0x00000000,
|
||||
with_csr=True, csr_data_width=32,
|
||||
with_uart=False,
|
||||
with_identifier=True,
|
||||
with_timer=False
|
||||
)
|
||||
self.submodules.crg = _CRG(platform)
|
||||
platform.misoc_path = "../../../../"
|
||||
|
||||
# PCIe endpoint
|
||||
self.submodules.pcie_phy = S7PCIEPHY(platform, link_width=2)
|
||||
self.submodules.pcie_endpoint = Endpoint(self.pcie_phy, with_reordering=True)
|
||||
|
||||
# PCIe Wishbone bridge
|
||||
self.add_cpu_or_bridge(LitePCIeWishboneBridge(self.pcie_endpoint, lambda a: 1))
|
||||
self.add_wb_master(self.cpu_or_bridge.wishbone)
|
||||
|
||||
# PCIe DMA
|
||||
self.submodules.dma = DMA(self.pcie_phy, self.pcie_endpoint, with_loopback=True)
|
||||
self.dma.source.connect(self.dma.sink)
|
||||
|
||||
if with_uart_bridge:
|
||||
self.submodules.uart_bridge = UARTWishboneBridge(platform.request("serial"), clk_freq, baudrate=115200)
|
||||
self.add_wb_master(self.uart_bridge.wishbone)
|
||||
|
||||
# IRQs
|
||||
self.submodules.irq_controller = InterruptController()
|
||||
self.comb += self.irq_controller.source.connect(self.pcie_phy.interrupt)
|
||||
self.interrupts = {
|
||||
"dma_writer": self.dma.writer.table.irq,
|
||||
"dma_reader": self.dma.reader.table.irq
|
||||
}
|
||||
for k, v in sorted(self.interrupts.items()):
|
||||
self.comb += self.irq_controller.irqs[self.interrupt_map[k]].eq(v)
|
||||
|
||||
default_subtarget = PCIeDMASoC
|
|
@ -1,41 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import importlib
|
||||
|
||||
|
||||
def _get_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-b", "--bridge", default="uart", help="Bridge to use")
|
||||
parser.add_argument("--port", default="2", help="UART port")
|
||||
parser.add_argument("--baudrate", default=115200, help="UART baudrate")
|
||||
parser.add_argument("--ip_address", default="192.168.0.42", help="Etherbone IP address")
|
||||
parser.add_argument("--udp_port", default=20000, help="Etherbone UDP port")
|
||||
parser.add_argument("--bar", default="/sys/bus/pci/devices/0000:04:00.0/resource0", help="PCIe BAR")
|
||||
parser.add_argument("--bar_size", default=1*1024*1024, help="PCIe BAR size")
|
||||
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()
|
||||
if args.bridge == "uart":
|
||||
from misoclib.com.uart.software.wishbone import UARTWishboneBridgeDriver
|
||||
port = args.port if not args.port.isdigit() else int(args.port)
|
||||
wb = UARTWishboneBridgeDriver(port, args.baudrate, "./csr.csv", int(args.busword), debug=False)
|
||||
elif args.bridge == "etherbone":
|
||||
from misoclib.com.liteeth.software.wishbone import LiteETHWishboneDriver
|
||||
wb = LiteETHWishboneDriver(args.ip_address, int(args.udp_port), "./csr.csv", int(args.busword), debug=False)
|
||||
elif args.bridge == "pcie":
|
||||
from misoclib.com.litepcie.software.linux.wishbone import LitePCIeWishboneDriver
|
||||
wb = LitePCIeWishboneDriver(args.bar, args.bar_size, "./csr.csv", int(args.busword), debug=False)
|
||||
else:
|
||||
ValueError("Invalid bridge {}".format(args.bridge))
|
||||
|
||||
def _import(name):
|
||||
return importlib.import_module(name)
|
||||
|
||||
for test in args.test:
|
||||
t = _import(test)
|
||||
t.main(wb)
|
|
@ -1,14 +0,0 @@
|
|||
def main(wb):
|
||||
wb.open()
|
||||
regs = wb.regs
|
||||
# # #
|
||||
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)))
|
||||
print("link up : {}".format(regs.pcie_phy_lnk_up.read()))
|
||||
print("bus_master_enable : {}".format(regs.pcie_phy_bus_master_enable.read()))
|
||||
print("msi_enable : {}".format(regs.pcie_phy_msi_enable.read()))
|
||||
print("max_req_request_size : {}".format(regs.pcie_phy_max_request_size.read()))
|
||||
print("max_payload_size : {}".format(regs.pcie_phy_max_payload_size.read()))
|
||||
# # #
|
||||
wb.close()
|
|
@ -1,121 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
from migen.actorlib.fifo import SyncFIFO as FIFO
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.frontend.dma.common import *
|
||||
from misoclib.com.litepcie.frontend.dma.writer import DMAWriter
|
||||
from misoclib.com.litepcie.frontend.dma.reader import DMAReader
|
||||
|
||||
|
||||
class DMALoopback(Module, AutoCSR):
|
||||
def __init__(self, dw):
|
||||
self._enable = CSRStorage()
|
||||
|
||||
self.sink = Sink(dma_layout(dw))
|
||||
self.source = Source(dma_layout(dw))
|
||||
|
||||
self.next_source = Source(dma_layout(dw))
|
||||
self.next_sink = Sink(dma_layout(dw))
|
||||
|
||||
# # #
|
||||
|
||||
enable = self._enable.storage
|
||||
self.comb += \
|
||||
If(enable,
|
||||
Record.connect(self.sink, self.source)
|
||||
).Else(
|
||||
Record.connect(self.sink, self.next_source),
|
||||
Record.connect(self.next_sink, self.source)
|
||||
)
|
||||
|
||||
|
||||
class DMASynchronizer(Module, AutoCSR):
|
||||
def __init__(self, dw):
|
||||
self._bypass = CSRStorage()
|
||||
self._enable = CSRStorage()
|
||||
self.ready = Signal(reset=1)
|
||||
self.pps = Signal()
|
||||
|
||||
self.sink = Sink(dma_layout(dw))
|
||||
self.source = Source(dma_layout(dw))
|
||||
|
||||
self.next_source = Source(dma_layout(dw))
|
||||
self.next_sink = Sink(dma_layout(dw))
|
||||
|
||||
# # #
|
||||
|
||||
bypass = self._bypass.storage
|
||||
enable = self._enable.storage
|
||||
synced = Signal()
|
||||
|
||||
self.sync += \
|
||||
If(~enable,
|
||||
synced.eq(0)
|
||||
).Else(
|
||||
If(self.ready & self.sink.stb & (self.pps | bypass),
|
||||
synced.eq(1)
|
||||
)
|
||||
)
|
||||
|
||||
self.comb += \
|
||||
If(synced,
|
||||
Record.connect(self.sink, self.next_source),
|
||||
Record.connect(self.next_sink, self.source),
|
||||
).Else(
|
||||
# Block sink
|
||||
self.next_source.stb.eq(0),
|
||||
self.sink.ack.eq(0),
|
||||
|
||||
# Ack next_sink
|
||||
self.source.stb.eq(0),
|
||||
self.next_sink.ack.eq(1),
|
||||
)
|
||||
|
||||
|
||||
class DMABuffering(Module, AutoCSR):
|
||||
def __init__(self, dw, depth):
|
||||
tx_fifo = FIFO(dma_layout(dw), depth//(dw//8), buffered=True)
|
||||
rx_fifo = FIFO(dma_layout(dw), depth//(dw//8), buffered=True)
|
||||
self.submodules += tx_fifo, rx_fifo
|
||||
|
||||
self.sink = tx_fifo.sink
|
||||
self.source = rx_fifo.source
|
||||
|
||||
self.next_source = tx_fifo.source
|
||||
self.next_sink = rx_fifo.sink
|
||||
|
||||
|
||||
class DMA(Module, AutoCSR):
|
||||
def __init__(self, phy, endpoint,
|
||||
with_buffering=False, buffering_depth=256*8,
|
||||
with_loopback=False,
|
||||
with_synchronizer=False):
|
||||
|
||||
# Writer, Reader
|
||||
self.submodules.writer = DMAWriter(endpoint, endpoint.crossbar.get_master_port(write_only=True))
|
||||
self.submodules.reader = DMAReader(endpoint, endpoint.crossbar.get_master_port(read_only=True))
|
||||
self.sink, self.source = self.writer.sink, self.reader.source
|
||||
|
||||
# Loopback
|
||||
if with_loopback:
|
||||
self.submodules.loopback = DMALoopback(phy.dw)
|
||||
self.insert_optional_module(self.loopback)
|
||||
|
||||
# Synchronizer
|
||||
if with_synchronizer:
|
||||
self.submodules.synchronizer = DMASynchronizer(phy.dw)
|
||||
self.insert_optional_module(self.synchronizer)
|
||||
|
||||
# Buffering
|
||||
if with_buffering:
|
||||
self.submodules.buffering = DMABuffering(phy.dw, buffering_depth)
|
||||
self.insert_optional_module(self.buffering)
|
||||
|
||||
|
||||
def insert_optional_module(self, m):
|
||||
self.comb += [
|
||||
Record.connect(self.source, m.sink),
|
||||
Record.connect(m.source, self.sink)
|
||||
]
|
||||
self.sink, self.source = m.next_sink, m.next_source
|
|
@ -1,164 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
from migen.genlib.fifo import SyncFIFOBuffered as SyncFIFO
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
from migen.genlib.misc import chooser, displacer
|
||||
from migen.flow.plumbing import Buffer
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
|
||||
|
||||
def descriptor_layout(with_user_id=False):
|
||||
layout = [
|
||||
("address", 32),
|
||||
("length", 16)
|
||||
]
|
||||
if with_user_id:
|
||||
layout += [("user_id", 8)]
|
||||
return EndpointDescription(layout, packetized=True)
|
||||
|
||||
|
||||
class DMARequestTable(Module, AutoCSR):
|
||||
def __init__(self, depth):
|
||||
self.source = source = Source(descriptor_layout())
|
||||
|
||||
aw = flen(source.address)
|
||||
lw = flen(source.length)
|
||||
|
||||
self._value = CSRStorage(aw+lw)
|
||||
self._we = CSR()
|
||||
self._loop_prog_n = CSRStorage()
|
||||
self._loop_status = CSRStatus(32)
|
||||
self._level = CSRStatus(log2_int(depth))
|
||||
self._flush = CSR()
|
||||
self.irq = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# CSR signals
|
||||
value = self._value.storage
|
||||
we = self._we.r & self._we.re
|
||||
loop_prog_n = self._loop_prog_n.storage
|
||||
loop_index = self._loop_status.status[:log2_int(depth)]
|
||||
loop_count = self._loop_status.status[16:]
|
||||
level = self._level.status
|
||||
flush = self._flush.r & self._flush.re
|
||||
|
||||
# FIFO
|
||||
# instance
|
||||
fifo_layout = [("address", aw), ("length", lw), ("start", 1)]
|
||||
fifo = InsertReset(SyncFIFO(fifo_layout, depth))
|
||||
self.submodules += fifo
|
||||
self.comb += [
|
||||
fifo.reset.eq(flush),
|
||||
level.eq(fifo.level)
|
||||
]
|
||||
|
||||
# write part
|
||||
self.sync += [
|
||||
# in "loop" mode, each data output of the fifo is
|
||||
# written back
|
||||
If(loop_prog_n,
|
||||
fifo.din.address.eq(fifo.dout.address),
|
||||
fifo.din.length.eq(fifo.dout.length),
|
||||
fifo.din.start.eq(fifo.dout.start),
|
||||
fifo.we.eq(fifo.re)
|
||||
# in "program" mode, fifo input is connected
|
||||
# to registers
|
||||
).Else(
|
||||
fifo.din.address.eq(value[:aw]),
|
||||
fifo.din.length.eq(value[aw:aw+lw]),
|
||||
fifo.din.start.eq(~fifo.readable),
|
||||
fifo.we.eq(we)
|
||||
)
|
||||
]
|
||||
|
||||
# read part
|
||||
self.comb += [
|
||||
source.stb.eq(fifo.readable),
|
||||
fifo.re.eq(source.stb & source.ack),
|
||||
source.address.eq(fifo.dout.address),
|
||||
source.length.eq(fifo.dout.length)
|
||||
]
|
||||
|
||||
# loop_index, loop_count
|
||||
# used by the software for synchronization in
|
||||
# "loop" mode
|
||||
self.sync += \
|
||||
If(flush,
|
||||
loop_index.eq(0),
|
||||
loop_count.eq(0),
|
||||
).Elif(source.stb & source.ack,
|
||||
If(fifo.dout.start,
|
||||
loop_index.eq(0),
|
||||
loop_count.eq(loop_count+1)
|
||||
).Else(
|
||||
loop_index.eq(loop_index+1)
|
||||
)
|
||||
)
|
||||
|
||||
# IRQ
|
||||
self.comb += self.irq.eq(source.stb & source.ack)
|
||||
|
||||
|
||||
class DMARequestSplitter(Module, AutoCSR):
|
||||
def __init__(self, max_size, buffered=True):
|
||||
self.sink = sink = Sink(descriptor_layout())
|
||||
if buffered:
|
||||
self.submodules.buffer = Buffer(descriptor_layout(True))
|
||||
source = self.buffer.d
|
||||
self.source = self.buffer.q
|
||||
else:
|
||||
self.source = source = Source(descriptor_layout(True))
|
||||
|
||||
# # #
|
||||
|
||||
offset = Signal(32)
|
||||
clr_offset = Signal()
|
||||
inc_offset = Signal()
|
||||
self.sync += \
|
||||
If(clr_offset,
|
||||
offset.eq(0)
|
||||
).Elif(inc_offset,
|
||||
offset.eq(offset + max_size)
|
||||
)
|
||||
user_id = Signal(8)
|
||||
self.sync += \
|
||||
If(sink.stb & sink.ack,
|
||||
user_id.eq(user_id+1)
|
||||
)
|
||||
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += fsm
|
||||
|
||||
length = Signal(16)
|
||||
update_length = Signal()
|
||||
self.sync += If(update_length, length.eq(sink.length))
|
||||
|
||||
fsm.act("IDLE",
|
||||
sink.ack.eq(1),
|
||||
clr_offset.eq(1),
|
||||
If(sink.stb,
|
||||
update_length.eq(1),
|
||||
sink.ack.eq(0),
|
||||
NextState("RUN")
|
||||
)
|
||||
)
|
||||
fsm.act("RUN",
|
||||
source.stb.eq(1),
|
||||
source.address.eq(sink.address + offset),
|
||||
source.user_id.eq(user_id),
|
||||
If((length - offset) > max_size,
|
||||
source.length.eq(max_size),
|
||||
inc_offset.eq(source.ack)
|
||||
).Else(
|
||||
source.length.eq(length - offset),
|
||||
If(source.ack,
|
||||
NextState("ACK")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("ACK",
|
||||
sink.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
|
@ -1,79 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
from migen.actorlib.fifo import SyncFIFO as FIFO
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
from misoclib.com.litepcie.frontend.dma.common import *
|
||||
|
||||
|
||||
class DMAReader(Module, AutoCSR):
|
||||
def __init__(self, endpoint, port, table_depth=256):
|
||||
self.source = Source(dma_layout(endpoint.phy.dw))
|
||||
self._enable = CSRStorage()
|
||||
|
||||
# # #
|
||||
|
||||
enable = self._enable.storage
|
||||
|
||||
max_words_per_request = max_request_size//(endpoint.phy.dw//8)
|
||||
max_pending_words = endpoint.max_pending_requests*max_words_per_request
|
||||
|
||||
fifo_depth = 2*max_pending_words
|
||||
|
||||
# Request generation
|
||||
# requests from table are splitted in chunks of "max_size"
|
||||
self.table = table = DMARequestTable(table_depth)
|
||||
splitter = InsertReset(DMARequestSplitter(endpoint.phy.max_request_size))
|
||||
self.submodules += table, splitter
|
||||
self.comb += splitter.reset.eq(~enable)
|
||||
self.comb += table.source.connect(splitter.sink)
|
||||
|
||||
# Request FSM
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
|
||||
request_ready = Signal()
|
||||
fsm.act("IDLE",
|
||||
If(request_ready,
|
||||
NextState("REQUEST"),
|
||||
)
|
||||
)
|
||||
fsm.act("REQUEST",
|
||||
port.source.stb.eq(1),
|
||||
port.source.channel.eq(port.channel),
|
||||
port.source.user_id.eq(splitter.source.user_id),
|
||||
port.source.sop.eq(1),
|
||||
port.source.eop.eq(1),
|
||||
port.source.we.eq(0),
|
||||
port.source.adr.eq(splitter.source.address),
|
||||
port.source.len.eq(splitter.source.length[2:]),
|
||||
port.source.req_id.eq(endpoint.phy.id),
|
||||
port.source.dat.eq(0),
|
||||
If(port.source.ack,
|
||||
splitter.source.ack.eq(1),
|
||||
NextState("IDLE"),
|
||||
)
|
||||
)
|
||||
|
||||
# Data FIFO
|
||||
# issue read requests when enough space available in fifo
|
||||
fifo = InsertReset(FIFO(dma_layout(endpoint.phy.dw), fifo_depth, buffered=True))
|
||||
self.submodules += fifo
|
||||
self.comb += fifo.reset.eq(~enable)
|
||||
|
||||
last_user_id = Signal(8, reset=255)
|
||||
self.sync += \
|
||||
If(port.sink.stb & port.sink.sop & port.sink.ack,
|
||||
last_user_id.eq(port.sink.user_id)
|
||||
)
|
||||
self.comb += [
|
||||
fifo.sink.stb.eq(port.sink.stb),
|
||||
fifo.sink.sop.eq(port.sink.sop & (port.sink.user_id != last_user_id)),
|
||||
fifo.sink.data.eq(port.sink.dat),
|
||||
port.sink.ack.eq(fifo.sink.ack | ~enable),
|
||||
]
|
||||
self.comb += Record.connect(fifo.source, self.source)
|
||||
|
||||
fifo_ready = fifo.fifo.level < (fifo_depth//2)
|
||||
self.comb += request_ready.eq(splitter.source.stb & fifo_ready)
|
|
@ -1,88 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
from migen.genlib.fifo import SyncFIFOBuffered as SyncFIFO
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
from misoclib.com.litepcie.frontend.dma.common import *
|
||||
|
||||
|
||||
class DMAWriter(Module, AutoCSR):
|
||||
def __init__(self, endpoint, port, table_depth=256):
|
||||
self.sink = sink = Sink(dma_layout(endpoint.phy.dw))
|
||||
self._enable = CSRStorage()
|
||||
|
||||
# # #
|
||||
|
||||
enable = self._enable.storage
|
||||
|
||||
max_words_per_request = max_request_size//(endpoint.phy.dw//8)
|
||||
fifo_depth = 4*max_words_per_request
|
||||
|
||||
# Data FIFO
|
||||
# store data until we have enough data to issue a
|
||||
# write request
|
||||
fifo = InsertReset(SyncFIFO(endpoint.phy.dw, fifo_depth))
|
||||
self.submodules += fifo
|
||||
self.comb += [
|
||||
fifo.we.eq(sink.stb & enable),
|
||||
sink.ack.eq(fifo.writable & sink.stb & enable),
|
||||
fifo.din.eq(sink.data),
|
||||
fifo.reset.eq(~enable)
|
||||
]
|
||||
|
||||
# Request generation
|
||||
# requests from table are splitted in chunks of "max_size"
|
||||
self.table = table = DMARequestTable(table_depth)
|
||||
splitter = InsertReset(DMARequestSplitter(endpoint.phy.max_payload_size))
|
||||
self.submodules += table, splitter
|
||||
self.comb += splitter.reset.eq(~enable)
|
||||
self.comb += table.source.connect(splitter.sink)
|
||||
|
||||
# Request FSM
|
||||
cnt = Signal(max=(2**flen(endpoint.phy.max_payload_size))/8)
|
||||
clr_cnt = Signal()
|
||||
inc_cnt = Signal()
|
||||
self.sync += \
|
||||
If(clr_cnt,
|
||||
cnt.eq(0)
|
||||
).Elif(inc_cnt,
|
||||
cnt.eq(cnt + 1)
|
||||
)
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
|
||||
request_ready = Signal()
|
||||
fsm.act("IDLE",
|
||||
clr_cnt.eq(1),
|
||||
If(request_ready,
|
||||
NextState("REQUEST"),
|
||||
)
|
||||
)
|
||||
fsm.act("REQUEST",
|
||||
inc_cnt.eq(port.source.stb & port.source.ack),
|
||||
|
||||
port.source.stb.eq(1),
|
||||
port.source.channel.eq(port.channel),
|
||||
port.source.user_id.eq(splitter.source.user_id),
|
||||
port.source.sop.eq(cnt == 0),
|
||||
port.source.eop.eq(cnt == splitter.source.length[3:]-1),
|
||||
port.source.we.eq(1),
|
||||
port.source.adr.eq(splitter.source.address),
|
||||
port.source.req_id.eq(endpoint.phy.id),
|
||||
port.source.tag.eq(0),
|
||||
port.source.len.eq(splitter.source.length[2:]),
|
||||
port.source.dat.eq(fifo.dout),
|
||||
|
||||
If(port.source.ack,
|
||||
fifo.re.eq(1),
|
||||
If(port.source.eop,
|
||||
splitter.source.ack.eq(1),
|
||||
NextState("IDLE"),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fifo_ready = fifo.level >= splitter.source.length[3:]
|
||||
self.sync += request_ready.eq(splitter.source.stb & fifo_ready)
|
|
@ -1,67 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
from migen.bus import wishbone
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
|
||||
|
||||
class LitePCIeWishboneBridge(Module):
|
||||
def __init__(self, endpoint, address_decoder):
|
||||
self.wishbone = wishbone.Interface()
|
||||
|
||||
# # #
|
||||
|
||||
port = endpoint.crossbar.get_slave_port(address_decoder)
|
||||
self.submodules.fsm = fsm = FSM()
|
||||
|
||||
fsm.act("IDLE",
|
||||
If(port.sink.stb & port.sink.sop,
|
||||
If(port.sink.we,
|
||||
NextState("WRITE"),
|
||||
).Else(
|
||||
NextState("READ")
|
||||
)
|
||||
).Else(
|
||||
port.sink.ack.eq(port.sink.stb)
|
||||
)
|
||||
)
|
||||
fsm.act("WRITE",
|
||||
self.wishbone.adr.eq(port.sink.adr[2:]),
|
||||
self.wishbone.dat_w.eq(port.sink.dat[:32]),
|
||||
self.wishbone.sel.eq(0xf),
|
||||
self.wishbone.stb.eq(1),
|
||||
self.wishbone.we.eq(1),
|
||||
self.wishbone.cyc.eq(1),
|
||||
If(self.wishbone.ack,
|
||||
port.sink.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
fsm.act("READ",
|
||||
self.wishbone.adr.eq(port.sink.adr[2:]),
|
||||
self.wishbone.stb.eq(1),
|
||||
self.wishbone.we.eq(0),
|
||||
self.wishbone.cyc.eq(1),
|
||||
If(self.wishbone.ack,
|
||||
NextState("COMPLETION")
|
||||
)
|
||||
)
|
||||
self.sync += \
|
||||
If(self.wishbone.stb & self.wishbone.ack,
|
||||
port.source.dat.eq(self.wishbone.dat_r),
|
||||
)
|
||||
fsm.act("COMPLETION",
|
||||
port.source.stb.eq(1),
|
||||
port.source.sop.eq(1),
|
||||
port.source.eop.eq(1),
|
||||
port.source.len.eq(1),
|
||||
port.source.err.eq(0),
|
||||
port.source.tag.eq(port.sink.tag),
|
||||
port.source.adr.eq(port.sink.adr),
|
||||
port.source.cmp_id.eq(endpoint.phy.id),
|
||||
port.source.req_id.eq(port.sink.req_id),
|
||||
If(port.source.ack,
|
||||
port.sink.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
|
@ -1,2 +0,0 @@
|
|||
0.9.0
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
import os
|
||||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
|
||||
|
||||
def get_gt(device):
|
||||
if device[:4] == "xc7k":
|
||||
return "GTX"
|
||||
elif device[:4] == "xc7a":
|
||||
return "GTP"
|
||||
else:
|
||||
raise ValueError("Unsupported device"+device)
|
||||
|
||||
|
||||
class S7PCIEPHY(Module, AutoCSR):
|
||||
def __init__(self, platform, dw=64, link_width=2, bar0_size=1*MB):
|
||||
pads = platform.request("pcie_x"+str(link_width))
|
||||
device = platform.device
|
||||
self.dw = dw
|
||||
self.link_width = link_width
|
||||
|
||||
self.sink = Sink(phy_layout(dw))
|
||||
self.source = Source(phy_layout(dw))
|
||||
self.interrupt = Sink(interrupt_layout())
|
||||
|
||||
self.id = Signal(16)
|
||||
|
||||
self.tx_buf_av = Signal(8)
|
||||
self.tx_terr_drop = Signal()
|
||||
self.tx_cfg_req = Signal()
|
||||
self.tx_cfg_gnt = Signal(reset=1)
|
||||
|
||||
self.rx_np_ok = Signal(reset=1)
|
||||
self.rx_np_req = Signal(reset=1)
|
||||
|
||||
self.cfg_to_turnoff = Signal()
|
||||
|
||||
self._lnk_up = CSRStatus()
|
||||
self._msi_enable = CSRStatus()
|
||||
self._bus_master_enable = CSRStatus()
|
||||
self._max_request_size = CSRStatus(16)
|
||||
self._max_payload_size = CSRStatus(16)
|
||||
self.max_request_size = self._max_request_size.status
|
||||
self.max_payload_size = self._max_payload_size.status
|
||||
|
||||
self.bar0_size = bar0_size
|
||||
self.bar0_mask = get_bar_mask(bar0_size)
|
||||
|
||||
# SHARED clock
|
||||
# In case we want to use the second QPLL of the quad
|
||||
self.shared_qpll_pd = Signal(reset=1)
|
||||
self.shared_qpll_rst = Signal(reset=1)
|
||||
self.shared_qpll_refclk = Signal()
|
||||
self.shared_qpll_outclk = Signal()
|
||||
self.shared_qpll_outrefclk = Signal()
|
||||
self.shared_qpll_lock = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
clk100 = Signal()
|
||||
self.specials += Instance("IBUFDS_GTE2",
|
||||
i_CEB=0,
|
||||
i_I=pads.clk_p,
|
||||
i_IB=pads.clk_n,
|
||||
o_O=clk100,
|
||||
o_ODIV2=Signal()
|
||||
)
|
||||
|
||||
bus_number = Signal(8)
|
||||
device_number = Signal(5)
|
||||
function_number = Signal(3)
|
||||
command = Signal(16)
|
||||
dcommand = Signal(16)
|
||||
|
||||
self.specials += Instance("pcie_phy",
|
||||
p_C_DATA_WIDTH=dw,
|
||||
p_C_PCIE_GT_DEVICE=get_gt(device),
|
||||
p_C_BAR0=get_bar_mask(self.bar0_size),
|
||||
|
||||
i_sys_clk=clk100,
|
||||
i_sys_rst_n=pads.rst_n,
|
||||
|
||||
o_pci_exp_txp=pads.tx_p,
|
||||
o_pci_exp_txn=pads.tx_n,
|
||||
|
||||
i_pci_exp_rxp=pads.rx_p,
|
||||
i_pci_exp_rxn=pads.rx_n,
|
||||
|
||||
o_user_clk=ClockSignal("clk125"),
|
||||
o_user_reset=ResetSignal("clk125"),
|
||||
o_user_lnk_up=self._lnk_up.status,
|
||||
|
||||
o_tx_buf_av=self.tx_buf_av,
|
||||
o_tx_terr_drop=self.tx_terr_drop,
|
||||
o_tx_cfg_req=self.tx_cfg_req,
|
||||
i_tx_cfg_gnt=self.tx_cfg_gnt,
|
||||
|
||||
i_s_axis_tx_tvalid=self.sink.stb,
|
||||
i_s_axis_tx_tlast=self.sink.eop,
|
||||
o_s_axis_tx_tready=self.sink.ack,
|
||||
i_s_axis_tx_tdata=self.sink.dat,
|
||||
i_s_axis_tx_tkeep=self.sink.be,
|
||||
i_s_axis_tx_tuser=0,
|
||||
|
||||
i_rx_np_ok=self.rx_np_ok,
|
||||
i_rx_np_req=self.rx_np_req,
|
||||
|
||||
o_m_axis_rx_tvalid=self.source.stb,
|
||||
o_m_axis_rx_tlast=self.source.eop,
|
||||
i_m_axis_rx_tready=self.source.ack,
|
||||
o_m_axis_rx_tdata=self.source.dat,
|
||||
o_m_axis_rx_tkeep=self.source.be,
|
||||
o_m_axis_rx_tuser=Signal(4),
|
||||
|
||||
o_cfg_to_turnoff=self.cfg_to_turnoff,
|
||||
o_cfg_bus_number=bus_number,
|
||||
o_cfg_device_number=device_number,
|
||||
o_cfg_function_number=function_number,
|
||||
o_cfg_command=command,
|
||||
o_cfg_dcommand=dcommand,
|
||||
o_cfg_interrupt_msienable=self._msi_enable.status,
|
||||
|
||||
i_cfg_interrupt=self.interrupt.stb,
|
||||
o_cfg_interrupt_rdy=self.interrupt.ack,
|
||||
i_cfg_interrupt_di=self.interrupt.dat,
|
||||
|
||||
i_SHARED_QPLL_PD=self.shared_qpll_pd,
|
||||
i_SHARED_QPLL_RST=self.shared_qpll_rst,
|
||||
i_SHARED_QPLL_REFCLK=self.shared_qpll_refclk,
|
||||
o_SHARED_QPLL_OUTCLK=self.shared_qpll_outclk,
|
||||
o_SHARED_QPLL_OUTREFCLK=self.shared_qpll_outrefclk,
|
||||
o_SHARED_QPLL_LOCK=self.shared_qpll_lock,
|
||||
)
|
||||
|
||||
# id
|
||||
self.comb += self.id.eq(Cat(function_number, device_number, bus_number))
|
||||
|
||||
# config
|
||||
def convert_size(command, size):
|
||||
cases = {}
|
||||
value = 128
|
||||
for i in range(6):
|
||||
cases[i] = size.eq(value)
|
||||
value = value*2
|
||||
return Case(command, cases)
|
||||
|
||||
self.sync += [
|
||||
self._bus_master_enable.status.eq(command[2]),
|
||||
convert_size(dcommand[12:15], self.max_request_size),
|
||||
convert_size(dcommand[5:8], self.max_payload_size)
|
||||
]
|
||||
if hasattr(platform, "misoc_path"):
|
||||
misoc_path = platform.misoc_path
|
||||
else:
|
||||
misoc_path = "./"
|
||||
litepcie_phy_wrapper_path = os.path.join(misoc_path, "extcores", "litepcie_phy_wrappers")
|
||||
platform.add_source_dir(os.path.join(litepcie_phy_wrapper_path, "xilinx", "7-series", "common"))
|
||||
if device[:4] == "xc7k":
|
||||
platform.add_source_dir(os.path.join(litepcie_phy_wrapper_path, "xilinx", "7-series", "kintex7"))
|
||||
elif device[:4] == "xc7a":
|
||||
platform.add_source_dir(os.path.join(litepcie_phy_wrapper_path, "xilinx", "7-series", "artix7"))
|
|
@ -1,16 +0,0 @@
|
|||
# Makefile for kernel module
|
||||
|
||||
KERNEL_VERSION:=$(shell uname -r)
|
||||
KERNEL_PATH:=/lib/modules/$(KERNEL_VERSION)/build
|
||||
|
||||
obj-m = litepcie.o
|
||||
litepcie-objs = main.o
|
||||
|
||||
all: litepcie.ko
|
||||
|
||||
litepcie.ko: main.c
|
||||
make -C $(KERNEL_PATH) M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
make -C $(KERNEL_PATH) M=$(PWD) clean
|
||||
rm -f *~
|
|
@ -1,9 +0,0 @@
|
|||
- Use 'make' to build the driver
|
||||
|
||||
- Install the driver and create the device with :
|
||||
|
||||
./init.sh
|
||||
|
||||
- Remove driver with
|
||||
|
||||
rmmod litepcie
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef __HW_CONFIG_H
|
||||
#define __HW_CONFIG_H
|
||||
|
||||
/* pci */
|
||||
#define PCI_FPGA_VENDOR_ID 0x10ee
|
||||
#define PCI_FPGA_DEVICE_ID 0x7022
|
||||
#define PCI_FPGA_BAR0_SIZE 0xa000
|
||||
|
||||
/* dma */
|
||||
#define DMA_BUFFER_COUNT 128
|
||||
|
||||
|
||||
#endif /* __HW_CONFIG_H */
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef __HW_FLAGS_H
|
||||
#define __HW_FLAGS_H
|
||||
|
||||
/* dma */
|
||||
#define DMA_LOOPBACK_ENABLE 0x1
|
||||
|
||||
#define DMA_TABLE_LOOP_INDEX 1 << 0
|
||||
#define DMA_TABLE_LOOP_COUNT 1 << 16
|
||||
|
||||
#endif /* __HW_FLAGS_H */
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/sh
|
||||
# TODO: use udev instead
|
||||
|
||||
insmod litepcie.ko
|
||||
|
||||
major=$(awk '/ litepcie$/{print $1}' /proc/devices)
|
||||
mknod -m 666 /dev/litepcie0 c $major 0
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* LitePCIe driver
|
||||
*
|
||||
*/
|
||||
#ifndef _LINUX_LITEPCIE_H
|
||||
#define _LINUX_LITEPCIE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct litepcie_ioctl_mmap_info {
|
||||
unsigned long reg_offset;
|
||||
unsigned long reg_size;
|
||||
|
||||
unsigned long dma_tx_buf_offset;
|
||||
unsigned long dma_tx_buf_size;
|
||||
unsigned long dma_tx_buf_count;
|
||||
|
||||
unsigned long dma_rx_buf_offset;
|
||||
unsigned long dma_rx_buf_size;
|
||||
unsigned long dma_rx_buf_count;
|
||||
};
|
||||
|
||||
struct litepcie_ioctl_dma_start {
|
||||
__u32 dma_flags; /* see LITEPCIE_DMA_FLAGS_x */
|
||||
__u32 tx_buf_size; /* in bytes, must be < dma_buf_pitch. 0 means no TX */
|
||||
__u32 tx_buf_count;
|
||||
__u32 rx_buf_size; /* in bytes, must be < dma_buf_pitch. 0 means no RX */
|
||||
__u32 rx_buf_count;
|
||||
};
|
||||
|
||||
/* if tx_wait is true, wait until the current TX bufffer is
|
||||
different from tx_buf_num. If tx_wait is false, wait until the
|
||||
current RX buffer is different from rx_buf_num. Return the last
|
||||
TX buffer in tx_buf_num and the last RX buffer in
|
||||
rx_buf_num. */
|
||||
struct litepcie_ioctl_dma_wait {
|
||||
__s32 timeout; /* in ms. Return -EAGAIN if timeout occured without event */
|
||||
__u32 tx_wait;
|
||||
__u32 tx_buf_num; /* read/write */
|
||||
__u32 rx_buf_num; /* read/write */
|
||||
};
|
||||
|
||||
#define LITEPCIE_IOCTL 'S'
|
||||
|
||||
#define LITEPCIE_IOCTL_GET_MMAP_INFO _IOR(LITEPCIE_IOCTL, 0, struct litepcie_ioctl_mmap_info)
|
||||
#define LITEPCIE_IOCTL_DMA_START _IOW(LITEPCIE_IOCTL, 1, struct litepcie_ioctl_dma_start)
|
||||
#define LITEPCIE_IOCTL_DMA_STOP _IO(LITEPCIE_IOCTL, 2)
|
||||
#define LITEPCIE_IOCTL_DMA_WAIT _IOWR(LITEPCIE_IOCTL, 3, struct litepcie_ioctl_dma_wait)
|
||||
|
||||
#endif /* _LINUX_LITEPCIE_H */
|
|
@ -1,639 +0,0 @@
|
|||
/*
|
||||
* LitePCIe driver
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mmtimer.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/posix-timers.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "litepcie.h"
|
||||
#include "config.h"
|
||||
#include "csr.h"
|
||||
#include "flags.h"
|
||||
|
||||
#define LITEPCIE_NAME "litepcie"
|
||||
#define LITEPCIE_MINOR_COUNT 4
|
||||
|
||||
#define DMA_BUFFER_SIZE PAGE_ALIGN(32768)
|
||||
#define DMA_BUFFER_MAP_SIZE (DMA_BUFFER_SIZE * DMA_BUFFER_COUNT)
|
||||
|
||||
#define IRQ_MASK_DMA_READER (1 << DMA_READER_INTERRUPT)
|
||||
#define IRQ_MASK_DMA_WRITER (1 << DMA_WRITER_INTERRUPT)
|
||||
|
||||
typedef struct {
|
||||
int minor;
|
||||
struct pci_dev *dev;
|
||||
|
||||
phys_addr_t bar0_phys_addr;
|
||||
uint8_t *bar0_addr; /* virtual address of BAR0 */
|
||||
|
||||
uint8_t *dma_tx_bufs[DMA_BUFFER_COUNT];
|
||||
unsigned long dma_tx_bufs_addr[DMA_BUFFER_COUNT];
|
||||
uint8_t *dma_rx_bufs[DMA_BUFFER_COUNT];
|
||||
unsigned long dma_rx_bufs_addr[DMA_BUFFER_COUNT];
|
||||
uint8_t tx_dma_started;
|
||||
uint8_t rx_dma_started;
|
||||
wait_queue_head_t dma_waitqueue;
|
||||
} LitePCIeState;
|
||||
|
||||
static dev_t litepcie_cdev;
|
||||
static struct cdev litepcie_cdev_struct;
|
||||
static LitePCIeState *litepcie_minor_table[LITEPCIE_MINOR_COUNT];
|
||||
|
||||
static void litepcie_end(struct pci_dev *dev, LitePCIeState *s);
|
||||
static int litepcie_dma_stop(LitePCIeState *s);
|
||||
|
||||
static inline uint32_t litepcie_readl(LitePCIeState *s, uint32_t addr)
|
||||
{
|
||||
return readl(s->bar0_addr + addr);
|
||||
}
|
||||
|
||||
static inline void litepcie_writel(LitePCIeState *s, uint32_t addr, uint32_t val)
|
||||
{
|
||||
return writel(val, s->bar0_addr + addr);
|
||||
}
|
||||
|
||||
static void litepcie_enable_interrupt(LitePCIeState *s, int irq_num)
|
||||
{
|
||||
uint32_t v;
|
||||
v = litepcie_readl(s, CSR_IRQ_CONTROLLER_ENABLE_ADDR);
|
||||
v |= (1 << irq_num);
|
||||
litepcie_writel(s, CSR_IRQ_CONTROLLER_ENABLE_ADDR, v);
|
||||
}
|
||||
|
||||
static void litepcie_disable_interrupt(LitePCIeState *s, int irq_num)
|
||||
{
|
||||
uint32_t v;
|
||||
v = litepcie_readl(s, CSR_IRQ_CONTROLLER_ENABLE_ADDR);
|
||||
v &= ~(1 << irq_num);
|
||||
litepcie_writel(s, CSR_IRQ_CONTROLLER_ENABLE_ADDR, v);
|
||||
}
|
||||
|
||||
static int litepcie_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
LitePCIeState *s;
|
||||
int minor;
|
||||
|
||||
/* find PCI device */
|
||||
minor = iminor(inode);
|
||||
if (minor < 0 || minor >= LITEPCIE_MINOR_COUNT)
|
||||
return -ENODEV;
|
||||
s = litepcie_minor_table[minor];
|
||||
if (!s)
|
||||
return -ENODEV;
|
||||
file->private_data = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* mmap the DMA buffers and registers to user space */
|
||||
static int litepcie_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
LitePCIeState *s = file->private_data;
|
||||
unsigned long pfn;
|
||||
int is_tx, i;
|
||||
|
||||
if (vma->vm_pgoff == 0) {
|
||||
if (vma->vm_end - vma->vm_start != DMA_BUFFER_MAP_SIZE)
|
||||
return -EINVAL;
|
||||
is_tx = 1;
|
||||
goto remap_ram;
|
||||
} else if (vma->vm_pgoff == (DMA_BUFFER_MAP_SIZE >> PAGE_SHIFT)) {
|
||||
if (vma->vm_end - vma->vm_start != DMA_BUFFER_MAP_SIZE)
|
||||
return -EINVAL;
|
||||
is_tx = 0;
|
||||
remap_ram:
|
||||
for(i = 0; i < DMA_BUFFER_COUNT; i++) {
|
||||
if (is_tx)
|
||||
pfn = __pa(s->dma_tx_bufs[i]) >> PAGE_SHIFT;
|
||||
else
|
||||
pfn = __pa(s->dma_rx_bufs[i]) >> PAGE_SHIFT;
|
||||
/* Note: the memory is cached, so the user must explicitly
|
||||
flush the CPU caches on architectures which require it. */
|
||||
if (remap_pfn_range(vma, vma->vm_start + i * DMA_BUFFER_SIZE, pfn,
|
||||
DMA_BUFFER_SIZE, vma->vm_page_prot)) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " remap_pfn_range failed\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
} else if (vma->vm_pgoff == ((2 * DMA_BUFFER_MAP_SIZE) >> PAGE_SHIFT)) {
|
||||
if (vma->vm_end - vma->vm_start != PCI_FPGA_BAR0_SIZE)
|
||||
return -EINVAL;
|
||||
pfn = s->bar0_phys_addr >> PAGE_SHIFT;
|
||||
/* not cached */
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
vma->vm_flags |= VM_IO;
|
||||
if (io_remap_pfn_range(vma, vma->vm_start, pfn,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot)) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " io_remap_pfn_range failed\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int litepcie_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
LitePCIeState *s = file->private_data;
|
||||
|
||||
litepcie_dma_stop(s); /* just in case: stop the DMA */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t litepcie_interrupt(int irq, void *data)
|
||||
{
|
||||
LitePCIeState *s = data;
|
||||
uint32_t clear_mask, irq_vector;
|
||||
|
||||
irq_vector = litepcie_readl(s, CSR_IRQ_CONTROLLER_VECTOR_ADDR);
|
||||
clear_mask = 0;
|
||||
if (irq_vector & (IRQ_MASK_DMA_READER | IRQ_MASK_DMA_WRITER)) {
|
||||
/* wake up processes waiting on dma_wait() */
|
||||
wake_up_interruptible(&s->dma_waitqueue);
|
||||
clear_mask |= (IRQ_MASK_DMA_READER | IRQ_MASK_DMA_WRITER);
|
||||
}
|
||||
|
||||
litepcie_writel(s, CSR_IRQ_CONTROLLER_CLEAR_ADDR, clear_mask);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int litepcie_dma_start(LitePCIeState *s, struct litepcie_ioctl_dma_start *m)
|
||||
{
|
||||
int i, val;
|
||||
|
||||
if (s->tx_dma_started || s->rx_dma_started)
|
||||
return -EIO;
|
||||
|
||||
if (m->tx_buf_size == 0 && m->rx_buf_size == 0)
|
||||
return -EINVAL;
|
||||
/* check alignment (XXX: what is the exact constraint ?) */
|
||||
if ((m->tx_buf_size & 7) != 0 ||
|
||||
(m->rx_buf_size & 7) != 0 ||
|
||||
m->tx_buf_size > DMA_BUFFER_SIZE ||
|
||||
m->rx_buf_size > DMA_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* check buffer count */
|
||||
if (m->tx_buf_count > DMA_BUFFER_COUNT)
|
||||
return -EINVAL;
|
||||
if (m->rx_buf_count > DMA_BUFFER_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
val = ((m->dma_flags & DMA_LOOPBACK_ENABLE) != 0);
|
||||
litepcie_writel(s, CSR_DMA_LOOPBACK_ENABLE_ADDR, val);
|
||||
|
||||
/* init DMA write */
|
||||
if (m->rx_buf_size != 0) {
|
||||
litepcie_writel(s, CSR_DMA_WRITER_ENABLE_ADDR, 0);
|
||||
litepcie_writel(s, CSR_DMA_WRITER_TABLE_FLUSH_ADDR, 1);
|
||||
litepcie_writel(s, CSR_DMA_WRITER_TABLE_LOOP_PROG_N_ADDR, 0);
|
||||
for(i = 0; i < m->rx_buf_count; i++) {
|
||||
litepcie_writel(s, CSR_DMA_WRITER_TABLE_VALUE_ADDR, m->rx_buf_size);
|
||||
litepcie_writel(s, CSR_DMA_WRITER_TABLE_VALUE_ADDR + 4,
|
||||
s->dma_rx_bufs_addr[i]);
|
||||
litepcie_writel(s, CSR_DMA_WRITER_TABLE_WE_ADDR, 1);
|
||||
}
|
||||
litepcie_writel(s, CSR_DMA_WRITER_TABLE_LOOP_PROG_N_ADDR, 1);
|
||||
}
|
||||
|
||||
/* init DMA read */
|
||||
if (m->tx_buf_size != 0) {
|
||||
litepcie_writel(s, CSR_DMA_READER_ENABLE_ADDR, 0);
|
||||
litepcie_writel(s, CSR_DMA_READER_TABLE_FLUSH_ADDR, 1);
|
||||
litepcie_writel(s, CSR_DMA_READER_TABLE_LOOP_PROG_N_ADDR, 0);
|
||||
for(i = 0; i < m->tx_buf_count; i++) {
|
||||
litepcie_writel(s, CSR_DMA_READER_TABLE_VALUE_ADDR, m->tx_buf_size);
|
||||
litepcie_writel(s, CSR_DMA_READER_TABLE_VALUE_ADDR + 4,
|
||||
s->dma_tx_bufs_addr[i]);
|
||||
litepcie_writel(s, CSR_DMA_READER_TABLE_WE_ADDR, 1);
|
||||
}
|
||||
litepcie_writel(s, CSR_DMA_READER_TABLE_LOOP_PROG_N_ADDR, 1);
|
||||
}
|
||||
|
||||
/* start DMA */
|
||||
if (m->rx_buf_size != 0) {
|
||||
litepcie_writel(s, CSR_DMA_WRITER_ENABLE_ADDR, 1);
|
||||
s->rx_dma_started = 1;
|
||||
}
|
||||
if (m->tx_buf_size != 0) {
|
||||
litepcie_writel(s, CSR_DMA_READER_ENABLE_ADDR, 1);
|
||||
s->tx_dma_started = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int litepcie_dma_wait(LitePCIeState *s, struct litepcie_ioctl_dma_wait *m)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int ret, last_buf_num;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
if (m->tx_wait) {
|
||||
if (!s->tx_dma_started)
|
||||
return -EIO;
|
||||
last_buf_num = m->tx_buf_num;
|
||||
litepcie_enable_interrupt(s, DMA_READER_INTERRUPT);
|
||||
} else {
|
||||
if (!s->rx_dma_started)
|
||||
return -EIO;
|
||||
last_buf_num = m->rx_buf_num;
|
||||
litepcie_enable_interrupt(s, DMA_WRITER_INTERRUPT);
|
||||
}
|
||||
|
||||
add_wait_queue(&s->dma_waitqueue, &wait);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(m->timeout);
|
||||
for (;;) {
|
||||
/* set current buffer */
|
||||
if (s->tx_dma_started) {
|
||||
m->tx_buf_num = (litepcie_readl(s, CSR_DMA_READER_TABLE_LOOP_STATUS_ADDR) & 0xffff);
|
||||
} else {
|
||||
m->tx_buf_num = 0;
|
||||
}
|
||||
if (s->rx_dma_started) {
|
||||
m->rx_buf_num = (litepcie_readl(s, CSR_DMA_WRITER_TABLE_LOOP_STATUS_ADDR) & 0xfffff);
|
||||
} else {
|
||||
m->rx_buf_num = 0;
|
||||
}
|
||||
if (m->tx_wait) {
|
||||
if (m->tx_buf_num != last_buf_num)
|
||||
break;
|
||||
} else {
|
||||
if (m->rx_buf_num != last_buf_num)
|
||||
break;
|
||||
}
|
||||
if ((long)(jiffies - timeout) > 0) {
|
||||
ret = -EAGAIN;
|
||||
goto done;
|
||||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (signal_pending(current)) {
|
||||
ret = -EINTR;
|
||||
goto done;
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
ret = 0;
|
||||
done:
|
||||
if (m->tx_wait) {
|
||||
litepcie_disable_interrupt(s, DMA_READER_INTERRUPT);
|
||||
} else {
|
||||
litepcie_disable_interrupt(s, DMA_WRITER_INTERRUPT);
|
||||
}
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&s->dma_waitqueue, &wait);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int litepcie_dma_stop(LitePCIeState *s)
|
||||
{
|
||||
/* just to be sure, we disable the interrupts */
|
||||
litepcie_disable_interrupt(s, DMA_READER_INTERRUPT);
|
||||
litepcie_disable_interrupt(s, DMA_WRITER_INTERRUPT);
|
||||
|
||||
s->tx_dma_started = 0;
|
||||
litepcie_writel(s, CSR_DMA_READER_TABLE_LOOP_PROG_N_ADDR, 0);
|
||||
litepcie_writel(s, CSR_DMA_READER_TABLE_FLUSH_ADDR, 1);
|
||||
udelay(100);
|
||||
litepcie_writel(s, CSR_DMA_READER_ENABLE_ADDR, 0);
|
||||
|
||||
s->rx_dma_started = 0;
|
||||
litepcie_writel(s, CSR_DMA_WRITER_TABLE_LOOP_PROG_N_ADDR, 0);
|
||||
litepcie_writel(s, CSR_DMA_WRITER_TABLE_FLUSH_ADDR, 1);
|
||||
udelay(100);
|
||||
litepcie_writel(s, CSR_DMA_WRITER_ENABLE_ADDR, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long litepcie_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
LitePCIeState *s = file->private_data;
|
||||
long ret;
|
||||
|
||||
switch(cmd) {
|
||||
case LITEPCIE_IOCTL_GET_MMAP_INFO:
|
||||
{
|
||||
struct litepcie_ioctl_mmap_info m;
|
||||
m.dma_tx_buf_offset = 0;
|
||||
m.dma_tx_buf_size = DMA_BUFFER_SIZE;
|
||||
m.dma_tx_buf_count = DMA_BUFFER_COUNT;
|
||||
|
||||
m.dma_rx_buf_offset = DMA_BUFFER_MAP_SIZE;
|
||||
m.dma_rx_buf_size = DMA_BUFFER_SIZE;
|
||||
m.dma_rx_buf_count = DMA_BUFFER_COUNT;
|
||||
|
||||
m.reg_offset = 2 * DMA_BUFFER_MAP_SIZE;
|
||||
m.reg_size = PCI_FPGA_BAR0_SIZE;
|
||||
if (copy_to_user((void *)arg, &m, sizeof(m))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
case LITEPCIE_IOCTL_DMA_START:
|
||||
{
|
||||
struct litepcie_ioctl_dma_start m;
|
||||
|
||||
if (copy_from_user(&m, (void *)arg, sizeof(m))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ret = litepcie_dma_start(s, &m);
|
||||
}
|
||||
break;
|
||||
case LITEPCIE_IOCTL_DMA_STOP:
|
||||
{
|
||||
ret = litepcie_dma_stop(s);
|
||||
}
|
||||
break;
|
||||
case LITEPCIE_IOCTL_DMA_WAIT:
|
||||
{
|
||||
struct litepcie_ioctl_dma_wait m;
|
||||
|
||||
if (copy_from_user(&m, (void *)arg, sizeof(m))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ret = litepcie_dma_wait(s, &m);
|
||||
if (ret == 0) {
|
||||
if (copy_to_user((void *)arg, &m, sizeof(m))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations litepcie_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = litepcie_ioctl,
|
||||
.open = litepcie_open,
|
||||
.release = litepcie_release,
|
||||
.mmap = litepcie_mmap,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static int litepcie_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
LitePCIeState *s = NULL;
|
||||
uint8_t rev_id;
|
||||
int ret, minor, i;
|
||||
|
||||
printk(KERN_INFO LITEPCIE_NAME " Probing device\n");
|
||||
|
||||
/* find available minor */
|
||||
for(minor = 0; minor < LITEPCIE_MINOR_COUNT; minor++) {
|
||||
if (!litepcie_minor_table[minor])
|
||||
break;
|
||||
}
|
||||
if (minor == LITEPCIE_MINOR_COUNT) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Cannot allocate a minor\n");
|
||||
ret = -ENODEV;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
s = kzalloc(sizeof(LitePCIeState), GFP_KERNEL);
|
||||
if (!s) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Cannot allocate memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
s->minor = minor;
|
||||
s->dev = dev;
|
||||
pci_set_drvdata(dev, s);
|
||||
|
||||
ret = pci_enable_device(dev);
|
||||
if (ret != 0) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Cannot enable device\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
/* check device version */
|
||||
pci_read_config_byte(dev, PCI_REVISION_ID, &rev_id);
|
||||
if (rev_id != 1) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Unsupported device version %d\n", rev_id);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if (pci_request_regions(dev, LITEPCIE_NAME) < 0) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Could not request regions\n");
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
/* check BAR0 config */
|
||||
if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Invalid BAR0 config\n");
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
s->bar0_phys_addr = pci_resource_start(dev, 0);
|
||||
s->bar0_addr = pci_ioremap_bar(dev, 0);
|
||||
if (!s->bar0_addr) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Could not map BAR0\n");
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
ret = pci_set_dma_mask(dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Failed to set DMA mask\n");
|
||||
goto fail4;
|
||||
};
|
||||
|
||||
ret = pci_enable_msi(dev);
|
||||
if (ret) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Failed to enable MSI\n");
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
if (request_irq(dev->irq, litepcie_interrupt, IRQF_SHARED, LITEPCIE_NAME, s) < 0) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Failed to allocate irq %d\n", dev->irq);
|
||||
goto fail5;
|
||||
}
|
||||
|
||||
/* soft reset */
|
||||
litepcie_writel(s, CSR_CRG_SOFT_RST_ADDR, 1);
|
||||
udelay(5);
|
||||
|
||||
/* allocate DMA buffers */
|
||||
for(i = 0; i < DMA_BUFFER_COUNT; i++) {
|
||||
s->dma_tx_bufs[i] = kzalloc(DMA_BUFFER_SIZE, GFP_KERNEL | GFP_DMA32);
|
||||
if (!s->dma_tx_bufs[i]) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Failed to allocate dma_tx_buf\n");
|
||||
goto fail6;
|
||||
}
|
||||
s->dma_tx_bufs_addr[i] = pci_map_single(dev, s->dma_tx_bufs[i],
|
||||
DMA_BUFFER_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
if (!s->dma_tx_bufs_addr[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto fail6;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < DMA_BUFFER_COUNT; i++) {
|
||||
s->dma_rx_bufs[i] = kzalloc(DMA_BUFFER_SIZE, GFP_KERNEL | GFP_DMA32);
|
||||
if (!s->dma_rx_bufs[i]) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Failed to allocate dma_rx_buf\n");
|
||||
goto fail6;
|
||||
}
|
||||
|
||||
s->dma_rx_bufs_addr[i] = pci_map_single(dev, s->dma_rx_bufs[i],
|
||||
DMA_BUFFER_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
if (!s->dma_rx_bufs_addr[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto fail6;
|
||||
}
|
||||
}
|
||||
|
||||
init_waitqueue_head(&s->dma_waitqueue);
|
||||
|
||||
litepcie_minor_table[minor] = s;
|
||||
printk(KERN_INFO LITEPCIE_NAME " Assigned to minor %d\n", minor);
|
||||
return 0;
|
||||
|
||||
fail6:
|
||||
litepcie_end(dev, s);
|
||||
free_irq(dev->irq, s);
|
||||
fail5:
|
||||
pci_disable_msi(dev);
|
||||
fail4:
|
||||
pci_iounmap(dev, s->bar0_addr);
|
||||
fail3:
|
||||
pci_release_regions(dev);
|
||||
fail2:
|
||||
pci_disable_device(dev);
|
||||
ret = -EIO;
|
||||
fail1:
|
||||
kfree(s);
|
||||
printk(KERN_ERR LITEPCIE_NAME " Error while probing device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void litepcie_end(struct pci_dev *dev, LitePCIeState *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < DMA_BUFFER_COUNT; i++) {
|
||||
if (s->dma_tx_bufs_addr[i]) {
|
||||
dma_unmap_single(&dev->dev, s->dma_tx_bufs_addr[i],
|
||||
DMA_BUFFER_SIZE, DMA_TO_DEVICE);
|
||||
}
|
||||
kfree(s->dma_tx_bufs[i]);
|
||||
}
|
||||
|
||||
for(i = 0; i < DMA_BUFFER_COUNT; i++) {
|
||||
if (s->dma_rx_bufs_addr[i]) {
|
||||
dma_unmap_single(&dev->dev, s->dma_rx_bufs_addr[i],
|
||||
DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
|
||||
}
|
||||
kfree(s->dma_rx_bufs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void litepcie_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
LitePCIeState *s = pci_get_drvdata(dev);
|
||||
|
||||
printk(KERN_INFO LITEPCIE_NAME " Removing device\n");
|
||||
litepcie_minor_table[s->minor] = NULL;
|
||||
|
||||
litepcie_end(dev, s);
|
||||
free_irq(dev->irq, s);
|
||||
pci_disable_msi(dev);
|
||||
pci_iounmap(dev, s->bar0_addr);
|
||||
pci_disable_device(dev);
|
||||
pci_release_regions(dev);
|
||||
kfree(s);
|
||||
};
|
||||
|
||||
static const struct pci_device_id litepcie_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_FPGA_VENDOR_ID, PCI_FPGA_DEVICE_ID), },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, litepcie_pci_ids);
|
||||
|
||||
|
||||
static struct pci_driver litepcie_pci_driver = {
|
||||
.name = LITEPCIE_NAME,
|
||||
.id_table = litepcie_pci_ids,
|
||||
.probe = litepcie_pci_probe,
|
||||
.remove = litepcie_pci_remove,
|
||||
};
|
||||
|
||||
static int __init litepcie_module_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pci_register_driver(&litepcie_pci_driver);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Error while registering PCI driver\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
ret = alloc_chrdev_region(&litepcie_cdev, 0, LITEPCIE_MINOR_COUNT, LITEPCIE_NAME);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Could not allocate char device\n");
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
cdev_init(&litepcie_cdev_struct, &litepcie_fops);
|
||||
ret = cdev_add(&litepcie_cdev_struct, litepcie_cdev, LITEPCIE_MINOR_COUNT);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR LITEPCIE_NAME " Could not register char device\n");
|
||||
goto fail3;
|
||||
}
|
||||
return 0;
|
||||
fail3:
|
||||
unregister_chrdev_region(litepcie_cdev, LITEPCIE_MINOR_COUNT);
|
||||
fail2:
|
||||
pci_unregister_driver(&litepcie_pci_driver);
|
||||
fail1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit litepcie_module_exit(void)
|
||||
{
|
||||
cdev_del(&litepcie_cdev_struct);
|
||||
unregister_chrdev_region(litepcie_cdev, LITEPCIE_MINOR_COUNT);
|
||||
|
||||
pci_unregister_driver(&litepcie_pci_driver);
|
||||
}
|
||||
|
||||
|
||||
module_init(litepcie_module_init);
|
||||
module_exit(litepcie_module_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,19 +0,0 @@
|
|||
CFLAGS=-O2 -Wall -g -I../kernel -MMD
|
||||
LDFLAGS=-g
|
||||
CC=gcc
|
||||
AR=ar
|
||||
|
||||
PROGS=litepcie_util
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
litepcie_util: litepcie_util.o litepcie_lib.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^ -lrt -lm
|
||||
|
||||
clean:
|
||||
rm -f $(PROGS) *.o *.a *.d *~
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
-include $(wildcard *.d)
|
|
@ -1,31 +0,0 @@
|
|||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <immintrin.h>
|
||||
|
||||
#ifndef _BOOL_defined
|
||||
#define _BOOL_defined
|
||||
#undef FALSE
|
||||
#undef TRUE
|
||||
|
||||
typedef int BOOL;
|
||||
enum {
|
||||
FALSE = 0,
|
||||
TRUE = 1,
|
||||
};
|
||||
#endif
|
||||
|
||||
static inline int sub_mod_int(int a, int b, int m)
|
||||
{
|
||||
a -= b;
|
||||
if (a < 0)
|
||||
a += m;
|
||||
return a;
|
||||
}
|
||||
|
||||
static inline int add_mod_int(int a, int b, int m)
|
||||
{
|
||||
a += b;
|
||||
if (a >= m)
|
||||
a -= m;
|
||||
return a;
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
/*
|
||||
* LitePCIe library
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "litepcie.h"
|
||||
#include "cutils.h"
|
||||
#include "config.h"
|
||||
#include "csr.h"
|
||||
#include "flags.h"
|
||||
|
||||
#include "litepcie_lib.h"
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- DMA overflow/underflow detection
|
||||
*/
|
||||
|
||||
void *litepcie_malloc(int size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void *litepcie_mallocz(int size)
|
||||
{
|
||||
void *ptr;
|
||||
ptr = litepcie_malloc(size);
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void litepcie_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void __attribute__((format(printf, 2, 3))) litepcie_log(LitePCIeState *s, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* in ms */
|
||||
int64_t litepcie_get_time_ms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (int64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000U);
|
||||
}
|
||||
|
||||
LitePCIeState *litepcie_open(const char *device_name)
|
||||
{
|
||||
LitePCIeState *s;
|
||||
|
||||
s = litepcie_mallocz(sizeof(LitePCIeState));
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
s->litepcie_fd = open(device_name, O_RDWR);
|
||||
if (s->litepcie_fd < 0) {
|
||||
perror(device_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* map the DMA buffers */
|
||||
if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_GET_MMAP_INFO, &s->mmap_info) != 0) {
|
||||
perror("LITEPCIE_IOCTL_GET_MMAP_INFO");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s->dma_tx_buf = mmap(NULL, s->mmap_info.dma_tx_buf_size *
|
||||
s->mmap_info.dma_tx_buf_count,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, s->litepcie_fd,
|
||||
s->mmap_info.dma_tx_buf_offset);
|
||||
if (s->dma_tx_buf == MAP_FAILED) {
|
||||
perror("mmap1");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s->dma_rx_buf = mmap(NULL, s->mmap_info.dma_rx_buf_size *
|
||||
s->mmap_info.dma_rx_buf_count,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, s->litepcie_fd,
|
||||
s->mmap_info.dma_rx_buf_offset);
|
||||
if (s->dma_rx_buf == MAP_FAILED) {
|
||||
perror("mmap2");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* map the registers */
|
||||
s->reg_buf = mmap(NULL, s->mmap_info.reg_size,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, s->litepcie_fd,
|
||||
s->mmap_info.reg_offset);
|
||||
if (s->reg_buf == MAP_FAILED) {
|
||||
perror("mmap2");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s->dma_tx_buf_size = s->mmap_info.dma_tx_buf_size;
|
||||
s->dma_rx_buf_size = s->mmap_info.dma_rx_buf_size;
|
||||
|
||||
pthread_mutex_init(&s->fifo_mutex, NULL);
|
||||
|
||||
return s;
|
||||
fail:
|
||||
litepcie_close(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void litepcie_dma_start(LitePCIeState *s, int buf_size, int buf_count, BOOL is_loopback)
|
||||
{
|
||||
struct litepcie_ioctl_dma_start dma_start;
|
||||
|
||||
if (buf_count > DMA_BUFFER_COUNT) {
|
||||
litepcie_log(s, "unsupported buf_count\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s->tx_buf_size = s->rx_buf_size = buf_size;
|
||||
s->tx_buf_count = s->rx_buf_count = buf_count;
|
||||
|
||||
dma_start.dma_flags = 0;
|
||||
if (is_loopback)
|
||||
dma_start.dma_flags |= DMA_LOOPBACK_ENABLE;
|
||||
dma_start.tx_buf_size = s->tx_buf_size;
|
||||
dma_start.tx_buf_count = s->tx_buf_count;
|
||||
dma_start.rx_buf_size = s->rx_buf_size;
|
||||
dma_start.rx_buf_count = s->rx_buf_count;
|
||||
if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_DMA_START, &dma_start) < 0) {
|
||||
perror("LITEPCIE_IOCTL_DMA_START");
|
||||
}
|
||||
}
|
||||
|
||||
void litepcie_dma_stop(LitePCIeState *s)
|
||||
{
|
||||
if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_DMA_STOP, NULL) < 0) {
|
||||
perror("LITEPCIE_IOCTL_DMA_STOP");
|
||||
}
|
||||
}
|
||||
|
||||
void litepcie_writel(LitePCIeState *s, uint32_t addr, uint32_t val)
|
||||
{
|
||||
*(volatile uint32_t *)(s->reg_buf + addr) = val;
|
||||
}
|
||||
|
||||
uint32_t litepcie_readl(LitePCIeState *s, uint32_t addr)
|
||||
{
|
||||
return *(volatile uint32_t *)(s->reg_buf + addr);
|
||||
}
|
||||
|
||||
void litepcie_close(LitePCIeState *s)
|
||||
{
|
||||
pthread_mutex_destroy(&s->fifo_mutex);
|
||||
|
||||
if (s->dma_tx_buf) {
|
||||
munmap(s->dma_tx_buf, s->mmap_info.dma_tx_buf_size *
|
||||
s->mmap_info.dma_tx_buf_count);
|
||||
}
|
||||
if (s->dma_rx_buf) {
|
||||
munmap(s->dma_rx_buf, s->mmap_info.dma_rx_buf_size *
|
||||
s->mmap_info.dma_rx_buf_count);
|
||||
}
|
||||
if (s->reg_buf)
|
||||
munmap(s->reg_buf, s->mmap_info.reg_size);
|
||||
if (s->litepcie_fd >= 0)
|
||||
close(s->litepcie_fd);
|
||||
litepcie_free(s);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* LitePCIe library
|
||||
*
|
||||
*/
|
||||
#ifndef LITEPCIE_LIB_H
|
||||
#define LITEPCIE_LIB_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define LITEPCIE_FILENAME "/dev/litepcie0"
|
||||
|
||||
typedef struct {
|
||||
int litepcie_fd;
|
||||
struct litepcie_ioctl_mmap_info mmap_info;
|
||||
uint8_t *dma_tx_buf;
|
||||
int dma_tx_buf_size;
|
||||
uint8_t *dma_rx_buf;
|
||||
int dma_rx_buf_size;
|
||||
uint8_t *reg_buf;
|
||||
|
||||
unsigned int tx_buf_size; /* in bytes */
|
||||
unsigned int tx_buf_count; /* number of buffers */
|
||||
unsigned int rx_buf_size; /* in bytes */
|
||||
unsigned int rx_buf_count; /* number of buffers */
|
||||
|
||||
unsigned int tx_buf_len; /* in samples */
|
||||
unsigned int rx_buf_len; /* in samples */
|
||||
|
||||
pthread_mutex_t fifo_mutex;
|
||||
int64_t rx_timestamp; /* timestamp (in samples) of the current RX buffer */
|
||||
unsigned int rx_buf_index; /* index of the current RX buffer */
|
||||
unsigned int rx_buf_next; /* index of the next buffer after the
|
||||
last received buffer */
|
||||
BOOL has_rx_timestamp; /* true if received at least one buffer */
|
||||
|
||||
int64_t tx_underflow_count; /* TX too late */
|
||||
int64_t rx_overflow_count; /* RX too late */
|
||||
} LitePCIeState;
|
||||
|
||||
void *litepcie_malloc(int size);
|
||||
void *litepcie_mallocz(int size);
|
||||
void litepcie_free(void *ptr);
|
||||
void __attribute__((format(printf, 2, 3))) litepcie_log(LitePCIeState *s, const char *fmt, ...);
|
||||
int64_t litepcie_get_time_ms(void);
|
||||
LitePCIeState *litepcie_open(const char *device_name);
|
||||
void litepcie_close(LitePCIeState *s);
|
||||
void litepcie_dma_start(LitePCIeState *s, int buf_size, int buf_count, BOOL is_loopback);
|
||||
void litepcie_dma_stop(LitePCIeState *s);
|
||||
void litepcie_writel(LitePCIeState *s, uint32_t addr, uint32_t val);
|
||||
uint32_t litepcie_readl(LitePCIeState *s, uint32_t addr);
|
||||
|
||||
#endif /* LITEPCIE_LIB_H */
|
|
@ -1,259 +0,0 @@
|
|||
/*
|
||||
* LitePCIe utilities
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "litepcie.h"
|
||||
#include "cutils.h"
|
||||
#include "config.h"
|
||||
#include "csr.h"
|
||||
#include "flags.h"
|
||||
#include "litepcie_lib.h"
|
||||
|
||||
static inline uint32_t seed_to_data(uint32_t seed)
|
||||
{
|
||||
#if 1
|
||||
/* more random but slower */
|
||||
return seed * 0x31415976 + 1;
|
||||
#else
|
||||
/* simplify debug: just copy the counter */
|
||||
return seed;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void write_pn_data(uint32_t *dst, int count, uint32_t *pseed)
|
||||
{
|
||||
int i;
|
||||
uint32_t seed;
|
||||
|
||||
seed = *pseed;
|
||||
for(i = 0; i < count; i++) {
|
||||
dst[i] = seed_to_data(seed);
|
||||
seed++;
|
||||
}
|
||||
*pseed = seed;
|
||||
}
|
||||
|
||||
/* Return the number of errors */
|
||||
static int check_pn_data(const uint32_t *tab, int count,
|
||||
uint32_t *pseed)
|
||||
{
|
||||
int i, errors;
|
||||
uint32_t seed;
|
||||
|
||||
errors = 0;
|
||||
seed = *pseed;
|
||||
for(i = 0; i < count; i++) {
|
||||
if (tab[i] != seed_to_data(seed)) {
|
||||
errors++;
|
||||
}
|
||||
seed++;
|
||||
}
|
||||
*pseed = seed;
|
||||
return errors;
|
||||
}
|
||||
|
||||
#define MAX_SHIFT_OFFSET 128
|
||||
|
||||
/* test DMA with a buffer size of buf_size bytes in loopback
|
||||
mode. */
|
||||
void dma_test(LitePCIeState *s, int buf_size, int buf_count, BOOL is_loopback)
|
||||
{
|
||||
int is_first, tx_buf_num, buf_num_cur, buf_num_next;
|
||||
struct litepcie_ioctl_dma_wait dma_wait;
|
||||
int buf_stats_count; /* statistics */
|
||||
int64_t last_time;
|
||||
uint32_t tx_seed, rx_seed;
|
||||
int buf_rx_count, first_rx_buf, rx_errors, shift, d, tx_underflows;
|
||||
|
||||
litepcie_dma_start(s, buf_size, buf_count, is_loopback);
|
||||
|
||||
is_first = 1;
|
||||
buf_num_cur = 0; /* next buffer to receive */
|
||||
/* PN data TX and RX state */
|
||||
tx_seed = MAX_SHIFT_OFFSET;
|
||||
rx_seed = 0;
|
||||
buf_rx_count = 0;
|
||||
first_rx_buf = 1;
|
||||
|
||||
/* statistics */
|
||||
buf_stats_count = 0;
|
||||
last_time = litepcie_get_time_ms();
|
||||
rx_errors = 0;
|
||||
shift = 0;
|
||||
tx_underflows = 0;
|
||||
|
||||
for(;;) {
|
||||
/* wait until at least one buffer is received */
|
||||
dma_wait.timeout = 1000; /* 1 second timeout */
|
||||
dma_wait.tx_wait = FALSE;
|
||||
dma_wait.tx_buf_num = -1; /* not used */
|
||||
if (is_first) {
|
||||
dma_wait.rx_buf_num = -1; /* don't wait, just get the last
|
||||
received buffer number */
|
||||
} else {
|
||||
dma_wait.rx_buf_num = sub_mod_int(buf_num_cur, 1, buf_count);
|
||||
}
|
||||
/* wait until the current buffer number is different from
|
||||
dma_wait.buf_num */
|
||||
if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_DMA_WAIT, &dma_wait) < 0) {
|
||||
perror("LITEPCIE_IOCTL_DMA_WAIT");
|
||||
}
|
||||
if (is_first) {
|
||||
buf_num_cur = dma_wait.rx_buf_num;
|
||||
is_first = 0;
|
||||
}
|
||||
buf_num_next = add_mod_int(dma_wait.rx_buf_num, 1, buf_count);
|
||||
|
||||
while (buf_num_cur != buf_num_next) {
|
||||
|
||||
/* write the TX data 4/10 of a DMA cycle in the future */
|
||||
tx_buf_num = add_mod_int(buf_num_cur, 4*buf_count/10, buf_count);
|
||||
d = sub_mod_int(tx_buf_num, buf_num_next, buf_count);
|
||||
if (d >= (buf_count / 2)) {
|
||||
/* we are too late in writing data, which necessarily
|
||||
gives read errors. */
|
||||
tx_underflows++;
|
||||
}
|
||||
|
||||
write_pn_data((uint32_t *)(s->dma_tx_buf +
|
||||
tx_buf_num * s->dma_tx_buf_size),
|
||||
s->tx_buf_size >> 2, &tx_seed);
|
||||
|
||||
if (buf_rx_count >= 4*buf_count/10) {
|
||||
const uint32_t *rx_buf;
|
||||
int rx_buf_len;
|
||||
|
||||
rx_buf = (uint32_t *)(s->dma_rx_buf + buf_num_cur * s->dma_rx_buf_size);
|
||||
rx_buf_len = s->rx_buf_size >> 2;
|
||||
|
||||
if (first_rx_buf) {
|
||||
uint32_t seed;
|
||||
|
||||
/* find the initial shift */
|
||||
for(shift = 0; shift < 2 * MAX_SHIFT_OFFSET; shift++) {
|
||||
seed = rx_seed + shift;
|
||||
rx_errors = check_pn_data(rx_buf, rx_buf_len, &seed);
|
||||
if (rx_errors <= (rx_buf_len / 2)) {
|
||||
rx_seed = seed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shift == 2 * MAX_SHIFT_OFFSET) {
|
||||
printf("Cannot find initial data\n");
|
||||
exit(1);
|
||||
} else {
|
||||
printf("RX shift = %d\n",
|
||||
-(shift - MAX_SHIFT_OFFSET));
|
||||
}
|
||||
first_rx_buf = 0;
|
||||
} else {
|
||||
/* count the number of errors */
|
||||
rx_errors += check_pn_data(rx_buf, rx_buf_len, &rx_seed);
|
||||
}
|
||||
} else {
|
||||
buf_rx_count++;
|
||||
}
|
||||
|
||||
buf_num_cur = add_mod_int(buf_num_cur, 1, buf_count);
|
||||
|
||||
/* statistics */
|
||||
if (++buf_stats_count == 10000) {
|
||||
int64_t duration;
|
||||
duration = litepcie_get_time_ms() - last_time;
|
||||
printf("%0.1f Gb/sec %0.1f bufs/sec tx_underflows=%d errors=%d\n",
|
||||
(double)buf_stats_count * buf_size * 8 / ((double)duration * 1e6),
|
||||
(double)buf_stats_count * 1000 / (double)duration,
|
||||
tx_underflows, rx_errors);
|
||||
last_time = litepcie_get_time_ms();
|
||||
buf_stats_count = 0;
|
||||
tx_underflows = 0;
|
||||
rx_errors = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
litepcie_dma_stop(s);
|
||||
}
|
||||
|
||||
void dma_loopback_test(void)
|
||||
{
|
||||
LitePCIeState *s;
|
||||
|
||||
s = litepcie_open(LITEPCIE_FILENAME);
|
||||
if (!s) {
|
||||
fprintf(stderr, "Could not init driver\n");
|
||||
exit(1);
|
||||
}
|
||||
dma_test(s, 16*1024, DMA_BUFFER_COUNT, TRUE);
|
||||
|
||||
litepcie_close(s);
|
||||
}
|
||||
|
||||
void dump_version(void)
|
||||
{
|
||||
LitePCIeState *s;
|
||||
|
||||
s = litepcie_open(LITEPCIE_FILENAME);
|
||||
if (!s) {
|
||||
fprintf(stderr, "Could not init driver\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("sysid=0x%x\n", litepcie_readl(s, CSR_IDENTIFIER_SYSID_ADDR));
|
||||
printf("frequency=%d\n", litepcie_readl(s, CSR_IDENTIFIER_FREQUENCY_ADDR));
|
||||
|
||||
litepcie_close(s);
|
||||
}
|
||||
|
||||
void help(void)
|
||||
{
|
||||
printf("usage: litepcie_util cmd [args...]\n"
|
||||
"\n"
|
||||
"available commands:\n"
|
||||
"dma_loopback_test test DMA loopback operation\n"
|
||||
"version return fpga version\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *cmd;
|
||||
int c;
|
||||
|
||||
for(;;) {
|
||||
c = getopt(argc, argv, "h");
|
||||
if (c == -1)
|
||||
break;
|
||||
switch(c) {
|
||||
case 'h':
|
||||
help();
|
||||
break;
|
||||
default:
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
help();
|
||||
cmd = argv[optind++];
|
||||
|
||||
if (!strcmp(cmd, "dma_loopback_test")) {
|
||||
dma_loopback_test();
|
||||
} else if (!strcmp(cmd, "version")) {
|
||||
dump_version();
|
||||
} else {
|
||||
help();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
import string
|
||||
import mmap
|
||||
import sys
|
||||
|
||||
from misoclib.tools.litescope.software.driver.reg import *
|
||||
|
||||
|
||||
class LitePCIeWishboneDriverLinux:
|
||||
def __init__(self, bar, bar_size, addrmap=None, busword=8, debug=False):
|
||||
self.bar = bar
|
||||
self.bar_size = bar_size
|
||||
self.debug = debug
|
||||
self.f = None
|
||||
self.mmap = None
|
||||
self.regs = build_map(addrmap, busword, self.read, self.write)
|
||||
|
||||
def open(self):
|
||||
self.f = open(self.bar, "r+b")
|
||||
self.f.flush()
|
||||
self.mmap = mmap.mmap(self.f.fileno(), self.bar_size)
|
||||
|
||||
def close(self):
|
||||
self.mmap.close()
|
||||
self.f.close()
|
||||
|
||||
def read(self, addr, burst_length=1):
|
||||
datas = []
|
||||
for i in range(burst_length):
|
||||
self.mmap.seek(addr + 4*i)
|
||||
dat = self.mmap.read(4)
|
||||
val = dat[3] << 24
|
||||
val |= dat[2] << 16
|
||||
val |= dat[1] << 8
|
||||
val |= dat[0] << 0
|
||||
if self.debug:
|
||||
print("RD {:08X} @ {:08X}".format(val, addr + 4*i))
|
||||
datas.append(val)
|
||||
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]
|
||||
|
||||
for i, dat in enumerate(data):
|
||||
dat_bytes = [0, 0, 0, 0]
|
||||
dat_bytes[3] = (dat >> 24) & 0xff
|
||||
dat_bytes[2] = (dat >> 16) & 0xff
|
||||
dat_bytes[1] = (dat >> 8) & 0xff
|
||||
dat_bytes[0] = (dat >> 0) & 0xff
|
||||
self.mmap[addr + 4*i:addr + 4*(i+1)] = bytes(dat_bytes)
|
||||
if self.debug:
|
||||
print("WR {:08X} @ {:08X}".format(dat, (addr + i)*4))
|
||||
|
||||
|
||||
def LitePCIeWishboneDriver(*args, **kwargs):
|
||||
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||
raise NotImplementedError
|
||||
else:
|
||||
return LitePCIeWishboneDriverLinux(*args, **kwargs)
|
|
@ -1,10 +0,0 @@
|
|||
MSCDIR = ../../../../
|
||||
PYTHON = python3
|
||||
|
||||
CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
|
||||
|
||||
wishbone_tb:
|
||||
$(CMD) wishbone_tb.py
|
||||
|
||||
dma_tb:
|
||||
$(CMD) dma_tb.py
|
|
@ -1,36 +0,0 @@
|
|||
import random
|
||||
|
||||
|
||||
def print_with_prefix(s, prefix=""):
|
||||
if not isinstance(s, str):
|
||||
s = s.__repr__()
|
||||
s = s.split("\n")
|
||||
for l in s:
|
||||
print(prefix + l)
|
||||
|
||||
|
||||
def seed_to_data(seed, random=True):
|
||||
if random:
|
||||
return (seed * 0x31415979 + 1) & 0xffffffff
|
||||
else:
|
||||
return seed
|
||||
|
||||
|
||||
def check(ref, res):
|
||||
if isinstance(ref, int):
|
||||
return 0, 1, int(ref != res)
|
||||
else:
|
||||
shift = 0
|
||||
while((ref[0] != res[0]) and (len(res) > 1)):
|
||||
res.pop(0)
|
||||
shift += 1
|
||||
length = min(len(ref), len(res))
|
||||
errors = 0
|
||||
for i in range(length):
|
||||
if ref.pop(0) != res.pop(0):
|
||||
errors += 1
|
||||
return shift, length, errors
|
||||
|
||||
|
||||
def randn(max_n):
|
||||
return random.randint(0, max_n-1)
|
|
@ -1,177 +0,0 @@
|
|||
import random
|
||||
from migen.fhdl.std import *
|
||||
from migen.sim.generic import run_simulation
|
||||
from migen.actorlib.structuring import Converter
|
||||
|
||||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core import Endpoint
|
||||
from misoclib.com.litepcie.core.irq import interrupt_controller
|
||||
from misoclib.com.litepcie.frontend.dma import writer, reader
|
||||
|
||||
from misoclib.com.litepcie.test.common import *
|
||||
from misoclib.com.litepcie.test.model.host import *
|
||||
|
||||
DMA_READER_IRQ = 1
|
||||
DMA_WRITER_IRQ = 2
|
||||
|
||||
root_id = 0x100
|
||||
endpoint_id = 0x400
|
||||
max_length = Signal(8, reset=128)
|
||||
dma_size = 1024
|
||||
|
||||
|
||||
class DMADriver():
|
||||
def __init__(self, dma, selfp):
|
||||
self.dma = dma
|
||||
self.selfp = selfp
|
||||
|
||||
def set_prog_mode(self):
|
||||
dma = getattr(self.selfp, self.dma)
|
||||
dma.table._loop_prog_n.storage = 0
|
||||
yield
|
||||
|
||||
def set_loop_mode(self):
|
||||
dma = getattr(self.selfp, self.dma)
|
||||
dma.table._loop_prog_n.storage = 1
|
||||
yield
|
||||
|
||||
def flush(self):
|
||||
dma = getattr(self.selfp, self.dma)
|
||||
dma.table._flush.re = 1
|
||||
yield
|
||||
dma.table._flush.re = 0
|
||||
yield
|
||||
|
||||
def program_descriptor(self, address, length):
|
||||
value = address
|
||||
value |= (length << 32)
|
||||
|
||||
dma = getattr(self.selfp, self.dma)
|
||||
|
||||
dma.table._value.storage = value
|
||||
dma.table._we.r = 1
|
||||
dma.table._we.re = 1
|
||||
yield
|
||||
dma.table._we.re = 0
|
||||
yield
|
||||
|
||||
def enable(self):
|
||||
dma = getattr(self.selfp, self.dma)
|
||||
dma._enable.storage = 1
|
||||
yield
|
||||
|
||||
def disable(self):
|
||||
dma = getattr(self.selfp, self.dma)
|
||||
dma._enable.storage = 0
|
||||
yield
|
||||
|
||||
|
||||
class InterruptHandler(Module):
|
||||
def __init__(self, debug=False):
|
||||
self.debug = debug
|
||||
self.sink = Sink(interrupt_layout())
|
||||
self.dma_writer_irq = 0
|
||||
|
||||
def set_tb_selfp(self, tb_selfp):
|
||||
self.tb_selfp = tb_selfp
|
||||
|
||||
def do_simulation(self, selfp):
|
||||
tb_selfp = self.tb_selfp
|
||||
tb_selfp.irq_controller._clear.r = 0
|
||||
tb_selfp.irq_controller._clear.re = 0
|
||||
selfp.sink.ack = 1
|
||||
self.dma_writer_irq = 0
|
||||
if selfp.sink.stb and (selfp.simulator.cycle_counter%4 == 0):
|
||||
# get vector
|
||||
irq_vector = tb_selfp.irq_controller._vector.status
|
||||
|
||||
# handle irq
|
||||
if irq_vector & DMA_READER_IRQ:
|
||||
if self.debug:
|
||||
print("DMA_READER IRQ : {}".format(tb_selfp.dma_reader.table._index.status))
|
||||
# clear irq_controller
|
||||
tb_selfp.irq_controller._clear.re = 1
|
||||
tb_selfp.irq_controller._clear.r |= DMA_READER_IRQ
|
||||
|
||||
if irq_vector & DMA_WRITER_IRQ:
|
||||
if self.debug:
|
||||
print("DMA_WRITER IRQ : {}".format(tb_selfp.dma_writer.table._index.status))
|
||||
# clear irq_controller
|
||||
tb_selfp.irq_controller._clear.re = 1
|
||||
tb_selfp.irq_controller._clear.r |= DMA_WRITER_IRQ
|
||||
self.dma_writer_irq = 1
|
||||
|
||||
|
||||
test_size = 16*1024
|
||||
|
||||
|
||||
class TB(Module):
|
||||
def __init__(self, with_converter=False):
|
||||
self.submodules.host = Host(64, root_id, endpoint_id,
|
||||
phy_debug=False,
|
||||
chipset_debug=False, chipset_split=True, chipset_reordering=True,
|
||||
host_debug=True)
|
||||
self.submodules.endpoint = Endpoint(self.host.phy, max_pending_requests=8, with_reordering=True)
|
||||
self.submodules.dma_reader = reader.DMAReader(self.endpoint, self.endpoint.crossbar.get_master_port(read_only=True))
|
||||
self.submodules.dma_writer = writer.DMAWriter(self.endpoint, self.endpoint.crossbar.get_master_port(write_only=True))
|
||||
|
||||
if with_converter:
|
||||
self.submodules.up_converter = Converter(dma_layout(16), dma_layout(64))
|
||||
self.submodules.down_converter = Converter(dma_layout(64), dma_layout(16))
|
||||
|
||||
self.comb += [
|
||||
self.dma_reader.source.connect(self.down_converter.sink),
|
||||
self.down_converter.source.connect(self.up_converter.sink),
|
||||
self.up_converter.source.connect(self.dma_writer.sink)
|
||||
]
|
||||
else:
|
||||
self.comb += self.dma_reader.source.connect(self.dma_writer.sink)
|
||||
|
||||
self.submodules.irq_controller = interrupt_controller.InterruptController(2)
|
||||
self.comb += [
|
||||
self.irq_controller.irqs[log2_int(DMA_READER_IRQ)].eq(self.dma_reader.table.irq),
|
||||
self.irq_controller.irqs[log2_int(DMA_WRITER_IRQ)].eq(self.dma_writer.table.irq)
|
||||
]
|
||||
self.submodules.irq_handler = InterruptHandler()
|
||||
self.comb += self.irq_controller.source.connect(self.irq_handler.sink)
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
self.host.malloc(0x00000000, test_size*2)
|
||||
self.host.chipset.enable()
|
||||
host_datas = [seed_to_data(i, True) for i in range(test_size//4)]
|
||||
self.host.write_mem(0x00000000, host_datas)
|
||||
|
||||
dma_reader_driver = DMADriver("dma_reader", selfp)
|
||||
dma_writer_driver = DMADriver("dma_writer", selfp)
|
||||
|
||||
self.irq_handler.set_tb_selfp(selfp)
|
||||
|
||||
yield from dma_reader_driver.set_prog_mode()
|
||||
yield from dma_reader_driver.flush()
|
||||
for i in range(8):
|
||||
yield from dma_reader_driver.program_descriptor((test_size//8)*i, test_size//8)
|
||||
|
||||
yield from dma_writer_driver.set_prog_mode()
|
||||
yield from dma_writer_driver.flush()
|
||||
for i in range(8):
|
||||
yield from dma_writer_driver.program_descriptor(test_size + (test_size//8)*i, test_size//8)
|
||||
|
||||
selfp.irq_controller._enable.storage = DMA_READER_IRQ | DMA_WRITER_IRQ
|
||||
|
||||
yield from dma_reader_driver.enable()
|
||||
yield from dma_writer_driver.enable()
|
||||
|
||||
i = 0
|
||||
while i != 8:
|
||||
i += self.irq_handler.dma_writer_irq
|
||||
yield
|
||||
|
||||
for i in range(100):
|
||||
yield
|
||||
loopback_datas = self.host.read_mem(test_size, test_size)
|
||||
|
||||
s, l, e = check(host_datas, loopback_datas)
|
||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_simulation(TB(with_converter=False), ncycles=4000, vcd_name="my.vcd", keep_files=True)
|
|
@ -1,142 +0,0 @@
|
|||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
from misoclib.com.litepcie.test.common import *
|
||||
from misoclib.com.litepcie.test.model.tlp import *
|
||||
|
||||
|
||||
def print_chipset(s):
|
||||
print_with_prefix(s, "[CHIPSET] ")
|
||||
|
||||
|
||||
def find_cmp_tags(queue):
|
||||
tags = []
|
||||
for tag, dwords in queue:
|
||||
if tag not in tags:
|
||||
tags.append(tag)
|
||||
return tags
|
||||
|
||||
|
||||
def find_first_cmp_msg(queue, msg_tag):
|
||||
for i, (tag, dwords) in enumerate(queue):
|
||||
if tag == msg_tag:
|
||||
return i
|
||||
|
||||
|
||||
# Chipset model
|
||||
class Chipset(Module):
|
||||
def __init__(self, phy, root_id, debug=False, with_reordering=False):
|
||||
self.phy = phy
|
||||
self.root_id = root_id
|
||||
self.debug = debug
|
||||
self.with_reordering = with_reordering
|
||||
###
|
||||
self.rd32_data = []
|
||||
self.cmp_queue = []
|
||||
self.en = False
|
||||
|
||||
def set_host_callback(self, callback):
|
||||
self.host_callback = callback
|
||||
|
||||
def enable(self):
|
||||
self.en = True
|
||||
|
||||
def disable(self):
|
||||
self.en = False
|
||||
|
||||
def wr32(self, adr, data):
|
||||
wr32 = WR32()
|
||||
wr32.fmt = 0b10
|
||||
wr32.type = 0b00000
|
||||
wr32.length = len(data)
|
||||
wr32.first_be = 0xf
|
||||
wr32.address = adr
|
||||
wr32.requester_id = self.root_id
|
||||
dwords = wr32.encode_dwords(data)
|
||||
if self.debug:
|
||||
print_chipset(">>>>>>>>")
|
||||
print_chipset(parse_dwords(dwords))
|
||||
yield from self.phy.send_blocking(dwords)
|
||||
|
||||
def rd32(self, adr, length=1):
|
||||
rd32 = RD32()
|
||||
rd32.fmt = 0b00
|
||||
rd32.type = 0b00000
|
||||
rd32.length = length
|
||||
rd32.first_be = 0xf
|
||||
rd32.address = adr
|
||||
rd32.requester_id = self.root_id
|
||||
dwords = rd32.encode_dwords()
|
||||
if self.debug:
|
||||
print_chipset(">>>>>>>>")
|
||||
print_chipset(parse_dwords(dwords))
|
||||
yield from self.phy.send_blocking(dwords)
|
||||
dwords = None
|
||||
while dwords is None:
|
||||
dwords = self.phy.receive()
|
||||
yield
|
||||
cpld = CPLD(dwords)
|
||||
self.rd32_data = cpld.data
|
||||
if self.debug:
|
||||
print_chipset("<<<<<<<<")
|
||||
print_chipset(cpld)
|
||||
|
||||
def cmp(self, req_id, data, byte_count=None, lower_address=0, tag=0, with_split=False):
|
||||
if with_split:
|
||||
d = random.choice([64, 128, 256])
|
||||
n = byte_count//d
|
||||
if n == 0:
|
||||
self.cmp(req_id, data, byte_count=byte_count, tag=tag)
|
||||
else:
|
||||
for i in range(n):
|
||||
cmp_data = data[i*byte_count//(4*n):(i+1)*byte_count//(4*n)]
|
||||
self.cmp(req_id, cmp_data, byte_count=byte_count-i*byte_count//n, tag=tag)
|
||||
else:
|
||||
if len(data) == 0:
|
||||
fmt = 0b00
|
||||
cpl = CPL()
|
||||
else:
|
||||
fmt = 0b10
|
||||
cpl = CPLD()
|
||||
cpl.fmt = fmt
|
||||
cpl.type = 0b01010
|
||||
cpl.length = len(data)
|
||||
cpl.lower_address = lower_address
|
||||
cpl.requester_id = req_id
|
||||
cpl.completer_id = self.root_id
|
||||
if byte_count is None:
|
||||
cpl.byte_count = len(data)*4
|
||||
else:
|
||||
cpl.byte_count = byte_count
|
||||
cpl.tag = tag
|
||||
if len(data) == 0:
|
||||
dwords = cpl.encode_dwords()
|
||||
else:
|
||||
dwords = cpl.encode_dwords(data)
|
||||
self.cmp_queue.append((tag, dwords))
|
||||
|
||||
def cmp_callback(self):
|
||||
if len(self.cmp_queue):
|
||||
if self.with_reordering:
|
||||
tags = find_cmp_tags(self.cmp_queue)
|
||||
tag = random.choice(tags)
|
||||
n = find_first_cmp_msg(self.cmp_queue, tag)
|
||||
tag, dwords = self.cmp_queue.pop(n)
|
||||
else:
|
||||
tag, dwords = self.cmp_queue.pop(0)
|
||||
if self.debug:
|
||||
print_chipset(">>>>>>>>")
|
||||
print_chipset(parse_dwords(dwords))
|
||||
self.phy.send(dwords)
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
while True:
|
||||
if self.en:
|
||||
dwords = self.phy.receive()
|
||||
if dwords is not None:
|
||||
msg = parse_dwords(dwords)
|
||||
if self.debug:
|
||||
print_chipset(" <<<<<<<< (Callback)")
|
||||
print_chipset(msg)
|
||||
self.host_callback(msg)
|
||||
self.cmp_callback()
|
||||
yield
|
|
@ -1,64 +0,0 @@
|
|||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
from misoclib.com.litepcie.test.common import *
|
||||
from misoclib.com.litepcie.test.model.phy import PHY
|
||||
from misoclib.com.litepcie.test.model.tlp import *
|
||||
from misoclib.com.litepcie.test.model.chipset import Chipset
|
||||
|
||||
|
||||
def print_host(s):
|
||||
print_with_prefix(s, "[HOST] ")
|
||||
|
||||
|
||||
# Host model
|
||||
class Host(Module):
|
||||
def __init__(self, dw, root_id, endpoint_id, bar0_size=1*MB,
|
||||
phy_debug=False,
|
||||
chipset_debug=False, chipset_split=False, chipset_reordering=False,
|
||||
host_debug=False):
|
||||
self.debug = host_debug
|
||||
self.chipset_split = chipset_split
|
||||
###
|
||||
self.submodules.phy = PHY(dw, endpoint_id, bar0_size, phy_debug)
|
||||
self.submodules.chipset = Chipset(self.phy, root_id, chipset_debug, chipset_reordering)
|
||||
self.chipset.set_host_callback(self.callback)
|
||||
|
||||
self.rd32_queue = []
|
||||
|
||||
def malloc(self, base, length):
|
||||
self.base = base
|
||||
self.buffer = [0]*(length//4)
|
||||
|
||||
def write_mem(self, adr, data):
|
||||
if self.debug:
|
||||
print_host("Writing {} bytes at 0x{:08x}".format(len(data)*4, adr))
|
||||
current_adr = (adr-self.base)//4
|
||||
for i in range(len(data)):
|
||||
self.buffer[current_adr+i] = data[i]
|
||||
|
||||
def read_mem(self, adr, length=1):
|
||||
if self.debug:
|
||||
print_host("Reading {} bytes at 0x{:08x}".format(length, adr))
|
||||
current_adr = (adr-self.base)//4
|
||||
data = []
|
||||
for i in range(length//4):
|
||||
data.append(self.buffer[current_adr+i])
|
||||
return data
|
||||
|
||||
def callback(self, msg):
|
||||
if isinstance(msg, WR32):
|
||||
address = msg.address*4
|
||||
self.write_mem(address, msg.data)
|
||||
elif isinstance(msg, RD32):
|
||||
self.rd32_queue.append(msg)
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
while True:
|
||||
if len(self.rd32_queue):
|
||||
msg = self.rd32_queue.pop(0)
|
||||
address = msg.address*4
|
||||
length = msg.length*4
|
||||
data = self.read_mem(address, length)
|
||||
self.chipset.cmp(msg.requester_id, data, byte_count=length, tag=msg.tag, with_split=self.chipset_split)
|
||||
else:
|
||||
yield
|
|
@ -1,144 +0,0 @@
|
|||
import math
|
||||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
from misoclib.com.litepcie.test.common import *
|
||||
|
||||
|
||||
def print_chipset(s):
|
||||
print_with_prefix(s, "[PHY] ")
|
||||
|
||||
|
||||
# PHY Layer model
|
||||
class PHYPacket():
|
||||
def __init__(self, dat=[], be=[]):
|
||||
self.dat = dat
|
||||
self.be = be
|
||||
self.start = 1
|
||||
self.done = 0
|
||||
|
||||
|
||||
class PHYSource(Module):
|
||||
def __init__(self, dw):
|
||||
self.source = Source(phy_layout(dw))
|
||||
###
|
||||
self.packets = []
|
||||
self.packet = PHYPacket()
|
||||
self.packet.done = 1
|
||||
|
||||
def send(self, packet):
|
||||
self.packets.append(packet)
|
||||
|
||||
def send_blocking(self, packet):
|
||||
self.send(packet)
|
||||
while packet.done == 0:
|
||||
yield
|
||||
|
||||
def do_simulation(self, selfp):
|
||||
if len(self.packets) and self.packet.done:
|
||||
self.packet = self.packets.pop(0)
|
||||
if self.packet.start and not self.packet.done:
|
||||
selfp.source.stb = 1
|
||||
selfp.source.sop = 1
|
||||
selfp.source.dat = self.packet.dat.pop(0)
|
||||
selfp.source.be = self.packet.be.pop(0)
|
||||
self.packet.start = 0
|
||||
elif selfp.source.stb == 1 and selfp.source.ack == 1:
|
||||
selfp.source.sop = 0
|
||||
selfp.source.eop = (len(self.packet.dat) == 1)
|
||||
if len(self.packet.dat) > 0:
|
||||
selfp.source.stb = 1
|
||||
selfp.source.dat = self.packet.dat.pop(0)
|
||||
selfp.source.be = self.packet.be.pop(0)
|
||||
else:
|
||||
self.packet.done = 1
|
||||
selfp.source.stb = 0
|
||||
|
||||
|
||||
class PHYSink(Module):
|
||||
def __init__(self, dw):
|
||||
self.sink = Sink(phy_layout(dw))
|
||||
###
|
||||
self.packet = PHYPacket()
|
||||
|
||||
def receive(self):
|
||||
self.packet.done = 0
|
||||
while self.packet.done == 0:
|
||||
yield
|
||||
|
||||
def do_simulation(self, selfp):
|
||||
self.packet.done = 0
|
||||
selfp.sink.ack = 1
|
||||
if selfp.sink.stb == 1 and selfp.sink.sop == 1:
|
||||
self.packet.start = 1
|
||||
self.packet.dat = [selfp.sink.dat]
|
||||
self.packet.be = [selfp.sink.be]
|
||||
elif selfp.sink.stb:
|
||||
self.packet.start = 0
|
||||
self.packet.dat.append(selfp.sink.dat)
|
||||
self.packet.be.append(selfp.sink.be)
|
||||
if (selfp.sink.stb == 1 and selfp.sink.eop == 1):
|
||||
self.packet.done = 1
|
||||
|
||||
|
||||
class PHY(Module):
|
||||
def __init__(self, dw, id, bar0_size, debug):
|
||||
self.dw = dw
|
||||
|
||||
self.id = id
|
||||
|
||||
self.bar0_size = bar0_size
|
||||
self.bar0_mask = get_bar_mask(bar0_size)
|
||||
|
||||
self.max_request_size = 512
|
||||
self.max_payload_size = 128
|
||||
|
||||
self.submodules.phy_source = PHYSource(dw)
|
||||
self.submodules.phy_sink = PHYSink(dw)
|
||||
|
||||
self.source = self.phy_source.source
|
||||
self.sink = self.phy_sink.sink
|
||||
|
||||
def dwords2packet(self, dwords):
|
||||
ratio = self.dw//32
|
||||
length = math.ceil(len(dwords)/ratio)
|
||||
dat = [0]*length
|
||||
be = [0]*length
|
||||
for n in range(length):
|
||||
for i in reversed(range(ratio)):
|
||||
dat[n] = dat[n] << 32
|
||||
be[n] = be[n] << 4
|
||||
try:
|
||||
dat[n] |= dwords[2*n+i]
|
||||
be[n] |= 0xF
|
||||
except:
|
||||
pass
|
||||
return dat, be
|
||||
|
||||
def send(self, dwords):
|
||||
dat, be = self.dwords2packet(dwords)
|
||||
packet = PHYPacket(dat, be)
|
||||
self.phy_source.send(packet)
|
||||
|
||||
def send_blocking(self, dwords):
|
||||
dat, be = self.dwords2packet(dwords)
|
||||
packet = PHYPacket(dat, be)
|
||||
yield from self.phy_source.send_blocking(packet)
|
||||
|
||||
def packet2dwords(self, p_dat, p_be):
|
||||
ratio = self.dw//32
|
||||
dwords = []
|
||||
for dat, be in zip(p_dat, p_be):
|
||||
for i in range(ratio):
|
||||
dword_be = (be >> (4*i)) & 0xf
|
||||
dword_dat = (dat >> (32*i)) & 0xffffffff
|
||||
if dword_be == 0xf:
|
||||
dwords.append(dword_dat)
|
||||
return dwords
|
||||
|
||||
def receive(self):
|
||||
if self.phy_sink.packet.done:
|
||||
self.phy_sink.packet.done = 0
|
||||
return self.packet2dwords(self.phy_sink.packet.dat, self.phy_sink.packet.be)
|
||||
else:
|
||||
return None
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
from misoclib.com.litepcie.common import *
|
||||
from misoclib.com.litepcie.core.packet.common import *
|
||||
|
||||
|
||||
# TLP Layer model
|
||||
def get_field_data(field, dwords):
|
||||
return (dwords[field.byte//4] >> field.offset) & (2**field.width-1)
|
||||
|
||||
tlp_headers_dict = {
|
||||
"RD32": tlp_request_header,
|
||||
"WR32": tlp_request_header,
|
||||
"CPLD": tlp_completion_header,
|
||||
"CPL": tlp_completion_header
|
||||
}
|
||||
|
||||
|
||||
class TLP():
|
||||
def __init__(self, name, dwords=[0, 0, 0]):
|
||||
self.name = name
|
||||
self.header = dwords[:3]
|
||||
self.data = dwords[3:]
|
||||
self.dwords = self.header + self.data
|
||||
self.decode_dwords()
|
||||
|
||||
def decode_dwords(self):
|
||||
for k, v in tlp_headers_dict[self.name].fields.items():
|
||||
setattr(self, k, get_field_data(v, self.header))
|
||||
|
||||
def encode_dwords(self, data=[]):
|
||||
self.header = [0, 0, 0]
|
||||
for k, v in tlp_headers_dict[self.name].fields.items():
|
||||
field = tlp_headers_dict[self.name].fields[k]
|
||||
self.header[field.byte//4] |= (getattr(self, k) << field.offset)
|
||||
self.data = data
|
||||
self.dwords = self.header + self.data
|
||||
return self.dwords
|
||||
|
||||
def __repr__(self):
|
||||
r = self.name + "\n"
|
||||
r += "--------\n"
|
||||
for k in sorted(tlp_headers_dict[self.name].keys()):
|
||||
r += k + " : 0x{:x}".format(getattr(self, k) + "\n")
|
||||
if len(self.data) != 0:
|
||||
r += "data:\n"
|
||||
for d in self.data:
|
||||
r += "{:08x}\n".format(d)
|
||||
return r
|
||||
|
||||
|
||||
class RD32(TLP):
|
||||
def __init__(self, dwords=[0, 0, 0]):
|
||||
TLP.__init__(self, "RD32", dwords)
|
||||
|
||||
|
||||
class WR32(TLP):
|
||||
def __init__(self, dwords=[0, 0, 0]):
|
||||
TLP.__init__(self, "WR32", dwords)
|
||||
|
||||
|
||||
class CPLD(TLP):
|
||||
def __init__(self, dwords=[0, 0, 0]):
|
||||
TLP.__init__(self, "CPLD", dwords)
|
||||
|
||||
|
||||
class CPL():
|
||||
def __init__(self, dwords=[0, 0, 0]):
|
||||
TLP.__init__(self, "CPL", dwords)
|
||||
|
||||
|
||||
class Unknown():
|
||||
def __repr__(self):
|
||||
r = "UNKNOWN\n"
|
||||
return r
|
||||
|
||||
fmt_type_dict = {
|
||||
fmt_type_dict["mem_rd32"]: (RD32, 3),
|
||||
fmt_type_dict["mem_wr32"]: (WR32, 4),
|
||||
fmt_type_dict["cpld"]: (CPLD, 4),
|
||||
fmt_type_dict["cpl"]: (CPL, 3)
|
||||
}
|
||||
|
||||
|
||||
def parse_dwords(dwords):
|
||||
f = get_field_data(tlp_common_header.fields["fmt"], dwords)
|
||||
t = get_field_data(tlp_common_header.fields["type"], dwords)
|
||||
fmt_type = (f << 5) | t
|
||||
try:
|
||||
tlp, min_len = fmt_type_dict[fmt_type]
|
||||
if len(dwords) >= min_len:
|
||||
return tlp(dwords)
|
||||
else:
|
||||
return Unknown()
|
||||
except:
|
||||
return Unknown()
|
|
@ -1,40 +0,0 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bus import wishbone
|
||||
from migen.sim.generic import run_simulation
|
||||
|
||||
from misoclib.com.litepcie.core import Endpoint
|
||||
from misoclib.com.litepcie.frontend.bridge.wishbone import WishboneBridge
|
||||
|
||||
from misoclib.com.litepcie.test.common import *
|
||||
from misoclib.com.litepcie.test.model.host import *
|
||||
|
||||
root_id = 0x100
|
||||
endpoint_id = 0x400
|
||||
|
||||
|
||||
class TB(Module):
|
||||
def __init__(self):
|
||||
self.submodules.host = Host(64, root_id, endpoint_id,
|
||||
phy_debug=False,
|
||||
chipset_debug=False,
|
||||
host_debug=False)
|
||||
self.submodules.endpoint = Endpoint(self.host.phy)
|
||||
|
||||
self.submodules.wishbone_bridge = WishboneBridge(self.endpoint, lambda a: 1)
|
||||
self.submodules.sram = wishbone.SRAM(1024, bus=self.wishbone_bridge.wishbone)
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
wr_datas = [seed_to_data(i, True) for i in range(64)]
|
||||
for i in range(64):
|
||||
yield from self.host.chipset.wr32(i, [wr_datas[i]])
|
||||
|
||||
rd_datas = []
|
||||
for i in range(64):
|
||||
yield from self.host.chipset.rd32(i)
|
||||
rd_datas.append(self.host.chipset.rd32_data[0])
|
||||
|
||||
s, l, e = check(wr_datas, rd_datas)
|
||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd", keep_files=True)
|
Loading…
Reference in a new issue