Compare commits
25 Commits
master
...
coax_proto
Author | SHA1 | Date |
---|---|---|
morgan | 8822fcbc1d | |
morgan | ca95feaabd | |
morgan | 4a1d41832b | |
morgan | 7fb4b4088e | |
morgan | 67a7d39b54 | |
morgan | 2991950757 | |
morgan | 3a61bc9ce5 | |
morgan | fbd244cbe5 | |
morgan | 5ba1da6e40 | |
morgan | 19beb8488e | |
morgan | 577fa03b78 | |
morgan | 1a3ac9763d | |
morgan | ba1e1f9951 | |
morgan | dd8f4a172b | |
morgan | 08ae1e92c5 | |
morgan | 3a5d508674 | |
morgan | 9f306063a2 | |
morgan | bfa16e0560 | |
morgan | f4362c1e54 | |
Sebastien Bourdeauducq | a3ae5ddd00 | |
abdul124 | cb1558ec4e | |
abdul124 | d514171d6f | |
abdul124 | c3ae34123c | |
abdul124 | 9f5e15e6d2 | |
Sebastien Bourdeauducq | fbb636460b |
90
README.md
90
README.md
|
@ -4,102 +4,60 @@ ARTIQ on Zynq
|
|||
How to use
|
||||
----------
|
||||
|
||||
1. [Install ARTIQ](https://m-labs.hk/artiq/manual/installing.html). Get the corresponding version to the ``artiq-zynq`` version you are targeting.
|
||||
2. To obtain firmware binaries, use AFWS or build your own; see [the ARTIQ manual](https://m-labs.hk/artiq/manual/building_developing.html) for detailed instructions or skip to "Development" below. ZC706 variants only can also be downloaded from latest successful build on [Hydra](https://nixbld.m-labs.hk/).
|
||||
3. Place ``boot.bin`` file at the root ``/`` of a FAT-formatted SD card.
|
||||
4. Optionally, create a ``config.txt`` configuration file containing ``key=value`` pairs on each line and place it at the root of the SD card. See below for valid keys. The ``ip``, ``ip6`` and ``mac`` keys can be used to set networking information. If these keys are not found, the firmware will use default values which may or may not be compatible with your network.
|
||||
5. Insert the SD card into the board and set the board to boot from the SD card. For ZC706, this is achieved by placing the large DIP switch SW11 into the 00110 position. On Kasli-SoC, place the BOOT MODE switches to SD.
|
||||
6. Power up the board. After successful boot the firmware should respond to ping at its IP addresses. Boot output can be observed from UART at 115200bps 8-N-1.
|
||||
7. Create and use an ARTIQ device database as usual.
|
||||
1. Install the ARTIQ version that corresponds to the artiq-zynq version you are targeting.
|
||||
2. To obtain firmware binaries, select the latest successful build on [Hydra](https://nixbld.m-labs.hk/) for the targeted artiq-zynq version, or use AFWS. If using Hydra, search for the job named ``<board>-<variant>-sd`` (for example: ``zc706-nist_clock-sd`` or ``zc706-nist_qc2-sd``).
|
||||
3. Place the ``boot.bin`` file, obtained from Hydra's "binary distribution" download link or from AFWS, at the root of a FAT-formatted SD card.
|
||||
4. Optionally, create a ``config.txt`` configuration file at the root of the SD card containing ``key=value`` pairs on each line. Use the ``ip``, ``ip6`` and ``mac`` keys to respectively set the IPv4, IPv6 and MAC address of the board. Configuring an IPv6 address is entirely optional. If these keys are not found, the firmware will use default values that may or may not be compatible with your network.
|
||||
5. Insert the SD card into the board and set up the board to boot from the SD card. For the ZC706, this is achieved by placing the large DIP switch SW11 in the 00110 position.
|
||||
6. Power up the board. After the firmware starts successfully, it should respond to ping at its IP addresses, and boot messages can be observed from its UART at 115200bps.
|
||||
7. Create and use an ARTIQ device database as usual, but set ``"target": "cortexa9"`` in the arguments of the core device.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Configuring the device is done using the ``config.txt`` text file at the root of the SD card plus optionally a ``config`` folder. When searching for a configuration key, the firmware first looks for a file named ``/config/[key].bin`` and, if it exists, returns the contents of that file. If not, it looks into ``/config.txt``, which should contain a list of ``key=value`` pairs, one per line. ``config.txt`` should be used for most keys but the ``config`` folder allows for setting configuration values which consist of binary data, such as the startup kernel.
|
||||
Configuring the device is done using the ``config.txt`` text file at the root of the SD card, plus the contents of the ``config`` folder. When searching for a configuration key, the firmware first looks for a file named ``/config/[key].bin`` and, if it exists, returns the contents of that file. If not, it looks into ``/config.txt``, which contains a list of ``key=value`` pairs, one per line. The ``config`` folder allows configuration values that consist in binary data, such as the startup kernel.
|
||||
|
||||
The following configuration keys are available among others:
|
||||
The following configuration keys are available:
|
||||
|
||||
- ``mac``: Ethernet MAC address.
|
||||
- ``ip``: IPv4 address.
|
||||
- ``ip6``: IPv6 address.
|
||||
- ``idle_kernel``: idle kernel in ELF format (as produced by ``artiq_compile``).
|
||||
- ``startup_kernel``: startup kernel in ELF format (as produced by ``artiq_compile``).
|
||||
- ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``).
|
||||
- ``rtio_clock``: source of RTIO clock; valid values are ``ext0_bypass`` and ``int_125``.
|
||||
- ``boot``: SD card "boot.bin" file, for replacing the boot firmware/gateware. Write only.
|
||||
|
||||
See [ARTIQ manual](https://m-labs.hk/artiq/manual-beta/core_device.html#configuration-storage) for full list. Configurations can be read/written/removed with ``artiq_coremgmt``. Config erase is not implemented, as it isn't particularly useful.
|
||||
|
||||
For convenience, the ``boot`` key can be used with ``artiq_coremgmt`` and a ``boot.bin`` file to replace firmware/gateware in a running system. This key is read-only. When loading ``boot.bin`` onto the SD card directly, place it at the root and not in the ``config`` folder.
|
||||
Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is
|
||||
not implemented as it seems not very useful.
|
||||
|
||||
Development instructions
|
||||
------------------------
|
||||
|
||||
ARTIQ on Zynq is packaged using [Nix](https://nixos.org) Flakes. Install Nix 2.8+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
|
||||
ARTIQ on Zynq is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.8+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
|
||||
|
||||
**Pure build with Nix:**
|
||||
Pure build with Nix and execution on a remote JTAG server:
|
||||
|
||||
```shell
|
||||
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock-sd or etc
|
||||
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock_satellite-jtag etc.
|
||||
./remote_run.sh
|
||||
```
|
||||
|
||||
Run ``nix flake show`` to see all valid build targets. Targets suffixed with ``-jtag`` produce separate firmware and gateware files, intended for use in booting via JTAG server/Ethernet, e.g. ``./remote_run.sh -i`` with a remote JTAG server. Targets suffixed with ``-sd`` will produce ``boot.bin`` file suitable for SD card boot. ``-firmware`` and ``-gateware`` respectively build firmware and gateware only.
|
||||
|
||||
The Kasli-SoC target requires a system description file as input. See ARTIQ manual for exact instructions or use incremental build.
|
||||
|
||||
**Impure incremental build:**
|
||||
|
||||
For boards with fixed variants, i.e. ZC706, etc. :
|
||||
Impure incremental build and execution on a remote JTAG server:
|
||||
|
||||
```shell
|
||||
nix develop
|
||||
cd src
|
||||
gateware/<board>.py -g ../build/gateware -V <variant> # gateware
|
||||
make GWARGS="-V <variant>" <runtime/satman> # firmware
|
||||
```
|
||||
|
||||
For boards with system descriptions, i.e. Kasli-SoC, etc. :
|
||||
|
||||
```shell
|
||||
nix develop
|
||||
cd src
|
||||
gateware/<board>.py -g ../build/gateware <description.json> # gateware
|
||||
make TARGET=<board> GWARGS="path/to/description.json" <runtime/satman> # firmware
|
||||
```
|
||||
|
||||
``szl.elf`` can be obtained with:
|
||||
|
||||
```shell
|
||||
nix build git+https://git.m-labs.hk/m-labs/zynq-rs#<board>-szl
|
||||
```
|
||||
|
||||
To generate ``boot.bin`` use ``mkbootimage``, e.g.:
|
||||
|
||||
```shell
|
||||
echo "the_ROM_image:
|
||||
{
|
||||
[bootloader]result/szl.elf
|
||||
gateware/top.bit
|
||||
firmware/armv7-none-eabihf/release/<runtime/satman>
|
||||
}
|
||||
EOF" >> boot.bif
|
||||
mkbootimage boot.bif boot.bin
|
||||
gateware/zc706.py -g ../build/gateware -V <variant> # build gateware
|
||||
make GWARGS="-V <variant>" <runtime/satman> # build firmware
|
||||
cd ..
|
||||
./remote_run.sh -i
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- The impure build process is also compatible with non-Nix systems.
|
||||
- When calling make, you need to specify both the variant and firmware type.
|
||||
- Firmware type must be either ``runtime`` for DRTIO-less or DRTIO master variants, or ``satman`` for DRTIO satellite.
|
||||
- If the board is connected to the local machine by JTAG, use the ``local_run.sh`` script.
|
||||
- A known Xilinx hardware bug prevents repeatedly loading the bootloader over JTAG without a POR reset. If booting over JTAG, install a jumper on ``PS_POR_B`` and use the POR reset script [here](https://git.m-labs.hk/M-Labs/zynq-rs/src/branch/master/kasli_soc_por.py).
|
||||
|
||||
Pre-Commit Hooks
|
||||
----------------
|
||||
|
||||
You are strongly recommended to use the provided pre-commit hooks to automatically reformat files and check for non-optimal Rust/C/C++ practices. Run `pre-commit install` to install the hook and `pre-commit` will automatically run `cargo fmt`, `cargo clippy`, and `clang-format` for you.
|
||||
|
||||
Several things to note:
|
||||
|
||||
- If `cargo fmt`, `cargo clippy`, or `clang-format` returns an error, the pre-commit hook will fail. You should fix all errors before trying to commit again.
|
||||
- If `cargo fmt` or `clang-format` reformats some files, the pre-commit hook will also fail. You should review the changes and, if satisfied, try to commit again.
|
||||
- If the board is connected to the local machine, use the ``local_run.sh`` script.
|
||||
|
||||
License
|
||||
-------
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
"""
|
||||
Non-realtime drivers for CXP.
|
||||
"""
|
||||
|
||||
# TODO: add api calls for CTRL packet similar i2c
|
||||
# TODO: add timing critical trigger ack
|
||||
|
||||
|
||||
from artiq.language.core import syscall, kernel
|
||||
from artiq.language.types import TBool, TInt32, TNone
|
||||
from artiq.coredevice.rtio import rtio_output
|
||||
from artiq.experiment import *
|
||||
|
||||
|
||||
class CoaXPress:
|
||||
def __init__(self, channel, core_device="core"):
|
||||
# __device_mgr is private
|
||||
# self.core = dmgr.get(core_device)
|
||||
|
||||
# you can get the channel via `print(len(rtio_channels))` before calling
|
||||
# `rtio_channels.append(rtio.Channel.from_phy(cxp_interface))`
|
||||
self.channel = channel
|
||||
# the first 8 bits is reserved for the rtlink.OInterface.addr not for channel no.
|
||||
self.target_o = channel << 8
|
||||
|
||||
@staticmethod
|
||||
def get_rtio_channels(channel, **kwargs):
|
||||
return [(channel, None)]
|
||||
|
||||
@kernel
|
||||
def trigger(self, linktrig, trigdelay):
|
||||
rtio_output(self.target_o, linktrig | trigdelay << 1)
|
||||
|
||||
|
||||
@syscall(flags={"nounwind", "nowrite"})
|
||||
def cxp_readu32(channel: TInt32, addr: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall(flags={"nounwind", "nowrite"})
|
||||
def cxp_writeu32(channel: TInt32, addr: TInt32, val: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
|
||||
class IdleKernel(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.setattr_device("led0")
|
||||
|
||||
# declare the class before using it in kernel
|
||||
self.cxp = CoaXPress(0x0)
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
# cxp_readu32(0, 3)
|
||||
# cxp_writeu32(0, 0, 0xABCD)
|
||||
self.cxp.trigger(1, 10)
|
|
@ -1,78 +0,0 @@
|
|||
core_addr = "192.168.1.57"
|
||||
|
||||
device_db = {
|
||||
"core": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {
|
||||
"host": core_addr,
|
||||
"ref_period": 1e-9,
|
||||
"target": "cortexa9",
|
||||
},
|
||||
},
|
||||
"core_log": {
|
||||
"type": "controller",
|
||||
"host": "::1",
|
||||
"port": 1068,
|
||||
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr,
|
||||
},
|
||||
"core_moninj": {
|
||||
"type": "controller",
|
||||
"host": "::1",
|
||||
"port_proxy": 1383,
|
||||
"port": 1384,
|
||||
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} "
|
||||
+ core_addr,
|
||||
},
|
||||
"core_analyzer": {
|
||||
"type": "controller",
|
||||
"host": "::1",
|
||||
"port_proxy": 1385,
|
||||
"port": 1386,
|
||||
"command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} "
|
||||
+ core_addr,
|
||||
},
|
||||
"core_cache": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.cache",
|
||||
"class": "CoreCache",
|
||||
},
|
||||
"core_dma": {"type": "local", "module": "artiq.coredevice.dma", "class": "CoreDMA"},
|
||||
"led0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 0},
|
||||
},
|
||||
"led1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 1},
|
||||
},
|
||||
}
|
||||
|
||||
# TTLs starting at RTIO channel 2, ending at RTIO channel 15
|
||||
for i in range(2, 16):
|
||||
device_db["ttl" + str(i)] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLInOut",
|
||||
"arguments": {"channel": i},
|
||||
}
|
||||
|
||||
device_db.update(
|
||||
spi0={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 16},
|
||||
},
|
||||
dds0={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ad9834",
|
||||
"class": "AD9834",
|
||||
"arguments": {"spi_device": "spi0"},
|
||||
},
|
||||
)
|
118
flake.lock
118
flake.lock
|
@ -11,11 +11,11 @@
|
|||
"src-pythonparser": "src-pythonparser"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1732066716,
|
||||
"narHash": "sha256-krjvt9+RccnAxSEZcFhRpjA2S3CoqE4MSa1JUg421b4=",
|
||||
"lastModified": 1725373154,
|
||||
"narHash": "sha256-fq9EW9fDWrV0v1vNj7ZqDNpNYx8+OxoFdPwpvkPf67g=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "270a417a28b516d36983779a1adb6d33a3c55a4a",
|
||||
"revCount": 9102,
|
||||
"rev": "0c1ffa9f4f6a3e7864459923ec4b9cc45f16327a",
|
||||
"revCount": 9005,
|
||||
"type": "git",
|
||||
"url": "https://github.com/m-labs/artiq.git"
|
||||
},
|
||||
|
@ -68,13 +68,45 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mozilla-overlay": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1704373101,
|
||||
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mozilla-overlay_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1704373101,
|
||||
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1731319897,
|
||||
"narHash": "sha256-PbABj4tnbWFMfBp6OcUK5iGy1QY+/Z96ZcLpooIbuEI=",
|
||||
"lastModified": 1724224976,
|
||||
"narHash": "sha256-Z/ELQhrSd7bMzTO8r7NZgi9g5emh+aRKoCdaAv5fiO0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "dc460ec76cbff0e66e269457d7b728432263166c",
|
||||
"rev": "c374d94f1536013ca8e92341b540eba4c22f9c62",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -84,9 +116,27 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_old": {
|
||||
"locked": {
|
||||
"lastModified": 1720535198,
|
||||
"narHash": "sha256-zwVvxrdIzralnSbcpghA92tWu2DV2lwv89xZc8MTrbg=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "205fd4226592cc83fd4c0885a3e4c9c400efabb5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"artiq": "artiq",
|
||||
"mozilla-overlay": "mozilla-overlay",
|
||||
"nixpkgs_old": "nixpkgs_old",
|
||||
"zynq-rs": "zynq-rs"
|
||||
}
|
||||
},
|
||||
|
@ -112,28 +162,6 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"rust-overlay_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"zynq-rs",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719454714,
|
||||
"narHash": "sha256-MojqG0lyUINkEk0b3kM2drsU5vyaF8DFZe/FAlZVOGs=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "d1c527659cf076ecc4b96a91c702d080b213801e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"ref": "snapshot/2024-08-01",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"sipyco": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
|
@ -142,11 +170,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1728371104,
|
||||
"narHash": "sha256-PPnAyDedUQ7Og/Cby9x5OT9wMkNGTP8GS53V6N/dk4w=",
|
||||
"lastModified": 1717637367,
|
||||
"narHash": "sha256-4mSm9wl5EMgzzrW6w86IDUevkEOT99FESHGcxcyQbD0=",
|
||||
"owner": "m-labs",
|
||||
"repo": "sipyco",
|
||||
"rev": "094a6cd63ffa980ef63698920170e50dc9ba77fd",
|
||||
"rev": "02b96ec2473a3c3d3c980899de2564ddce949dab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -158,11 +186,11 @@
|
|||
"src-migen": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1727677091,
|
||||
"narHash": "sha256-Zg3SQnTwMM/VkOGKogbPyuCC2NhLy8HB2SPEUWWNgCU=",
|
||||
"lastModified": 1724304798,
|
||||
"narHash": "sha256-tQ02N0eXY5W/Z7CrOy3Cu4WjDZDQWb8hYlzsFzr3Mus=",
|
||||
"owner": "m-labs",
|
||||
"repo": "migen",
|
||||
"rev": "c19ae9f8ae162ffe2d310a92bfce53ac2a821bc8",
|
||||
"rev": "832a7240ba32af9cbd4fdd519ddcb4f912534726",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -174,11 +202,11 @@
|
|||
"src-misoc": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1729234629,
|
||||
"narHash": "sha256-TLsTCXV5AC2xh+bS7EhBVBKqdqIU3eKrnlWcFF9LtAM=",
|
||||
"lastModified": 1715647536,
|
||||
"narHash": "sha256-q+USDcaKHABwW56Jzq8u94iGPWlyLXMyVt0j/Gyg+IE=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "6085a312bca26adeca6584e37d08c8ba2e1d6e38",
|
||||
"revCount": 2460,
|
||||
"rev": "fea9de558c730bc394a5936094ae95bb9d6fa726",
|
||||
"revCount": 2455,
|
||||
"submodules": true,
|
||||
"type": "git",
|
||||
"url": "https://github.com/m-labs/misoc.git"
|
||||
|
@ -222,18 +250,18 @@
|
|||
},
|
||||
"zynq-rs": {
|
||||
"inputs": {
|
||||
"mozilla-overlay": "mozilla-overlay_2",
|
||||
"nixpkgs": [
|
||||
"artiq",
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-overlay": "rust-overlay_2"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731749494,
|
||||
"narHash": "sha256-WGigAhvVCGN5YZ1dHPyvoqAh47W1Gtph036O1aKFlLE=",
|
||||
"lastModified": 1720537402,
|
||||
"narHash": "sha256-ybvaQ48SVBqYVqgYmGUdefGZkni7PJ90qYQPHnFOwDs=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "12975de2e110d7948bf47b768559f727d0abc8fc",
|
||||
"revCount": 655,
|
||||
"rev": "b2b3e5c933cbc4b7cb14adde480d7561a3ae71ee",
|
||||
"revCount": 648,
|
||||
"type": "git",
|
||||
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
|
||||
},
|
||||
|
|
20
flake.nix
20
flake.nix
|
@ -2,15 +2,20 @@
|
|||
description = "ARTIQ port to the Zynq-7000 platform";
|
||||
|
||||
inputs.artiq.url = git+https://github.com/m-labs/artiq.git;
|
||||
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
|
||||
inputs.zynq-rs.url = git+https://git.m-labs.hk/m-labs/zynq-rs;
|
||||
inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs";
|
||||
|
||||
outputs = { self, zynq-rs, artiq }:
|
||||
inputs.nixpkgs_old.url = "github:nixos/nixpkgs?ref=nixos-23.11";
|
||||
|
||||
outputs = { self, mozilla-overlay, zynq-rs, artiq, nixpkgs_old }:
|
||||
let
|
||||
pkgs = import artiq.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import zynq-rs.inputs.rust-overlay) ]; };
|
||||
pkgs_old = import nixpkgs_old { system = "x86_64-linux";};
|
||||
pkgs = import artiq.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
|
||||
zynqpkgs = zynq-rs.packages.x86_64-linux;
|
||||
artiqpkgs = artiq.packages.x86_64-linux;
|
||||
llvmPackages_11 = zynq-rs.llvmPackages_11;
|
||||
# llvmPackages_11 = zynq-rs.llvmPackages_11;
|
||||
llvmPackages_11 = pkgs_old.llvmPackages_11;
|
||||
|
||||
rust = zynq-rs.rust;
|
||||
rustPlatform = zynq-rs.rustPlatform;
|
||||
|
@ -74,7 +79,7 @@
|
|||
|
||||
propagatedBuildInputs = with pkgs.python3Packages; [ setuptools click numpy toolz jinja2 ramda artiqpkgs.migen artiqpkgs.misoc ];
|
||||
|
||||
checkInputs = with pkgs.python3Packages; [ pytestCheckHook pytest-timeout ];
|
||||
checkInputs = with pkgs.python3Packages; [ pytest-runner pytestCheckHook pytest-timeout ];
|
||||
|
||||
# migen/misoc version checks are broken with pyproject for some reason
|
||||
postPatch = ''
|
||||
|
@ -341,6 +346,7 @@
|
|||
{
|
||||
inherit fastnumbers artiq-netboot ramda migen-axi binutils-arm;
|
||||
} //
|
||||
(board-package-set { target = "zc706"; variant = "cxp_demo"; }) //
|
||||
(board-package-set { target = "zc706"; variant = "nist_clock"; }) //
|
||||
(board-package-set { target = "zc706"; variant = "nist_clock_master"; }) //
|
||||
(board-package-set { target = "zc706"; variant = "nist_clock_master_100mhz"; }) //
|
||||
|
@ -363,8 +369,7 @@
|
|||
(board-package-set { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) //
|
||||
(board-package-set { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) //
|
||||
(board-package-set { target = "kasli_soc"; variant = "master"; json = ./kasli-soc-master.json; }) //
|
||||
(board-package-set { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; }) //
|
||||
(board-package-set { target = "ebaz4205"; variant = "base"; });
|
||||
(board-package-set { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; });
|
||||
|
||||
hydraJobs = packages.x86_64-linux // { inherit zc706-hitl-tests; inherit gateware-sim; inherit fmt-check; };
|
||||
|
||||
|
@ -380,11 +385,10 @@
|
|||
zynqpkgs.mkbootimage
|
||||
openocd
|
||||
openssh rsync
|
||||
(python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq artiq-netboot ps.jsonschema ps.pyftdi ])))
|
||||
(python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq artiq-netboot ps.jsonschema ps.pyftdi ps.pillow ])))
|
||||
artiqpkgs.artiq
|
||||
artiqpkgs.vivado
|
||||
binutils-arm
|
||||
pre-commit
|
||||
];
|
||||
XARGO_RUST_SRC = "${rust}/lib/rustlib/src/rust/library";
|
||||
CLANG_EXTRA_INCLUDE_DIR = "${llvmPackages_11.clang-unwrapped.lib}/lib/clang/11.1.0/include";
|
||||
|
|
|
@ -13,7 +13,7 @@ fi
|
|||
|
||||
impure=0
|
||||
load_bitstream=1
|
||||
board_type="kasli_soc"
|
||||
board_type="zc706"
|
||||
fw_type="runtime"
|
||||
|
||||
while getopts "ilb:t:f:" opt; do
|
||||
|
@ -36,7 +36,7 @@ done
|
|||
if [ -z "$board_host" ]; then
|
||||
case $board_type in
|
||||
kasli_soc) board_host="192.168.1.56";;
|
||||
zc706) board_host="192.168.1.52";;
|
||||
zc706) board_host="192.168.1.14";;
|
||||
*) echo "Unknown board type"; exit 1;;
|
||||
esac
|
||||
fi
|
||||
|
@ -58,4 +58,4 @@ else
|
|||
load_bitstream_cmd="-g $result_dir/top.bit"
|
||||
fi
|
||||
artiq_netboot $load_bitstream_cmd -f $result_dir/$fw_type.bin -b $board_host
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
from migen import *
|
||||
from misoc.interconnect import stream
|
||||
|
||||
from sim_pipeline import *
|
||||
from sim_generator import CXPCRC32Inserter
|
||||
from sim_frame_gen import get_frame_packet
|
||||
|
||||
from src.gateware.cxp_pipeline import *
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class Frame(Module):
|
||||
def __init__(self):
|
||||
# to construct correct crc and ack/stb signal
|
||||
self.submodules.buffer = buffer = stream.SyncFIFO(word_layout, 32)
|
||||
self.submodules.crc_inserter = crc_inserter = CXPCRC32Inserter()
|
||||
self.submodules.dchar_decoder = dchar_decoder = Duplicated_Char_Decoder()
|
||||
|
||||
self.submodules.stream_pipe = stream_pipe = Stream_Pipeline()
|
||||
|
||||
pipeline = [buffer, crc_inserter, dchar_decoder, stream_pipe]
|
||||
for s, d in zip(pipeline, pipeline[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
||||
self.sink = pipeline[0].sink
|
||||
self.source = pipeline[-1].source
|
||||
|
||||
# no backpressure for sim
|
||||
self.sync += self.source.ack.eq(1)
|
||||
|
||||
|
||||
dut = Frame()
|
||||
|
||||
|
||||
def check_case(packet=[]):
|
||||
print("=================TEST========================")
|
||||
|
||||
sink = dut.sink
|
||||
stream_pipe = dut.stream_pipe
|
||||
|
||||
for i, p in enumerate(packet):
|
||||
yield sink.data.eq(p["data"])
|
||||
yield sink.k.eq(p["k"])
|
||||
yield sink.stb.eq(1)
|
||||
if "eop" in p:
|
||||
yield sink.eop.eq(1)
|
||||
else:
|
||||
yield sink.eop.eq(0)
|
||||
|
||||
# check cycle result
|
||||
yield
|
||||
# source = dut.dchar_decoder.source
|
||||
# source = dut.stream_pipe.frame_extractor.sink
|
||||
source = dut.sink
|
||||
# print(
|
||||
# f"\nCYCLE#{i} : source char = {yield source.data:#X} k = {yield source.k:#X} stb = {yield source.stb} ack = {yield source.ack} eop = {yield source.eop}"
|
||||
# )
|
||||
|
||||
# extra clk cycles
|
||||
cyc = i + 1
|
||||
img = []
|
||||
line = -1
|
||||
total_pixel = 1000
|
||||
for i in range(cyc, cyc + total_pixel):
|
||||
yield sink.data.eq(0)
|
||||
yield sink.k.eq(0)
|
||||
yield sink.stb.eq(0)
|
||||
yield sink.eop.eq(0)
|
||||
yield
|
||||
# print(
|
||||
# f"\nCYCLE#{i} : source char = {yield source.data:#X} k = {yield source.k:#X} stb = {yield source.stb} ack = {yield source.ack} eop = {yield source.eop}"
|
||||
# )
|
||||
|
||||
frame_extractoer = dut.stream_pipe.frame_extractor
|
||||
new_line = yield frame_extractoer.new_line
|
||||
if new_line:
|
||||
img.append([])
|
||||
line += 1
|
||||
|
||||
stb = yield frame_extractoer.source.stb
|
||||
data = yield frame_extractoer.source.data
|
||||
if stb:
|
||||
# CXP use MSB
|
||||
img[line].append(np.uint16(data & 0xFFFF))
|
||||
img[line].append(np.uint16(data >> 16))
|
||||
|
||||
# metadata = dut.stream_pipe.frame_extractor.metadata
|
||||
# img_header_layout = [
|
||||
# "stream_id",
|
||||
# "source_tag",
|
||||
# "x_size",
|
||||
# "x_offset",
|
||||
# "y_size",
|
||||
# "y_offset",
|
||||
# "l_size", # number of data words per image line
|
||||
# "pixel_format",
|
||||
# "tap_geo",
|
||||
# "flag",
|
||||
# ]
|
||||
# for name in img_header_layout:
|
||||
# print(f"{name} = {yield getattr(metadata, name):#04X} ", end="")
|
||||
# print()
|
||||
Image.fromarray(np.array(img, dtype=np.uint8)).show()
|
||||
assert True
|
||||
|
||||
|
||||
def testbench():
|
||||
stream_id = 0x69
|
||||
packet_tag = 0
|
||||
frame_packet = get_frame_packet(stream_id)
|
||||
packet = [
|
||||
{"data": Replicate(C(stream_id, char_width), 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(C(packet_tag, char_width), 4), "k": Replicate(0, 4)},
|
||||
{
|
||||
"data": Replicate(C(len(frame_packet), 2*char_width)[8:], 4),
|
||||
"k": Replicate(0, 4),
|
||||
},
|
||||
{
|
||||
"data": Replicate(C(len(frame_packet), 2*char_width)[:8], 4),
|
||||
"k": Replicate(0, 4),
|
||||
},
|
||||
]
|
||||
packet += frame_packet
|
||||
# NOTE: for crc inserter!!!!
|
||||
packet[-1]["eop"] = 0
|
||||
|
||||
yield from check_case(packet)
|
||||
|
||||
|
||||
run_simulation(dut, testbench(), vcd_name="sim-cxp.vcd")
|
|
@ -0,0 +1,148 @@
|
|||
from migen import *
|
||||
from misoc.interconnect import stream
|
||||
|
||||
from src.gateware.cxp_pipeline import *
|
||||
from sim_pipeline import *
|
||||
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
|
||||
def get_image_header(
|
||||
stream_id, source_tag, xsize, xoffset, ysize, yoffset, dsize, pixelF, tag_geo, flag
|
||||
):
|
||||
stream_id = C(stream_id, char_width)
|
||||
source_tag = C(source_tag, 2 * char_width)
|
||||
xsize = C(xsize, 3 * char_width)
|
||||
xoffset = C(xoffset, 3 * char_width)
|
||||
ysize = C(ysize, 3 * char_width)
|
||||
yoffset = C(yoffset, 3 * char_width)
|
||||
dsize = C(dsize, 3 * char_width)
|
||||
pixelF = C(pixelF, 2 * char_width)
|
||||
tag_geo = C(tag_geo, 2 * char_width)
|
||||
flag = C(flag, char_width)
|
||||
|
||||
assert len(stream_id) == len(flag) == char_width
|
||||
assert len(source_tag) == len(pixelF) == len(tag_geo) == 2 * char_width
|
||||
assert len(xsize) == len(xoffset) == len(ysize) == len(yoffset) == 3 * char_width
|
||||
return [
|
||||
{"data": Replicate(KCode["stream_marker"], 4), "k": Replicate(1, 4)},
|
||||
{"data": Replicate(C(0x01, char_width), 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(stream_id, 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(source_tag[8:], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(source_tag[:8], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(xsize[16:], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(xsize[8:16], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(xsize[:8], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(xoffset[16:], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(xoffset[8:16], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(xoffset[:8], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(ysize[16:], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(ysize[8:16], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(ysize[:8], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(yoffset[16:], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(yoffset[8:16], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(yoffset[:8], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(dsize[16:], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(dsize[8:16], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(dsize[:8], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(pixelF[8:], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(pixelF[:8], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(tag_geo[8:], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(tag_geo[:8], 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(flag, 4), "k": Replicate(0, 4)},
|
||||
]
|
||||
|
||||
|
||||
def get_line_marker():
|
||||
return [
|
||||
{"data": Replicate(KCode["stream_marker"], 4), "k": Replicate(1, 4)},
|
||||
{"data": Replicate(C(0x02, char_width), 4), "k": Replicate(0, 4)},
|
||||
]
|
||||
|
||||
|
||||
def get_frame_packet(stream_id, pixel_format="mono16"):
|
||||
assert pixel_format in ["mono16"]
|
||||
|
||||
arr = [
|
||||
[204, 200, 203, 205, 190, 187, 189, 205, 214, 197, 188, 185, 181, 178, 193, 209, 211, 207, 211, 192, 168, 168, 171, 199, 210, 212, 203, 196],
|
||||
[218, 205, 199, 190, 192, 197, 196, 195, 184, 178, 182, 173, 166, 132, 122, 114, 154, 184, 187, 188, 171, 168, 170, 180, 192, 196, 202, 198],
|
||||
[223, 222, 222, 224, 216, 199, 199, 207, 205, 189, 183, 182, 144, 66, 61, 66, 80, 148, 181, 175, 169, 170, 174, 177, 196, 206, 223, 218],
|
||||
[221, 226, 225, 222, 211, 200, 202, 208, 215, 201, 187, 180, 133, 116, 113, 118, 96, 111, 206, 193, 170, 169, 186, 211, 218, 224, 231, 223],
|
||||
[219, 216, 206, 197, 210, 201, 206, 203, 191, 190, 185, 145, 134, 140, 159, 170, 150, 116, 180, 173, 179, 170, 172, 185, 201, 218, 227, 227],
|
||||
[203, 198, 194, 208, 227, 201, 201, 201, 215, 221, 209, 170, 136, 113, 141, 139, 141, 145, 188, 170, 180, 169, 184, 173, 174, 192, 215, 230],
|
||||
[206, 224, 213, 213, 233, 207, 204, 226, 233, 227, 214, 166, 145, 123, 145, 155, 147, 186, 213, 187, 171, 169, 193, 193, 171, 178, 186, 207],
|
||||
[212, 228, 216, 205, 214, 205, 204, 230, 235, 225, 219, 187, 143, 122, 146, 163, 158, 195, 209, 203, 174, 168, 190, 185, 187, 202, 180, 174],
|
||||
[197, 206, 201, 223, 213, 201, 203, 231, 234, 225, 218, 206, 147, 125, 149, 155, 190, 208, 206, 203, 175, 168, 171, 179, 184, 206, 189, 176],
|
||||
[213, 202, 209, 235, 223, 200, 202, 202, 227, 227, 202, 176, 138, 122, 144, 153, 190, 209, 207, 191, 172, 167, 179, 204, 190, 191, 180, 193],
|
||||
[225, 225, 207, 231, 219, 197, 215, 200, 194, 199, 181, 172, 131, 129, 147, 159, 113, 175, 196, 179, 184, 169, 181, 210, 202, 204, 200, 177],
|
||||
[208, 222, 204, 223, 210, 191, 195, 198, 203, 167, 171, 168, 135, 129, 149, 175, 66, 57, 90, 121, 147, 165, 181, 205, 195, 217, 209, 173],
|
||||
[188, 216, 201, 206, 199, 180, 185, 180, 129, 75, 139, 166, 124, 146, 189, 135, 51, 41, 38, 40, 45, 63, 131, 201, 189, 215, 193, 170],
|
||||
[188, 194, 195, 192, 182, 180, 134, 68, 45, 41, 96, 130, 116, 156, 163, 64, 46, 41, 43, 41, 42, 42, 74, 181, 177, 198, 175, 193],
|
||||
[179, 179, 209, 224, 198, 182, 99, 42, 44, 41, 44, 100, 116, 125, 100, 46, 45, 42, 42, 37, 44, 43, 49, 150, 183, 170, 172, 198],
|
||||
[175, 177, 208, 223, 197, 180, 94, 40, 42, 40, 41, 99, 134, 117, 80, 43, 46, 43, 37, 37, 44, 42, 35, 129, 195, 170, 170, 180],
|
||||
[179, 181, 187, 217, 193, 175, 91, 38, 41, 41, 42, 106, 151, 107, 62, 43, 45, 41, 33, 38, 42, 34, 33, 77, 188, 175, 173, 208],
|
||||
[190, 191, 180, 213, 194, 175, 78, 38, 40, 40, 40, 98, 134, 97, 51, 44, 59, 50, 37, 40, 36, 26, 36, 44, 100, 178, 192, 206],
|
||||
[199, 191, 184, 204, 196, 176, 78, 33, 38, 38, 39, 80, 102, 83, 43, 44, 112, 130, 122, 63, 33, 24, 29, 34, 33, 74, 162, 195],
|
||||
[191, 170, 196, 193, 186, 177, 88, 27, 34, 37, 36, 74, 101, 70, 36, 37, 81, 127, 137, 113, 40, 28, 30, 32, 36, 29, 69, 173],
|
||||
[164, 189, 190, 180, 176, 172, 83, 26, 28, 33, 32, 68, 97, 62, 32, 30, 44, 97, 123, 136, 58, 42, 44, 43, 43, 40, 58, 162],
|
||||
[177, 202, 205, 181, 174, 163, 78, 38, 35, 47, 54, 67, 92, 51, 28, 29, 26, 21, 39, 85, 47, 46, 52, 47, 46, 45, 48, 141],
|
||||
[181, 193, 199, 192, 171, 163, 91, 67, 121, 123, 91, 63, 89, 45, 25, 25, 23, 20, 15, 13, 20, 48, 54, 35, 34, 34, 68, 146],
|
||||
[175, 192, 195, 179, 165, 163, 100, 64, 99, 94, 82, 58, 83, 37, 23, 22, 22, 27, 21, 15, 14, 44, 98, 83, 94, 118, 164, 157],
|
||||
[153, 184, 171, 163, 161, 157, 140, 70, 58, 89, 61, 53, 76, 30, 20, 20, 20, 31, 24, 19, 16, 47, 159, 163, 160, 171, 160, 142],
|
||||
[142, 150, 161, 168, 154, 154, 164, 138, 76, 55, 26, 37, 62, 24, 19, 19, 20, 21, 23, 27, 31, 46, 142, 156, 151, 153, 147, 145],
|
||||
[153, 147, 174, 171, 151, 150, 169, 158, 142, 92, 28, 60, 59, 20, 20, 18, 20, 26, 27, 29, 33, 38, 125, 153, 150, 147, 147, 148],
|
||||
[138, 141, 166, 164, 146, 144, 164, 149, 132, 72, 34, 88, 72, 24, 19, 18, 18, 23, 25, 28, 31, 30, 98, 150, 146, 144, 146, 144]
|
||||
]
|
||||
source_tag = 0
|
||||
xsize, ysize = len(arr[0]), len(arr)
|
||||
|
||||
xoffset, yoffset = 0, 0
|
||||
if pixel_format == "mono16":
|
||||
dsize = xsize // 2
|
||||
pixelF = 0x0105
|
||||
tag_geo = 0
|
||||
flag = 0
|
||||
|
||||
packet = []
|
||||
# Image header
|
||||
packet += get_image_header(
|
||||
stream_id,
|
||||
source_tag,
|
||||
xsize,
|
||||
xoffset,
|
||||
ysize,
|
||||
yoffset,
|
||||
dsize,
|
||||
pixelF,
|
||||
tag_geo,
|
||||
flag,
|
||||
)
|
||||
|
||||
for line in arr:
|
||||
packet += get_line_marker()
|
||||
if pixel_format == "mono16":
|
||||
for i in range(len(line)):
|
||||
if (i % 2) == 0:
|
||||
if i == len(line) - 1:
|
||||
# print(C(line[i]))
|
||||
packet += [
|
||||
{
|
||||
"data": C(line[i], 4 * char_width),
|
||||
"k": Replicate(0, 4),
|
||||
},
|
||||
]
|
||||
else:
|
||||
# print(C(line[i], 2 * char_width), C(line[i + 1]))
|
||||
# CXP use MSB
|
||||
packet += [
|
||||
{
|
||||
"data": Cat(
|
||||
C(line[i], 2 * char_width),
|
||||
C(line[i + 1], 2 * char_width),
|
||||
),
|
||||
"k": Replicate(0, 4),
|
||||
},
|
||||
]
|
||||
|
||||
return packet
|
|
@ -0,0 +1,120 @@
|
|||
from migen import *
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.interconnect import stream
|
||||
|
||||
from sim_pipeline import CXPCRC32
|
||||
from src.gateware.cxp_pipeline import *
|
||||
|
||||
class CXPCRC32Inserter(Module):
|
||||
def __init__(self):
|
||||
self.sink = stream.Endpoint(word_layout)
|
||||
self.source = stream.Endpoint(word_layout)
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.crc = crc = CXPCRC32(word_dw)
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
|
||||
fsm.act(
|
||||
"IDLE",
|
||||
crc.reset.eq(1),
|
||||
self.sink.ack.eq(1),
|
||||
If(
|
||||
self.sink.stb,
|
||||
self.sink.ack.eq(0),
|
||||
NextState("COPY"),
|
||||
),
|
||||
)
|
||||
fsm.act(
|
||||
"COPY",
|
||||
crc.ce.eq(self.sink.stb & self.source.ack),
|
||||
crc.data.eq(self.sink.data),
|
||||
self.sink.connect(self.source),
|
||||
self.source.eop.eq(0),
|
||||
If(
|
||||
self.sink.stb & self.sink.eop & self.source.ack,
|
||||
NextState("INSERT"),
|
||||
),
|
||||
)
|
||||
fsm.act(
|
||||
"INSERT",
|
||||
self.source.stb.eq(1),
|
||||
self.source.eop.eq(1),
|
||||
self.source.data.eq(crc.value),
|
||||
If(self.source.ack, NextState("IDLE")),
|
||||
)
|
||||
|
||||
|
||||
class StreamPacket_Wrapper(Module):
|
||||
def __init__(self):
|
||||
self.sink = stream.Endpoint(word_layout)
|
||||
self.source = stream.Endpoint(word_layout)
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
|
||||
fsm.act(
|
||||
"IDLE",
|
||||
self.sink.ack.eq(1),
|
||||
If(
|
||||
self.sink.stb,
|
||||
self.sink.ack.eq(0),
|
||||
NextState("INSERT_HEADER_0"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"INSERT_HEADER_0",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Replicate(KCode["pak_start"], 4)),
|
||||
self.source.k.eq(Replicate(1, 4)),
|
||||
If(self.source.ack, NextState("INSERT_HEADER_1")),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"INSERT_HEADER_1",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Replicate(C(0x01, char_width), 4)),
|
||||
self.source.k.eq(Replicate(0, 4)),
|
||||
If(self.source.ack, NextState("COPY")),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"COPY",
|
||||
self.sink.connect(self.source),
|
||||
self.source.eop.eq(0),
|
||||
If(
|
||||
self.sink.stb & self.sink.eop & self.source.ack,
|
||||
NextState("INSERT_FOOTER"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"INSERT_FOOTER",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Replicate(KCode["pak_end"], 4)),
|
||||
self.source.k.eq(Replicate(1, 4)),
|
||||
# Simulate RX don't have eop tagged
|
||||
# self.source.eop.eq(1),
|
||||
If(self.source.ack, NextState("IDLE")),
|
||||
)
|
||||
|
||||
|
||||
# With KCode & 0x01*4
|
||||
class StreamData_Generator(Module):
|
||||
def __init__(self):
|
||||
# should be big enough for all test
|
||||
self.submodules.buffer = buffer = stream.SyncFIFO(word_layout, 32)
|
||||
self.submodules.crc_inserter = crc_inserter = CXPCRC32Inserter()
|
||||
self.submodules.wrapper = wrapper = StreamPacket_Wrapper()
|
||||
|
||||
# # #
|
||||
pipeline = [buffer, crc_inserter, wrapper]
|
||||
for s, d in zip(pipeline, pipeline[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
||||
self.sink = pipeline[0].sink
|
||||
self.source = pipeline[-1].source
|
|
@ -0,0 +1,406 @@
|
|||
from migen import *
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.interconnect import stream
|
||||
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
|
||||
|
||||
from src.gateware.cxp_pipeline import *
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
class EOP_Marker(Module):
|
||||
def __init__(self):
|
||||
self.sink = stream.Endpoint(word_layout_dchar)
|
||||
self.source = stream.Endpoint(word_layout_dchar)
|
||||
|
||||
# # #
|
||||
|
||||
last_stb = Signal()
|
||||
self.sync += [
|
||||
If((~self.source.stb | self.source.ack),
|
||||
self.source.stb.eq(self.sink.stb),
|
||||
self.source.payload.eq(self.sink.payload),
|
||||
),
|
||||
last_stb.eq(self.sink.stb),
|
||||
]
|
||||
self.comb += [
|
||||
self.sink.ack.eq(~self.source.stb | self.source.ack),
|
||||
self.source.eop.eq(~self.sink.stb & last_stb),
|
||||
]
|
||||
|
||||
class Streams_Crossbar(Module):
|
||||
def __init__(self, downconn_sources, stream_sinks):
|
||||
n_downconn = len(downconn_sources)
|
||||
self.active_conn= C(n_downconn)
|
||||
# TODO: change self.active_conns to signal and link it to rx_ready of GTX lanes
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.mux = mux = stream.Multiplexer(word_layout_dchar, n_downconn)
|
||||
|
||||
for i, downconn in enumerate(downconn_sources):
|
||||
self.comb += downconn.source.connect(getattr(mux, "sink"+str(i)))
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="WAIT_HEADER")
|
||||
|
||||
self.stream_id = Signal(char_width)
|
||||
case = dict((i, mux.source.connect(b.sink)) for i, b in enumerate(stream_sinks))
|
||||
fsm.act(
|
||||
"WAIT_HEADER",
|
||||
NextValue(self.stream_id, mux.source.dchar),
|
||||
If(mux.source.stb,
|
||||
NextState("COPY"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"COPY",
|
||||
Case(self.stream_id, case),
|
||||
If(mux.source.eop,
|
||||
NextState("SWITCH_CONN"),
|
||||
),
|
||||
)
|
||||
|
||||
# Section 9.5.5 (CXP-001-2021)
|
||||
# When Multiple connections are active, stream packets are transmitted in
|
||||
# ascending order of Connection ID. And one connection shall be transmitting data at a time.
|
||||
read_mask = Signal(max=n_downconn)
|
||||
self.comb += mux.sel.eq(read_mask)
|
||||
fsm.act(
|
||||
"SWITCH_CONN",
|
||||
# assuming downconn_sources have ascending Connection ID
|
||||
If(read_mask == self.active_conn - 1,
|
||||
NextValue(read_mask, read_mask.reset),
|
||||
).Else(
|
||||
NextValue(read_mask, read_mask + 1),
|
||||
),
|
||||
NextState("WAIT_HEADER"),
|
||||
)
|
||||
|
||||
|
||||
def reverse_bytes(s):
|
||||
assert len(s) % 8 == 0
|
||||
char = [s[i*8:(i+1)*8] for i in range(len(s)//8)]
|
||||
return Cat(char[::-1])
|
||||
|
||||
@ResetInserter()
|
||||
@CEInserter()
|
||||
class CXPCRC32(Module):
|
||||
# Section 9.2.2.2 (CXP-001-2021)
|
||||
width = 32
|
||||
polynom = 0x04C11DB7
|
||||
seed = 2**width - 1
|
||||
check = 0x00000000
|
||||
|
||||
def __init__(self, data_width):
|
||||
self.data = Signal(data_width)
|
||||
self.value = Signal(self.width)
|
||||
self.error = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.engine = LiteEthMACCRCEngine(
|
||||
data_width, self.width, self.polynom
|
||||
)
|
||||
reg = Signal(self.width, reset=self.seed)
|
||||
self.sync += reg.eq(self.engine.next)
|
||||
self.comb += [
|
||||
# the CRC Engine use Big Endian, need to reverse the bytes
|
||||
self.engine.data.eq(reverse_bytes(self.data)),
|
||||
self.engine.last.eq(reg),
|
||||
self.value.eq(reverse_bytes(reg[::-1])),
|
||||
self.error.eq(reg != self.check),
|
||||
]
|
||||
|
||||
# For verifying crc in stream data packet
|
||||
class Double_Stream_Buffer(Module):
|
||||
def __init__(self, size):
|
||||
self.sink = stream.Endpoint(word_layout_dchar)
|
||||
|
||||
self.submodules.crc = crc = CXPCRC32(word_dw)
|
||||
self.comb += crc.data.eq(self.sink.data)
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="RESET")
|
||||
|
||||
write_mask = Signal()
|
||||
self.submodules.line_buffer0 = line_buffer0 = ResetInserter()(stream.SyncFIFO(word_layout_dchar, 2**bits_for(size//word_dw)))
|
||||
self.submodules.line_buffer1 = line_buffer1 = ResetInserter()(stream.SyncFIFO(word_layout_dchar, 2**bits_for(size//word_dw)))
|
||||
fsm.act("RESET",
|
||||
Case(write_mask,
|
||||
{
|
||||
0: line_buffer0.reset.eq(1),
|
||||
1: line_buffer1.reset.eq(1),
|
||||
}
|
||||
),
|
||||
crc.reset.eq(1),
|
||||
NextState("CHECKING"),
|
||||
)
|
||||
|
||||
fsm.act("CHECKING",
|
||||
self.sink.ack.eq(1),
|
||||
If(self.sink.stb,
|
||||
crc.ce.eq(1),
|
||||
If(self.sink.eop,
|
||||
# discard the crc at the end
|
||||
NextState("SWITCH_BUFFER")
|
||||
).Else(
|
||||
If(write_mask == 0,
|
||||
self.sink.connect(line_buffer0.sink),
|
||||
).Else(
|
||||
self.sink.connect(line_buffer1.sink),
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# only valid data will be passed to downstream
|
||||
fsm.act("SWITCH_BUFFER",
|
||||
If(~crc.error,
|
||||
NextValue(write_mask, ~write_mask),
|
||||
),
|
||||
NextState("RESET"),
|
||||
)
|
||||
|
||||
self.submodules.mux = mux = stream.Multiplexer(word_layout_dchar, 2)
|
||||
self.comb += [
|
||||
line_buffer0.source.connect(mux.sink0),
|
||||
line_buffer1.source.connect(mux.sink1),
|
||||
mux.sel.eq(~write_mask),
|
||||
]
|
||||
self.source = mux.source
|
||||
|
||||
# DEBUG:
|
||||
self.write = Signal()
|
||||
self.error = Signal()
|
||||
self.comb += [
|
||||
self.write.eq(write_mask),
|
||||
self.error.eq(crc.error),
|
||||
]
|
||||
|
||||
# # to add eop in the same cycle
|
||||
# # the tricks relies on the fact source lags sink one cycle
|
||||
# # but fsm .connect by default use combinational logic which the same cycle rising/falling edge check immpossible
|
||||
# fsm.act("CHECKING",
|
||||
# NextValue(self.source.payload.raw_bits(), 0),
|
||||
# NextValue(self.source.stb, 0),
|
||||
# If(self.sink.stb,
|
||||
# crc.ce.eq(1),
|
||||
# If(self.sink.eop,
|
||||
# NextState("RESET")
|
||||
# ).Else(
|
||||
# NextValue(self.source.payload.raw_bits(), self.sink.payload.raw_bits()),
|
||||
# NextValue(self.source.stb, 1),
|
||||
# )
|
||||
# )
|
||||
# )
|
||||
|
||||
# last_eop = Signal()
|
||||
# self.comb += self.source.eop.eq(~last_eop & self.sink.eop)
|
||||
|
||||
|
||||
class Stream_Parser(Module):
|
||||
def __init__(self):
|
||||
self.sink = stream.Endpoint(word_layout_dchar)
|
||||
self.source = stream.Endpoint(word_layout_dchar)
|
||||
|
||||
# # #
|
||||
|
||||
self.stream_id = Signal(char_width)
|
||||
self.pak_tag = Signal(char_width)
|
||||
self.stream_pak_size = Signal(char_width * 2)
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="WAIT_HEADER")
|
||||
|
||||
fsm.act(
|
||||
"WAIT_HEADER",
|
||||
NextValue(self.stream_id, self.stream_id.reset),
|
||||
NextValue(self.pak_tag, self.pak_tag.reset),
|
||||
NextValue(self.stream_pak_size, self.stream_pak_size.reset),
|
||||
self.sink.ack.eq(1),
|
||||
If(
|
||||
self.sink.stb,
|
||||
NextValue(self.stream_id, self.sink.dchar),
|
||||
NextState("GET_PAK_TAG"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"GET_PAK_TAG",
|
||||
self.sink.ack.eq(1),
|
||||
If(
|
||||
self.sink.stb,
|
||||
NextValue(self.pak_tag, self.sink.dchar),
|
||||
NextState("GET_PAK_SIZE_0"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"GET_PAK_SIZE_0",
|
||||
self.sink.ack.eq(1),
|
||||
If(
|
||||
self.sink.stb,
|
||||
NextValue(self.stream_pak_size[8:], self.sink.dchar),
|
||||
NextState("GET_PAK_SIZE_1"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"GET_PAK_SIZE_1",
|
||||
self.sink.ack.eq(1),
|
||||
If(
|
||||
self.sink.stb,
|
||||
NextValue(self.stream_pak_size[:8], self.sink.dchar),
|
||||
NextState("STORE_BUFFER"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act(
|
||||
"STORE_BUFFER",
|
||||
self.sink.connect(self.source),
|
||||
# both serve the same function but using the pak size I can remove eop injecter and save 1 cycle
|
||||
If(self.sink.stb,
|
||||
NextValue(self.stream_pak_size, self.stream_pak_size - 1),
|
||||
If(self.stream_pak_size == 1,
|
||||
NextState("WAIT_HEADER"),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
class Frame_Extractor(Module):
|
||||
def __init__(self, pixel_format="mono16"):
|
||||
assert pixel_format in ["mono16"]
|
||||
pixel_format = {
|
||||
"mono16": C(0x0105, 2*char_width)
|
||||
}
|
||||
self.format_error = Signal()
|
||||
self.decode_err = Signal()
|
||||
|
||||
self.new_frame = Signal()
|
||||
self.new_line = Signal()
|
||||
|
||||
n_metadata_chars = 23
|
||||
img_header_layout = [
|
||||
("stream_id", char_width),
|
||||
("source_tag", 2*char_width),
|
||||
("x_size", 3*char_width),
|
||||
("x_offset", 3*char_width),
|
||||
("y_size", 3*char_width),
|
||||
("y_offset", 3*char_width),
|
||||
("l_size", 3*char_width), # number of data words per image line
|
||||
("pixel_format", 2*char_width),
|
||||
("tap_geo", 2*char_width),
|
||||
("flag", char_width),
|
||||
]
|
||||
assert layout_len(img_header_layout) == n_metadata_chars*char_width
|
||||
|
||||
|
||||
# # #
|
||||
|
||||
# TODO: decode Image header, line break
|
||||
self.sink = stream.Endpoint(word_layout_dchar)
|
||||
self.source = stream.Endpoint(word_layout_dchar)
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
|
||||
# DEBUG: remove this
|
||||
self.fsm_state = Signal()
|
||||
self.comb += self.fsm_state.eq(fsm.ongoing("IDLE"))
|
||||
|
||||
fsm.act("IDLE",
|
||||
self.sink.ack.eq(1),
|
||||
If((self.sink.stb & (self.sink.dchar == KCode["stream_marker"]) & (self.sink.dchar_k == 1)),
|
||||
NextState("DECODE"),
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("COPY",
|
||||
# until for new line or new frame
|
||||
If((self.sink.stb & (self.sink.dchar == KCode["stream_marker"]) & (self.sink.dchar_k == 1)),
|
||||
self.sink.ack.eq(1),
|
||||
NextState("DECODE"),
|
||||
).Else(
|
||||
self.sink.connect(self.source),
|
||||
)
|
||||
)
|
||||
|
||||
type = {
|
||||
"new_frame": 0x01,
|
||||
"line_break": 0x02,
|
||||
}
|
||||
|
||||
cnt = Signal(max=n_metadata_chars)
|
||||
fsm.act("DECODE",
|
||||
self.sink.ack.eq(1),
|
||||
If(self.sink.stb,
|
||||
Case(self.sink.dchar, {
|
||||
type["new_frame"]: [
|
||||
self.new_frame.eq(1),
|
||||
NextValue(cnt, cnt.reset),
|
||||
NextState("GET_FRAME_DATA"),
|
||||
],
|
||||
type["line_break"]: [
|
||||
self.new_line.eq(1),
|
||||
NextState("COPY"),
|
||||
],
|
||||
"default": [
|
||||
self.decode_err.eq(1),
|
||||
# discard all data until valid frame
|
||||
NextState("IDLE"),
|
||||
],
|
||||
}),
|
||||
)
|
||||
)
|
||||
|
||||
packet_buffer = Signal(layout_len(img_header_layout))
|
||||
case = dict(
|
||||
(i, NextValue(packet_buffer[8*i:8*(i+1)], self.sink.dchar))
|
||||
for i in range(n_metadata_chars)
|
||||
)
|
||||
fsm.act("GET_FRAME_DATA",
|
||||
self.sink.ack.eq(1),
|
||||
If(self.sink.stb,
|
||||
Case(cnt, case),
|
||||
If(cnt == n_metadata_chars - 1,
|
||||
NextState("COPY"),
|
||||
NextValue(cnt, cnt.reset),
|
||||
).Else(
|
||||
NextValue(cnt, cnt + 1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
# dissect packet
|
||||
self.metadata = SimpleNamespace()
|
||||
idx = 0
|
||||
for name, size in img_header_layout:
|
||||
# CXP use MSB even when sending duplicate chars
|
||||
setattr(self.metadata, name, reverse_bytes(packet_buffer[idx:idx+size]))
|
||||
idx += size
|
||||
|
||||
|
||||
class Pixel_Decoder(Module):
|
||||
def __init__(self, pixel_format="mono16"):
|
||||
assert pixel_format == "mono16"
|
||||
|
||||
self.sink = stream.Endpoint(word_layout_dchar)
|
||||
self.source = stream.Endpoint(word_layout_dchar)
|
||||
|
||||
# # #
|
||||
|
||||
# TODO: support mono16 for now?
|
||||
|
||||
class Stream_Pipeline(Module):
|
||||
# optimal stream packet size is 2 kBtyes - Section 9.5.2 (CXP-001-2021)
|
||||
def __init__(self, size=16000):
|
||||
|
||||
self.submodules.double_buffer = double_buffer = Double_Stream_Buffer(size)
|
||||
self.submodules.parser = parser = Stream_Parser()
|
||||
self.submodules.frame_extractor = frame_extractor = Frame_Extractor()
|
||||
|
||||
pipeline = [double_buffer, parser, frame_extractor]
|
||||
for s, d in zip(pipeline, pipeline[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
||||
self.sink = pipeline[0].sink
|
||||
self.source = pipeline[-1].source
|
||||
|
||||
# no backpressure for sim purposes
|
||||
self.sync += self.source.ack.eq(1)
|
|
@ -0,0 +1,154 @@
|
|||
from migen import *
|
||||
from misoc.interconnect import stream
|
||||
|
||||
from sim_pipeline import *
|
||||
from sim_generator import StreamData_Generator
|
||||
|
||||
from src.gateware.cxp_pipeline import *
|
||||
|
||||
class CXP_Links(Module):
|
||||
def __init__(self):
|
||||
# TODO: select the correct buffer to read from
|
||||
# NOTE: although there are double buffer in each connect, the reading must be faster than writing to avoid data loss
|
||||
|
||||
self.downconn_sources = []
|
||||
self.stream_sinks = []
|
||||
for i in range(2):
|
||||
downconn = Pipeline()
|
||||
setattr(self.submodules, "cxp_conn"+str(i), downconn)
|
||||
self.downconn_sources.append(downconn)
|
||||
|
||||
stream_pipeline = Stream_Pipeline()
|
||||
setattr(self.submodules, "stream_pipeline"+str(i), stream_pipeline)
|
||||
self.stream_sinks.append(stream_pipeline)
|
||||
|
||||
self.submodules.crossbar = Streams_Crossbar(self.downconn_sources, self.stream_sinks)
|
||||
|
||||
class Pipeline(Module):
|
||||
def __init__(self):
|
||||
self.submodules.generator = generator = StreamData_Generator()
|
||||
self.submodules.dchar_decoder = dchar_decoder = Duplicated_Char_Decoder()
|
||||
self.submodules.data_decoder = data_decoder = RX_Bootstrap()
|
||||
self.submodules.eop_marker = eop_marker = EOP_Marker()
|
||||
|
||||
# # #
|
||||
|
||||
pipeline = [generator, dchar_decoder, data_decoder, eop_marker]
|
||||
for s, d in zip(pipeline, pipeline[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
||||
self.sink = pipeline[0].sink
|
||||
self.source = pipeline[-1].source
|
||||
|
||||
# self.comb += self.source.ack.eq(1)
|
||||
|
||||
|
||||
dut = CXP_Links()
|
||||
|
||||
def check_case(packet=[]):
|
||||
print("=================TEST========================")
|
||||
|
||||
downconns = dut.downconn_sources
|
||||
stream_buffers = dut.stream_sinks
|
||||
ch = 0
|
||||
|
||||
for i, p in enumerate(packet):
|
||||
for x in range(len(downconns)):
|
||||
if x == ch:
|
||||
yield downconns[x].sink.data.eq(p["data"])
|
||||
yield downconns[x].sink.k.eq(p["k"])
|
||||
yield downconns[x].sink.stb.eq(1)
|
||||
else:
|
||||
yield downconns[x].sink.data.eq(0)
|
||||
yield downconns[x].sink.k.eq(0)
|
||||
yield downconns[x].sink.stb.eq(0)
|
||||
yield downconns[x].sink.eop.eq(0)
|
||||
if "eop" in p:
|
||||
yield downconns[ch].sink.eop.eq(1)
|
||||
# compensate for delay
|
||||
# yield
|
||||
# yield downconns[ch].sink.data.eq(0)
|
||||
# yield downconns[ch].sink.k.eq(0)
|
||||
# yield downconns[ch].sink.stb.eq(0)
|
||||
# yield downconns[ch].sink.eop.eq(0)
|
||||
# yield
|
||||
# yield
|
||||
# yield
|
||||
ch = (ch + 1) % len(downconns)
|
||||
else:
|
||||
yield downconns[ch].sink.eop.eq(0)
|
||||
|
||||
# check cycle result
|
||||
yield
|
||||
# source = dut.stream_pipeline_sinks[0].source
|
||||
source = dut.stream_sinks[0].double_buffer.source
|
||||
print(
|
||||
f"\nCYCLE#{i} : source char = {yield source.data:#X} k = {yield source.k:#X} stb = {yield source.stb} ack = {yield source.ack} eop = {yield source.eop}"
|
||||
# f" source dchar = {yield source.dchar:#X} dchar_k = {yield source.dchar_k:#X}"
|
||||
f"\nCYCLE#{i} : read mask = {yield dut.crossbar.mux.sel}"
|
||||
# f"\nCYCLE#{i} : stream id = {yield decoder.stream_id:#X} pak_tag = {yield decoder.pak_tag:#X}"
|
||||
# f" stream_pak_size = {yield decoder.stream_pak_size:#X}"
|
||||
)
|
||||
# crc = downconns[1].generator.crc_inserter.crc
|
||||
# crc = dut.double_buffer.crc
|
||||
# print(
|
||||
# f"CYCLE#{i} : crc error = {yield crc.error:#X} crc value = {yield crc.value:#X}"
|
||||
# f" crc data = {yield crc.data:#X} engine next = {yield crc.engine.next:#X} ce = {yield crc.ce}"
|
||||
# )
|
||||
|
||||
|
||||
# extra clk cycles
|
||||
cyc = i + 1
|
||||
for i in range(cyc, cyc + 30):
|
||||
for x in range(len(downconns)):
|
||||
# yield won't reset every cycle
|
||||
yield downconns[x].sink.data.eq(0)
|
||||
yield downconns[x].sink.k.eq(0)
|
||||
yield downconns[x].sink.stb.eq(0)
|
||||
yield downconns[x].sink.eop.eq(0)
|
||||
yield
|
||||
print(
|
||||
f"\nCYCLE#{i} : source char = {yield source.data:#X} k = {yield source.k:#X} stb = {yield source.stb} ack = {yield source.ack} eop = {yield source.eop}"
|
||||
# f" source dchar = {yield source.dchar:#X} dchar_k = {yield source.dchar_k:#X}"
|
||||
f"\nCYCLE#{i} : read mask = {yield dut.crossbar .mux.sel}"
|
||||
# f"\nCYCLE#{i} : stream id = {yield decoder.stream_id:#X} pak_tag = {yield decoder.pak_tag:#X}"
|
||||
# f" stream_pak_size = {yield decoder.stream_pak_size:#X}"
|
||||
)
|
||||
assert True
|
||||
|
||||
|
||||
def testbench():
|
||||
# stream_id = 0x01
|
||||
streams = [
|
||||
[
|
||||
{"data": 0x11111111, "k": Replicate(0, 4)},
|
||||
{"data": 0xB105F00D, "k": Replicate(0, 4)},
|
||||
],
|
||||
[
|
||||
{"data": 0x22222222, "k": Replicate(0, 4)},
|
||||
{"data": 0xC001BEA0, "k": Replicate(0, 4)},
|
||||
],
|
||||
[
|
||||
{"data": 0x33333333, "k": Replicate(0, 4)},
|
||||
{"data": 0xC0A79AE5, "k": Replicate(0, 4)},
|
||||
],
|
||||
]
|
||||
|
||||
packet = []
|
||||
for i, s in enumerate(streams):
|
||||
s[-1]["eop"] = 0
|
||||
packet += [
|
||||
{"data": Replicate(C(i % 2, char_width), 4), "k": Replicate(0, 4)},
|
||||
{"data": Replicate(C(i, char_width), 4), "k": Replicate(0, 4)},
|
||||
{
|
||||
"data": Replicate(C(len(s) >> 8 & 0xFF, char_width), 4),
|
||||
"k": Replicate(0, 4),
|
||||
},
|
||||
{"data": Replicate(C(len(s) & 0xFF, char_width), 4), "k": Replicate(0, 4)},
|
||||
*s,
|
||||
]
|
||||
|
||||
|
||||
yield from check_case(packet)
|
||||
|
||||
|
||||
run_simulation(dut, testbench(), vcd_name="sim-cxp.vcd")
|
|
@ -1,32 +0,0 @@
|
|||
BasedOnStyle: LLVM
|
||||
|
||||
Language: Cpp
|
||||
Standard: Cpp11
|
||||
|
||||
AccessModifierOffset: -1
|
||||
AlignEscapedNewlines: Left
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
BinPackParameters: false
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
ColumnLimit: 120
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ContinuationIndentWidth: 4
|
||||
DerivePointerAlignment: false
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
MaxEmptyLinesToKeep: 1
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
|
@ -1 +0,0 @@
|
|||
doc-valid-idents = ["CPython", "NumPy", ".."]
|
|
@ -1,32 +0,0 @@
|
|||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
|
||||
default_stages: [commit]
|
||||
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
name: artiq-zynq cargo format
|
||||
entry: nix
|
||||
language: system
|
||||
types: [file, rust]
|
||||
pass_filenames: false
|
||||
description: Runs cargo fmt on the codebase.
|
||||
args: [develop, -c, cargo, fmt, --manifest-path, src/Cargo.toml, --all]
|
||||
- id: cargo-clippy
|
||||
name: artiq-zynq cargo clippy
|
||||
entry: nix
|
||||
language: system
|
||||
types: [file, rust]
|
||||
pass_filenames: false
|
||||
description: Runs cargo clippy on the codebase.
|
||||
args: [develop, -c, cargo, clippy, --manifest-path, src/Cargo.toml, --tests]
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v19.1.0
|
||||
hooks:
|
||||
- id: clang-format
|
||||
name: artiq-zynq clang-format
|
||||
description: Runs clang-format on the codebase.
|
||||
files: \.(cpp|h|hpp|c)$
|
||||
args: [-style=file, -fallback-style=none, -assume-filename=src/.clang-format]
|
|
@ -559,9 +559,7 @@ name = "satman"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"build_zynq",
|
||||
"byteorder",
|
||||
"core_io",
|
||||
"crc",
|
||||
"cslice",
|
||||
"embedded-hal",
|
||||
"io",
|
||||
|
|
|
@ -0,0 +1,381 @@
|
|||
from migen import *
|
||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
from artiq.gateware.rtio import rtlink
|
||||
|
||||
from cxp_downconn import CXP_DownConn_PHYS
|
||||
from cxp_upconn import CXP_UpConn_PHYS
|
||||
from cxp_pipeline import *
|
||||
|
||||
|
||||
class CXP_PHYS(Module, AutoCSR):
|
||||
def __init__(self, refclk, upconn_pads, downconn_pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||
assert len(upconn_pads) == len(downconn_pads)
|
||||
|
||||
self.submodules.upconn = CXP_UpConn_PHYS(upconn_pads, sys_clk_freq, debug_sma, pmod_pads)
|
||||
self.submodules.downconn = CXP_DownConn_PHYS(refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads)
|
||||
|
||||
@FullMemoryWE()
|
||||
class CXP_Interface(Module, AutoCSR):
|
||||
def __init__(self, upconn_phy, downconn_phy, debug_sma, pmod_pads):
|
||||
self.submodules.upconn = UpConn_Interface(upconn_phy, debug_sma, pmod_pads)
|
||||
self.submodules.downconn = DownConn_Interface(downconn_phy, debug_sma, pmod_pads)
|
||||
|
||||
def get_tx_port(self):
|
||||
return self.upconn.bootstrap.mem.get_port(write_capable=True)
|
||||
|
||||
def get_tx_mem_size(self):
|
||||
# FIXME: if tx mem size is same as rx, for some reason when rx mem is writen, tx mem cannot be access anymore
|
||||
# and each time tx mem is read, CPU will return rx mem instead
|
||||
return self.upconn.bootstrap.mem.depth*self.upconn.bootstrap.mem.width // 8
|
||||
# return self.downconn.bootstrap.mem.depth*self.downconn.bootstrap.mem.width // 8
|
||||
|
||||
def get_rx_port(self):
|
||||
return self.downconn.bootstrap.mem.get_port(write_capable=False)
|
||||
|
||||
def get_rx_mem_size(self):
|
||||
return self.downconn.bootstrap.mem.depth*self.downconn.bootstrap.mem.width // 8
|
||||
|
||||
def get_loopback_tx_port(self):
|
||||
return self.downconn.bootstrap_loopback.mem.get_port(write_capable=True)
|
||||
|
||||
def get_loopback_tx_mem_size(self):
|
||||
return self.downconn.bootstrap_loopback.mem.depth*self.downconn.bootstrap_loopback.mem.width // 8
|
||||
|
||||
class CXP_Master(CXP_Interface):
|
||||
def __init__(self, upconn_phy, downconn_phy, debug_sma, pmod_pads):
|
||||
CXP_Interface.__init__(self, upconn_phy, downconn_phy, debug_sma, pmod_pads)
|
||||
nbit_trigdelay = 8
|
||||
nbit_linktrig = 1
|
||||
|
||||
self.rtlink = rtlink.Interface(
|
||||
rtlink.OInterface(nbit_trigdelay + nbit_linktrig),
|
||||
rtlink.IInterface(word_dw, timestamped=False)
|
||||
)
|
||||
|
||||
self.sync.rio += [
|
||||
If(self.rtlink.o.stb,
|
||||
self.upconn.trig.delay.eq(self.rtlink.o.data[nbit_linktrig:]),
|
||||
self.upconn.trig.linktrig_mode.eq(self.rtlink.o.data[:nbit_linktrig]),
|
||||
),
|
||||
self.upconn.trig.stb.eq(self.rtlink.o.stb),
|
||||
]
|
||||
|
||||
# DEBUG: out
|
||||
self.specials += Instance("OBUF", i_I=self.rtlink.o.stb, o_O=debug_sma.p_tx),
|
||||
# self.specials += Instance("OBUF", i_I=self.rtlink.o.stb, o_O=debug_sma.n_rx),
|
||||
|
||||
class CXP_Extension(CXP_Interface):
|
||||
def __init__(self, upconn_phy, downconn_phy, debug_sma, pmod_pads):
|
||||
CXP_Interface.__init__(self, upconn_phy, downconn_phy, debug_sma, pmod_pads)
|
||||
|
||||
|
||||
class DownConn_Interface(Module, AutoCSR):
|
||||
def __init__(self, phy, debug_sma, pmod_pads):
|
||||
self.rx_start_init = CSRStorage()
|
||||
self.rx_restart = CSR()
|
||||
self.rx_ready = CSRStatus()
|
||||
|
||||
# # #
|
||||
|
||||
gtx = phy.gtx
|
||||
|
||||
# GTX Control
|
||||
self.sync += [
|
||||
gtx.rx_restart.eq(self.rx_restart.re),
|
||||
gtx.rx_init.clk_path_ready.eq(self.rx_start_init.storage),
|
||||
self.rx_ready.status.eq(gtx.rx_ready),
|
||||
]
|
||||
|
||||
|
||||
# DEBUG: tx control
|
||||
self.tx_start_init = CSRStorage()
|
||||
self.tx_restart = CSR()
|
||||
self.txenable = CSRStorage()
|
||||
self.sync += [
|
||||
gtx.txenable.eq(self.txenable.storage),
|
||||
gtx.tx_restart.eq(self.tx_restart.re),
|
||||
gtx.tx_init.clk_path_ready.eq(self.tx_start_init.storage),
|
||||
]
|
||||
|
||||
# DEBUG: loopback control
|
||||
self.loopback_mode = CSRStorage(3)
|
||||
self.comb += gtx.loopback_mode.eq(self.loopback_mode.storage)
|
||||
|
||||
|
||||
# DEBUG: init status
|
||||
self.txinit_phaligndone = CSRStatus()
|
||||
self.rxinit_phaligndone = CSRStatus()
|
||||
self.comb += [
|
||||
self.txinit_phaligndone.status.eq(gtx.tx_init.Xxphaligndone),
|
||||
self.rxinit_phaligndone.status.eq(gtx.rx_init.Xxphaligndone),
|
||||
]
|
||||
|
||||
# Connect all GTX connections' DRP
|
||||
self.gtx_daddr = CSRStorage(9)
|
||||
self.gtx_dread = CSR()
|
||||
self.gtx_din_stb = CSR()
|
||||
self.gtx_din = CSRStorage(16)
|
||||
|
||||
self.gtx_dout = CSRStatus(16)
|
||||
self.gtx_dready = CSR()
|
||||
|
||||
self.comb += gtx.dclk.eq(ClockSignal("sys"))
|
||||
self.sync += [
|
||||
gtx.daddr.eq(self.gtx_daddr.storage),
|
||||
gtx.den.eq(self.gtx_dread.re | self.gtx_din_stb.re),
|
||||
gtx.dwen.eq(self.gtx_din_stb.re),
|
||||
gtx.din.eq(self.gtx_din.storage),
|
||||
If(gtx.dready,
|
||||
self.gtx_dready.w.eq(1),
|
||||
self.gtx_dout.status.eq(gtx.dout),
|
||||
).Elif(self.gtx_dready.re,
|
||||
self.gtx_dready.w.eq(0),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
# DEBUG: txusrclk PLL DRP
|
||||
|
||||
self.txpll_reset = CSRStorage()
|
||||
self.pll_daddr = CSRStorage(7)
|
||||
self.pll_dclk = CSRStorage()
|
||||
self.pll_den = CSRStorage()
|
||||
self.pll_din = CSRStorage(16)
|
||||
self.pll_dwen = CSRStorage()
|
||||
|
||||
self.txpll_locked = CSRStatus()
|
||||
self.pll_dout = CSRStatus(16)
|
||||
self.pll_dready = CSRStatus()
|
||||
|
||||
self.comb += [
|
||||
gtx.txpll_reset.eq(self.txpll_reset.storage),
|
||||
gtx.pll_daddr.eq(self.pll_daddr.storage),
|
||||
gtx.pll_dclk.eq(self.pll_dclk.storage),
|
||||
gtx.pll_den.eq(self.pll_den.storage),
|
||||
gtx.pll_din.eq(self.pll_din.storage),
|
||||
gtx.pll_dwen.eq(self.pll_dwen.storage),
|
||||
|
||||
self.txinit_phaligndone.status.eq(gtx.tx_init.Xxphaligndone),
|
||||
self.rxinit_phaligndone.status.eq(gtx.rx_init.Xxphaligndone),
|
||||
self.txpll_locked.status.eq(gtx.txpll_locked),
|
||||
self.pll_dout.status.eq(gtx.pll_dout),
|
||||
self.pll_dready.status.eq(gtx.pll_dready),
|
||||
]
|
||||
|
||||
|
||||
# DEBUG: Transmission Pipeline
|
||||
#
|
||||
# rtio pak ----+
|
||||
# from gw | 32 32
|
||||
# mux---/---> packet -----> trigger ack ---/---> PHY
|
||||
# | wrapper inserter
|
||||
# data/test ----+
|
||||
# pak from fw
|
||||
#
|
||||
|
||||
|
||||
# DEBUG: TX pipeline
|
||||
self.submodules.bootstrap_loopback = bootstrap_loopback = TX_Bootstrap()
|
||||
self.submodules.pak_wrp = pak_wrp = Packet_Wrapper()
|
||||
self.submodules.trig_ack = trig_ack = Trigger_ACK_Inserter()
|
||||
|
||||
self.ack = CSR()
|
||||
|
||||
self.sync += trig_ack.stb.eq(self.ack.re),
|
||||
|
||||
tx_pipeline = [bootstrap_loopback, pak_wrp, trig_ack, phy]
|
||||
for s, d in zip(tx_pipeline, tx_pipeline[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
||||
|
||||
|
||||
|
||||
|
||||
# Receiver Pipeline WIP
|
||||
#
|
||||
# 32 32+8(dchar)
|
||||
# PHY ---/---> dchar -----/-----> trigger ack ------> packet ------> CDC FIFO ------> debug buffer
|
||||
# decoder checker decoder
|
||||
#
|
||||
cdr = ClockDomainsRenamer("cxp_gtx_rx")
|
||||
|
||||
# decode all incoming data as duplicate char and inject the result into the bus for downstream modules
|
||||
self.submodules.dchar_decoder = dchar_decoder = cdr(Duplicated_Char_Decoder())
|
||||
|
||||
# Priority level 1 packet - Trigger ack packet
|
||||
self.submodules.trig_ack_checker = trig_ack_checker = cdr(Trigger_Ack_Checker())
|
||||
|
||||
self.submodules.trig_ack_ps = trig_ack_ps = PulseSynchronizer("cxp_gtx_rx", "sys")
|
||||
self.sync.cxp_gtx_rx += trig_ack_ps.i.eq(trig_ack_checker.ack)
|
||||
|
||||
self.trig_ack = Signal()
|
||||
self.trig_clr = Signal()
|
||||
# Error are latched
|
||||
self.sync += [
|
||||
If(trig_ack_ps.o,
|
||||
self.trig_ack.eq(1),
|
||||
).Elif(self.trig_clr,
|
||||
self.trig_ack.eq(0),
|
||||
),
|
||||
]
|
||||
|
||||
# Priority level 2 packet - data, test packet
|
||||
self.submodules.bootstrap = bootstrap = cdr(RX_Bootstrap())
|
||||
|
||||
self.bootstrap_decoder_err = CSR()
|
||||
self.bootstrap_test_err = CSR()
|
||||
self.boostrap_buffer_err = CSR()
|
||||
|
||||
decode_err_ps = PulseSynchronizer("cxp_gtx_rx", "sys")
|
||||
test_err_ps = PulseSynchronizer("cxp_gtx_rx", "sys")
|
||||
buffer_err_ps = PulseSynchronizer("cxp_gtx_rx", "sys")
|
||||
self.submodules += decode_err_ps, test_err_ps, buffer_err_ps
|
||||
self.sync.cxp_gtx_rx += [
|
||||
decode_err_ps.i.eq(bootstrap.decode_err),
|
||||
test_err_ps.i.eq(bootstrap.test_err),
|
||||
buffer_err_ps.i.eq(bootstrap.buffer_err),
|
||||
]
|
||||
self.sync += [
|
||||
If(decode_err_ps.o,
|
||||
self.bootstrap_decoder_err.w.eq(1),
|
||||
).Elif(self.bootstrap_decoder_err.re,
|
||||
self.bootstrap_decoder_err.w.eq(0),
|
||||
),
|
||||
If(test_err_ps.o,
|
||||
self.bootstrap_test_err.w.eq(1),
|
||||
).Elif(self.bootstrap_test_err.re,
|
||||
self.bootstrap_test_err.w.eq(0),
|
||||
),
|
||||
If(buffer_err_ps.o,
|
||||
self.boostrap_buffer_err.w.eq(1),
|
||||
).Elif(self.bootstrap_test_err.re,
|
||||
self.boostrap_buffer_err.w.eq(0),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
# Cicular buffer interface
|
||||
self.packet_type = CSRStatus(8)
|
||||
self.pending_packet = CSR()
|
||||
self.read_ptr = CSRStatus(log2_int(buffer_count))
|
||||
|
||||
self.specials += [
|
||||
MultiReg(bootstrap.packet_type, self.packet_type.status),
|
||||
MultiReg(self.read_ptr.status, bootstrap.read_ptr_rx, odomain="cxp_gtx_rx"),
|
||||
]
|
||||
self.sync += [
|
||||
self.pending_packet.w.eq(self.read_ptr.status != bootstrap.write_ptr_sys),
|
||||
If(~gtx.rx_ready,
|
||||
self.read_ptr.status.eq(0),
|
||||
).Elif(self.pending_packet.re & self.pending_packet.w,
|
||||
self.read_ptr.status.eq(self.read_ptr.status + 1),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
cdc_fifo = stream.AsyncFIFO(word_layout_dchar, 512)
|
||||
self.submodules += ClockDomainsRenamer({"write": "cxp_gtx_rx", "read": "sys"})(cdc_fifo)
|
||||
self.submodules.debug_out = debug_out = RX_Debug_Buffer()
|
||||
|
||||
rx_pipeline = [phy, dchar_decoder, trig_ack_checker, bootstrap, cdc_fifo, debug_out]
|
||||
for s, d in zip(rx_pipeline, rx_pipeline[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
||||
|
||||
|
||||
# DEBUG: CSR
|
||||
self.trigger_ack = CSR()
|
||||
self.sync += [
|
||||
self.trig_clr.eq(self.trigger_ack.re),
|
||||
self.trigger_ack.w.eq(self.trig_ack),
|
||||
]
|
||||
|
||||
pak_start = Signal()
|
||||
self.sync += [
|
||||
pak_start.eq(bootstrap.sink.data == 0xFBFBFBFB),
|
||||
]
|
||||
|
||||
self.specials += [
|
||||
# Instance("OBUF", i_I=phy.gtx.cd_cxp_gtx_rx.clk, o_O=debug_sma.p_tx),
|
||||
# Instance("OBUF", i_I=, o_O=debug_sma.p_rx),
|
||||
# # pmod 0-7 pin
|
||||
# Instance("OBUF", i_I=bootstrap.test_err, o_O=pmod_pads[0]),
|
||||
# Instance("OBUF", i_I=pak_start, o_O=pmod_pads[1]),
|
||||
# Instance("OBUF", i_I=fifo_in.source.ack, o_O=pmod_pads[2]),
|
||||
# Instance("OBUF", i_I=gtx.comma_checker.aligner_en, o_O=pmod_pads[3]),
|
||||
# Instance("OBUF", i_I=gtx.comma_checker.check_reset, o_O=pmod_pads[4]),
|
||||
# Instance("OBUF", i_I=gtx.comma_checker.has_comma, o_O=pmod_pads[5]),
|
||||
# Instance("OBUF", i_I=gtx.comma_checker.has_error, o_O=pmod_pads[6]),
|
||||
# Instance("OBUF", i_I=gtx.comma_checker.ready_sys, o_O=pmod_pads[7]),
|
||||
]
|
||||
|
||||
|
||||
class UpConn_Interface(Module, AutoCSR):
|
||||
def __init__(self, phy, debug_sma, pmod_pads):
|
||||
# Transmission Pipeline
|
||||
#
|
||||
# 32 32 8
|
||||
# ctrl/test ---/---> packet -----> idle word -----> trigger ack ---/--> conv ---/---> trigger -----> PHY
|
||||
# packet wrapper inserter inserter inserter
|
||||
#
|
||||
# Equivalent transmission priority:
|
||||
# trigger > trigger ack > idle > test/data packet
|
||||
# To maintain the trigger performance, idle word should not be inserted into trigger or trigger ack.
|
||||
#
|
||||
# In low speed CoaXpress, the higher priority packet can be inserted in two types of boundary
|
||||
# Insertion @ char boundary: Trigger packets
|
||||
# Insertion @ word boundary: Trigger ack & IDLE packets
|
||||
# The 32 bit part of the pipeline handles the word boundary insertion while the 8 bit part handles the char boundary insertion
|
||||
|
||||
|
||||
|
||||
# Packet FIFOs with transmission priority
|
||||
# 0: Trigger packet
|
||||
self.submodules.trig = trig = TX_Trigger()
|
||||
|
||||
# # DEBUG: INPUT
|
||||
self.trig_stb = CSR()
|
||||
self.trig_delay = CSRStorage(8)
|
||||
self.linktrigger = CSRStorage()
|
||||
|
||||
# self.sync += [
|
||||
# trig.stb.eq(self.trig_stb.re),
|
||||
# trig.delay.eq(self.trig_delay.storage),
|
||||
# trig.linktrig_mode.eq(self.linktrigger.storage),
|
||||
# ]
|
||||
|
||||
|
||||
# 1: IO acknowledgment for trigger packet
|
||||
self.submodules.trig_ack = trig_ack = Trigger_ACK_Inserter()
|
||||
|
||||
# DEBUG: INPUT
|
||||
self.ack = CSR()
|
||||
self.sync += trig_ack.stb.eq(self.ack.re),
|
||||
|
||||
|
||||
# 2: All other packets (data & test packet)
|
||||
# Control is not timing dependent, all the data packets are handled in firmware
|
||||
self.submodules.bootstrap = bootstrap = TX_Bootstrap()
|
||||
|
||||
self.submodules.pak_wrp = pak_wrp = Packet_Wrapper()
|
||||
self.submodules.idle = idle = Idle_Word_Inserter()
|
||||
|
||||
# Section 9.2.5.1 (CXP-001-2021)
|
||||
# IDLE should be transmitter every 10000 words
|
||||
cnt = Signal(max=10000)
|
||||
|
||||
self.sync += [
|
||||
idle.stb.eq(0),
|
||||
If((~idle.sink.stb) | (cnt == 9999),
|
||||
idle.stb.eq(1),
|
||||
cnt.eq(cnt.reset),
|
||||
).Else(
|
||||
cnt.eq(cnt + 1),
|
||||
),
|
||||
]
|
||||
|
||||
self.submodules.converter = converter = stream.StrideConverter(word_layout, char_layout)
|
||||
|
||||
tx_pipeline = [bootstrap, pak_wrp, idle, trig_ack, converter, trig, phy]
|
||||
for s, d in zip(tx_pipeline, tx_pipeline[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
|
@ -0,0 +1,87 @@
|
|||
from migen.build.generic_platform import *
|
||||
|
||||
fmc_adapter_io = [
|
||||
|
||||
# CoaXPress high speed link
|
||||
("CXP_HS", 0,
|
||||
Subsignal("txp", Pins("HPC:DP0_C2M_P")),
|
||||
Subsignal("txn", Pins("HPC:DP0_C2M_N")),
|
||||
Subsignal("rxp", Pins("HPC:DP0_M2C_P")),
|
||||
Subsignal("rxn", Pins("HPC:DP0_M2C_N")),
|
||||
),
|
||||
("CXP_HS", 1,
|
||||
Subsignal("txp", Pins("HPC:DP1_C2M_P")),
|
||||
Subsignal("txn", Pins("HPC:DP1_C2M_N")),
|
||||
Subsignal("rxp", Pins("HPC:DP1_M2C_P")),
|
||||
Subsignal("rxn", Pins("HPC:DP1_M2C_N")),
|
||||
),
|
||||
("CXP_HS", 2,
|
||||
Subsignal("txp", Pins("HPC:DP2_C2M_P")),
|
||||
Subsignal("txn", Pins("HPC:DP2_C2M_N")),
|
||||
Subsignal("rxp", Pins("HPC:DP2_M2C_P")),
|
||||
Subsignal("rxn", Pins("HPC:DP2_M2C_N")),
|
||||
),
|
||||
("CXP_HS", 3,
|
||||
Subsignal("txp", Pins("HPC:DP3_C2M_P")),
|
||||
Subsignal("txn", Pins("HPC:DP3_C2M_N")),
|
||||
Subsignal("rxp", Pins("HPC:DP3_M2C_P")),
|
||||
Subsignal("rxn", Pins("HPC:DP3_M2C_N")),
|
||||
),
|
||||
|
||||
# CoaXPress low speed link
|
||||
("CXP_LS", 0, Pins("HPC:LA00_CC_P"), IOStandard("LVCMOS33")),
|
||||
("CXP_LS", 1, Pins("HPC:LA01_CC_N"), IOStandard("LVCMOS33")),
|
||||
("CXP_LS", 2, Pins("HPC:LA01_CC_P"), IOStandard("LVCMOS33")),
|
||||
("CXP_LS", 3, Pins("HPC:LA02_N"), IOStandard("LVCMOS33")),
|
||||
|
||||
# CoaXPress green and red LED
|
||||
("CXP_LED", 0,
|
||||
Subsignal("green", Pins("HPC:LA11_P"), IOStandard("LVCMOS33")),
|
||||
Subsignal("red", Pins("HPC:LA11_N"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("CXP_LED", 1,
|
||||
Subsignal("green", Pins("HPC:LA12_P"), IOStandard("LVCMOS33")),
|
||||
Subsignal("red", Pins("HPC:LA12_N"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("CXP_LED", 2,
|
||||
Subsignal("green", Pins("HPC:LA13_P"), IOStandard("LVCMOS33")),
|
||||
Subsignal("red", Pins("HPC:LA13_N"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("CXP_LED", 3,
|
||||
Subsignal("green", Pins("HPC:LA14_P"), IOStandard("LVCMOS33")),
|
||||
Subsignal("red", Pins("HPC:LA14_N"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
|
||||
# Power over CoaXPress
|
||||
("PoCXP", 0,
|
||||
Subsignal("enable", Pins("HPC:LA21_N"), IOStandard("LVCMOS33")),
|
||||
Subsignal("alert", Pins("HPC:LA18_CC_P"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("PoCXP", 1,
|
||||
Subsignal("enable", Pins("HPC:LA21_P"), IOStandard("LVCMOS33")),
|
||||
Subsignal("alert", Pins("HPC:LA19_N"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("PoCXP", 2,
|
||||
Subsignal("enable", Pins("HPC:LA22_N"), IOStandard("LVCMOS33")),
|
||||
Subsignal("alert", Pins("HPC:LA19_P"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("PoCXP", 3,
|
||||
Subsignal("enable", Pins("HPC:LA22_P"), IOStandard("LVCMOS33")),
|
||||
Subsignal("alert", Pins("HPC:LA20_N"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
|
||||
("i2c_fmc", 0,
|
||||
Subsignal("scl", Pins("HPC:IIC_SCL")),
|
||||
Subsignal("sda", Pins("HPC:IIC_SDA")),
|
||||
IOStandard("LVCMOS33")
|
||||
),
|
||||
|
||||
("3V3", 0, Pins("HPC:PG_M2C")),
|
||||
("GND", 0, Pins("HPC:PRSNT_M2C_L HPC:CLK0_M2C_P")),
|
||||
("VADJ", 0, Pins("HPC:GBTCLK1_M2C_N CLK0_M2C_N")),
|
||||
|
||||
("clk125_fmc", 0,
|
||||
Subsignal("p", Pins("HPC:GBTCLK0_M2C_P")),
|
||||
Subsignal("n", Pins("HPC:GBTCLK0_M2C_n")),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,742 @@
|
|||
from migen import *
|
||||
from migen.genlib.cdc import MultiReg
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
|
||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.interconnect import stream
|
||||
|
||||
from artiq.gateware.drtio.transceiver.gtx_7series_init import *
|
||||
from cxp_pipeline import word_layout
|
||||
|
||||
from functools import reduce
|
||||
from operator import add
|
||||
|
||||
class CXP_DownConn_PHYS(Module, AutoCSR):
|
||||
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||
self.qpll_reset = CSR()
|
||||
self.qpll_locked = CSRStatus()
|
||||
|
||||
self.rx_phys = []
|
||||
# # #
|
||||
|
||||
# For speed higher than 6.6Gbps, QPLL need to be used instead of CPLL
|
||||
self.submodules.qpll = qpll = QPLL(refclk, sys_clk_freq)
|
||||
self.sync += [
|
||||
qpll.reset.eq(self.qpll_reset.re),
|
||||
self.qpll_locked.status.eq(qpll.lock),
|
||||
]
|
||||
|
||||
|
||||
for i, pad in enumerate(pads):
|
||||
rx = Receiver(qpll, pad, sys_clk_freq, "single", "single", debug_sma, pmod_pads)
|
||||
self.rx_phys.append(rx)
|
||||
setattr(self.submodules, "rx"+str(i), rx)
|
||||
|
||||
# TODO: add extension gtx connections
|
||||
# TODO: add connection interface
|
||||
|
||||
# TODO: Connect slave cxp_gtx_rx clock tgt
|
||||
# checkout channel interfaces & drtio_gtx
|
||||
# GTPTXPhaseAlignement for inspiration
|
||||
|
||||
# Connect slave i's `cxp_gtx_rx` clock to `cxp_gtx_rxi` clock
|
||||
for rx in self.rx_phys:
|
||||
name = "cd_cxp_gtx_rx" + str(i)
|
||||
setattr(self.clock_domains, name, ClockDomain(name=name))
|
||||
self.comb += [
|
||||
getattr(self, name).clk.eq(rx.gtx.cd_cxp_gtx_rx.clk),
|
||||
getattr(self, name).rst.eq(rx.gtx.cd_cxp_gtx_rx.rst)
|
||||
]
|
||||
|
||||
|
||||
class Receiver(Module):
|
||||
def __init__(self, qpll, pad, sys_clk_freq, tx_mode, rx_mode, debug_sma, pmod_pads):
|
||||
self.submodules.gtx = gtx = GTX(qpll, pad, sys_clk_freq, tx_mode="single", rx_mode="single")
|
||||
|
||||
self.source = stream.Endpoint(word_layout)
|
||||
|
||||
data_valid = Signal()
|
||||
self.sync.cxp_gtx_rx += [
|
||||
data_valid.eq(gtx.comma_aligner.rxfsm.ongoing("READY")),
|
||||
|
||||
self.source.stb.eq(0),
|
||||
If(data_valid & self.source.ack & ~((gtx.decoders[0].d == 0xBC) & (gtx.decoders[0].k == 1)),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Cat(gtx.decoders[i].d for i in range(4))),
|
||||
self.source.k.eq(Cat(gtx.decoders[i].k for i in range(4))),
|
||||
)
|
||||
]
|
||||
|
||||
# DEBUG: tx fifos for loopback
|
||||
# fw -> -> cdc fifo -> buffered fifo -> gtx tx
|
||||
|
||||
cdc_fifo = stream.AsyncFIFO(word_layout, 512)
|
||||
self.submodules += ClockDomainsRenamer({"write": "sys", "read": "cxp_gtx_tx"})(cdc_fifo)
|
||||
self.sink = cdc_fifo.sink
|
||||
|
||||
# fix timing violation
|
||||
cdr = ClockDomainsRenamer("cxp_gtx_tx")
|
||||
self.submodules.buf = tx_fifo = cdr(stream.SyncFIFO(word_layout, 2, buffered=True))
|
||||
|
||||
self.comb += [
|
||||
cdc_fifo.source.connect(tx_fifo.sink),
|
||||
]
|
||||
|
||||
idle_period = 50 # express in word
|
||||
word_count = Signal(max=idle_period)
|
||||
|
||||
# JANK: fix the every 98th word got eaten
|
||||
# cnt 97 98 99 0
|
||||
# out fifo[97] IDLE IDLE fifo[99]
|
||||
# ack 1 0 0 1
|
||||
self.sync.cxp_gtx_tx += [
|
||||
tx_fifo.source.ack.eq(0),
|
||||
|
||||
If(word_count == idle_period-1,
|
||||
word_count.eq(word_count.reset),
|
||||
).Else(
|
||||
If(tx_fifo.source.stb,
|
||||
If(word_count != idle_period-2, tx_fifo.source.ack.eq(1)),
|
||||
word_count.eq(word_count + 1),
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
# NOTE: prevent the first word send twice due to stream stb delay
|
||||
self.comb += [
|
||||
If((tx_fifo.source.stb & tx_fifo.source.ack & (word_count != idle_period-1)),
|
||||
gtx.encoder.d[0].eq(tx_fifo.source.data[:8]),
|
||||
gtx.encoder.d[1].eq(tx_fifo.source.data[8:16]),
|
||||
gtx.encoder.d[2].eq(tx_fifo.source.data[16:24]),
|
||||
gtx.encoder.d[3].eq(tx_fifo.source.data[24:]),
|
||||
gtx.encoder.k[0].eq(tx_fifo.source.k[0]),
|
||||
gtx.encoder.k[1].eq(tx_fifo.source.k[1]),
|
||||
gtx.encoder.k[2].eq(tx_fifo.source.k[2]),
|
||||
gtx.encoder.k[3].eq(tx_fifo.source.k[3]),
|
||||
).Else(
|
||||
# NOTE: IDLE WORD
|
||||
gtx.encoder.d[0].eq(0xBC),
|
||||
gtx.encoder.k[0].eq(1),
|
||||
gtx.encoder.d[1].eq(0x3C),
|
||||
gtx.encoder.k[1].eq(1),
|
||||
gtx.encoder.d[2].eq(0x3C),
|
||||
gtx.encoder.k[2].eq(1),
|
||||
gtx.encoder.d[3].eq(0xB5),
|
||||
gtx.encoder.k[3].eq(0),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class QPLL(Module, AutoCSR):
|
||||
def __init__(self, refclk, sys_clk_freq):
|
||||
self.clk = Signal()
|
||||
self.refclk = Signal()
|
||||
self.lock = Signal()
|
||||
self.reset = Signal()
|
||||
|
||||
self.daddr = CSRStorage(8)
|
||||
self.dread = CSR()
|
||||
self.din_stb = CSR()
|
||||
self.din = CSRStorage(16)
|
||||
|
||||
self.dout = CSRStatus(16)
|
||||
self.dready = CSR()
|
||||
|
||||
# # #
|
||||
|
||||
# VCO @ 10GHz, linerate = 1.25Gbps
|
||||
# feedback divider = 80
|
||||
qpll_fbdiv = 0b0100100000
|
||||
qpll_fbdiv_ratio = 1
|
||||
refclk_div = 1
|
||||
self.Xxout_div = 8
|
||||
|
||||
# DEBUG: txuserclk
|
||||
fbdiv_real = 80
|
||||
self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40
|
||||
|
||||
dready = Signal()
|
||||
self.specials += [
|
||||
Instance("GTXE2_COMMON",
|
||||
i_QPLLREFCLKSEL=0b001,
|
||||
i_GTREFCLK0=refclk,
|
||||
|
||||
i_QPLLPD=0,
|
||||
i_QPLLRESET=self.reset,
|
||||
i_QPLLLOCKEN=1,
|
||||
o_QPLLLOCK=self.lock,
|
||||
o_QPLLOUTCLK=self.clk,
|
||||
o_QPLLOUTREFCLK=self.refclk,
|
||||
|
||||
# See UG476 (v1.12.1) Table 2-16
|
||||
p_QPLL_FBDIV=qpll_fbdiv,
|
||||
p_QPLL_FBDIV_RATIO=qpll_fbdiv_ratio,
|
||||
p_QPLL_REFCLK_DIV=refclk_div,
|
||||
|
||||
# From 7 Series FPGAs Transceivers Wizard
|
||||
p_BIAS_CFG=0x0000040000001000,
|
||||
p_COMMON_CFG=0x00000000,
|
||||
p_QPLL_CFG=0x0680181,
|
||||
p_QPLL_CLKOUT_CFG=0b0000,
|
||||
p_QPLL_COARSE_FREQ_OVRD=0b010000,
|
||||
p_QPLL_COARSE_FREQ_OVRD_EN=0b0,
|
||||
p_QPLL_CP=0b0000011111,
|
||||
p_QPLL_CP_MONITOR_EN=0b0,
|
||||
p_QPLL_DMONITOR_SEL=0b0,
|
||||
p_QPLL_FBDIV_MONITOR_EN= 0b0,
|
||||
p_QPLL_INIT_CFG=0x000006,
|
||||
p_QPLL_LOCK_CFG=0x21E8,
|
||||
p_QPLL_LPF=0b1111,
|
||||
|
||||
# Reserved, values cannot be modified
|
||||
i_BGBYPASSB=0b1,
|
||||
i_BGMONITORENB=0b1,
|
||||
i_BGPDB=0b1,
|
||||
i_BGRCALOVRD=0b11111,
|
||||
i_RCALENB=0b1,
|
||||
i_QPLLRSVD1=0b0,
|
||||
i_QPLLRSVD2=0b11111,
|
||||
|
||||
# Dynamic Reconfiguration Ports
|
||||
i_DRPADDR=self.daddr.storage,
|
||||
i_DRPCLK=ClockSignal("sys"),
|
||||
i_DRPEN=(self.dread.re | self.din_stb.re),
|
||||
i_DRPWE=self.din_stb.re,
|
||||
i_DRPDI=self.din.storage,
|
||||
o_DRPDO=self.dout.status,
|
||||
o_DRPRDY=dready,
|
||||
)
|
||||
]
|
||||
|
||||
self.sync += [
|
||||
If(dready,
|
||||
self.dready.w.eq(1),
|
||||
),
|
||||
If(self.dready.re,
|
||||
self.dready.w.eq(0),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class RX_Resetter(Module):
|
||||
def __init__(self, reset_period=10_000_000):
|
||||
self.rx_ready = Signal()
|
||||
self.rx_reset = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# periodically reset rx until rx is connected and receiving valid data
|
||||
# as after connecting RXP/RXN, the whole RX need to be reset
|
||||
|
||||
reset_counter = Signal(reset=reset_period-1, max=reset_period)
|
||||
self.sync += [
|
||||
self.rx_reset.eq(0),
|
||||
If(~self.rx_ready,
|
||||
If(reset_counter == 0,
|
||||
reset_counter.eq(reset_counter.reset),
|
||||
self.rx_reset.eq(1),
|
||||
).Else(
|
||||
reset_counter.eq(reset_counter - 1),
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
|
||||
# compared to the usual 8b10b binary representation.
|
||||
class Comma_Aligner(Module):
|
||||
def __init__(self, comma, reset_period=10_000_000):
|
||||
self.data = Signal(20)
|
||||
self.comma_aligned = Signal()
|
||||
self.comma_realigned = Signal()
|
||||
self.comma_det = Signal()
|
||||
|
||||
self.aligner_en = Signal()
|
||||
self.ready_sys = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# Data and comma checker
|
||||
# From UG476 (v1.12.1) p.228
|
||||
# The built-in RXBYTEISALIGNED can be falsely asserted at linerate higher than 5Gbps
|
||||
# The validity of data and comma needed to be checked externally
|
||||
|
||||
comma_n = ~comma & 0b1111111111
|
||||
|
||||
# DEBUG: remove after use
|
||||
self.has_comma = Signal()
|
||||
self.has_error = Signal()
|
||||
|
||||
comma_seen = Signal()
|
||||
error_seen = Signal()
|
||||
one_counts = Signal(max=11)
|
||||
|
||||
# From CXP-001-2021 section 9.2.5.1
|
||||
# For high speed connection an IDLE word shall be transmitted at least once every 100 words
|
||||
counter_period = 200
|
||||
|
||||
counter = Signal(reset=counter_period-1, max=counter_period)
|
||||
check_reset = Signal()
|
||||
check = Signal()
|
||||
|
||||
self.sync.cxp_gtx_rx += [
|
||||
If(check_reset,
|
||||
counter.eq(counter.reset),
|
||||
check.eq(0),
|
||||
).Elif(counter == 0,
|
||||
check.eq(1),
|
||||
).Else(
|
||||
counter.eq(counter - 1),
|
||||
),
|
||||
|
||||
If(check_reset,
|
||||
comma_seen.eq(0),
|
||||
).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
|
||||
comma_seen.eq(1)
|
||||
),
|
||||
|
||||
one_counts.eq(reduce(add, [self.data[i] for i in range(10)])),
|
||||
If(check_reset,
|
||||
error_seen.eq(0),
|
||||
).Elif((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
|
||||
error_seen.eq(1),
|
||||
),
|
||||
|
||||
# DEBUG:
|
||||
self.has_comma.eq(0),
|
||||
If((self.data[:10] == comma) | (self.data[:10] == comma_n),
|
||||
self.has_comma.eq(1),
|
||||
),
|
||||
|
||||
self.has_error.eq(0),
|
||||
If((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
|
||||
self.has_error.eq(1),
|
||||
),
|
||||
]
|
||||
|
||||
# DEBUG: expose signal
|
||||
self.check_reset = Signal()
|
||||
self.comb +=[
|
||||
self.check_reset.eq(check_reset),
|
||||
]
|
||||
|
||||
self.submodules.rxfsm = rxfsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="WAIT_COMMA"))
|
||||
|
||||
rxfsm.act("WAIT_COMMA",
|
||||
If(self.comma_det,
|
||||
NextState("ALIGNING"),
|
||||
)
|
||||
)
|
||||
|
||||
rxfsm.act("ALIGNING",
|
||||
If(self.comma_aligned & (~self.comma_realigned),
|
||||
NextState("WAIT_ALIGNED_DATA"),
|
||||
).Else(
|
||||
self.aligner_en.eq(1),
|
||||
)
|
||||
)
|
||||
|
||||
# wait for the aligned data to arrive at the FPGA RX interface
|
||||
# as there is a delay before the data is avaiable after RXBYTEISALIGNED is asserted
|
||||
self.submodules.timer = timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(10_000))
|
||||
|
||||
rxfsm.act("WAIT_ALIGNED_DATA",
|
||||
timer.wait.eq(1),
|
||||
If(timer.done,
|
||||
check_reset.eq(1),
|
||||
NextState("CHECKING"),
|
||||
)
|
||||
)
|
||||
|
||||
rxfsm.act("CHECKING",
|
||||
If(check,
|
||||
check_reset.eq(1),
|
||||
If(comma_seen & (~error_seen),
|
||||
NextState("READY"),
|
||||
).Else(
|
||||
NextState("WAIT_COMMA")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
ready = Signal()
|
||||
self.specials += MultiReg(ready, self.ready_sys)
|
||||
rxfsm.act("READY",
|
||||
ready.eq(1),
|
||||
If(check,
|
||||
check_reset.eq(1),
|
||||
If(~(comma_seen & (~error_seen)),
|
||||
NextState("WAIT_COMMA"),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class GTX(Module):
|
||||
# Settings:
|
||||
# * GTX reference clock @ 125MHz
|
||||
# * GTX data width = 20
|
||||
# * GTX PLL frequency @ 3.125GHz
|
||||
# * GTX line rate (TX & RX) @ 3.125Gb/s
|
||||
# * GTX TX/RX USRCLK @ PLL/datawidth = 156MHz
|
||||
def __init__(self, qpll, pads, sys_clk_freq, tx_mode="single", rx_mode="single"):
|
||||
assert tx_mode in ["single", "master", "slave"]
|
||||
assert rx_mode in ["single", "master", "slave"]
|
||||
|
||||
# linerate = USRCLK * datawidth
|
||||
pll_fbout_mult = 8
|
||||
txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq
|
||||
|
||||
self.tx_restart = Signal()
|
||||
self.rx_restart = Signal()
|
||||
self.loopback_mode = Signal(3)
|
||||
|
||||
self.txenable = Signal()
|
||||
self.rx_ready = Signal()
|
||||
|
||||
# Dynamic Reconfiguration Ports
|
||||
self.daddr = Signal(9)
|
||||
self.dclk = Signal()
|
||||
self.den = Signal()
|
||||
self.dwen = Signal()
|
||||
self.din = Signal(16)
|
||||
self.dout = Signal(16)
|
||||
self.dready = Signal()
|
||||
|
||||
self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(4, True))
|
||||
self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")(
|
||||
(Decoder(True))) for _ in range(4)]
|
||||
|
||||
|
||||
# transceiver direct clock outputs
|
||||
# useful to specify clock constraints in a way palatable to Vivado
|
||||
self.txoutclk = Signal()
|
||||
self.rxoutclk = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# TX generates cxp_tx clock, init must be in system domain
|
||||
# FIXME: 500e6 is used to fix Xx reset by holding gtxXxreset for a couple cycle more
|
||||
self.submodules.tx_init = tx_init = GTXInit(500e6, False, mode=tx_mode)
|
||||
self.submodules.rx_init = rx_init = GTXInit(sys_clk_freq, True, mode=rx_mode)
|
||||
|
||||
# RX receives restart commands from txusrclk domain
|
||||
# self.submodules.rx_init = rx_init = ClockDomainsRenamer("cxp_gtx_tx")(GTXInit(500e6, True, mode=rx_mode))
|
||||
|
||||
self.comb += [
|
||||
tx_init.cplllock.eq(qpll.lock),
|
||||
rx_init.cplllock.eq(qpll.lock)
|
||||
]
|
||||
|
||||
txdata = Signal(40)
|
||||
rxdata = Signal(40)
|
||||
|
||||
comma_aligned = Signal()
|
||||
comma_realigned = Signal()
|
||||
comma_det = Signal()
|
||||
comma_aligner_en = Signal()
|
||||
# Note: the following parameters were set after consulting AR45360
|
||||
self.specials += \
|
||||
Instance("GTXE2_CHANNEL",
|
||||
# PMA Attributes
|
||||
p_PMA_RSV=0x001E7080,
|
||||
p_PMA_RSV2=0x2050, # PMA_RSV2[5] = 0: Eye scan feature disabled
|
||||
p_PMA_RSV3=0,
|
||||
p_PMA_RSV4=1, # PMA_RSV[4],RX_CM_TRIM[2:0] = 0b1010: Common mode 800mV
|
||||
p_RX_BIAS_CFG=0b000000000100,
|
||||
p_RX_OS_CFG=0b0000010000000,
|
||||
p_RX_CLK25_DIV=5,
|
||||
p_TX_CLK25_DIV=5,
|
||||
|
||||
# Power-Down Attributes
|
||||
p_PD_TRANS_TIME_FROM_P2=0x3c,
|
||||
p_PD_TRANS_TIME_NONE_P2=0x3c,
|
||||
p_PD_TRANS_TIME_TO_P2=0x64,
|
||||
i_CPLLPD=1,
|
||||
|
||||
# QPLL
|
||||
i_QPLLCLK=qpll.clk,
|
||||
i_QPLLREFCLK=qpll.refclk,
|
||||
p_RXOUT_DIV=qpll.Xxout_div,
|
||||
p_TXOUT_DIV=qpll.Xxout_div,
|
||||
i_RXSYSCLKSEL=0b11, # use QPLL & QPLL's REFCLK
|
||||
i_TXSYSCLKSEL=0b11, # use QPLL & CPLL's REFCLK
|
||||
|
||||
# TX clock
|
||||
p_TXBUF_EN="FALSE",
|
||||
p_TX_XCLK_SEL="TXUSR",
|
||||
o_TXOUTCLK=self.txoutclk,
|
||||
# i_TXSYSCLKSEL=0b00,
|
||||
i_TXOUTCLKSEL=0b11,
|
||||
|
||||
# TX Startup/Reset
|
||||
i_TXPHDLYRESET=0,
|
||||
i_TXDLYBYPASS=0,
|
||||
i_TXPHALIGNEN=1 if tx_mode != "single" else 0,
|
||||
i_GTTXRESET=tx_init.gtXxreset,
|
||||
o_TXRESETDONE=tx_init.Xxresetdone,
|
||||
i_TXDLYSRESET=tx_init.Xxdlysreset,
|
||||
o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
|
||||
i_TXPHINIT=tx_init.txphinit if tx_mode != "single" else 0,
|
||||
o_TXPHINITDONE=tx_init.txphinitdone if tx_mode != "single" else Signal(),
|
||||
i_TXPHALIGN=tx_init.Xxphalign if tx_mode != "single" else 0,
|
||||
i_TXDLYEN=tx_init.Xxdlyen if tx_mode != "single" else 0,
|
||||
o_TXPHALIGNDONE=tx_init.Xxphaligndone,
|
||||
i_TXUSERRDY=tx_init.Xxuserrdy,
|
||||
p_TXPMARESET_TIME=1,
|
||||
p_TXPCSRESET_TIME=1,
|
||||
i_TXINHIBIT=~self.txenable,
|
||||
|
||||
# TX data
|
||||
p_TX_DATA_WIDTH=40,
|
||||
p_TX_INT_DATAWIDTH=1, # 1 if a line rate is greater than 6.6 Gbps
|
||||
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19], txdata[29], txdata[39]),
|
||||
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18], txdata[28], txdata[38]),
|
||||
i_TXDATA=Cat(txdata[:8], txdata[10:18], txdata[20:28], txdata[30:38]),
|
||||
i_TXUSRCLK=ClockSignal("cxp_gtx_tx"),
|
||||
i_TXUSRCLK2=ClockSignal("cxp_gtx_tx"),
|
||||
|
||||
# TX electrical
|
||||
i_TXBUFDIFFCTRL=0b100,
|
||||
i_TXDIFFCTRL=0b1000,
|
||||
|
||||
# RX Startup/Reset
|
||||
i_RXPHDLYRESET=0,
|
||||
i_RXDLYBYPASS=0,
|
||||
i_RXPHALIGNEN=1 if rx_mode != "single" else 0,
|
||||
i_GTRXRESET=rx_init.gtXxreset,
|
||||
o_RXRESETDONE=rx_init.Xxresetdone,
|
||||
i_RXDLYSRESET=rx_init.Xxdlysreset,
|
||||
o_RXDLYSRESETDONE=rx_init.Xxdlysresetdone,
|
||||
i_RXPHALIGN=rx_init.Xxphalign if rx_mode != "single" else 0,
|
||||
i_RXDLYEN=rx_init.Xxdlyen if rx_mode != "single" else 0,
|
||||
o_RXPHALIGNDONE=rx_init.Xxphaligndone,
|
||||
i_RXUSERRDY=rx_init.Xxuserrdy,
|
||||
p_RXPMARESET_TIME=1,
|
||||
p_RXPCSRESET_TIME=1,
|
||||
|
||||
# RX AFE
|
||||
p_RX_DFE_XYD_CFG=0,
|
||||
p_RX_CM_SEL=0b11, # RX_CM_SEL = 0b11: Common mode is programmable
|
||||
p_RX_CM_TRIM=0b010, # PMA_RSV[4],RX_CM_TRIM[2:0] = 0b1010: Common mode 800mV
|
||||
i_RXDFEXYDEN=1,
|
||||
i_RXDFEXYDHOLD=0,
|
||||
i_RXDFEXYDOVRDEN=0,
|
||||
i_RXLPMEN=1, # RXLPMEN = 1: LPM mode is enable for non scramble 8b10b data
|
||||
p_RXLPM_HF_CFG=0b00000011110000,
|
||||
p_RXLPM_LF_CFG=0b00000011110000,
|
||||
|
||||
p_RX_DFE_GAIN_CFG=0x0207EA,
|
||||
p_RX_DFE_VP_CFG=0b00011111100000011,
|
||||
p_RX_DFE_UT_CFG=0b10001000000000000,
|
||||
p_RX_DFE_KL_CFG=0b0000011111110,
|
||||
p_RX_DFE_KL_CFG2=0x3788140A,
|
||||
p_RX_DFE_H2_CFG=0b000110000000,
|
||||
p_RX_DFE_H3_CFG=0b000110000000,
|
||||
p_RX_DFE_H4_CFG=0b00011100000,
|
||||
p_RX_DFE_H5_CFG=0b00011100000,
|
||||
p_RX_DFE_LPM_CFG=0x0904, # RX_DFE_LPM_CFG = 0x0904: linerate <= 6.6Gb/s
|
||||
# = 0x0104: linerate > 6.6Gb/s
|
||||
|
||||
# RX clock
|
||||
i_RXDDIEN=1,
|
||||
# i_RXSYSCLKSEL=0b00,
|
||||
i_RXOUTCLKSEL=0b010,
|
||||
o_RXOUTCLK=self.rxoutclk,
|
||||
i_RXUSRCLK=ClockSignal("cxp_gtx_rx"),
|
||||
i_RXUSRCLK2=ClockSignal("cxp_gtx_rx"),
|
||||
|
||||
# RX Clock Correction Attributes
|
||||
p_CLK_CORRECT_USE="FALSE",
|
||||
p_CLK_COR_SEQ_1_1=0b0100000000,
|
||||
p_CLK_COR_SEQ_2_1=0b0100000000,
|
||||
p_CLK_COR_SEQ_1_ENABLE=0b1111,
|
||||
p_CLK_COR_SEQ_2_ENABLE=0b1111,
|
||||
|
||||
# RX data
|
||||
p_RX_DATA_WIDTH=40,
|
||||
p_RX_INT_DATAWIDTH=1, # 1 if a line rate is greater than 6.6 Gbps
|
||||
o_RXDISPERR=Cat(rxdata[9], rxdata[19], rxdata[29], rxdata[39]),
|
||||
o_RXCHARISK=Cat(rxdata[8], rxdata[18], rxdata[28], rxdata[38]),
|
||||
o_RXDATA=Cat(rxdata[:8], rxdata[10:18], rxdata[20:28], rxdata[30:38]),
|
||||
|
||||
# RX Byte and Word Alignment Attributes
|
||||
p_ALIGN_COMMA_DOUBLE="FALSE",
|
||||
p_ALIGN_COMMA_ENABLE=0b1111111111,
|
||||
p_ALIGN_COMMA_WORD=4, # align comma to rxdata[:10] only
|
||||
p_ALIGN_MCOMMA_DET="TRUE",
|
||||
p_ALIGN_MCOMMA_VALUE=0b1010000011,
|
||||
p_ALIGN_PCOMMA_DET="TRUE",
|
||||
p_ALIGN_PCOMMA_VALUE=0b0101111100,
|
||||
p_SHOW_REALIGN_COMMA="FALSE",
|
||||
p_RXSLIDE_AUTO_WAIT=7,
|
||||
p_RXSLIDE_MODE="OFF",
|
||||
p_RX_SIG_VALID_DLY=10,
|
||||
i_RXPCOMMAALIGNEN=comma_aligner_en,
|
||||
i_RXMCOMMAALIGNEN=comma_aligner_en,
|
||||
i_RXCOMMADETEN=1,
|
||||
i_RXSLIDE=0,
|
||||
o_RXBYTEISALIGNED=comma_aligned,
|
||||
o_RXBYTEREALIGN=comma_realigned,
|
||||
o_RXCOMMADET=comma_det,
|
||||
|
||||
# RX 8B/10B Decoder Attributes
|
||||
p_RX_DISPERR_SEQ_MATCH="FALSE",
|
||||
p_DEC_MCOMMA_DETECT="TRUE",
|
||||
p_DEC_PCOMMA_DETECT="TRUE",
|
||||
p_DEC_VALID_COMMA_ONLY="FALSE",
|
||||
|
||||
# RX Buffer Attributes
|
||||
p_RXBUF_ADDR_MODE="FAST",
|
||||
p_RXBUF_EIDLE_HI_CNT=0b1000,
|
||||
p_RXBUF_EIDLE_LO_CNT=0b0000,
|
||||
p_RXBUF_EN="FALSE",
|
||||
p_RX_BUFFER_CFG=0b000000,
|
||||
p_RXBUF_RESET_ON_CB_CHANGE="TRUE",
|
||||
p_RXBUF_RESET_ON_COMMAALIGN="FALSE",
|
||||
p_RXBUF_RESET_ON_EIDLE="FALSE", # RXBUF_RESET_ON_EIDLE = FALSE: OOB is disabled
|
||||
p_RXBUF_RESET_ON_RATE_CHANGE="TRUE",
|
||||
p_RXBUFRESET_TIME=0b00001,
|
||||
p_RXBUF_THRESH_OVFLW=61,
|
||||
p_RXBUF_THRESH_OVRD="FALSE",
|
||||
p_RXBUF_THRESH_UNDFLW=4,
|
||||
p_RXDLY_CFG=0x001F,
|
||||
p_RXDLY_LCFG=0x030,
|
||||
p_RXDLY_TAP_CFG=0x0000,
|
||||
p_RXPH_CFG=0xC00002,
|
||||
p_RXPHDLY_CFG=0x084020,
|
||||
p_RXPH_MONITOR_SEL=0b00000,
|
||||
p_RX_XCLK_SEL="RXUSR",
|
||||
p_RX_DDI_SEL=0b000000,
|
||||
p_RX_DEFER_RESET_BUF_EN="TRUE",
|
||||
|
||||
# CDR Attributes
|
||||
p_RXCDR_CFG=0x03_0000_23FF_1008_0020, # LPM @ 0.5G-1.5625G , 8B/10B encoded data, CDR setting < +/- 200ppm
|
||||
# (See UG476 (v1.12.1), p.206)
|
||||
p_RXCDR_FR_RESET_ON_EIDLE=0b0,
|
||||
p_RXCDR_HOLD_DURING_EIDLE=0b0,
|
||||
p_RXCDR_PH_RESET_ON_EIDLE=0b0,
|
||||
p_RXCDR_LOCK_CFG=0b010101,
|
||||
|
||||
# Pads
|
||||
i_GTXRXP=pads.rxp,
|
||||
i_GTXRXN=pads.rxn,
|
||||
o_GTXTXP=pads.txp,
|
||||
o_GTXTXN=pads.txn,
|
||||
|
||||
# Dynamic Reconfiguration Ports
|
||||
p_IS_DRPCLK_INVERTED=0b0,
|
||||
i_DRPADDR=self.daddr,
|
||||
i_DRPCLK=self.dclk,
|
||||
i_DRPEN=self.den,
|
||||
i_DRPWE=self.dwen,
|
||||
i_DRPDI=self.din,
|
||||
o_DRPDO=self.dout,
|
||||
o_DRPRDY=self.dready,
|
||||
|
||||
# ! loopback for debugging
|
||||
i_LOOPBACK = self.loopback_mode,
|
||||
p_TX_LOOPBACK_DRIVE_HIZ = "FALSE",
|
||||
p_RXPRBS_ERR_LOOPBACK = 0b0,
|
||||
|
||||
# Other parameters
|
||||
p_PCS_RSVD_ATTR=(
|
||||
(tx_mode != "single") << 1 | # PCS_RSVD_ATTR[1] = 0: TX Single Lane Auto Mode
|
||||
# = 1: TX Manual Mode
|
||||
(rx_mode != "single") << 2 | # [2] = 0: RX Single Lane Auto Mode
|
||||
# = 1: RX Manual Mode
|
||||
0 << 8 # [8] = 0: OOB is disabled
|
||||
),
|
||||
i_RXELECIDLEMODE=0b11, # RXELECIDLEMODE = 0b11: OOB is disabled
|
||||
p_RX_DFE_LPM_HOLD_DURING_EIDLE=0b0,
|
||||
p_ES_EYE_SCAN_EN="TRUE", # Must be TRUE for GTX
|
||||
)
|
||||
|
||||
|
||||
# TX clocking
|
||||
# A PLL is used to generate the correct frequency for TXUSRCLK (UG476 Equation 3-1)
|
||||
self.clock_domains.cd_cxp_gtx_tx = ClockDomain()
|
||||
txpll_fb_clk = Signal()
|
||||
txoutclk_buf = Signal()
|
||||
txpll_clkout = Signal()
|
||||
|
||||
self.txpll_reset = Signal()
|
||||
self.pll_daddr = Signal(7)
|
||||
self.pll_dclk = Signal()
|
||||
self.pll_den = Signal()
|
||||
self.pll_din = Signal(16)
|
||||
self.pll_dwen = Signal()
|
||||
|
||||
self.txpll_locked = Signal()
|
||||
self.pll_dout = Signal(16)
|
||||
self.pll_dready = Signal()
|
||||
self.specials += [
|
||||
Instance("PLLE2_ADV",
|
||||
p_BANDWIDTH="HIGH",
|
||||
o_LOCKED=self.txpll_locked,
|
||||
i_RST=self.txpll_reset,
|
||||
|
||||
p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns
|
||||
i_CLKIN1=txoutclk_buf,
|
||||
|
||||
# VCO @ 1.25GHz
|
||||
p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1,
|
||||
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
|
||||
|
||||
# frequency = linerate/40
|
||||
p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
|
||||
|
||||
# Dynamic Reconfiguration Ports
|
||||
i_DADDR = self.pll_daddr,
|
||||
i_DCLK = self.pll_dclk,
|
||||
i_DEN = self.pll_den,
|
||||
i_DI = self.pll_din,
|
||||
i_DWE = self.pll_dwen,
|
||||
o_DO = self.pll_dout,
|
||||
o_DRDY = self.pll_dready,
|
||||
),
|
||||
Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_buf),
|
||||
Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk),
|
||||
AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~self.txpll_locked & ~tx_init.done)
|
||||
]
|
||||
self.comb += tx_init.restart.eq(self.tx_restart)
|
||||
|
||||
# RX clocking
|
||||
# the CDR matches the required frequency for RXUSRCLK, no need for PLL
|
||||
|
||||
# Slave Rx will use cxp_gtx_rx instead
|
||||
if rx_mode == "single" or rx_mode == "master":
|
||||
self.clock_domains.cd_cxp_gtx_rx = ClockDomain()
|
||||
self.specials += [
|
||||
Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_cxp_gtx_rx.clk),
|
||||
AsyncResetSynchronizer(self.cd_cxp_gtx_rx, ~rx_init.done)
|
||||
]
|
||||
self.submodules.rx_resetter = rx_resetter = RX_Resetter()
|
||||
self.comb += [
|
||||
rx_resetter.rx_ready.eq(self.rx_ready),
|
||||
rx_init.restart.eq(self.rx_restart | rx_resetter.rx_reset),
|
||||
]
|
||||
else:
|
||||
self.comb += rx_init.restart.eq(self.rx_restart),
|
||||
|
||||
|
||||
|
||||
# 8b10b Encoder/Decoder
|
||||
|
||||
self.comb += [
|
||||
txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1], self.encoder.output[2], self.encoder.output[3])),
|
||||
self.decoders[0].input.eq(rxdata[:10]),
|
||||
self.decoders[1].input.eq(rxdata[10:20]),
|
||||
self.decoders[2].input.eq(rxdata[20:30]),
|
||||
self.decoders[3].input.eq(rxdata[30:]),
|
||||
]
|
||||
|
||||
self.submodules.comma_aligner = comma_aligner = Comma_Aligner(0b0101111100)
|
||||
self.comb += [
|
||||
comma_aligner.data.eq(rxdata),
|
||||
comma_aligner.comma_aligned.eq(comma_aligned),
|
||||
comma_aligner.comma_realigned.eq(comma_realigned),
|
||||
comma_aligner.comma_det.eq(comma_det),
|
||||
comma_aligner_en.eq(comma_aligner.aligner_en),
|
||||
self.rx_ready.eq(comma_aligner.ready_sys),
|
||||
]
|
|
@ -0,0 +1,526 @@
|
|||
from migen import *
|
||||
from migen.genlib.cdc import MultiReg
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.interconnect import stream
|
||||
|
||||
from functools import reduce
|
||||
from itertools import combinations
|
||||
from operator import or_, and_
|
||||
|
||||
char_width = 8
|
||||
char_layout = [("data", char_width), ("k", char_width//8)]
|
||||
|
||||
word_dw = 32
|
||||
word_layout = [("data", word_dw), ("k", word_dw//8)]
|
||||
|
||||
word_layout_dchar = [
|
||||
("data", word_dw),
|
||||
("k", word_dw//8),
|
||||
("dchar", char_width),
|
||||
("dchar_k", char_width//8)
|
||||
]
|
||||
|
||||
buffer_count = 4
|
||||
buffer_depth = 512
|
||||
|
||||
def K(x, y):
|
||||
return ((y << 5) | x)
|
||||
|
||||
KCode = {
|
||||
"pak_start" : C(K(27, 7), char_width),
|
||||
"io_ack" : C(K(28, 6), char_width),
|
||||
"trig_indic_28_2" : C(K(28, 2), char_width),
|
||||
"stream_marker" : C(K(28, 3), char_width),
|
||||
"trig_indic_28_4" : C(K(28, 4), char_width),
|
||||
"pak_end" : C(K(29, 7), char_width),
|
||||
"idle_comma" : C(K(28, 5), char_width),
|
||||
"idle_alignment" : C(K(28, 1), char_width),
|
||||
}
|
||||
|
||||
class Packet_Wrapper(Module):
|
||||
def __init__(self):
|
||||
self.sink = stream.Endpoint(word_layout)
|
||||
self.source = stream.Endpoint(word_layout)
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
|
||||
fsm.act("IDLE",
|
||||
self.sink.ack.eq(1),
|
||||
If(self.sink.stb,
|
||||
self.sink.ack.eq(0),
|
||||
NextState("INSERT_HEADER"),
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("INSERT_HEADER",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Replicate(KCode["pak_start"], 4)),
|
||||
self.source.k.eq(Replicate(1, 4)),
|
||||
If(self.source.ack, NextState("COPY")),
|
||||
)
|
||||
|
||||
fsm.act("COPY",
|
||||
self.sink.connect(self.source),
|
||||
self.source.eop.eq(0),
|
||||
If(self.sink.stb & self.sink.eop & self.source.ack,
|
||||
NextState("INSERT_FOOTER"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act("INSERT_FOOTER",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Replicate(KCode["pak_end"], 4)),
|
||||
self.source.k.eq(Replicate(1, 4)),
|
||||
self.source.eop.eq(1),
|
||||
If(self.source.ack, NextState("IDLE")),
|
||||
)
|
||||
|
||||
class TX_Trigger(Module):
|
||||
def __init__(self):
|
||||
self.stb = Signal()
|
||||
self.delay = Signal(char_width)
|
||||
self.linktrig_mode = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.sink = stream.Endpoint(char_layout)
|
||||
self.source = stream.Endpoint(char_layout)
|
||||
|
||||
# Table 15 & 16 (CXP-001-2021)
|
||||
# Send [K28.2, K28.4, K28.4] or [K28.4, K28.2, K28.2] and 3x delay as trigger packet
|
||||
|
||||
trig_packet = [Signal(char_width), Signal(char_width), Signal(char_width), self.delay, self.delay, self.delay]
|
||||
trig_packet_k = [1, 1, 1, 0, 0, 0]
|
||||
self.comb += [
|
||||
If(self.linktrig_mode,
|
||||
trig_packet[0].eq(KCode["trig_indic_28_4"]),
|
||||
trig_packet[1].eq(KCode["trig_indic_28_2"]),
|
||||
trig_packet[2].eq(KCode["trig_indic_28_2"]),
|
||||
).Else(
|
||||
trig_packet[0].eq(KCode["trig_indic_28_2"]),
|
||||
trig_packet[1].eq(KCode["trig_indic_28_4"]),
|
||||
trig_packet[2].eq(KCode["trig_indic_28_4"]),
|
||||
),
|
||||
]
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="COPY")
|
||||
|
||||
cnt = Signal(max=6)
|
||||
fsm.act("COPY",
|
||||
NextValue(cnt, cnt.reset),
|
||||
self.sink.connect(self.source),
|
||||
If(self.stb, NextState("WRITE_TRIG"))
|
||||
)
|
||||
|
||||
fsm.act("WRITE_TRIG",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Array(trig_packet)[cnt]),
|
||||
self.source.k.eq(Array(trig_packet_k)[cnt]),
|
||||
If(self.source.ack,
|
||||
If(cnt == 5,
|
||||
NextState("COPY"),
|
||||
).Else(
|
||||
NextValue(cnt, cnt + 1),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
class Idle_Word_Inserter(Module):
|
||||
def __init__(self):
|
||||
self.stb = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# Section 9.2.5 (CXP-001-2021)
|
||||
# Send K28.5, K28.1, K28.1, D21.5 as idle word
|
||||
self.submodules.fsm = fsm = FSM(reset_state="COPY")
|
||||
|
||||
self.sink = stream.Endpoint(word_layout)
|
||||
self.source = stream.Endpoint(word_layout)
|
||||
fsm.act("COPY",
|
||||
self.sink.connect(self.source),
|
||||
If(self.stb, NextState("WRITE_IDLE"))
|
||||
)
|
||||
|
||||
fsm.act("WRITE_IDLE",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Cat(KCode["idle_comma"], KCode["idle_alignment"], KCode["idle_alignment"], C(0xB5, char_width))),
|
||||
self.source.k.eq(Cat(1, 1, 1, 0)),
|
||||
If(self.source.ack, NextState("COPY")),
|
||||
)
|
||||
|
||||
|
||||
class Trigger_ACK_Inserter(Module):
|
||||
def __init__(self):
|
||||
self.stb = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# Section 9.3.2 (CXP-001-2021)
|
||||
# Send 4x K28.6 and 4x 0x01 as trigger packet ack
|
||||
self.submodules.fsm = fsm = FSM(reset_state="COPY")
|
||||
|
||||
self.sink = stream.Endpoint(word_layout)
|
||||
self.source = stream.Endpoint(word_layout)
|
||||
fsm.act("COPY",
|
||||
self.sink.connect(self.source),
|
||||
If(self.stb, NextState("WRITE_ACK0"))
|
||||
)
|
||||
|
||||
fsm.act("WRITE_ACK0",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Replicate(KCode["io_ack"], 4)),
|
||||
self.source.k.eq(Replicate(1, 4)),
|
||||
If(self.source.ack, NextState("WRITE_ACK1")),
|
||||
)
|
||||
|
||||
fsm.act("WRITE_ACK1",
|
||||
self.sink.ack.eq(0),
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Replicate(C(0x01, char_width), 4)),
|
||||
self.source.k.eq(Replicate(0, 4)),
|
||||
If(self.source.ack, NextState("COPY")),
|
||||
)
|
||||
|
||||
|
||||
@FullMemoryWE()
|
||||
class TX_Bootstrap(Module, AutoCSR):
|
||||
def __init__(self):
|
||||
self.tx_word_len = CSRStorage(log2_int(buffer_depth))
|
||||
self.tx = CSR()
|
||||
self.tx_testseq = CSR()
|
||||
|
||||
self.tx_busy = CSRStatus()
|
||||
|
||||
# # #
|
||||
|
||||
self.specials.mem = mem = Memory(word_dw, buffer_depth)
|
||||
self.specials.mem_port = mem_port = mem.get_port()
|
||||
self.source = stream.Endpoint(word_layout)
|
||||
|
||||
# increment addr in the same cycle the moment addr_inc is high
|
||||
# as memory takes one cycle to shift to the correct addr
|
||||
addr_next = Signal(log2_int(buffer_depth))
|
||||
addr = Signal.like(addr_next)
|
||||
addr_rst = Signal()
|
||||
addr_inc = Signal()
|
||||
self.sync += addr.eq(addr_next),
|
||||
|
||||
self.comb += [
|
||||
addr_next.eq(addr),
|
||||
If(addr_rst,
|
||||
addr_next.eq(addr_next.reset),
|
||||
).Elif(addr_inc,
|
||||
addr_next.eq(addr + 1),
|
||||
),
|
||||
mem_port.adr.eq(addr_next),
|
||||
self.source.data.eq(mem_port.dat_r)
|
||||
]
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
self.sync += self.tx_busy.status.eq(~fsm.ongoing("IDLE"))
|
||||
|
||||
cnt = Signal(max=0xFFF)
|
||||
fsm.act("IDLE",
|
||||
addr_rst.eq(1),
|
||||
If(self.tx.re, NextState("TRANSMIT")),
|
||||
If(self.tx_testseq.re,
|
||||
NextValue(cnt, cnt.reset),
|
||||
NextState("WRITE_TEST_PACKET_TYPE"),
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("TRANSMIT",
|
||||
self.source.stb.eq(1),
|
||||
If(self.source.ack,
|
||||
addr_inc.eq(1),
|
||||
),
|
||||
If(addr_next == self.tx_word_len.storage,
|
||||
self.source.eop.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("WRITE_TEST_PACKET_TYPE",
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Replicate(C(0x04, char_width), 4)),
|
||||
self.source.k.eq(Replicate(0, 4)),
|
||||
If(self.source.ack,NextState("WRITE_TEST_COUNTER"))
|
||||
)
|
||||
|
||||
fsm.act("WRITE_TEST_COUNTER",
|
||||
self.source.stb.eq(1),
|
||||
self.source.data.eq(Cat(cnt[:8], cnt[:8]+1, cnt[:8]+2, cnt[:8]+3)),
|
||||
self.source.k.eq(Cat(0, 0, 0, 0)),
|
||||
If(self.source.ack,
|
||||
If(cnt == 0xFFF-3,
|
||||
self.source.eop.eq(1),
|
||||
NextState("IDLE")
|
||||
).Else(
|
||||
NextValue(cnt, cnt + 4),
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
class RX_Debug_Buffer(Module,AutoCSR):
|
||||
def __init__(self):
|
||||
self.submodules.buf_out = buf_out = stream.SyncFIFO(word_layout_dchar, 128)
|
||||
self.sink = buf_out.sink
|
||||
|
||||
self.inc = CSR()
|
||||
self.dout_pak = CSRStatus(word_dw)
|
||||
self.kout_pak = CSRStatus(word_dw//8)
|
||||
self.dout_valid = CSRStatus()
|
||||
|
||||
self.sync += [
|
||||
# output
|
||||
buf_out.source.ack.eq(self.inc.re),
|
||||
self.dout_pak.status.eq(buf_out.source.data),
|
||||
self.kout_pak.status.eq(buf_out.source.k),
|
||||
self.dout_valid.status.eq(buf_out.source.stb),
|
||||
]
|
||||
|
||||
class Duplicated_Char_Decoder(Module):
|
||||
def __init__(self):
|
||||
self.sink = stream.Endpoint(word_layout)
|
||||
self.source = stream.Endpoint(word_layout_dchar)
|
||||
|
||||
# # #
|
||||
|
||||
|
||||
# For duplicated characters, an error correction method (e.g. majority voting) is required to meet the CXP spec:
|
||||
# RX decoder should immune to single bit errors when handling duplicated characters - Section 9.2.2.1 (CXP-001-2021)
|
||||
#
|
||||
#
|
||||
# 32
|
||||
# +---> buffer -----/-----+
|
||||
# 32 | | 32+8(dchar)
|
||||
# sink ---/---+ ---> source -----/-----> downstream
|
||||
# | 8(dchar) | decoders
|
||||
# +---> majority -----/-----+
|
||||
# voting
|
||||
#
|
||||
#
|
||||
# Due to the tight setup/hold time requiremnt for 12.5Gbps CXP, the voting logic cannot be implemented as combinational logic
|
||||
# Hence, a pipeline approach is needed to avoid any s/h violation, where the majority voting result are pre-calculate and injected into the bus immediate after the PHY.
|
||||
# And any downstream modules can access the voting result without implementing the voting logic inside the decoder
|
||||
|
||||
# cycle 1 - buffer data & calculate intermediate result
|
||||
buffer = stream.Endpoint(word_layout)
|
||||
self.sync += [
|
||||
If((~buffer.stb | buffer.ack),
|
||||
buffer.stb.eq(self.sink.stb),
|
||||
buffer.payload.eq(self.sink.payload),
|
||||
)
|
||||
]
|
||||
self.comb += self.sink.ack.eq(~buffer.stb | buffer.ack)
|
||||
|
||||
# calculate ABC, ABD, ACD, BCD
|
||||
char = [[self.sink.data[i*8:(i+1)*8], self.sink.k[i]] for i in range(4)]
|
||||
voters = [Record([("data", 8), ("k", 1)]) for _ in range(4)]
|
||||
|
||||
for i, comb in enumerate(combinations(char, 3)):
|
||||
self.sync += [
|
||||
If((~buffer.stb | buffer.ack),
|
||||
voters[i].data.eq(reduce(and_, [code[0] for code in comb])),
|
||||
voters[i].k.eq(reduce(and_, [code[1] for code in comb])),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
# cycle 2 - inject the voting result
|
||||
self.sync += [
|
||||
If((~self.source.stb | self.source.ack),
|
||||
self.source.stb.eq(buffer.stb),
|
||||
self.source.data.eq(buffer.data),
|
||||
self.source.k.eq(buffer.k),
|
||||
self.source.dchar.eq(Replicate(reduce(or_, [v.data for v in voters]), 4)),
|
||||
self.source.dchar_k.eq(Replicate(reduce(or_, [v.k for v in voters]), 4)),
|
||||
)
|
||||
]
|
||||
self.comb += buffer.ack.eq(~self.source.stb | self.source.ack)
|
||||
|
||||
|
||||
@FullMemoryWE()
|
||||
class RX_Bootstrap(Module):
|
||||
def __init__(self):
|
||||
self.packet_type = Signal(8)
|
||||
|
||||
self.decode_err = Signal()
|
||||
self.test_err = Signal()
|
||||
self.buffer_err = Signal()
|
||||
# # #
|
||||
|
||||
# TODO: heartbeat
|
||||
type = {
|
||||
"data_stream": 0x01,
|
||||
"control_ack_no_tag": 0x03,
|
||||
"test_packet": 0x04,
|
||||
"control_ack_with_tag": 0x06,
|
||||
"event": 0x07,
|
||||
"heartbeat": 0x09,
|
||||
}
|
||||
|
||||
self.sink = stream.Endpoint(word_layout_dchar)
|
||||
self.source = stream.Endpoint(word_layout_dchar)
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
|
||||
fsm.act("IDLE",
|
||||
self.sink.ack.eq(1),
|
||||
If((self.sink.stb & (self.sink.dchar == KCode["pak_start"]) & (self.sink.dchar_k == 1)),
|
||||
NextState("DECODE"),
|
||||
)
|
||||
)
|
||||
|
||||
cnt = Signal(max=0x100)
|
||||
addr_nbits = log2_int(buffer_depth)
|
||||
addr = Signal(addr_nbits)
|
||||
fsm.act("DECODE",
|
||||
self.sink.ack.eq(1),
|
||||
If(self.sink.stb,
|
||||
NextValue(self.packet_type, self.sink.dchar),
|
||||
|
||||
Case(self.sink.dchar, {
|
||||
type["data_stream"]: NextState("STREAMING"),
|
||||
type["test_packet"]: [
|
||||
NextValue(cnt, cnt.reset),
|
||||
NextState("VERIFY_TEST_PATTERN"),
|
||||
],
|
||||
type["control_ack_no_tag"]:[
|
||||
NextValue(addr, addr.reset),
|
||||
NextState("LOAD_BUFFER"),
|
||||
],
|
||||
type["control_ack_with_tag"]:[
|
||||
NextValue(addr, addr.reset),
|
||||
NextState("LOAD_BUFFER"),
|
||||
],
|
||||
type["event"]: [
|
||||
NextValue(addr, addr.reset),
|
||||
NextState("LOAD_BUFFER"),
|
||||
],
|
||||
"default": [
|
||||
self.decode_err.eq(1),
|
||||
# wait till next valid packet
|
||||
NextState("IDLE"),
|
||||
],
|
||||
}),
|
||||
)
|
||||
)
|
||||
# For stream data packet
|
||||
fsm.act("STREAMING",
|
||||
If((self.sink.stb & (self.sink.dchar == KCode["pak_end"]) & (self.sink.dchar_k == 1)),
|
||||
# discard K29,7
|
||||
self.sink.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
).Else(
|
||||
self.sink.connect(self.source),
|
||||
)
|
||||
)
|
||||
|
||||
# Section 9.9.1 (CXP-001-2021)
|
||||
# the received test data packet (0x00, 0x01 ... 0xFF)
|
||||
# need to be compared against the local test sequence generator
|
||||
fsm.act("VERIFY_TEST_PATTERN",
|
||||
self.sink.ack.eq(1),
|
||||
If(self.sink.stb,
|
||||
If(((self.sink.dchar == KCode["pak_end"]) & (self.sink.dchar_k == 1)),
|
||||
NextState("IDLE"),
|
||||
).Else(
|
||||
If(((self.sink.data != Cat(cnt, cnt+1, cnt+2, cnt+3))),
|
||||
self.test_err.eq(1),
|
||||
),
|
||||
If(cnt == 0xFC,
|
||||
NextValue(cnt, cnt.reset),
|
||||
).Else(
|
||||
NextValue(cnt, cnt + 4)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
# A circular buffer for firmware to read packet from
|
||||
self.specials.mem = mem = Memory(word_dw, buffer_count*buffer_depth)
|
||||
self.specials.mem_port = mem_port = mem.get_port(write_capable=True)
|
||||
|
||||
write_ptr = Signal(log2_int(buffer_count))
|
||||
self.write_ptr_sys = Signal.like(write_ptr)
|
||||
self.specials += MultiReg(write_ptr, self.write_ptr_sys),
|
||||
|
||||
self.comb += [
|
||||
mem_port.adr[:addr_nbits].eq(addr),
|
||||
mem_port.adr[addr_nbits:].eq(write_ptr),
|
||||
]
|
||||
|
||||
# For control ack, event packet
|
||||
fsm.act("LOAD_BUFFER",
|
||||
mem_port.we.eq(0),
|
||||
self.sink.ack.eq(1),
|
||||
If(self.sink.stb,
|
||||
If(((self.sink.dchar == KCode["pak_end"]) & (self.sink.dchar_k == 1)),
|
||||
NextState("MOVE_BUFFER_PTR"),
|
||||
).Else(
|
||||
mem_port.we.eq(1),
|
||||
mem_port.dat_w.eq(self.sink.data),
|
||||
NextValue(addr, addr + 1),
|
||||
If(addr == buffer_depth - 1,
|
||||
# discard the packet
|
||||
self.buffer_err.eq(1),
|
||||
NextState("IDLE"),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.read_ptr_rx = Signal.like(write_ptr)
|
||||
fsm.act("MOVE_BUFFER_PTR",
|
||||
self.sink.ack.eq(0),
|
||||
If(write_ptr + 1 == self.read_ptr_rx,
|
||||
# if next one hasn't been read, overwrite the current buffer when new packet comes in
|
||||
self.buffer_err.eq(1),
|
||||
).Else(
|
||||
NextValue(write_ptr, write_ptr + 1),
|
||||
),
|
||||
NextState("IDLE"),
|
||||
)
|
||||
|
||||
class Trigger_Ack_Checker(Module, AutoCSR):
|
||||
def __init__(self):
|
||||
self.sink = stream.Endpoint(word_layout_dchar)
|
||||
self.source = stream.Endpoint(word_layout_dchar)
|
||||
|
||||
self.ack = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="COPY")
|
||||
|
||||
fsm.act("COPY",
|
||||
If((self.sink.stb & (self.sink.dchar == KCode["io_ack"]) & (self.sink.dchar_k == 1)),
|
||||
# discard K28,6
|
||||
self.sink.ack.eq(1),
|
||||
NextState("CHECK_ACK")
|
||||
).Else(
|
||||
self.sink.connect(self.source),
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("CHECK_ACK",
|
||||
If(self.sink.stb,
|
||||
NextState("COPY"),
|
||||
# discard the word after K28,6
|
||||
self.sink.ack.eq(1),
|
||||
If((self.sink.dchar == 0x01) & (self.sink.dchar_k == 0),
|
||||
self.ack.eq(1),
|
||||
)
|
||||
)
|
||||
)
|
|
@ -0,0 +1,224 @@
|
|||
from math import ceil
|
||||
|
||||
from migen import *
|
||||
|
||||
from misoc.cores.code_8b10b import SingleEncoder
|
||||
from misoc.interconnect import stream
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
from cxp_pipeline import char_layout
|
||||
|
||||
@ResetInserter()
|
||||
class UpConn_ClockGen(Module):
|
||||
def __init__(self, sys_clk_freq):
|
||||
self.clk = Signal()
|
||||
self.clk_10x = Signal() # 20.83MHz 48ns or 41.66MHz 24ns
|
||||
|
||||
self.freq2x_enable = Signal()
|
||||
# # #
|
||||
|
||||
period = 1e9/sys_clk_freq
|
||||
max_count = ceil(48/period)
|
||||
counter = Signal(max=max_count, reset=max_count-1)
|
||||
|
||||
clk_div = Signal(max=10, reset=9)
|
||||
|
||||
self.sync += [
|
||||
self.clk.eq(0),
|
||||
self.clk_10x.eq(0),
|
||||
|
||||
If(counter == 0,
|
||||
self.clk_10x.eq(1),
|
||||
If(self.freq2x_enable,
|
||||
counter.eq(int(max_count/2)-1),
|
||||
).Else(
|
||||
counter.eq(counter.reset),
|
||||
),
|
||||
).Else(
|
||||
counter.eq(counter-1),
|
||||
),
|
||||
|
||||
If(counter == 0,
|
||||
If(clk_div == 0,
|
||||
self.clk.eq(1),
|
||||
clk_div.eq(clk_div.reset),
|
||||
).Else(
|
||||
clk_div.eq(clk_div-1),
|
||||
)
|
||||
)
|
||||
|
||||
]
|
||||
|
||||
@ResetInserter()
|
||||
@CEInserter()
|
||||
class SERDES_10bits(Module):
|
||||
def __init__(self, pad):
|
||||
self.oe = Signal()
|
||||
self.d = Signal(10)
|
||||
|
||||
# # #
|
||||
|
||||
o = Signal()
|
||||
tx_bitcount = Signal(max=10)
|
||||
tx_reg = Signal(10)
|
||||
|
||||
# DEBUG:
|
||||
self.o = Signal()
|
||||
self.comb += self.o.eq(o)
|
||||
|
||||
self.specials += Instance("OBUF", i_I=o, o_O=pad),
|
||||
|
||||
self.sync += [
|
||||
If(self.oe,
|
||||
# send LSB first
|
||||
o.eq(tx_reg[0]),
|
||||
tx_reg.eq(Cat(tx_reg[1:], 0)),
|
||||
tx_bitcount.eq(tx_bitcount + 1),
|
||||
|
||||
If(tx_bitcount == 9,
|
||||
tx_bitcount.eq(0),
|
||||
tx_reg.eq(self.d),
|
||||
),
|
||||
).Else(
|
||||
o.eq(0),
|
||||
tx_bitcount.eq(0),
|
||||
)
|
||||
]
|
||||
|
||||
class Debug_buffer(Module,AutoCSR):
|
||||
def __init__(self, layout):
|
||||
self.sink_stb = Signal()
|
||||
self.sink_ack = Signal()
|
||||
self.sink_data = Signal(8)
|
||||
self.sink_k = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.buf_out = buf_out = stream.SyncFIFO(layout, 512)
|
||||
|
||||
self.sync += [
|
||||
buf_out.sink.stb.eq(self.sink_stb),
|
||||
self.sink_ack.eq(buf_out.sink.ack),
|
||||
buf_out.sink.data.eq(self.sink_data),
|
||||
buf_out.sink.k.eq(self.sink_k),
|
||||
]
|
||||
|
||||
self.inc = CSR()
|
||||
self.dout_pak = CSRStatus(8)
|
||||
self.kout_pak = CSRStatus()
|
||||
self.dout_valid = CSRStatus()
|
||||
|
||||
self.sync += [
|
||||
# output
|
||||
buf_out.source.ack.eq(self.inc.re),
|
||||
self.dout_pak.status.eq(buf_out.source.data),
|
||||
self.kout_pak.status.eq(buf_out.source.k),
|
||||
self.dout_valid.status.eq(buf_out.source.stb),
|
||||
]
|
||||
|
||||
|
||||
class Transmitter(Module, AutoCSR):
|
||||
def __init__(self, pad, sys_clk_freq, debug_sma, pmod_pads):
|
||||
self.bitrate2x_enable = Signal()
|
||||
self.clk_reset = Signal()
|
||||
self.tx_enable = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.sink = stream.Endpoint(char_layout)
|
||||
|
||||
self.submodules.cg = cg = UpConn_ClockGen(sys_clk_freq)
|
||||
self.submodules.encoder = encoder = SingleEncoder(True)
|
||||
self.submodules.debug_buf = debug_buf = Debug_buffer(char_layout)
|
||||
|
||||
oe = Signal()
|
||||
self.sync += [
|
||||
If(self.tx_enable,
|
||||
self.sink.ack.eq(0),
|
||||
|
||||
# DEBUG:
|
||||
debug_buf.sink_stb.eq(0),
|
||||
|
||||
If(cg.clk,
|
||||
oe.eq(1),
|
||||
encoder.disp_in.eq(encoder.disp_out),
|
||||
self.sink.ack.eq(1),
|
||||
encoder.d.eq(self.sink.data),
|
||||
encoder.k.eq(self.sink.k),
|
||||
|
||||
# DEBUG:
|
||||
If(debug_buf.sink_ack,
|
||||
debug_buf.sink_stb.eq(1),
|
||||
debug_buf.sink_data.eq(self.sink.data),
|
||||
debug_buf.sink_k.eq(self.sink.k),
|
||||
)
|
||||
)
|
||||
).Else(
|
||||
# DEBUG:
|
||||
debug_buf.sink_stb.eq(0),
|
||||
|
||||
# no backpressure
|
||||
self.sink.ack.eq(1),
|
||||
oe.eq(0),
|
||||
)
|
||||
]
|
||||
|
||||
self.submodules.serdes = serdes = SERDES_10bits(pad)
|
||||
|
||||
self.comb += [
|
||||
cg.reset.eq(self.clk_reset),
|
||||
cg.freq2x_enable.eq(self.bitrate2x_enable),
|
||||
|
||||
serdes.reset.eq(self.clk_reset),
|
||||
serdes.ce.eq(cg.clk_10x),
|
||||
serdes.d.eq(encoder.output),
|
||||
serdes.oe.eq(oe),
|
||||
]
|
||||
|
||||
# DEBUG: remove pads
|
||||
|
||||
self.specials += [
|
||||
# # debug sma
|
||||
# Instance("OBUF", i_I=serdes.o, o_O=debug_sma.p_tx),
|
||||
Instance("OBUF", i_I=serdes.o, o_O=debug_sma.n_rx),
|
||||
|
||||
|
||||
|
||||
# # pmod 0-7 pin
|
||||
# Instance("OBUF", i_I=serdes.o, o_O=pmod_pads[0]),
|
||||
# Instance("OBUF", i_I=cg.clk_10x, o_O=pmod_pads[1]),
|
||||
# Instance("OBUF", i_I=~tx_fifos.pe.n, o_O=pmod_pads[2]),
|
||||
# Instance("OBUF", i_I=prioity_0, o_O=pmod_pads[3]),
|
||||
# Instance("OBUF", i_I=word_bound, o_O=pmod_pads[4]),
|
||||
# Instance("OBUF", i_I=debug_buf.buf_out.sink.stb, o_O=pmod_pads[4]),
|
||||
# Instance("OBUF", i_I=debug_buf.buf_out.sink.ack, o_O=pmod_pads[5]),
|
||||
# Instance("OBUF", i_I=debug_buf.buf_out.source.stb, o_O=pmod_pads[6]),
|
||||
# Instance("OBUF", i_I=debug_buf.buf_out.source.ack, o_O=pmod_pads[7]),
|
||||
|
||||
# Instance("OBUF", i_I=scheduler.idling, o_O=pmod_pads[5]),
|
||||
# # Instance("OBUF", i_I=tx_fifos.source_ack[0], o_O=pmod[6]),
|
||||
# # Instance("OBUF", i_I=tx_fifos.source_ack[2], o_O=pmod[6]),
|
||||
# # Instance("OBUF", i_I=tx_fifos.source_ack[1], o_O=pmod[7]),
|
||||
# Instance("OBUF", i_I=p0, o_O=pmod_pads[6]),
|
||||
# Instance("OBUF", i_I=p3, o_O=pmod_pads[7]),
|
||||
]
|
||||
|
||||
class CXP_UpConn_PHYS(Module, AutoCSR):
|
||||
def __init__(self, pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||
self.clk_reset = CSR()
|
||||
self.bitrate2x_enable = CSRStorage()
|
||||
self.tx_enable = CSRStorage()
|
||||
|
||||
# # #
|
||||
|
||||
|
||||
self.tx_phys = []
|
||||
for i, pad in enumerate(pads):
|
||||
tx = Transmitter(pad, sys_clk_freq, debug_sma, pmod_pads)
|
||||
self.tx_phys.append(tx)
|
||||
setattr(self.submodules, "tx"+str(i), tx)
|
||||
self.sync += [
|
||||
tx.clk_reset.eq(self.clk_reset.re),
|
||||
tx.bitrate2x_enable.eq(self.bitrate2x_enable.storage),
|
||||
tx.tx_enable.eq(self.tx_enable.storage),
|
||||
]
|
|
@ -1,307 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
|
||||
import analyzer
|
||||
import dma
|
||||
from artiq.gateware import rtio
|
||||
from artiq.gateware.rtio.phy import spi2, ttl_simple
|
||||
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
||||
from config import write_csr_file, write_mem_file, write_rustc_cfg_file
|
||||
from migen import *
|
||||
from migen.build.generic_platform import IOStandard, Misc, Pins, Subsignal
|
||||
from migen.build.platforms import ebaz4205
|
||||
from migen_axi.integration.soc_core import SoCCore
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
_ps = [
|
||||
(
|
||||
"ps",
|
||||
0,
|
||||
Subsignal("clk", Pins("E7"), IOStandard("LVCMOS33"), Misc("SLEW=FAST")),
|
||||
Subsignal("por_b", Pins("C7"), IOStandard("LVCMOS33"), Misc("SLEW=FAST")),
|
||||
Subsignal("srst_b", Pins("B10"), IOStandard("LVCMOS18"), Misc("SLEW=FAST")),
|
||||
)
|
||||
]
|
||||
|
||||
_ddr = [
|
||||
(
|
||||
"ddr",
|
||||
0,
|
||||
Subsignal(
|
||||
"a",
|
||||
Pins("N2 K2 M3 K3 M4 L1 L4 K4 K1 J4 F5 G4 E4 D4 F4"),
|
||||
IOStandard("SSTL15"),
|
||||
),
|
||||
Subsignal("ba", Pins("L5 R4 J5"), IOStandard("SSTL15")),
|
||||
Subsignal("cas_n", Pins("P5"), IOStandard("SSTL15")),
|
||||
Subsignal("cke", Pins("N3"), IOStandard("SSTL15")),
|
||||
Subsignal("cs_n", Pins("N1"), IOStandard("SSTL15")),
|
||||
Subsignal("ck_n", Pins("M2"), IOStandard("DIFF_SSTL15"), Misc("SLEW=FAST")),
|
||||
Subsignal("ck_p", Pins("L2"), IOStandard("DIFF_SSTL15"), Misc("SLEW=FAST")),
|
||||
# Pins "T1 Y1" not connected
|
||||
Subsignal("dm", Pins("A1 F1"), IOStandard("SSTL15_T_DCI"), Misc("SLEW=FAST")),
|
||||
Subsignal(
|
||||
"dq",
|
||||
Pins("C3 B3 A2 A4 D3 D1 C1 E1 E2 E3 G3 H3 J3 H2 H1 J1"),
|
||||
# Pins "P1 P3 R3 R1 T4 U4 U2 U3 V1 Y3 W1 Y4 Y2 W3 V2 V3" not connected
|
||||
IOStandard("SSTL15_T_DCI"),
|
||||
Misc("SLEW=FAST"),
|
||||
),
|
||||
Subsignal(
|
||||
"dqs_n",
|
||||
Pins("B2 F2"), # Pins "T2 W4" not connected
|
||||
IOStandard("DIFF_SSTL15_T_DCI"),
|
||||
Misc("SLEW=FAST"),
|
||||
),
|
||||
Subsignal(
|
||||
"dqs_p",
|
||||
Pins("C2 G2"), # Pins "R2 W5" not connected
|
||||
IOStandard("DIFF_SSTL15_T_DCI"),
|
||||
Misc("SLEW=FAST"),
|
||||
),
|
||||
Subsignal("vrn", Pins("G5"), IOStandard("SSTL15_T_DCI"), Misc("SLEW=FAST")),
|
||||
Subsignal("vrp", Pins("H5"), IOStandard("SSTL15_T_DCI"), Misc("SLEW=FAST")),
|
||||
Subsignal("drst_n", Pins("B4"), IOStandard("SSTL15"), Misc("SLEW=FAST")),
|
||||
Subsignal("odt", Pins("N5"), IOStandard("SSTL15")),
|
||||
Subsignal("ras_n", Pins("P4"), IOStandard("SSTL15")),
|
||||
Subsignal("we_n", Pins("M5"), IOStandard("SSTL15")),
|
||||
)
|
||||
]
|
||||
|
||||
# Connector J3
|
||||
_i2c = [
|
||||
(
|
||||
"i2c",
|
||||
0,
|
||||
Subsignal("scl", Pins("U12"), IOStandard("LVCMOS33")),
|
||||
Subsignal("sda", Pins("V13"), IOStandard("LVCMOS33")),
|
||||
)
|
||||
]
|
||||
|
||||
_spi = [
|
||||
(
|
||||
"spi",
|
||||
0,
|
||||
Subsignal("clk", Pins("V20")),
|
||||
Subsignal("mosi", Pins("U20")),
|
||||
Subsignal("cs_n", Pins("P19")),
|
||||
IOStandard("LVCMOS33"),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
# Connector DATA1
|
||||
def _create_ttl():
|
||||
_ttl = []
|
||||
|
||||
for idx, elem in enumerate([x for x in range(5, 21) if x not in (10, 12)]):
|
||||
_ttl.append(
|
||||
("ttl", idx, Pins("DATA1:DATA1-{}".format(elem)), IOStandard("LVCMOS33")),
|
||||
)
|
||||
return _ttl
|
||||
|
||||
|
||||
class EBAZ4205(SoCCore):
|
||||
def __init__(self, rtio_clk=125e6, acpki=False):
|
||||
self.acpki = acpki
|
||||
|
||||
platform = ebaz4205.Platform()
|
||||
platform.toolchain.bitstream_commands.extend(
|
||||
[
|
||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||
]
|
||||
)
|
||||
platform.add_extension(_ps)
|
||||
platform.add_extension(_ddr)
|
||||
platform.add_extension(_i2c)
|
||||
platform.add_extension(_spi)
|
||||
platform.add_extension(_create_ttl())
|
||||
|
||||
gmii = platform.request("gmii")
|
||||
platform.add_period_constraint(gmii.rx_clk, 10)
|
||||
platform.add_period_constraint(gmii.tx_clk, 10)
|
||||
platform.add_platform_command(
|
||||
"set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets gmii_tx_clk_IBUF]"
|
||||
)
|
||||
|
||||
ident = self.__class__.__name__
|
||||
if self.acpki:
|
||||
ident = "acpki_" + ident
|
||||
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
||||
fix_serdes_timing_path(platform)
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk / 1e6)
|
||||
platform.add_period_constraint(self.ps7.cd_sys.clk, 10)
|
||||
|
||||
self.comb += [
|
||||
self.ps7.enet0.enet.gmii.tx_clk.eq(gmii.tx_clk),
|
||||
self.ps7.enet0.enet.gmii.rx_clk.eq(gmii.rx_clk),
|
||||
]
|
||||
self.clock_domains.cd_eth_rx = ClockDomain(reset_less=False)
|
||||
self.clock_domains.cd_eth_tx = ClockDomain(reset_less=False)
|
||||
self.comb += [
|
||||
ClockSignal("eth_rx").eq(gmii.rx_clk),
|
||||
ClockSignal("eth_tx").eq(gmii.tx_clk),
|
||||
]
|
||||
self.sync.eth_tx += [
|
||||
gmii.txd.eq(self.ps7.enet0.enet.gmii.txd),
|
||||
gmii.tx_en.eq(self.ps7.enet0.enet.gmii.tx_en),
|
||||
]
|
||||
self.sync.eth_rx += [
|
||||
self.ps7.enet0.enet.gmii.rxd.eq(gmii.rxd),
|
||||
self.ps7.enet0.enet.gmii.rx_dv.eq(gmii.rx_dv),
|
||||
]
|
||||
|
||||
# MDIO
|
||||
mdio = platform.request("mdio")
|
||||
self.comb += mdio.mdc.eq(self.ps7.enet0.enet.mdio.mdc)
|
||||
self.specials += Instance(
|
||||
"IOBUF",
|
||||
i_I=self.ps7.enet0.enet.mdio.o,
|
||||
io_IO=mdio.mdio,
|
||||
o_O=self.ps7.enet0.enet.mdio.i,
|
||||
i_T=~self.ps7.enet0.enet.mdio.t_n,
|
||||
)
|
||||
|
||||
# I2C
|
||||
i2c = self.platform.request("i2c")
|
||||
self.specials += [
|
||||
# SCL
|
||||
Instance(
|
||||
"IOBUF",
|
||||
i_I=self.ps7.i2c0.scl.o,
|
||||
io_IO=i2c.scl,
|
||||
o_O=self.ps7.i2c0.scl.i,
|
||||
i_T=~self.ps7.i2c0.scl.t_n,
|
||||
),
|
||||
# SDA
|
||||
Instance(
|
||||
"IOBUF",
|
||||
i_I=self.ps7.i2c0.sda.o,
|
||||
io_IO=i2c.sda,
|
||||
o_O=self.ps7.i2c0.sda.i,
|
||||
i_T=~self.ps7.i2c0.sda.t_n,
|
||||
),
|
||||
]
|
||||
|
||||
self.rtio_channels = []
|
||||
for i in (0, 1):
|
||||
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||
user_led = self.platform.request("user_led", i)
|
||||
phy = ttl_simple.Output(user_led)
|
||||
self.submodules += phy
|
||||
self.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
for i in range(14):
|
||||
print("TTL at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||
ttl = self.platform.request("ttl", i)
|
||||
phy = ttl_simple.InOut(ttl)
|
||||
self.submodules += phy
|
||||
self.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
print("SPI at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||
spi_phy = spi2.SPIMaster(platform.request("spi"))
|
||||
self.submodules += spi_phy
|
||||
self.rtio_channels.append(rtio.Channel.from_phy(spi_phy, ififo_depth=4))
|
||||
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||
self.rtio_channels.append(rtio.LogChannel())
|
||||
|
||||
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
||||
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
|
||||
self.csr_devices.append("rtio_core")
|
||||
if self.acpki:
|
||||
import acpki
|
||||
|
||||
self.config["KI_IMPL"] = "acp"
|
||||
self.submodules.rtio = acpki.KernelInitiator(
|
||||
self.rtio_tsc,
|
||||
bus=self.ps7.s_axi_acp,
|
||||
user=self.ps7.s_axi_acp_user,
|
||||
evento=self.ps7.event.o,
|
||||
)
|
||||
self.csr_devices.append("rtio")
|
||||
else:
|
||||
self.config["KI_IMPL"] = "csr"
|
||||
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
|
||||
self.csr_devices.append("rtio")
|
||||
|
||||
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
|
||||
self.csr_devices.append("rtio_dma")
|
||||
|
||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||
[self.rtio.cri, self.rtio_dma.cri],
|
||||
[self.rtio_core.cri],
|
||||
enable_routing=True,
|
||||
)
|
||||
self.csr_devices.append("cri_con")
|
||||
|
||||
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
|
||||
self.csr_devices.append("rtio_moninj")
|
||||
|
||||
self.submodules.rtio_analyzer = analyzer.Analyzer(
|
||||
self.rtio_tsc, self.rtio_core.cri, self.ps7.s_axi_hp1
|
||||
)
|
||||
self.csr_devices.append("rtio_analyzer")
|
||||
|
||||
|
||||
class BASE(EBAZ4205):
|
||||
def __init__(self, rtio_clk, acpki):
|
||||
EBAZ4205.__init__(self, rtio_clk, acpki)
|
||||
|
||||
|
||||
VARIANTS = {cls.__name__.lower(): cls for cls in [BASE]}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="ARTIQ port to the EBAZ4205 control card of Ebit E9+ BTC miner"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r", default=None, help="build Rust interface into the specified file"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m", default=None, help="build Rust memory interface into the specified file"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
default=None,
|
||||
help="build Rust compiler configuration into the specified file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-g", default=None, help="build gateware into the specified directory"
|
||||
)
|
||||
parser.add_argument("--rtio-clk", default=125e6, help="RTIO Clock Frequency (Hz)")
|
||||
parser.add_argument(
|
||||
"-V",
|
||||
"--variant",
|
||||
default="base",
|
||||
help="variant: " "[acpki_]base" "(default: %(default)s)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
rtio_clk = int(args.rtio_clk)
|
||||
variant = args.variant.lower()
|
||||
acpki = variant.startswith("acpki_")
|
||||
if acpki:
|
||||
variant = variant[6:]
|
||||
|
||||
try:
|
||||
cls = VARIANTS[variant]
|
||||
except KeyError:
|
||||
raise SystemExit("Invalid variant (-V/--variant)")
|
||||
|
||||
soc = cls(rtio_clk=rtio_clk, acpki=acpki)
|
||||
soc.finalize()
|
||||
|
||||
if args.r is not None:
|
||||
write_csr_file(soc, args.r)
|
||||
if args.m is not None:
|
||||
write_mem_file(soc, args.m)
|
||||
if args.c is not None:
|
||||
write_rustc_cfg_file(soc, args.c)
|
||||
if args.g is not None:
|
||||
soc.build(build_dir=args.g)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -25,6 +25,8 @@ import analyzer
|
|||
import acpki
|
||||
import drtio_aux_controller
|
||||
import zynq_clocking
|
||||
import cxp_4r_fmc
|
||||
import cxp
|
||||
from config import write_csr_file, write_mem_file, write_rustc_cfg_file
|
||||
|
||||
class SMAClkinForward(Module):
|
||||
|
@ -138,7 +140,7 @@ class ZC706(SoCCore):
|
|||
platform.add_extension(si5324_fmc33)
|
||||
self.comb += platform.request("si5324_33").rst_n.eq(1)
|
||||
|
||||
cdr_clk = Signal()
|
||||
self.cdr_clk = Signal()
|
||||
cdr_clk_buf = Signal()
|
||||
si5324_out = platform.request("si5324_clkout")
|
||||
platform.add_period_constraint(si5324_out.p, 8.0)
|
||||
|
@ -146,11 +148,11 @@ class ZC706(SoCCore):
|
|||
Instance("IBUFDS_GTE2",
|
||||
i_CEB=0,
|
||||
i_I=si5324_out.p, i_IB=si5324_out.n,
|
||||
o_O=cdr_clk,
|
||||
o_O=self.cdr_clk,
|
||||
p_CLKCM_CFG="TRUE",
|
||||
p_CLKRCV_TRST="TRUE",
|
||||
p_CLKSWING_CFG=3),
|
||||
Instance("BUFG", i_I=cdr_clk, o_O=cdr_clk_buf)
|
||||
Instance("BUFG", i_I=self.cdr_clk, o_O=cdr_clk_buf)
|
||||
]
|
||||
self.config["HAS_SI5324"] = None
|
||||
self.config["SI5324_AS_SYNTHESIZER"] = None
|
||||
|
@ -652,6 +654,129 @@ class _NIST_QC2_RTIO:
|
|||
self.add_rtio(rtio_channels)
|
||||
|
||||
|
||||
class CXP_FMC():
|
||||
"""
|
||||
CoaXpress FMC with 4 CXP channel and 1 SMA trigger
|
||||
"""
|
||||
def __init__(self):
|
||||
platform = self.platform
|
||||
platform.add_extension(cxp_4r_fmc.fmc_adapter_io)
|
||||
platform.add_extension(leds_fmc33)
|
||||
|
||||
debug_sma = [
|
||||
("user_sma_clock_33", 0,
|
||||
Subsignal("p_tx", Pins("AD18"), IOStandard("LVCMOS33")),
|
||||
Subsignal("n_rx", Pins("AD19"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
]
|
||||
|
||||
pmod1_33 = [
|
||||
("pmod1_33", 0, Pins("AJ21"), IOStandard("LVCMOS33")),
|
||||
("pmod1_33", 1, Pins("AK21"), IOStandard("LVCMOS33")),
|
||||
("pmod1_33", 2, Pins("AB21"), IOStandard("LVCMOS33")),
|
||||
("pmod1_33", 3, Pins("AB16"), IOStandard("LVCMOS33")),
|
||||
("pmod1_33", 4, Pins("Y20"), IOStandard("LVCMOS33")),
|
||||
("pmod1_33", 5, Pins("AA20"), IOStandard("LVCMOS33")),
|
||||
("pmod1_33", 6, Pins("AC18"), IOStandard("LVCMOS33")),
|
||||
("pmod1_33", 7, Pins("AC19"), IOStandard("LVCMOS33")),
|
||||
]
|
||||
|
||||
platform.add_extension(debug_sma)
|
||||
platform.add_extension(pmod1_33)
|
||||
debug_sma_pad = platform.request("user_sma_clock_33")
|
||||
pmod_pads = [platform.request("pmod1_33", i) for i in range(8)]
|
||||
|
||||
clk_freq = 125e6
|
||||
|
||||
links = 1
|
||||
cxp_downconn_pads = [platform.request("CXP_HS", i) for i in range(links)]
|
||||
cxp_upconn_pads = [platform.request("CXP_LS", i) for i in range(links)]
|
||||
|
||||
|
||||
self.submodules.cxp_phys = cxp_phys = cxp.CXP_PHYS(
|
||||
refclk=self.cdr_clk,
|
||||
upconn_pads=cxp_upconn_pads,
|
||||
downconn_pads=cxp_downconn_pads,
|
||||
sys_clk_freq=clk_freq,
|
||||
debug_sma=debug_sma_pad,
|
||||
pmod_pads = pmod_pads
|
||||
)
|
||||
self.csr_devices.append("cxp_phys")
|
||||
|
||||
|
||||
rtio_channels = []
|
||||
cxp_csr_group = []
|
||||
cxp_tx_mem_group = []
|
||||
cxp_rx_mem_group = []
|
||||
cxp_loopback_mem_group = []
|
||||
for i, (tx, rx) in enumerate(zip(cxp_phys.upconn.tx_phys, cxp_phys.downconn.rx_phys)):
|
||||
cxp_name = "cxp" + str(i)
|
||||
|
||||
cdr = ClockDomainsRenamer({"cxp_gtx_rx": "cxp_gtx_rx" + str(i)})
|
||||
|
||||
if i == 0:
|
||||
cxp_interface = cdr(cxp.CXP_Master(tx, rx, debug_sma_pad, pmod_pads))
|
||||
|
||||
# Add rtlink for Master Connection only
|
||||
print("CoaXPress at RTIO channel 0x{:06x}".format(len(rtio_channels)))
|
||||
rtio_channels.append(rtio.Channel.from_phy(cxp_interface))
|
||||
|
||||
else:
|
||||
cxp_interface = cdr(cxp.CXP_Extension(tx, rx, debug_sma_pad, pmod_pads))
|
||||
|
||||
setattr(self.submodules, cxp_name, cxp_interface)
|
||||
self.csr_devices.append(cxp_name)
|
||||
cxp_csr_group.append(cxp_name)
|
||||
|
||||
|
||||
|
||||
# Add memory group
|
||||
rx_mem_name = "cxp_rx" + str(i) + "_mem"
|
||||
rx_mem_size = cxp_interface.get_rx_mem_size()
|
||||
cxp_rx_mem_group.append(rx_mem_name)
|
||||
memory_address = self.axi2csr.register_port(cxp_interface.get_rx_port(), rx_mem_size)
|
||||
self.add_memory_region(rx_mem_name, self.mem_map["csr"] + memory_address, rx_mem_size)
|
||||
|
||||
tx_mem_name = "cxp_tx" + str(i) + "_mem"
|
||||
tx_mem_size = cxp_interface.get_tx_mem_size()
|
||||
cxp_tx_mem_group.append(tx_mem_name)
|
||||
memory_address = self.axi2csr.register_port(cxp_interface.get_tx_port(), tx_mem_size)
|
||||
self.add_memory_region(tx_mem_name, self.mem_map["csr"] + memory_address, tx_mem_size)
|
||||
|
||||
# DEBUG loopback tx memory
|
||||
loopback_mem_name = "cxp_loopback_tx" + str(i) + "_mem"
|
||||
loopback_mem_size = cxp_interface.get_loopback_tx_mem_size()
|
||||
cxp_loopback_mem_group.append(loopback_mem_name)
|
||||
memory_address = self.axi2csr.register_port(cxp_interface.get_loopback_tx_port(), loopback_mem_size)
|
||||
self.add_memory_region(loopback_mem_name, self.mem_map["csr"] + memory_address, loopback_mem_size)
|
||||
|
||||
self.add_memory_group("cxp_tx_mem", cxp_tx_mem_group)
|
||||
self.add_memory_group("cxp_rx_mem", cxp_rx_mem_group)
|
||||
self.add_memory_group("cxp_loopback_mem", cxp_loopback_mem_group)
|
||||
self.add_csr_group("cxp", cxp_csr_group)
|
||||
|
||||
# max freq of cxp_gtx_rx = linerate/internal_datawidth = 12.5Gbps/40 = 312.5MHz
|
||||
# zc706 use speed grade 2 which only support up to 10.3125Gbps (4ns)
|
||||
# pushing to 12.5Gbps (3.2ns) will result in Pulse width violation but setup/hold times are met
|
||||
for rx in cxp_phys.downconn.rx_phys :
|
||||
platform.add_period_constraint(rx.gtx.cd_cxp_gtx_tx.clk, 3.2)
|
||||
platform.add_period_constraint(rx.gtx.cd_cxp_gtx_rx.clk, 3.2)
|
||||
# constraint the CLK path
|
||||
platform.add_false_path_constraints(self.sys_crg.cd_sys.clk, rx.gtx.cd_cxp_gtx_tx.clk, rx.gtx.cd_cxp_gtx_rx.clk)
|
||||
|
||||
# FIXME remove this placeholder RTIO channel
|
||||
# There are too few RTIO channels and cannot be compiled (adr width issue of the lane distributor)
|
||||
# see https://github.com/m-labs/artiq/pull/2158 for similar issue
|
||||
print("USER LED at RTIO channel 0x{:06x}".format(len(rtio_channels)))
|
||||
phy = ttl_simple.Output(self.platform.request("user_led_33", 0))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
self.config["HAS_RTIO_LOG"] = None
|
||||
rtio_channels.append(rtio.LogChannel())
|
||||
|
||||
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
|
||||
self.add_rtio(rtio_channels)
|
||||
|
||||
class NIST_CLOCK(ZC706, _NIST_CLOCK_RTIO):
|
||||
def __init__(self, acpki, drtio100mhz):
|
||||
ZC706.__init__(self, acpki)
|
||||
|
@ -684,8 +809,13 @@ class NIST_QC2_Satellite(_SatelliteBase, _NIST_QC2_RTIO):
|
|||
_SatelliteBase.__init__(self, acpki, drtio100mhz)
|
||||
_NIST_QC2_RTIO.__init__(self)
|
||||
|
||||
class CXP_Demo(ZC706, CXP_FMC):
|
||||
def __init__(self, acpki, drtio100mhz):
|
||||
ZC706.__init__(self, acpki)
|
||||
CXP_FMC.__init__(self)
|
||||
|
||||
VARIANTS = {cls.__name__.lower(): cls for cls in [NIST_CLOCK, NIST_CLOCK_Master, NIST_CLOCK_Satellite,
|
||||
NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite]}
|
||||
NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite, CXP_Demo]}
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
|
|
|
@ -10,7 +10,6 @@ name = "libboard_artiq"
|
|||
[features]
|
||||
target_zc706 = ["libboard_zynq/target_zc706", "libconfig/target_zc706"]
|
||||
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
|
||||
target_ebaz4205 = ["libboard_zynq/target_ebaz4205", "libconfig/target_ebaz4205"]
|
||||
calibrate_wrpll_skew = []
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -24,6 +23,7 @@ core_io = { version = "0.1", features = ["collections"] }
|
|||
embedded-hal = "0.2"
|
||||
nb = "1.0"
|
||||
void = { version = "1", default-features = false }
|
||||
byteorder = { version = "1.3", default-features = false }
|
||||
|
||||
io = { path = "../libio", features = ["byteorder"] }
|
||||
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq" }
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
|
||||
use libboard_zynq::{println, timer::GlobalTimer};
|
||||
use log::info;
|
||||
|
||||
use crate::{cxp_phys, cxp_proto, pl::csr::CXP};
|
||||
|
||||
pub fn loopback_testing(channel: usize, timer: &mut GlobalTimer, speed: cxp_phys::CXP_SPEED) {
|
||||
println!("==============================================================================");
|
||||
cxp_phys::change_linerate(speed);
|
||||
|
||||
unsafe {
|
||||
info!("waiting for tx&rx setup...");
|
||||
timer.delay_us(50_000);
|
||||
info!(
|
||||
"tx_phaligndone = {} | rx_phaligndone = {}",
|
||||
(CXP[channel].downconn_txinit_phaligndone_read)(),
|
||||
(CXP[channel].downconn_rxinit_phaligndone_read)(),
|
||||
);
|
||||
|
||||
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
|
||||
(CXP[channel].downconn_txenable_write)(1);
|
||||
|
||||
info!("waiting for rx to align...");
|
||||
while (CXP[channel].downconn_rx_ready_read)() != 1 {}
|
||||
info!("rx ready!");
|
||||
|
||||
cxp_proto::downconn_send_test_packet(channel);
|
||||
|
||||
// FIXME: why test + trig ack doesn't work well for rx??
|
||||
cxp_proto::downconn_debug_send_trig_ack(channel);
|
||||
|
||||
const DATA_MAXSIZE: usize = 253;
|
||||
let data_size = 4; // no. of bytes
|
||||
|
||||
let data: u32 = 0xDADA as u32;
|
||||
let mut data_slice: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
|
||||
data_slice[..4].clone_from_slice(&data.to_be_bytes());
|
||||
cxp_proto::downconn_debug_send(
|
||||
channel,
|
||||
&cxp_proto::UpConnPacket::Event {
|
||||
conn_id: 0x1234_5678_u32,
|
||||
packet_tag: 0x69_u8,
|
||||
length: data_size + 3,
|
||||
event_size: data_size,
|
||||
namespace: 0x02_u8,
|
||||
event_id: 0x00_6969u16,
|
||||
timestamp: 0x1234_5678u64,
|
||||
data: data_slice,
|
||||
},
|
||||
)
|
||||
.expect("loopback gtx tx error");
|
||||
|
||||
timer.delay_us(1000); // wait packet has arrive at RX async fifo
|
||||
|
||||
if (CXP[channel].downconn_trigger_ack_read)() == 1 {
|
||||
(CXP[channel].downconn_trigger_ack_write)(1);
|
||||
info!("trig ack and cleared");
|
||||
}
|
||||
|
||||
if (CXP[channel].downconn_bootstrap_decoder_err_read)() == 1 {
|
||||
info!("!!!!!!!DECODER ERROR!!!!!!! and cleared");
|
||||
(CXP[channel].downconn_bootstrap_decoder_err_write)(1);
|
||||
}
|
||||
|
||||
if (CXP[channel].downconn_bootstrap_test_err_read)() == 1 {
|
||||
info!("!!!!!!!TEST ERROR!!!!!!! and cleared");
|
||||
(CXP[channel].downconn_bootstrap_test_err_write)(1);
|
||||
}
|
||||
|
||||
info!("packet type = {:#06X}", (CXP[channel].downconn_packet_type_read)());
|
||||
|
||||
cxp_proto::receive(channel).expect("loopback gtx rx error");
|
||||
// cxp_proto::downconn_debug_mem_print(channel);
|
||||
|
||||
// DEBUG: print loopback packets
|
||||
const LEN: usize = 20;
|
||||
let mut pak_arr: [u32; LEN] = [0; LEN];
|
||||
let mut k_arr: [u8; LEN] = [0; LEN];
|
||||
let mut i: usize = 0;
|
||||
while (CXP[channel].downconn_debug_out_dout_valid_read)() == 1 {
|
||||
pak_arr[i] = (CXP[channel].downconn_debug_out_dout_pak_read)();
|
||||
k_arr[i] = (CXP[channel].downconn_debug_out_kout_pak_read)();
|
||||
// println!("received {:#04X}", pak_arr[i]);
|
||||
(CXP[channel].downconn_debug_out_inc_write)(1);
|
||||
i += 1;
|
||||
if i == LEN {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info!("rx ready = {}", (CXP[channel].downconn_rx_ready_read)());
|
||||
// cxp_proto::print_packetu32(&pak_arr, &k_arr);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,491 @@
|
|||
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
|
||||
use libboard_zynq::{println, timer::GlobalTimer};
|
||||
use log::info;
|
||||
|
||||
use crate::pl::{csr, csr::CXP};
|
||||
|
||||
const CHANNEL_LEN: usize = csr::CXP_LEN;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum CXP_SPEED {
|
||||
CXP_1,
|
||||
CXP_2,
|
||||
CXP_3,
|
||||
CXP_5,
|
||||
CXP_6,
|
||||
CXP_10,
|
||||
CXP_12,
|
||||
}
|
||||
|
||||
pub fn setup(timer: &mut GlobalTimer) {
|
||||
down_conn::setup(timer);
|
||||
up_conn::setup();
|
||||
change_linerate(CXP_SPEED::CXP_1);
|
||||
}
|
||||
|
||||
pub fn change_linerate(speed: CXP_SPEED) {
|
||||
info!("Changing all channels datarate to {:?}", speed);
|
||||
down_conn::change_linerate(speed);
|
||||
up_conn::change_linerate(speed);
|
||||
}
|
||||
|
||||
mod up_conn {
|
||||
use super::*;
|
||||
|
||||
pub fn setup() {
|
||||
unsafe {
|
||||
csr::cxp_phys::upconn_tx_enable_write(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_linerate(speed: CXP_SPEED) {
|
||||
unsafe {
|
||||
match speed {
|
||||
CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => {
|
||||
csr::cxp_phys::upconn_bitrate2x_enable_write(0);
|
||||
}
|
||||
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => {
|
||||
csr::cxp_phys::upconn_bitrate2x_enable_write(1);
|
||||
}
|
||||
};
|
||||
csr::cxp_phys::upconn_clk_reset_write(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod down_conn {
|
||||
use super::*;
|
||||
|
||||
pub fn setup(timer: &mut GlobalTimer) {
|
||||
unsafe {
|
||||
info!("turning on pmc loopback mode...");
|
||||
for channel in 0..CHANNEL_LEN {
|
||||
(CXP[channel].downconn_loopback_mode_write)(0b010); // Near-End PMA Loopback
|
||||
}
|
||||
|
||||
// QPLL setup
|
||||
csr::cxp_phys::downconn_qpll_reset_write(1);
|
||||
info!("waiting for QPLL/CPLL to lock...");
|
||||
while csr::cxp_phys::downconn_qpll_locked_read() != 1 {}
|
||||
info!("QPLL locked");
|
||||
|
||||
for channel in 0..CHANNEL_LEN {
|
||||
// tx/rx setup
|
||||
(CXP[channel].downconn_tx_start_init_write)(1);
|
||||
(CXP[channel].downconn_rx_start_init_write)(1);
|
||||
}
|
||||
|
||||
// DEBUG: printout
|
||||
info!("waiting for tx & rx setup...");
|
||||
timer.delay_us(50_000);
|
||||
for channel in 0..CHANNEL_LEN {
|
||||
info!(
|
||||
"tx_phaligndone = {} | rx_phaligndone = {}",
|
||||
(CXP[channel].downconn_txinit_phaligndone_read)(),
|
||||
(CXP[channel].downconn_rxinit_phaligndone_read)(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_linerate(speed: CXP_SPEED) {
|
||||
// DEBUG: DRP pll for TXUSRCLK = freq(linerate)/20
|
||||
let settings = txusrclk::get_txusrclk_config(speed);
|
||||
txusrclk::setup(settings);
|
||||
|
||||
change_qpll_fb_divider(speed);
|
||||
change_gtx_divider(speed);
|
||||
change_cdr_cfg(speed);
|
||||
|
||||
unsafe {
|
||||
csr::cxp_phys::downconn_qpll_reset_write(1);
|
||||
info!("waiting for QPLL/CPLL to lock...");
|
||||
while csr::cxp_phys::downconn_qpll_locked_read() != 1 {}
|
||||
info!("QPLL locked");
|
||||
|
||||
for channel in 0..CHANNEL_LEN {
|
||||
(CXP[channel].downconn_tx_restart_write)(1);
|
||||
(CXP[channel].downconn_rx_restart_write)(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change_qpll_fb_divider(speed: CXP_SPEED) {
|
||||
let qpll_div_reg = match speed {
|
||||
CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_10 => 0x0120, // FB_Divider = 80
|
||||
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x0170, // FB_Divider = 100
|
||||
};
|
||||
|
||||
println!("0x36 = {:#06x}", qpll_read(0x36));
|
||||
qpll_write(0x36, qpll_div_reg);
|
||||
println!("0x36 = {:#06x}", qpll_read(0x36));
|
||||
}
|
||||
|
||||
fn change_gtx_divider(speed: CXP_SPEED) {
|
||||
let div_reg = match speed {
|
||||
CXP_SPEED::CXP_1 => 0x33, // RXOUT_DIV = 8
|
||||
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0x22, // RXOUT_DIV = 4
|
||||
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0x11, // RXOUT_DIV = 2
|
||||
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0x00, // RXOUT_DIV = 1
|
||||
};
|
||||
|
||||
for channel in 0..CHANNEL_LEN {
|
||||
println!("channel {}, 0x88 = {:#06x}", channel, gtx_read(channel, 0x88));
|
||||
gtx_write(channel, 0x88, div_reg);
|
||||
println!("channel {}, 0x88 = {:#06x}", channel, gtx_read(channel, 0x88));
|
||||
}
|
||||
}
|
||||
|
||||
fn change_cdr_cfg(speed: CXP_SPEED) {
|
||||
struct CdrConfig {
|
||||
pub cfg_reg0: u16, // addr = 0xA8
|
||||
pub cfg_reg1: u16, // addr = 0xA9
|
||||
pub cfg_reg2: u16, // addr = 0xAA
|
||||
pub cfg_reg3: u16, // addr = 0xAB
|
||||
pub cfg_reg4: u16, // addr = 0xAC
|
||||
}
|
||||
|
||||
let cdr_cfg = match speed {
|
||||
// when RXOUT_DIV = 8
|
||||
CXP_SPEED::CXP_1 => CdrConfig {
|
||||
cfg_reg0: 0x0020,
|
||||
cfg_reg1: 0x1008,
|
||||
cfg_reg2: 0x23FF,
|
||||
cfg_reg3: 0x0000,
|
||||
cfg_reg4: 0x0003,
|
||||
},
|
||||
// when RXOUT_DIV = 4
|
||||
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 => CdrConfig {
|
||||
cfg_reg0: 0x0020,
|
||||
cfg_reg1: 0x1010,
|
||||
cfg_reg2: 0x23FF,
|
||||
cfg_reg3: 0x0000,
|
||||
cfg_reg4: 0x0003,
|
||||
},
|
||||
// when RXOUT_DIV= 2
|
||||
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 => CdrConfig {
|
||||
cfg_reg0: 0x0020,
|
||||
cfg_reg1: 0x1020,
|
||||
cfg_reg2: 0x23FF,
|
||||
cfg_reg3: 0x0000,
|
||||
cfg_reg4: 0x0003,
|
||||
},
|
||||
// when RXOUT_DIV= 1
|
||||
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => CdrConfig {
|
||||
cfg_reg0: 0x0020,
|
||||
cfg_reg1: 0x1040,
|
||||
cfg_reg2: 0x23FF,
|
||||
cfg_reg3: 0x0000,
|
||||
cfg_reg4: 0x000B,
|
||||
},
|
||||
};
|
||||
|
||||
for channel in 0..CHANNEL_LEN {
|
||||
gtx_write(channel, 0x0A8, cdr_cfg.cfg_reg0);
|
||||
gtx_write(channel, 0x0A9, cdr_cfg.cfg_reg1);
|
||||
gtx_write(channel, 0x0AA, cdr_cfg.cfg_reg2);
|
||||
gtx_write(channel, 0x0AB, cdr_cfg.cfg_reg3);
|
||||
gtx_write(channel, 0x0AC, cdr_cfg.cfg_reg4);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn gtx_read(channel: usize, address: u16) -> u16 {
|
||||
unsafe {
|
||||
(CXP[channel].downconn_gtx_daddr_write)(address);
|
||||
(CXP[channel].downconn_gtx_dread_write)(1);
|
||||
while (CXP[channel].downconn_gtx_dready_read)() != 1 {}
|
||||
(CXP[channel].downconn_gtx_dout_read)()
|
||||
}
|
||||
}
|
||||
|
||||
fn gtx_write(channel: usize, address: u16, value: u16) {
|
||||
unsafe {
|
||||
(CXP[channel].downconn_gtx_daddr_write)(address);
|
||||
(CXP[channel].downconn_gtx_din_write)(value);
|
||||
(CXP[channel].downconn_gtx_din_stb_write)(1);
|
||||
while (CXP[channel].downconn_gtx_dready_read)() != 1 {}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn qpll_read(address: u8) -> u16 {
|
||||
unsafe {
|
||||
csr::cxp_phys::downconn_qpll_daddr_write(address);
|
||||
csr::cxp_phys::downconn_qpll_dread_write(1);
|
||||
while csr::cxp_phys::downconn_qpll_dready_read() != 1 {}
|
||||
csr::cxp_phys::downconn_qpll_dout_read()
|
||||
}
|
||||
}
|
||||
|
||||
fn qpll_write(address: u8, value: u16) {
|
||||
unsafe {
|
||||
csr::cxp_phys::downconn_qpll_daddr_write(address);
|
||||
csr::cxp_phys::downconn_qpll_din_write(value);
|
||||
csr::cxp_phys::downconn_qpll_din_stb_write(1);
|
||||
while csr::cxp_phys::downconn_qpll_dready_read() != 1 {}
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG: remove this
|
||||
pub mod txusrclk {
|
||||
use super::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PLLSetting {
|
||||
pub clkout0_reg1: u16, //0x08
|
||||
pub clkout0_reg2: u16, //0x09
|
||||
pub clkfbout_reg1: u16, //0x14
|
||||
pub clkfbout_reg2: u16, //0x15
|
||||
pub div_reg: u16, //0x16
|
||||
pub lock_reg1: u16, //0x18
|
||||
pub lock_reg2: u16, //0x19
|
||||
pub lock_reg3: u16, //0x1A
|
||||
pub power_reg: u16, //0x28
|
||||
pub filt_reg1: u16, //0x4E
|
||||
pub filt_reg2: u16, //0x4F
|
||||
}
|
||||
|
||||
fn one_clock_cycle(channel: usize) {
|
||||
unsafe {
|
||||
(CXP[channel].downconn_pll_dclk_write)(1);
|
||||
(CXP[channel].downconn_pll_dclk_write)(0);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_addr(channel: usize, address: u8) {
|
||||
unsafe {
|
||||
(CXP[channel].downconn_pll_daddr_write)(address);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_data(channel: usize, value: u16) {
|
||||
unsafe {
|
||||
(CXP[channel].downconn_pll_din_write)(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_enable(channel: usize, en: bool) {
|
||||
unsafe {
|
||||
let val = if en { 1 } else { 0 };
|
||||
(CXP[channel].downconn_pll_den_write)(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_write_enable(channel: usize, en: bool) {
|
||||
unsafe {
|
||||
let val = if en { 1 } else { 0 };
|
||||
(CXP[channel].downconn_pll_dwen_write)(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_data(channel: usize) -> u16 {
|
||||
unsafe { (CXP[channel].downconn_pll_dout_read)() }
|
||||
}
|
||||
|
||||
fn drp_ready(channel: usize) -> bool {
|
||||
unsafe { (CXP[channel].downconn_pll_dready_read)() == 1 }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn read(channel: usize, address: u8) -> u16 {
|
||||
set_addr(channel, address);
|
||||
set_enable(channel, true);
|
||||
// Set DADDR on the mmcm and assert DEN for one clock cycle
|
||||
one_clock_cycle(channel);
|
||||
|
||||
set_enable(channel, false);
|
||||
while !drp_ready(channel) {
|
||||
// keep the clock signal until data is ready
|
||||
one_clock_cycle(channel);
|
||||
}
|
||||
get_data(channel)
|
||||
}
|
||||
|
||||
fn write(channel: usize, address: u8, value: u16) {
|
||||
set_addr(channel, address);
|
||||
set_data(channel, value);
|
||||
set_write_enable(channel, true);
|
||||
set_enable(channel, true);
|
||||
// Set DADDR, DI on the mmcm and assert DWE, DEN for one clock cycle
|
||||
one_clock_cycle(channel);
|
||||
|
||||
set_write_enable(channel, false);
|
||||
set_enable(channel, false);
|
||||
while !drp_ready(channel) {
|
||||
// keep the clock signal until write is finished
|
||||
one_clock_cycle(channel);
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(channel: usize, rst: bool) {
|
||||
unsafe {
|
||||
let val = if rst { 1 } else { 0 };
|
||||
(CXP[channel].downconn_txpll_reset_write)(val)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup(settings: PLLSetting) {
|
||||
for channel in 0..CHANNEL_LEN {
|
||||
if false {
|
||||
info!("0x08 = {:#06x}", read(channel, 0x08));
|
||||
info!("0x09 = {:#06x}", read(channel, 0x09));
|
||||
info!("0x14 = {:#06x}", read(channel, 0x14));
|
||||
info!("0x15 = {:#06x}", read(channel, 0x15));
|
||||
info!("0x16 = {:#06x}", read(channel, 0x16));
|
||||
info!("0x18 = {:#06x}", read(channel, 0x18));
|
||||
info!("0x19 = {:#06x}", read(channel, 0x19));
|
||||
info!("0x1A = {:#06x}", read(channel, 0x1A));
|
||||
info!("0x28 = {:#06x}", read(channel, 0x28));
|
||||
info!("0x4E = {:#06x}", read(channel, 0x4E));
|
||||
info!("0x4F = {:#06x}", read(channel, 0x4F));
|
||||
} else {
|
||||
// Based on "DRP State Machine" from XAPP888
|
||||
// hold reset HIGH during pll config
|
||||
reset(channel, true);
|
||||
write(channel, 0x08, settings.clkout0_reg1);
|
||||
write(channel, 0x09, settings.clkout0_reg2);
|
||||
write(channel, 0x14, settings.clkfbout_reg1);
|
||||
write(channel, 0x15, settings.clkfbout_reg2);
|
||||
write(channel, 0x16, settings.div_reg);
|
||||
write(channel, 0x18, settings.lock_reg1);
|
||||
write(channel, 0x19, settings.lock_reg2);
|
||||
write(channel, 0x1A, settings.lock_reg3);
|
||||
write(channel, 0x28, settings.power_reg);
|
||||
write(channel, 0x4E, settings.filt_reg1);
|
||||
write(channel, 0x4F, settings.filt_reg2);
|
||||
reset(channel, false);
|
||||
|
||||
info!("waiting for PLL of txusrclk to lock...");
|
||||
while unsafe { (CXP[channel].downconn_txpll_locked_read)() == 0 } {}
|
||||
info!("txusrclk locked :D");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_txusrclk_config(speed: CXP_SPEED) -> PLLSetting {
|
||||
match speed {
|
||||
CXP_SPEED::CXP_1 => {
|
||||
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 32
|
||||
// TXUSRCLK=62.5MHz
|
||||
PLLSetting {
|
||||
clkout0_reg1: 0x1410, //0x08
|
||||
clkout0_reg2: 0x0000, //0x09
|
||||
clkfbout_reg1: 0x1104, //0x14
|
||||
clkfbout_reg2: 0x0000, //0x15
|
||||
div_reg: 0x1041, //0x16
|
||||
lock_reg1: 0x03e8, //0x18
|
||||
lock_reg2: 0x5801, //0x19
|
||||
lock_reg3: 0xdbe9, //0x1A
|
||||
power_reg: 0x0000, //0x28
|
||||
filt_reg1: 0x9808, //0x4E
|
||||
filt_reg2: 0x9100, //0x4F
|
||||
}
|
||||
}
|
||||
CXP_SPEED::CXP_2 => {
|
||||
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 16
|
||||
// TXUSRCLK=62.5MHz
|
||||
PLLSetting {
|
||||
clkout0_reg1: 0x1208, //0x08
|
||||
clkout0_reg2: 0x0000, //0x09
|
||||
clkfbout_reg1: 0x1104, //0x14
|
||||
clkfbout_reg2: 0x0000, //0x15
|
||||
div_reg: 0x1041, //0x16
|
||||
lock_reg1: 0x03e8, //0x18
|
||||
lock_reg2: 0x5801, //0x19
|
||||
lock_reg3: 0xdbe9, //0x1A
|
||||
power_reg: 0x0000, //0x28
|
||||
filt_reg1: 0x9808, //0x4E
|
||||
filt_reg2: 0x9100, //0x4F
|
||||
}
|
||||
}
|
||||
CXP_SPEED::CXP_3 => {
|
||||
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 16
|
||||
// TXUSRCLK=78.125MHz
|
||||
PLLSetting {
|
||||
clkout0_reg1: 0x1208, //0x08
|
||||
clkout0_reg2: 0x0000, //0x09
|
||||
clkfbout_reg1: 0x1145, //0x14
|
||||
clkfbout_reg2: 0x0000, //0x15
|
||||
div_reg: 0x1041, //0x16
|
||||
lock_reg1: 0x03e8, //0x18
|
||||
lock_reg2: 0x7001, //0x19
|
||||
lock_reg3: 0xf3e9, //0x1A
|
||||
power_reg: 0x0000, //0x28
|
||||
filt_reg1: 0x9908, //0x4E
|
||||
filt_reg2: 0x1900, //0x4F
|
||||
}
|
||||
}
|
||||
CXP_SPEED::CXP_5 => {
|
||||
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
|
||||
// TXUSRCLK=125MHz
|
||||
PLLSetting {
|
||||
clkout0_reg1: 0x1104, //0x08
|
||||
clkout0_reg2: 0x0000, //0x09
|
||||
clkfbout_reg1: 0x1104, //0x14
|
||||
clkfbout_reg2: 0x0000, //0x15
|
||||
div_reg: 0x1041, //0x16
|
||||
lock_reg1: 0x03e8, //0x18
|
||||
lock_reg2: 0x5801, //0x19
|
||||
lock_reg3: 0xdbe9, //0x1A
|
||||
power_reg: 0x0000, //0x28
|
||||
filt_reg1: 0x9808, //0x4E
|
||||
filt_reg2: 0x9100, //0x4F
|
||||
}
|
||||
}
|
||||
CXP_SPEED::CXP_6 => {
|
||||
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
|
||||
// TXUSRCLK=156.25MHz
|
||||
PLLSetting {
|
||||
clkout0_reg1: 0x1104, //0x08
|
||||
clkout0_reg2: 0x0000, //0x09
|
||||
clkfbout_reg1: 0x1145, //0x14
|
||||
clkfbout_reg2: 0x0000, //0x15
|
||||
div_reg: 0x1041, //0x16
|
||||
lock_reg1: 0x03e8, //0x18
|
||||
lock_reg2: 0x7001, //0x19
|
||||
lock_reg3: 0xf3e9, //0x1A
|
||||
power_reg: 0x0000, //0x28
|
||||
filt_reg1: 0x9908, //0x4E
|
||||
filt_reg2: 0x1900, //0x4F
|
||||
}
|
||||
}
|
||||
CXP_SPEED::CXP_10 => {
|
||||
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
|
||||
// TXUSRCLK=250MHz
|
||||
PLLSetting {
|
||||
clkout0_reg1: 0x1082, //0x08
|
||||
clkout0_reg2: 0x0000, //0x09
|
||||
clkfbout_reg1: 0x1104, //0x14
|
||||
clkfbout_reg2: 0x0000, //0x15
|
||||
div_reg: 0x1041, //0x16
|
||||
lock_reg1: 0x03e8, //0x18
|
||||
lock_reg2: 0x5801, //0x19
|
||||
lock_reg3: 0xdbe9, //0x1A
|
||||
power_reg: 0x0000, //0x28
|
||||
filt_reg1: 0x9808, //0x4E
|
||||
filt_reg2: 0x9100, //0x4F
|
||||
}
|
||||
}
|
||||
CXP_SPEED::CXP_12 => {
|
||||
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
|
||||
// TXUSRCLK=312.5MHz
|
||||
PLLSetting {
|
||||
clkout0_reg1: 0x1082, //0x08
|
||||
clkout0_reg2: 0x0000, //0x09
|
||||
clkfbout_reg1: 0x1145, //0x14
|
||||
clkfbout_reg2: 0x0000, //0x15
|
||||
div_reg: 0x1041, //0x16
|
||||
lock_reg1: 0x03e8, //0x18
|
||||
lock_reg2: 0x7001, //0x19
|
||||
lock_reg3: 0xf3e9, //0x1A
|
||||
power_reg: 0x0000, //0x28
|
||||
filt_reg1: 0x9908, //0x4E
|
||||
filt_reg2: 0x1900, //0x4F
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,608 @@
|
|||
use core::slice;
|
||||
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core_io::{Error as IoError, Read, Write};
|
||||
use crc::crc32::checksum_ieee;
|
||||
use io::Cursor;
|
||||
use libboard_zynq::println;
|
||||
|
||||
use crate::{mem::mem::{CXP_LOOPBACK_MEM, CXP_RX_MEM, CXP_TX_MEM},
|
||||
pl::{csr, csr::CXP}};
|
||||
|
||||
const BUF_LEN: usize = 0x800;
|
||||
const DATA_MAXSIZE: usize = 48;
|
||||
const EV_MAXSIZE: usize = 253;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
BufferError,
|
||||
CorruptedPacket,
|
||||
CtrlAckError(u8),
|
||||
LinkDown,
|
||||
UnknownPacket(u8),
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(_: IoError) -> Error {
|
||||
Error::BufferError
|
||||
}
|
||||
}
|
||||
|
||||
// Section 9.2.2.2 (CXP-001-2021)
|
||||
// Only Control packet need CRC32 appended in the end of the packet
|
||||
// CoaXpress use the polynomial of IEEE-802.3 (Ethernet) CRC but the checksum calculation is different
|
||||
fn get_cxp_crc(bytes: &[u8]) -> u32 {
|
||||
(!checksum_ieee(bytes)).swap_bytes()
|
||||
}
|
||||
|
||||
trait CxpRead {
|
||||
fn read_u8(&mut self) -> Result<u8, Error>;
|
||||
|
||||
fn read_u16(&mut self) -> Result<u16, Error>;
|
||||
|
||||
fn read_u32(&mut self) -> Result<u32, Error>;
|
||||
|
||||
fn read_u64(&mut self) -> Result<u64, Error>;
|
||||
|
||||
fn read_exact_4x(&mut self, buf: &mut [u8]) -> Result<(), Error>;
|
||||
|
||||
fn read_4x_u8(&mut self) -> Result<u8, Error>;
|
||||
|
||||
fn read_4x_u16(&mut self) -> Result<u16, Error>;
|
||||
|
||||
fn read_4x_u32(&mut self) -> Result<u32, Error>;
|
||||
|
||||
fn read_4x_u64(&mut self) -> Result<u64, Error>;
|
||||
}
|
||||
impl<Cursor: Read> CxpRead for Cursor {
|
||||
fn read_u8(&mut self) -> Result<u8, Error> {
|
||||
let mut bytes = [0; 1];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(bytes[0])
|
||||
}
|
||||
|
||||
fn read_u16(&mut self) -> Result<u16, Error> {
|
||||
let mut bytes = [0; 2];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u16(&bytes))
|
||||
}
|
||||
|
||||
fn read_u32(&mut self) -> Result<u32, Error> {
|
||||
let mut bytes = [0; 4];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u32(&bytes))
|
||||
}
|
||||
|
||||
fn read_u64(&mut self) -> Result<u64, Error> {
|
||||
let mut bytes = [0; 8];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u64(&bytes))
|
||||
}
|
||||
|
||||
fn read_exact_4x(&mut self, buf: &mut [u8]) -> Result<(), Error> {
|
||||
for byte in buf {
|
||||
// Section 9.2.2.1 (CXP-001-2021)
|
||||
// decoder should immune to single bit errors when handling 4x duplicated characters
|
||||
let a = self.read_u8()?;
|
||||
let b = self.read_u8()?;
|
||||
let c = self.read_u8()?;
|
||||
let d = self.read_u8()?;
|
||||
// vote and return majority
|
||||
*byte = a & b & c | a & b & d | a & c & d | b & c & d;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_4x_u8(&mut self) -> Result<u8, Error> {
|
||||
let mut bytes = [0; 1];
|
||||
self.read_exact_4x(&mut bytes)?;
|
||||
Ok(bytes[0])
|
||||
}
|
||||
|
||||
fn read_4x_u16(&mut self) -> Result<u16, Error> {
|
||||
let mut bytes = [0; 2];
|
||||
self.read_exact_4x(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u16(&bytes))
|
||||
}
|
||||
|
||||
fn read_4x_u32(&mut self) -> Result<u32, Error> {
|
||||
let mut bytes = [0; 4];
|
||||
self.read_exact_4x(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u32(&bytes))
|
||||
}
|
||||
|
||||
fn read_4x_u64(&mut self) -> Result<u64, Error> {
|
||||
let mut bytes = [0; 6];
|
||||
self.read_exact_4x(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u64(&bytes))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NameSpace {
|
||||
GenICam,
|
||||
DeviceSpecific,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DownConnPacket {
|
||||
CtrlReply {
|
||||
tag: Option<u8>,
|
||||
length: u32,
|
||||
data: [u8; DATA_MAXSIZE],
|
||||
},
|
||||
CtrlDelay {
|
||||
tag: Option<u8>,
|
||||
length: u32,
|
||||
time: [u8; DATA_MAXSIZE],
|
||||
},
|
||||
CtrlAck {
|
||||
tag: Option<u8>,
|
||||
},
|
||||
Event {
|
||||
conn_id: u32,
|
||||
packet_tag: u8,
|
||||
length: u16,
|
||||
ev_size: u16,
|
||||
namespace: NameSpace,
|
||||
event_id: u16,
|
||||
timestamp: u64,
|
||||
ev: [u8; EV_MAXSIZE],
|
||||
},
|
||||
}
|
||||
|
||||
impl DownConnPacket {
|
||||
pub fn read_from(reader: &mut Cursor<&mut [u8]>, packet_type: u8) -> Result<Self, Error> {
|
||||
match packet_type {
|
||||
0x03 => DownConnPacket::get_ctrl_packet(reader, false),
|
||||
0x06 => DownConnPacket::get_ctrl_packet(reader, true),
|
||||
0x07 => DownConnPacket::get_event_packet(reader),
|
||||
_ => Err(Error::UnknownPacket(packet_type)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ctrl_packet(reader: &mut Cursor<&mut [u8]>, with_tag: bool) -> Result<Self, Error> {
|
||||
let mut tag: Option<u8> = None;
|
||||
if with_tag {
|
||||
tag = Some(reader.read_4x_u8()?);
|
||||
}
|
||||
|
||||
let ackcode = reader.read_4x_u8()?;
|
||||
|
||||
match ackcode {
|
||||
0x00 | 0x04 => {
|
||||
let length = reader.read_u32()?;
|
||||
let mut data: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
|
||||
reader.read(&mut data[0..length as usize])?;
|
||||
|
||||
let checksum = get_cxp_crc(&reader.get_ref()[0..reader.position()]);
|
||||
if reader.read_u32()? != checksum {
|
||||
return Err(Error::CorruptedPacket);
|
||||
}
|
||||
|
||||
if ackcode == 0x00 {
|
||||
return Ok(DownConnPacket::CtrlReply { tag, length, data });
|
||||
} else {
|
||||
return Ok(DownConnPacket::CtrlDelay {
|
||||
tag,
|
||||
length,
|
||||
time: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
0x01 => return Ok(DownConnPacket::CtrlAck { tag }),
|
||||
_ => return Err(Error::CtrlAckError(ackcode)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_event_packet(reader: &mut Cursor<&mut [u8]>) -> Result<Self, Error> {
|
||||
let conn_id = reader.read_4x_u32()?;
|
||||
let packet_tag = reader.read_4x_u8()?;
|
||||
let length = reader.read_4x_u16()?;
|
||||
|
||||
let ev_size = reader.read_u16()?;
|
||||
if ev_size + 3 != length {
|
||||
println!("length mismatch");
|
||||
return Err(Error::CorruptedPacket);
|
||||
}
|
||||
|
||||
let mut bytes = [0; 2];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
let namespace_bits = (bytes[0] & 0xC0) >> 6;
|
||||
let namespace = match namespace_bits {
|
||||
0 => NameSpace::GenICam,
|
||||
2 => NameSpace::DeviceSpecific,
|
||||
_ => {
|
||||
println!("namespace = {} error", namespace_bits);
|
||||
return Err(Error::CorruptedPacket);
|
||||
}
|
||||
};
|
||||
|
||||
let event_id = (bytes[0] & 0xF) as u16 | (bytes[1] as u16);
|
||||
|
||||
let timestamp = reader.read_u64()?;
|
||||
|
||||
let mut ev: [u8; EV_MAXSIZE] = [0; EV_MAXSIZE];
|
||||
reader.read(&mut ev[0..ev_size as usize])?;
|
||||
|
||||
let checksum = get_cxp_crc(&reader.get_ref()[0..reader.position()]);
|
||||
if reader.read_u32()? != checksum {
|
||||
println!("crc error");
|
||||
return Err(Error::CorruptedPacket);
|
||||
}
|
||||
|
||||
Ok(DownConnPacket::Event {
|
||||
conn_id,
|
||||
packet_tag,
|
||||
length,
|
||||
ev_size,
|
||||
namespace,
|
||||
event_id,
|
||||
timestamp,
|
||||
ev,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(channel: usize) -> Result<Option<DownConnPacket>, Error> {
|
||||
unsafe {
|
||||
if (CXP[channel].downconn_pending_packet_read)() == 1 {
|
||||
let read_buffer_ptr = (CXP[channel].downconn_read_ptr_read)() as usize;
|
||||
println!("buffer ptr = {}", read_buffer_ptr);
|
||||
let ptr = (CXP_RX_MEM[channel].base + read_buffer_ptr * BUF_LEN) as *mut u32;
|
||||
|
||||
let mut reader = Cursor::new(slice::from_raw_parts_mut(ptr as *mut u8, BUF_LEN));
|
||||
let packet_type = (CXP[channel].downconn_packet_type_read)();
|
||||
|
||||
let packet = DownConnPacket::read_from(&mut reader, packet_type);
|
||||
println!("{:X?}", packet);
|
||||
|
||||
(CXP[channel].downconn_pending_packet_write)(1);
|
||||
Ok(Some(packet?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait CxpWrite {
|
||||
fn write_all_4x(&mut self, buf: &[u8]) -> Result<(), Error>;
|
||||
|
||||
fn write_4x_u8(&mut self, value: u8) -> Result<(), Error>;
|
||||
|
||||
fn write_4x_u16(&mut self, value: u16) -> Result<(), Error>;
|
||||
|
||||
fn write_4x_u32(&mut self, value: u32) -> Result<(), Error>;
|
||||
|
||||
fn write_4x_u64(&mut self, value: u64) -> Result<(), Error>;
|
||||
|
||||
fn write_u32(&mut self, value: u32) -> Result<(), Error>;
|
||||
}
|
||||
impl<Cursor: Write> CxpWrite for Cursor {
|
||||
fn write_all_4x(&mut self, buf: &[u8]) -> Result<(), Error> {
|
||||
for byte in buf {
|
||||
self.write_all(&[*byte; 4])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_4x_u8(&mut self, value: u8) -> Result<(), Error> {
|
||||
self.write_all_4x(&[value])
|
||||
}
|
||||
|
||||
fn write_4x_u16(&mut self, value: u16) -> Result<(), Error> {
|
||||
let mut bytes = [0; 2];
|
||||
NetworkEndian::write_u16(&mut bytes, value);
|
||||
self.write_all_4x(&bytes)
|
||||
}
|
||||
|
||||
fn write_4x_u32(&mut self, value: u32) -> Result<(), Error> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_u32(&mut bytes, value);
|
||||
self.write_all_4x(&bytes)
|
||||
}
|
||||
|
||||
fn write_4x_u64(&mut self, value: u64) -> Result<(), Error> {
|
||||
let mut bytes = [0; 6];
|
||||
NetworkEndian::write_u64(&mut bytes, value);
|
||||
self.write_all_4x(&bytes)
|
||||
}
|
||||
|
||||
fn write_u32(&mut self, value: u32) -> Result<(), Error> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_u32(&mut bytes, value);
|
||||
self.write_all(&bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UpConnPacket {
|
||||
CtrlRead {
|
||||
tag: Option<u8>,
|
||||
addr: u32,
|
||||
length: u8,
|
||||
},
|
||||
CtrlWrite {
|
||||
tag: Option<u8>,
|
||||
addr: u32,
|
||||
length: u8,
|
||||
data: [u8; DATA_MAXSIZE],
|
||||
}, // max register size is 8 bytes
|
||||
EventAck {
|
||||
packet_tag: u8,
|
||||
},
|
||||
TestPacket,
|
||||
|
||||
// DEBUG: Loopback message
|
||||
CtrlAckLoopback {
|
||||
ackcode: u8,
|
||||
length: u8,
|
||||
data: [u8; DATA_MAXSIZE],
|
||||
},
|
||||
Event {
|
||||
conn_id: u32,
|
||||
packet_tag: u8,
|
||||
length: u16,
|
||||
event_size: u16,
|
||||
namespace: u8,
|
||||
event_id: u16,
|
||||
timestamp: u64,
|
||||
data: [u8; 253],
|
||||
},
|
||||
}
|
||||
|
||||
impl UpConnPacket {
|
||||
pub fn write_to(&self, writer: &mut Cursor<&mut [u8]>) -> Result<(), Error> {
|
||||
match *self {
|
||||
UpConnPacket::CtrlRead { tag, addr, length } => {
|
||||
match tag {
|
||||
Some(t) => {
|
||||
writer.write_4x_u8(0x05)?;
|
||||
writer.write_4x_u8(t)?;
|
||||
}
|
||||
None => {
|
||||
writer.write_4x_u8(0x02)?;
|
||||
}
|
||||
}
|
||||
writer.write_all(&[0x00, 0x00, 0x00, length])?;
|
||||
writer.write_u32(addr)?;
|
||||
|
||||
// Section 9.6.2 (CXP-001-2021)
|
||||
// only bytes after the first 4 are used in calculating the checksum
|
||||
let checksum = get_cxp_crc(&writer.get_ref()[4..writer.position()]);
|
||||
writer.write_u32(checksum)?;
|
||||
}
|
||||
UpConnPacket::CtrlWrite {
|
||||
tag,
|
||||
addr,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
match tag {
|
||||
Some(t) => {
|
||||
writer.write_4x_u8(0x05)?;
|
||||
writer.write_4x_u8(t)?;
|
||||
}
|
||||
None => {
|
||||
writer.write_4x_u8(0x02)?;
|
||||
}
|
||||
}
|
||||
writer.write_all(&[0x01, 0x00, 0x00, length])?;
|
||||
writer.write_u32(addr)?;
|
||||
writer.write_all(&data[0..length as usize])?;
|
||||
|
||||
// Section 9.6.2 (CXP-001-2021)
|
||||
// only bytes after the first 4 are used in calculating the checksum
|
||||
let checksum = get_cxp_crc(&writer.get_ref()[4..writer.position()]);
|
||||
writer.write_u32(checksum)?;
|
||||
}
|
||||
UpConnPacket::EventAck { packet_tag } => {
|
||||
writer.write_4x_u8(0x08)?;
|
||||
writer.write_4x_u8(packet_tag)?;
|
||||
}
|
||||
// DEBUG: Loopback message
|
||||
UpConnPacket::CtrlAckLoopback { ackcode, length, data } => {
|
||||
writer.write_4x_u8(0x03)?;
|
||||
writer.write_4x_u8(ackcode)?;
|
||||
|
||||
if ackcode == 0x00 || ackcode == 0x04 {
|
||||
writer.write_all(&[0x00, 0x00, 0x00, length])?;
|
||||
writer.write_all(&data[0..length as usize])?;
|
||||
}
|
||||
|
||||
let checksum = get_cxp_crc(&writer.get_ref()[4..writer.position()]);
|
||||
writer.write_u32(checksum)?;
|
||||
}
|
||||
UpConnPacket::Event {
|
||||
conn_id,
|
||||
packet_tag,
|
||||
length,
|
||||
event_size,
|
||||
namespace,
|
||||
event_id,
|
||||
timestamp,
|
||||
data,
|
||||
} => {
|
||||
// event packet header
|
||||
writer.write_4x_u8(0x07)?;
|
||||
writer.write_4x_u32(conn_id)?;
|
||||
writer.write_4x_u8(packet_tag)?;
|
||||
writer.write_4x_u16(length)?;
|
||||
|
||||
// event message
|
||||
let ev_size = event_size.to_be_bytes();
|
||||
let p2: u8 = ((namespace & 0b11) << 6) | ((event_id & 0xF00) >> 8) as u8;
|
||||
let p3: u8 = (event_id & 0xFF) as u8;
|
||||
writer.write_all(&[ev_size[0], ev_size[1], p2, p3])?;
|
||||
writer.write_all(×tamp.to_be_bytes())?;
|
||||
writer.write_all(&data[0..event_size as usize])?;
|
||||
|
||||
let checksum = get_cxp_crc(&writer.get_ref()[4..writer.position()]);
|
||||
writer.write_u32(checksum)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(channel: usize, packet: &UpConnPacket) -> Result<(), Error> {
|
||||
if unsafe { csr::cxp_phys::upconn_tx_enable_read() } == 0 {
|
||||
Err(Error::LinkDown)?
|
||||
}
|
||||
|
||||
match *packet {
|
||||
UpConnPacket::TestPacket => send_test_packet(channel),
|
||||
_ => send_data_packet(channel, packet),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_data_packet(channel: usize, packet: &UpConnPacket) -> Result<(), Error> {
|
||||
unsafe {
|
||||
while (CXP[channel].upconn_bootstrap_tx_busy_read)() == 1 {}
|
||||
let ptr = CXP_TX_MEM[0].base as *mut u32;
|
||||
let mut writer = Cursor::new(slice::from_raw_parts_mut(ptr as *mut u8, BUF_LEN));
|
||||
|
||||
packet.write_to(&mut writer)?;
|
||||
println!("TX MEM after writing");
|
||||
print_packet(&writer.get_ref()[0..40]);
|
||||
|
||||
(CXP[channel].upconn_bootstrap_tx_word_len_write)(writer.position() as u16 / 4);
|
||||
(CXP[channel].upconn_bootstrap_tx_write)(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_test_packet(channel: usize) -> Result<(), Error> {
|
||||
unsafe {
|
||||
while (CXP[channel].upconn_bootstrap_tx_busy_read)() == 1 {}
|
||||
(CXP[channel].upconn_bootstrap_tx_testseq_write)(1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_u32(channel: u8, addr: u32, val: u32) -> Result<(), Error> {
|
||||
// TODO: add tags after connection & verify it's CXPv2
|
||||
|
||||
let mut data: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
|
||||
NetworkEndian::write_u32(&mut data[..4], val);
|
||||
send(
|
||||
channel as usize,
|
||||
&UpConnPacket::CtrlWrite {
|
||||
tag: None,
|
||||
addr,
|
||||
length: 4,
|
||||
data,
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_u32(channel: u8, addr: u32) -> Result<(), Error> {
|
||||
// TODO: add tags after connection & verify it's CXPv2
|
||||
|
||||
send(
|
||||
channel as usize,
|
||||
&UpConnPacket::CtrlRead {
|
||||
tag: None,
|
||||
addr,
|
||||
length: 4,
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// pub fn write_u64(channel: usize, addr: u32, data: u64) -> Result<(), Error> {
|
||||
// let mut data_slice: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
|
||||
// data_slice[..8].clone_from_slice(&data.to_be_bytes());
|
||||
// send(
|
||||
// channel,
|
||||
// &UpConnPacket::CtrlWrite {
|
||||
// tag: None,
|
||||
// addr,
|
||||
// length: 8,
|
||||
// data: data_slice,
|
||||
// },
|
||||
// )?;
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
//
|
||||
// DEBUG: use only
|
||||
//
|
||||
//
|
||||
//
|
||||
pub fn print_packet(pak: &[u8]) {
|
||||
println!("pak = [");
|
||||
for i in 0..(pak.len() / 4) {
|
||||
println!(
|
||||
"{:#03} {:#04X} {:#04X} {:#04X} {:#04X},",
|
||||
i + 1,
|
||||
pak[i * 4],
|
||||
pak[i * 4 + 1],
|
||||
pak[i * 4 + 2],
|
||||
pak[i * 4 + 3]
|
||||
)
|
||||
}
|
||||
println!("]");
|
||||
println!("============================================");
|
||||
}
|
||||
|
||||
pub fn print_packetu32(pak: &[u32], k: &[u8]) {
|
||||
println!("pak = [");
|
||||
for i in 0..(pak.len()) {
|
||||
let data: [u8; 4] = pak[i].to_le_bytes();
|
||||
println!(
|
||||
"{:#03} {:#04X} {:#04X} {:#04X} {:#04X} | K {:04b},",
|
||||
i + 1,
|
||||
data[0],
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
k[i],
|
||||
)
|
||||
}
|
||||
println!("]");
|
||||
println!("============================================");
|
||||
}
|
||||
|
||||
pub fn downconn_debug_send(channel: usize, packet: &UpConnPacket) -> Result<(), Error> {
|
||||
unsafe {
|
||||
while (CXP[channel].downconn_bootstrap_loopback_tx_busy_read)() == 1 {}
|
||||
let ptr = CXP_LOOPBACK_MEM[0].base as *mut u32;
|
||||
let mut writer = Cursor::new(slice::from_raw_parts_mut(ptr as *mut u8, BUF_LEN));
|
||||
|
||||
packet.write_to(&mut writer)?;
|
||||
|
||||
(CXP[channel].downconn_bootstrap_loopback_tx_word_len_write)(writer.position() as u16 / 4);
|
||||
(CXP[channel].downconn_bootstrap_loopback_tx_write)(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn downconn_debug_mem_print(channel: usize) {
|
||||
unsafe {
|
||||
let ptr = CXP_RX_MEM[channel].base as *mut u32;
|
||||
let arr = slice::from_raw_parts_mut(ptr as *mut u8, BUF_LEN * 4);
|
||||
print_packet(arr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downconn_debug_send_trig_ack(channel: usize) {
|
||||
unsafe {
|
||||
(CXP[channel].downconn_ack_write)(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downconn_send_test_packet(channel: usize) {
|
||||
unsafe {
|
||||
while (CXP[channel].downconn_bootstrap_loopback_tx_busy_read)() == 1 {}
|
||||
(CXP[channel].downconn_bootstrap_loopback_tx_testseq_write)(1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
|
||||
use libboard_zynq::timer::GlobalTimer;
|
||||
|
||||
pub use crate::cxp_proto;
|
||||
use crate::pl::{csr, csr::CXP};
|
||||
|
||||
pub fn tx_test(channel: usize, timer: &mut GlobalTimer) {
|
||||
const LEN: usize = 4 * 30;
|
||||
let mut pak_arr: [u8; LEN] = [0; LEN];
|
||||
|
||||
unsafe {
|
||||
// cxp_proto::read_u32(channel, 0x00).expect("Cannot Write CoaXpress Register");
|
||||
// cxp_proto::write_u64(channel, 0x00, 0x01);
|
||||
// cxp_proto::send(channel, &cxp_proto::Packet::EventAck { packet_tag: 0x04 }).expect("Cannot send CoaXpress packet");
|
||||
// cxp_proto::send(channel, &cxp_proto::Packet::TestPacket).expect("Cannot send CoaXpress packet");
|
||||
|
||||
timer.delay_us(2); // send one word
|
||||
// DEBUG: Trigger packet
|
||||
(CXP[channel].upconn_trig_delay_write)(0x86);
|
||||
(CXP[channel].upconn_linktrigger_write)(0x00);
|
||||
(CXP[channel].upconn_trig_stb_write)(1); // send trig
|
||||
|
||||
// DEBUG: Trigger ACK packet
|
||||
// CXP[channel].upconn_ack_write(1);
|
||||
|
||||
timer.delay_us(20);
|
||||
csr::cxp_phys::upconn_tx_enable_write(0);
|
||||
|
||||
// Collect data
|
||||
let mut i: usize = 0;
|
||||
while csr::cxp_phys::upconn_tx0_debug_buf_dout_valid_read() == 1 {
|
||||
pak_arr[i] = csr::cxp_phys::upconn_tx0_debug_buf_dout_pak_read();
|
||||
csr::cxp_phys::upconn_tx0_debug_buf_inc_write(1);
|
||||
i += 1;
|
||||
if i == LEN {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cxp_proto::print_packet(&pak_arr);
|
||||
}
|
||||
}
|
|
@ -185,24 +185,6 @@ unsafe fn align_comma(timer: &mut GlobalTimer) {
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn align_wordslip(timer: &mut GlobalTimer, trx_no: u8) -> bool {
|
||||
pl::csr::eem_transceiver::transceiver_sel_write(trx_no);
|
||||
|
||||
for slip in 0..=1 {
|
||||
pl::csr::eem_transceiver::wordslip_write(slip as u8);
|
||||
timer.delay_us(1);
|
||||
pl::csr::eem_transceiver::comma_align_reset_write(1);
|
||||
timer.delay_us(100);
|
||||
|
||||
if pl::csr::eem_transceiver::comma_read() == 1 {
|
||||
debug!("comma alignment completed with {} wordslip", slip);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
||||
for trx_no in 0..pl::csr::CONFIG_EEM_DRTIO_COUNT {
|
||||
unsafe {
|
||||
|
@ -240,6 +222,7 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
|||
|
||||
unsafe {
|
||||
align_comma(timer);
|
||||
pl::csr::eem_transceiver::rx_ready_write(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -255,7 +255,6 @@ pub enum Packet {
|
|||
destination: u8,
|
||||
id: u32,
|
||||
run: bool,
|
||||
timestamp: u64,
|
||||
},
|
||||
SubkernelLoadRunReply {
|
||||
destination: u8,
|
||||
|
@ -288,77 +287,6 @@ pub enum Packet {
|
|||
SubkernelMessageAck {
|
||||
destination: u8,
|
||||
},
|
||||
|
||||
CoreMgmtGetLogRequest {
|
||||
destination: u8,
|
||||
clear: bool,
|
||||
},
|
||||
CoreMgmtClearLogRequest {
|
||||
destination: u8,
|
||||
},
|
||||
CoreMgmtSetLogLevelRequest {
|
||||
destination: u8,
|
||||
log_level: u8,
|
||||
},
|
||||
CoreMgmtSetUartLogLevelRequest {
|
||||
destination: u8,
|
||||
log_level: u8,
|
||||
},
|
||||
CoreMgmtConfigReadRequest {
|
||||
destination: u8,
|
||||
length: u16,
|
||||
key: [u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||
},
|
||||
CoreMgmtConfigReadContinue {
|
||||
destination: u8,
|
||||
},
|
||||
CoreMgmtConfigWriteRequest {
|
||||
destination: u8,
|
||||
last: bool,
|
||||
length: u16,
|
||||
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||
},
|
||||
CoreMgmtConfigRemoveRequest {
|
||||
destination: u8,
|
||||
length: u16,
|
||||
key: [u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||
},
|
||||
CoreMgmtConfigEraseRequest {
|
||||
destination: u8,
|
||||
},
|
||||
CoreMgmtRebootRequest {
|
||||
destination: u8,
|
||||
},
|
||||
CoreMgmtAllocatorDebugRequest {
|
||||
destination: u8,
|
||||
},
|
||||
CoreMgmtFlashRequest {
|
||||
destination: u8,
|
||||
payload_length: u32,
|
||||
},
|
||||
CoreMgmtFlashAddDataRequest {
|
||||
destination: u8,
|
||||
last: bool,
|
||||
length: u16,
|
||||
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||
},
|
||||
CoreMgmtDropLinkAck {
|
||||
destination: u8,
|
||||
},
|
||||
CoreMgmtDropLink,
|
||||
CoreMgmtGetLogReply {
|
||||
last: bool,
|
||||
length: u16,
|
||||
data: [u8; SAT_PAYLOAD_MAX_SIZE],
|
||||
},
|
||||
CoreMgmtConfigReadReply {
|
||||
last: bool,
|
||||
length: u16,
|
||||
value: [u8; SAT_PAYLOAD_MAX_SIZE],
|
||||
},
|
||||
CoreMgmtReply {
|
||||
succeeded: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
|
@ -586,7 +514,6 @@ impl Packet {
|
|||
destination: reader.read_u8()?,
|
||||
id: reader.read_u32()?,
|
||||
run: reader.read_bool()?,
|
||||
timestamp: reader.read_u64()?,
|
||||
},
|
||||
0xc5 => Packet::SubkernelLoadRunReply {
|
||||
destination: reader.read_u8()?,
|
||||
|
@ -636,115 +563,6 @@ impl Packet {
|
|||
destination: reader.read_u8()?,
|
||||
},
|
||||
|
||||
0xd0 => Packet::CoreMgmtGetLogRequest {
|
||||
destination: reader.read_u8()?,
|
||||
clear: reader.read_bool()?,
|
||||
},
|
||||
0xd1 => Packet::CoreMgmtClearLogRequest {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xd2 => Packet::CoreMgmtSetLogLevelRequest {
|
||||
destination: reader.read_u8()?,
|
||||
log_level: reader.read_u8()?,
|
||||
},
|
||||
0xd3 => Packet::CoreMgmtSetUartLogLevelRequest {
|
||||
destination: reader.read_u8()?,
|
||||
log_level: reader.read_u8()?,
|
||||
},
|
||||
0xd4 => {
|
||||
let destination = reader.read_u8()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut key[0..length as usize])?;
|
||||
Packet::CoreMgmtConfigReadRequest {
|
||||
destination: destination,
|
||||
length: length,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
0xd5 => Packet::CoreMgmtConfigReadContinue {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xd6 => {
|
||||
let destination = reader.read_u8()?;
|
||||
let last = reader.read_bool()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut data[0..length as usize])?;
|
||||
Packet::CoreMgmtConfigWriteRequest {
|
||||
destination: destination,
|
||||
last: last,
|
||||
length: length,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
0xd7 => {
|
||||
let destination = reader.read_u8()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut key[0..length as usize])?;
|
||||
Packet::CoreMgmtConfigRemoveRequest {
|
||||
destination: destination,
|
||||
length: length,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
0xd8 => Packet::CoreMgmtConfigEraseRequest {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xd9 => Packet::CoreMgmtRebootRequest {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xda => Packet::CoreMgmtAllocatorDebugRequest {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xdb => Packet::CoreMgmtFlashRequest {
|
||||
destination: reader.read_u8()?,
|
||||
payload_length: reader.read_u32()?,
|
||||
},
|
||||
0xdc => {
|
||||
let destination = reader.read_u8()?;
|
||||
let last = reader.read_bool()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut data[0..length as usize])?;
|
||||
Packet::CoreMgmtFlashAddDataRequest {
|
||||
destination: destination,
|
||||
last: last,
|
||||
length: length,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
0xdd => Packet::CoreMgmtDropLinkAck {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xde => Packet::CoreMgmtDropLink,
|
||||
0xdf => {
|
||||
let last = reader.read_bool()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut data: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut data[0..length as usize])?;
|
||||
Packet::CoreMgmtGetLogReply {
|
||||
last: last,
|
||||
length: length,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
0xe0 => {
|
||||
let last = reader.read_bool()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut value: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut value[0..length as usize])?;
|
||||
Packet::CoreMgmtConfigReadReply {
|
||||
last: last,
|
||||
length: length,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
0xe1 => Packet::CoreMgmtReply {
|
||||
succeeded: reader.read_bool()?,
|
||||
},
|
||||
|
||||
ty => return Err(Error::UnknownPacket(ty)),
|
||||
})
|
||||
}
|
||||
|
@ -1059,14 +877,12 @@ impl Packet {
|
|||
destination,
|
||||
id,
|
||||
run,
|
||||
timestamp,
|
||||
} => {
|
||||
writer.write_u8(0xc4)?;
|
||||
writer.write_u8(source)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u32(id)?;
|
||||
writer.write_bool(run)?;
|
||||
writer.write_u64(timestamp)?;
|
||||
}
|
||||
Packet::SubkernelLoadRunReply { destination, succeeded } => {
|
||||
writer.write_u8(0xc5)?;
|
||||
|
@ -1122,115 +938,6 @@ impl Packet {
|
|||
writer.write_u8(0xcc)?;
|
||||
writer.write_u8(destination)?;
|
||||
}
|
||||
|
||||
Packet::CoreMgmtGetLogRequest { destination, clear } => {
|
||||
writer.write_u8(0xd0)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_bool(clear)?;
|
||||
}
|
||||
Packet::CoreMgmtClearLogRequest { destination } => {
|
||||
writer.write_u8(0xd1)?;
|
||||
writer.write_u8(destination)?;
|
||||
}
|
||||
Packet::CoreMgmtSetLogLevelRequest { destination, log_level } => {
|
||||
writer.write_u8(0xd2)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u8(log_level)?;
|
||||
}
|
||||
Packet::CoreMgmtSetUartLogLevelRequest { destination, log_level } => {
|
||||
writer.write_u8(0xd3)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u8(log_level)?;
|
||||
}
|
||||
Packet::CoreMgmtConfigReadRequest {
|
||||
destination,
|
||||
length,
|
||||
key,
|
||||
} => {
|
||||
writer.write_u8(0xd4)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&key[0..length as usize])?;
|
||||
}
|
||||
Packet::CoreMgmtConfigReadContinue { destination } => {
|
||||
writer.write_u8(0xd5)?;
|
||||
writer.write_u8(destination)?;
|
||||
}
|
||||
Packet::CoreMgmtConfigWriteRequest {
|
||||
destination,
|
||||
last,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
writer.write_u8(0xd6)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_bool(last)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&data[0..length as usize])?;
|
||||
}
|
||||
Packet::CoreMgmtConfigRemoveRequest {
|
||||
destination,
|
||||
length,
|
||||
key,
|
||||
} => {
|
||||
writer.write_u8(0xd7)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&key[0..length as usize])?;
|
||||
}
|
||||
Packet::CoreMgmtConfigEraseRequest { destination } => {
|
||||
writer.write_u8(0xd8)?;
|
||||
writer.write_u8(destination)?;
|
||||
}
|
||||
Packet::CoreMgmtRebootRequest { destination } => {
|
||||
writer.write_u8(0xd9)?;
|
||||
writer.write_u8(destination)?;
|
||||
}
|
||||
Packet::CoreMgmtAllocatorDebugRequest { destination } => {
|
||||
writer.write_u8(0xda)?;
|
||||
writer.write_u8(destination)?;
|
||||
}
|
||||
Packet::CoreMgmtFlashRequest {
|
||||
destination,
|
||||
payload_length,
|
||||
} => {
|
||||
writer.write_u8(0xdb)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u32(payload_length)?;
|
||||
}
|
||||
Packet::CoreMgmtFlashAddDataRequest {
|
||||
destination,
|
||||
last,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
writer.write_u8(0xdc)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_bool(last)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&data[..length as usize])?;
|
||||
}
|
||||
Packet::CoreMgmtDropLinkAck { destination } => {
|
||||
writer.write_u8(0xdd)?;
|
||||
writer.write_u8(destination)?;
|
||||
}
|
||||
Packet::CoreMgmtDropLink => writer.write_u8(0xde)?,
|
||||
Packet::CoreMgmtGetLogReply { last, length, data } => {
|
||||
writer.write_u8(0xdf)?;
|
||||
writer.write_bool(last)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&data[0..length as usize])?;
|
||||
}
|
||||
Packet::CoreMgmtConfigReadReply { last, length, value } => {
|
||||
writer.write_u8(0xe0)?;
|
||||
writer.write_bool(last)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&value[0..length as usize])?;
|
||||
}
|
||||
Packet::CoreMgmtReply { succeeded } => {
|
||||
writer.write_u8(0xe1)?;
|
||||
writer.write_bool(succeeded)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1267,8 +974,7 @@ impl Packet {
|
|||
| Packet::SubkernelLoadRunReply { .. }
|
||||
| Packet::SubkernelMessageAck { .. }
|
||||
| Packet::DmaPlaybackStatus { .. }
|
||||
| Packet::SubkernelFinished { .. }
|
||||
| Packet::CoreMgmtDropLinkAck { .. } => false,
|
||||
| Packet::SubkernelFinished { .. } => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#![feature(naked_functions)]
|
||||
#![feature(asm)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate core_io;
|
||||
extern crate crc;
|
||||
extern crate embedded_hal;
|
||||
|
@ -25,7 +26,7 @@ pub mod fiq;
|
|||
#[cfg(feature = "target_kasli_soc")]
|
||||
pub mod io_expander;
|
||||
pub mod logger;
|
||||
#[cfg(has_drtio)]
|
||||
#[cfg(any(has_drtio, has_cxp_phys))]
|
||||
#[rustfmt::skip]
|
||||
#[path = "../../../build/mem.rs"]
|
||||
pub mod mem;
|
||||
|
@ -42,6 +43,15 @@ pub mod si5324;
|
|||
pub mod si549;
|
||||
use core::{cmp, str};
|
||||
|
||||
#[cfg(has_cxp_phys)]
|
||||
pub mod cxp_downconn;
|
||||
#[cfg(has_cxp_phys)]
|
||||
pub mod cxp_upconn;
|
||||
|
||||
pub mod cxp_proto;
|
||||
|
||||
pub mod cxp_phys;
|
||||
|
||||
pub fn identifier_read(buf: &mut [u8]) -> &str {
|
||||
unsafe {
|
||||
pl::csr::identifier::address_write(0);
|
||||
|
|
|
@ -85,7 +85,10 @@ unsafe fn get_ttype_entry(
|
|||
encoding | DW_EH_PE_pcrel,
|
||||
ttype_base,
|
||||
)
|
||||
.map(|v| (v != ttype_base).then(|| v as *const u8))
|
||||
.map(|v| match v {
|
||||
ttype_base => None,
|
||||
ttype_entry => Some(ttype_entry as *const u8),
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn find_eh_action(
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
use libboard_artiq::cxp_proto;
|
||||
|
||||
pub extern "C" fn cxp_readu32(channel: i32, addr: i32) {
|
||||
// TODO: use artiq_raise like i2c?
|
||||
cxp_proto::read_u32(channel as u8, addr as u32).expect("CXP transmission failed");
|
||||
}
|
||||
|
||||
pub extern "C" fn cxp_writeu32(channel: i32, addr: i32, val: i32) {
|
||||
// TODO: use artiq_raise like i2c?
|
||||
cxp_proto::write_u32(channel as u8, addr as u32, val as u32).expect("CXP transmission failed");
|
||||
}
|
|
@ -476,7 +476,7 @@ extern "C" fn stop_fn(
|
|||
}
|
||||
|
||||
// Must be kept in sync with preallocate_runtime_exception_names() in `artiq.compiler.embedding`
|
||||
static EXCEPTION_ID_LOOKUP: [(&str, u32); 22] = [
|
||||
static EXCEPTION_ID_LOOKUP: [(&str, u32); 20] = [
|
||||
("RTIOUnderflow", 0),
|
||||
("RTIOOverflow", 1),
|
||||
("RTIODestinationUnreachable", 2),
|
||||
|
@ -497,8 +497,6 @@ static EXCEPTION_ID_LOOKUP: [(&str, u32); 22] = [
|
|||
("TypeError", 17),
|
||||
("ValueError", 18),
|
||||
("ZeroDivisionError", 19),
|
||||
("LinAlgError", 20),
|
||||
("UnwrapNoneError", 21),
|
||||
];
|
||||
|
||||
pub fn get_exception_id(name: &str) -> u32 {
|
||||
|
|
|
@ -11,6 +11,8 @@ use super::{cache,
|
|||
core1::rtio_get_destination_status,
|
||||
dma, linalg,
|
||||
rpc::{rpc_recv, rpc_send, rpc_send_async}};
|
||||
#[cfg(has_cxp_phys)]
|
||||
use crate::cxp;
|
||||
use crate::{eh_artiq, i2c, rtio};
|
||||
|
||||
extern "C" {
|
||||
|
@ -126,6 +128,12 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
|||
#[cfg(has_drtio)]
|
||||
api!(subkernel_await_message = subkernel::await_message),
|
||||
|
||||
// CoaXPress
|
||||
#[cfg(has_cxp_phys)]
|
||||
api!(cxp_readu32 = cxp::cxp_readu32),
|
||||
#[cfg(has_cxp_phys)]
|
||||
api!(cxp_writeu32 = cxp::cxp_writeu32),
|
||||
|
||||
// Double-precision floating-point arithmetic helper functions
|
||||
// RTABI chapter 4.1.2, Table 2
|
||||
api!(__aeabi_dadd),
|
||||
|
|
|
@ -81,7 +81,6 @@ pub enum Message {
|
|||
id: u32,
|
||||
destination: u8,
|
||||
run: bool,
|
||||
timestamp: u64,
|
||||
},
|
||||
#[cfg(has_drtio)]
|
||||
SubkernelLoadRunReply {
|
||||
|
|
|
@ -3,7 +3,7 @@ use alloc::vec::Vec;
|
|||
use cslice::CSlice;
|
||||
|
||||
use super::{Message, SubkernelStatus, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0};
|
||||
use crate::{artiq_raise, eh_artiq, rpc::send_args, rtio::now_mu};
|
||||
use crate::{artiq_raise, eh_artiq, rpc::send_args};
|
||||
|
||||
pub extern "C" fn load_run(id: u32, destination: u8, run: bool) {
|
||||
unsafe {
|
||||
|
@ -14,7 +14,6 @@ pub extern "C" fn load_run(id: u32, destination: u8, run: bool) {
|
|||
id: id,
|
||||
destination: destination,
|
||||
run: run,
|
||||
timestamp: now_mu() as u64,
|
||||
});
|
||||
}
|
||||
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
|
||||
|
|
|
@ -35,6 +35,8 @@ pub mod rtio;
|
|||
#[path = "../../../build/pl.rs"]
|
||||
pub mod pl;
|
||||
|
||||
#[cfg(has_cxp_phys)]
|
||||
pub mod cxp;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RPCException {
|
||||
|
|
|
@ -8,7 +8,6 @@ edition = "2018"
|
|||
[features]
|
||||
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
|
||||
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
|
||||
target_ebaz4205 = ["libboard_zynq/target_ebaz4205", "libsupport_zynq/target_ebaz4205", "libconfig/target_ebaz4205", "libboard_artiq/target_ebaz4205"]
|
||||
default = ["target_zc706"]
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -21,7 +20,6 @@ cslice = "0.3"
|
|||
log = "0.4"
|
||||
embedded-hal = "0.2"
|
||||
core_io = { version = "0.1", features = ["collections"] }
|
||||
crc = { version = "1.7", default-features = false }
|
||||
byteorder = { version = "1.3", default-features = false }
|
||||
void = { version = "1", default-features = false }
|
||||
futures = { version = "0.3", default-features = false, features = ["async-await"] }
|
||||
|
|
|
@ -405,9 +405,8 @@ async fn handle_run_kernel(
|
|||
id,
|
||||
destination: _,
|
||||
run,
|
||||
timestamp,
|
||||
} => {
|
||||
let succeeded = match subkernel::load(aux_mutex, routing_table, timer, id, run, timestamp).await {
|
||||
let succeeded = match subkernel::load(aux_mutex, routing_table, timer, id, run).await {
|
||||
Ok(()) => true,
|
||||
Err(e) => {
|
||||
error!("Error loading subkernel: {:?}", e);
|
||||
|
@ -697,6 +696,27 @@ async fn handle_connection(
|
|||
}
|
||||
}
|
||||
|
||||
async fn load_and_run_idle_kernel(
|
||||
buffer: &Vec<u8>,
|
||||
control: &Rc<RefCell<kernel::Control>>,
|
||||
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||
aux_mutex: &Rc<Mutex<bool>>,
|
||||
routing_table: &drtio_routing::RoutingTable,
|
||||
timer: GlobalTimer,
|
||||
) {
|
||||
info!("Loading idle kernel");
|
||||
let res = handle_flash_kernel(buffer, control, up_destinations, aux_mutex, routing_table, timer).await;
|
||||
match res {
|
||||
Err(_) => warn!("error loading idle kernel"),
|
||||
_ => (),
|
||||
}
|
||||
info!("Running idle kernel");
|
||||
let _ = handle_run_kernel(None, control, up_destinations, aux_mutex, routing_table, timer)
|
||||
.await
|
||||
.map_err(|_| warn!("error running idle kernel"));
|
||||
info!("Idle kernel terminated");
|
||||
}
|
||||
|
||||
pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||
let net_addresses = net_settings::get_addresses(&cfg);
|
||||
info!("network addresses: {}", net_addresses);
|
||||
|
@ -757,6 +777,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
|||
moninj::start(timer, &aux_mutex, &drtio_routing_table);
|
||||
|
||||
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
|
||||
let idle_kernel = Rc::new(cfg.read("idle_kernel").ok());
|
||||
if let Ok(buffer) = cfg.read("startup_kernel") {
|
||||
info!("Loading startup kernel...");
|
||||
let routing_table = drtio_routing_table.borrow();
|
||||
|
@ -783,34 +804,35 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
|||
}
|
||||
}
|
||||
|
||||
let cfg = Rc::new(cfg);
|
||||
let restart_idle = Rc::new(Semaphore::new(1, 1));
|
||||
mgmt::start(
|
||||
cfg.clone(),
|
||||
restart_idle.clone(),
|
||||
Some(mgmt::DrtioContext(
|
||||
aux_mutex.clone(),
|
||||
drtio_routing_table.clone(),
|
||||
timer,
|
||||
)),
|
||||
);
|
||||
mgmt::start(cfg);
|
||||
|
||||
task::spawn(async move {
|
||||
let connection = Rc::new(Semaphore::new(1, 1));
|
||||
let connection = Rc::new(Semaphore::new(0, 1));
|
||||
let terminate = Rc::new(Semaphore::new(0, 1));
|
||||
let can_restart_idle = Rc::new(Semaphore::new(1, 1));
|
||||
let restart_idle = restart_idle.clone();
|
||||
loop {
|
||||
{
|
||||
let control = control.clone();
|
||||
let mut maybe_stream = select_biased! {
|
||||
s = (async {
|
||||
TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap()
|
||||
}).fuse() => Some(s),
|
||||
_ = (async {
|
||||
restart_idle.async_wait().await;
|
||||
can_restart_idle.async_wait().await;
|
||||
}).fuse() => None
|
||||
};
|
||||
let idle_kernel = idle_kernel.clone();
|
||||
let connection = connection.clone();
|
||||
let terminate = terminate.clone();
|
||||
let up_destinations = up_destinations.clone();
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
let routing_table = drtio_routing_table.clone();
|
||||
task::spawn(async move {
|
||||
let routing_table = routing_table.borrow();
|
||||
select_biased! {
|
||||
_ = (async {
|
||||
if let Some(buffer) = &*idle_kernel {
|
||||
load_and_run_idle_kernel(&buffer, &control, &up_destinations, &aux_mutex, &routing_table, timer).await;
|
||||
}
|
||||
}).fuse() => (),
|
||||
_ = terminate.async_wait().fuse() => ()
|
||||
}
|
||||
connection.signal();
|
||||
});
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
|
||||
|
||||
if connection.try_wait().is_none() {
|
||||
// there is an existing connection
|
||||
|
@ -818,58 +840,32 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
|||
connection.async_wait().await;
|
||||
}
|
||||
|
||||
let maybe_idle_kernel = cfg.read("idle_kernel").ok();
|
||||
if maybe_idle_kernel.is_none() && maybe_stream.is_none() {
|
||||
control.borrow_mut().restart(); // terminate idle kernel if running
|
||||
}
|
||||
|
||||
let control = control.clone();
|
||||
let idle_kernel = idle_kernel.clone();
|
||||
let connection = connection.clone();
|
||||
let terminate = terminate.clone();
|
||||
let can_restart_idle = can_restart_idle.clone();
|
||||
let up_destinations = up_destinations.clone();
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
let routing_table = drtio_routing_table.clone();
|
||||
|
||||
// we make sure the value of terminate is 0 before we start
|
||||
let _ = terminate.try_wait();
|
||||
let _ = can_restart_idle.try_wait();
|
||||
task::spawn(async move {
|
||||
let routing_table = routing_table.borrow();
|
||||
select_biased! {
|
||||
_ = (async {
|
||||
if let Some(stream) = &mut maybe_stream {
|
||||
let _ = handle_connection(stream, control.clone(), &up_destinations, &aux_mutex, &routing_table, timer)
|
||||
.await
|
||||
.map_err(|e| warn!("connection terminated: {}", e));
|
||||
}
|
||||
can_restart_idle.signal();
|
||||
match maybe_idle_kernel {
|
||||
Some(buffer) => {
|
||||
loop {
|
||||
info!("loading idle kernel");
|
||||
match handle_flash_kernel(&buffer, &control, &up_destinations, &aux_mutex, &routing_table, timer).await {
|
||||
Ok(_) => {
|
||||
info!("running idle kernel");
|
||||
match handle_run_kernel(None, &control, &up_destinations, &aux_mutex, &routing_table, timer).await {
|
||||
Ok(_) => info!("idle kernel finished"),
|
||||
Err(_) => warn!("idle kernel running error")
|
||||
}
|
||||
},
|
||||
Err(_) => warn!("idle kernel loading error")
|
||||
}
|
||||
}
|
||||
},
|
||||
None => info!("no idle kernel found")
|
||||
let _ = handle_connection(&mut stream, control.clone(), &up_destinations, &aux_mutex, &routing_table, timer)
|
||||
.await
|
||||
.map_err(|e| warn!("connection terminated: {}", e));
|
||||
if let Some(buffer) = &*idle_kernel {
|
||||
load_and_run_idle_kernel(&buffer, &control, &up_destinations, &aux_mutex, &routing_table, timer).await;
|
||||
}
|
||||
}).fuse() => (),
|
||||
_ = terminate.async_wait().fuse() => ()
|
||||
}
|
||||
connection.signal();
|
||||
if let Some(stream) = maybe_stream {
|
||||
let _ = stream.flush().await;
|
||||
let _ = stream.abort().await;
|
||||
}
|
||||
let _ = stream.flush().await;
|
||||
let _ = stream.abort().await;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -918,8 +914,7 @@ pub fn soft_panic_main(timer: GlobalTimer, cfg: Config) -> ! {
|
|||
|
||||
Sockets::init(32);
|
||||
|
||||
let dummy = Rc::new(Semaphore::new(0, 1));
|
||||
mgmt::start(Rc::new(cfg), dummy, None);
|
||||
mgmt::start(cfg);
|
||||
|
||||
// getting eth settings disables the LED as it resets GPIO
|
||||
// need to re-enable it here
|
||||
|
|
|
@ -17,7 +17,7 @@ use libasync::task;
|
|||
use libboard_artiq::drtio_eem;
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
use libboard_artiq::io_expander;
|
||||
use libboard_artiq::{identifier_read, logger, pl};
|
||||
use libboard_artiq::{cxp_downconn, cxp_phys, cxp_proto, cxp_upconn, identifier_read, logger, pl};
|
||||
use libboard_zynq::{gic, mpcore, timer::GlobalTimer};
|
||||
use libconfig::Config;
|
||||
use libcortex_a9::l2c::enable_l2_cache;
|
||||
|
@ -150,5 +150,15 @@ pub fn main_core0() {
|
|||
|
||||
task::spawn(ksupport::report_async_rtio_errors());
|
||||
|
||||
cxp_phys::setup(&mut timer);
|
||||
cxp_downconn::loopback_testing(0, &mut timer, cxp_phys::CXP_SPEED::CXP_1);
|
||||
// cxp_downconn::loopback_testing(0, &mut timer, cxp_phys::CXP_SPEED::CXP_2);
|
||||
// cxp_downconn::loopback_testing(0, &mut timer, cxp_phys::CXP_SPEED::CXP_3);
|
||||
// cxp_downconn::loopback_testing(0, &mut timer, cxp_phys::CXP_SPEED::CXP_5);
|
||||
// cxp_downconn::loopback_testing(0, &mut timer, cxp_phys::CXP_SPEED::CXP_6);
|
||||
// cxp_downconn::loopback_testing(0, &mut timer, cxp_phys::CXP_SPEED::CXP_10);
|
||||
// cxp_downconn::loopback_testing(0, &mut timer, cxp_phys::CXP_SPEED::CXP_12);
|
||||
// cxp_upconn::tx_test(0, &mut timer);
|
||||
|
||||
comms::main(timer, cfg);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -102,7 +102,7 @@ mod remote_moninj {
|
|||
overrd: i8,
|
||||
value: i8,
|
||||
) {
|
||||
let _lock = aux_mutex.async_lock().await;
|
||||
let _lock = aux_mutex.lock();
|
||||
drtioaux_async::send(
|
||||
linkno,
|
||||
&drtioaux_async::Packet::InjectionRequest {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#[cfg(not(feature = "target_ebaz4205"))]
|
||||
use embedded_hal::blocking::delay::DelayMs;
|
||||
#[cfg(has_si5324)]
|
||||
use ksupport::i2c;
|
||||
#[cfg(not(feature = "target_ebaz4205"))]
|
||||
use libboard_artiq::pl;
|
||||
#[cfg(has_si5324)]
|
||||
use libboard_artiq::si5324;
|
||||
|
@ -13,8 +11,6 @@ use libboard_zynq::i2c::I2c;
|
|||
use libboard_zynq::timer::GlobalTimer;
|
||||
use libconfig::Config;
|
||||
use log::{info, warn};
|
||||
#[cfg(feature = "target_ebaz4205")]
|
||||
use {libboard_zynq::slcr, libregister::RegisterRW};
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
|
@ -73,7 +69,7 @@ fn get_rtio_clock_cfg(cfg: &Config) -> RtioClock {
|
|||
res
|
||||
}
|
||||
|
||||
#[cfg(not(any(has_drtio, feature = "target_ebaz4205")))]
|
||||
#[cfg(not(has_drtio))]
|
||||
fn init_rtio(timer: &mut GlobalTimer) {
|
||||
info!("Switching SYS clocks...");
|
||||
unsafe {
|
||||
|
@ -410,38 +406,6 @@ fn get_si549_setting(clk: RtioClock) -> si549::FrequencySetting {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "target_ebaz4205")]
|
||||
fn set_fclk0_freq(clk: RtioClock, cfg: &Config) {
|
||||
let io_pll_freq: u32 = 1_000_000_000; // Hardcoded in zynq-rs
|
||||
let mut target_freq = 0;
|
||||
let mut divisor0 = 1u8;
|
||||
|
||||
match clk {
|
||||
RtioClock::Int_100 => {
|
||||
target_freq = 100_000_000;
|
||||
divisor0 = 10;
|
||||
}
|
||||
RtioClock::Int_125 => {
|
||||
target_freq = 125_000_000;
|
||||
divisor0 = 8;
|
||||
}
|
||||
_ => {
|
||||
warn!("Unsupported RTIO Clock: '{:?}'", clk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
slcr::RegisterBlock::unlocked(|slcr| {
|
||||
slcr.fpga0_clk_ctrl.modify(|_, w| w.divisor0(divisor0));
|
||||
});
|
||||
|
||||
info!(
|
||||
"Set FCLK0 to {:.2} MHz (target: {} MHz).",
|
||||
io_pll_freq as f64 / divisor0 as f64,
|
||||
target_freq / 1_000_000
|
||||
);
|
||||
}
|
||||
|
||||
pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
||||
let clk = get_rtio_clock_cfg(cfg);
|
||||
#[cfg(has_si5324)]
|
||||
|
@ -465,19 +429,9 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
|||
#[cfg(has_drtio)]
|
||||
init_drtio(timer);
|
||||
|
||||
#[cfg(not(any(has_drtio, feature = "target_ebaz4205")))]
|
||||
#[cfg(not(has_drtio))]
|
||||
init_rtio(timer);
|
||||
|
||||
#[cfg(feature = "target_ebaz4205")]
|
||||
{
|
||||
match clk {
|
||||
RtioClock::Int_100 | RtioClock::Int_125 => {
|
||||
set_fclk0_freq(clk, cfg);
|
||||
}
|
||||
_ => {} // Not set for external clocks
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(has_si549, has_wrpll))]
|
||||
{
|
||||
// SYS CLK switch will reset CSRs that are used by WRPLL
|
||||
|
|
|
@ -13,13 +13,9 @@ pub mod drtio {
|
|||
use core::fmt;
|
||||
|
||||
use embedded_hal::blocking::delay::DelayMs;
|
||||
#[cfg(has_drtio_eem)]
|
||||
use embedded_hal::blocking::delay::DelayUs;
|
||||
use ksupport::{resolve_channel_name, ASYNC_ERROR_BUSY, ASYNC_ERROR_COLLISION, ASYNC_ERROR_SEQUENCE_ERROR,
|
||||
SEEN_ASYNC_ERRORS};
|
||||
use libasync::{delay, task};
|
||||
#[cfg(has_drtio_eem)]
|
||||
use libboard_artiq::drtio_eem;
|
||||
use libboard_artiq::{drtioaux::Error as DrtioError,
|
||||
drtioaux_async,
|
||||
drtioaux_async::Packet,
|
||||
|
@ -30,10 +26,6 @@ pub mod drtio {
|
|||
use super::*;
|
||||
use crate::{analyzer::remote_analyzer::RemoteBuffer, rtio_dma::remote_dma, subkernel};
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
const DRTIO_EEM_LINKNOS: core::ops::Range<usize> =
|
||||
(csr::DRTIO.len() - csr::CONFIG_EEM_DRTIO_COUNT as usize)..csr::DRTIO.len();
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Error {
|
||||
Timeout,
|
||||
|
@ -84,18 +76,8 @@ pub mod drtio {
|
|||
});
|
||||
}
|
||||
|
||||
async fn link_rx_up(linkno: u8, _timer: &mut GlobalTimer) -> bool {
|
||||
async fn link_rx_up(linkno: u8) -> bool {
|
||||
let linkno = linkno as usize;
|
||||
#[cfg(has_drtio_eem)]
|
||||
if DRTIO_EEM_LINKNOS.contains(&linkno) {
|
||||
let eem_trx_no = linkno - DRTIO_EEM_LINKNOS.start;
|
||||
unsafe {
|
||||
csr::eem_transceiver::transceiver_sel_write(eem_trx_no as u8);
|
||||
csr::eem_transceiver::comma_align_reset_write(1);
|
||||
}
|
||||
_timer.delay_us(100);
|
||||
return unsafe { csr::eem_transceiver::comma_read() == 1 };
|
||||
}
|
||||
unsafe { (csr::DRTIO[linkno].rx_up_read)() == 1 }
|
||||
}
|
||||
|
||||
|
@ -170,8 +152,8 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
|
||||
async fn recv_aux_timeout(linkno: u8, timeout: u64, mut timer: GlobalTimer) -> Result<Packet, Error> {
|
||||
if !link_rx_up(linkno, &mut timer).await {
|
||||
async fn recv_aux_timeout(linkno: u8, timeout: u64, timer: GlobalTimer) -> Result<Packet, Error> {
|
||||
if !link_rx_up(linkno).await {
|
||||
return Err(Error::LinkDown);
|
||||
}
|
||||
match drtioaux_async::recv_timeout(linkno, Some(timeout), timer).await {
|
||||
|
@ -186,9 +168,9 @@ pub mod drtio {
|
|||
linkno: u8,
|
||||
routing_table: &RoutingTable,
|
||||
request: &Packet,
|
||||
mut timer: GlobalTimer,
|
||||
timer: GlobalTimer,
|
||||
) -> Result<Packet, Error> {
|
||||
if !link_rx_up(linkno, &mut timer).await {
|
||||
if !link_rx_up(linkno).await {
|
||||
return Err(Error::LinkDown);
|
||||
}
|
||||
let _lock = aux_mutex.async_lock().await;
|
||||
|
@ -212,11 +194,11 @@ pub mod drtio {
|
|||
aux_mutex: &Rc<Mutex<bool>>,
|
||||
linkno: u8,
|
||||
routing_table: &RoutingTable,
|
||||
mut timer: GlobalTimer,
|
||||
timer: GlobalTimer,
|
||||
) -> u32 {
|
||||
let mut count = 0;
|
||||
loop {
|
||||
if !link_rx_up(linkno, &mut timer).await {
|
||||
if !link_rx_up(linkno).await {
|
||||
return 0;
|
||||
}
|
||||
count += 1;
|
||||
|
@ -480,7 +462,7 @@ pub mod drtio {
|
|||
aux_mutex: &Rc<Mutex<bool>>,
|
||||
routing_table: &RoutingTable,
|
||||
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||
mut timer: GlobalTimer,
|
||||
timer: GlobalTimer,
|
||||
) {
|
||||
let mut up_links = [false; csr::DRTIO.len()];
|
||||
loop {
|
||||
|
@ -488,35 +470,16 @@ pub mod drtio {
|
|||
let linkno = linkno as u8;
|
||||
if up_links[linkno as usize] {
|
||||
/* link was previously up */
|
||||
if link_rx_up(linkno, &mut timer).await {
|
||||
if link_rx_up(linkno).await {
|
||||
process_unsolicited_aux(aux_mutex, linkno, routing_table).await;
|
||||
process_local_errors(linkno).await;
|
||||
} else {
|
||||
info!("[LINK#{}] link is down", linkno);
|
||||
up_links[linkno as usize] = false;
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
if DRTIO_EEM_LINKNOS.contains(&(linkno as usize)) {
|
||||
unsafe {
|
||||
csr::eem_transceiver::rx_ready_write(0);
|
||||
}
|
||||
while !matches!(drtioaux_async::recv(linkno).await, Ok(None)) {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* link was previously down */
|
||||
#[cfg(has_drtio_eem)]
|
||||
if DRTIO_EEM_LINKNOS.contains(&(linkno as usize)) {
|
||||
let eem_trx_no = linkno - DRTIO_EEM_LINKNOS.start as u8;
|
||||
if !unsafe { drtio_eem::align_wordslip(&mut timer, eem_trx_no) } {
|
||||
continue;
|
||||
}
|
||||
unsafe {
|
||||
csr::eem_transceiver::rx_ready_write(1);
|
||||
}
|
||||
}
|
||||
|
||||
if link_rx_up(linkno, &mut timer).await {
|
||||
if link_rx_up(linkno).await {
|
||||
info!("[LINK#{}] link RX became up, pinging", linkno);
|
||||
let ping_count = ping_remote(aux_mutex, linkno, routing_table, timer).await;
|
||||
if ping_count > 0 {
|
||||
|
@ -560,7 +523,7 @@ pub mod drtio {
|
|||
|
||||
for linkno in 0..csr::DRTIO.len() {
|
||||
let linkno = linkno as u8;
|
||||
if task::block_on(link_rx_up(linkno, &mut timer)) {
|
||||
if task::block_on(link_rx_up(linkno)) {
|
||||
let reply = task::block_on(aux_transact(
|
||||
&aux_mutex,
|
||||
linkno,
|
||||
|
@ -577,7 +540,7 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn partition_data<PacketF, HandlerF>(
|
||||
async fn partition_data<PacketF, HandlerF>(
|
||||
linkno: u8,
|
||||
aux_mutex: &Rc<Mutex<bool>>,
|
||||
routing_table: &RoutingTable,
|
||||
|
@ -829,7 +792,6 @@ pub mod drtio {
|
|||
id: u32,
|
||||
destination: u8,
|
||||
run: bool,
|
||||
timestamp: u64,
|
||||
) -> Result<(), Error> {
|
||||
let linkno = routing_table.0[destination as usize][0] - 1;
|
||||
let reply = aux_transact(
|
||||
|
@ -841,7 +803,6 @@ pub mod drtio {
|
|||
source: 0,
|
||||
destination: destination,
|
||||
run: run,
|
||||
timestamp,
|
||||
},
|
||||
timer,
|
||||
)
|
||||
|
|
|
@ -100,22 +100,12 @@ pub async fn load(
|
|||
timer: GlobalTimer,
|
||||
id: u32,
|
||||
run: bool,
|
||||
timestamp: u64,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(subkernel) = SUBKERNELS.async_lock().await.get_mut(&id) {
|
||||
if subkernel.state != SubkernelState::Uploaded {
|
||||
return Err(Error::IncorrectState);
|
||||
}
|
||||
drtio::subkernel_load(
|
||||
aux_mutex,
|
||||
routing_table,
|
||||
timer,
|
||||
id,
|
||||
subkernel.destination,
|
||||
run,
|
||||
timestamp,
|
||||
)
|
||||
.await?;
|
||||
drtio::subkernel_load(aux_mutex, routing_table, timer, id, subkernel.destination, run).await?;
|
||||
if run {
|
||||
subkernel.state = SubkernelState::Running;
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@ build_zynq = { path = "../libbuild_zynq" }
|
|||
|
||||
[dependencies]
|
||||
log = { version = "0.4", default-features = false }
|
||||
byteorder = { version = "1.3", default-features = false }
|
||||
core_io = { version = "0.1", features = ["collections"] }
|
||||
crc = { version = "1.7", default-features = false }
|
||||
cslice = "0.3"
|
||||
embedded-hal = "0.2"
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate byteorder;
|
||||
extern crate core_io;
|
||||
extern crate crc;
|
||||
extern crate cslice;
|
||||
extern crate embedded_hal;
|
||||
|
||||
|
@ -40,18 +38,16 @@ use libboard_artiq::{drtio_routing, drtioaux,
|
|||
pl::csr};
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
use libboard_zynq::error_led::ErrorLED;
|
||||
use libboard_zynq::{i2c::I2c, print, println, slcr, time::Milliseconds, timer::GlobalTimer};
|
||||
use libboard_zynq::{i2c::I2c, print, println, time::Milliseconds, timer::GlobalTimer};
|
||||
use libconfig::Config;
|
||||
use libcortex_a9::{l2c::enable_l2_cache, regs::MPIDR};
|
||||
use libregister::RegisterR;
|
||||
use libsupport_zynq::{exception_vectors, ram};
|
||||
use mgmt::Manager as CoreManager;
|
||||
use routing::Router;
|
||||
use subkernel::Manager as KernelManager;
|
||||
|
||||
mod analyzer;
|
||||
mod dma;
|
||||
mod mgmt;
|
||||
mod repeater;
|
||||
mod routing;
|
||||
mod subkernel;
|
||||
|
@ -153,7 +149,6 @@ fn process_aux_packet(
|
|||
dma_manager: &mut DmaManager,
|
||||
analyzer: &mut Analyzer,
|
||||
kernel_manager: &mut KernelManager,
|
||||
core_manager: &mut CoreManager,
|
||||
router: &mut Router,
|
||||
) -> Result<(), drtioaux::Error> {
|
||||
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
|
||||
|
@ -831,7 +826,6 @@ fn process_aux_packet(
|
|||
destination: _destination,
|
||||
id,
|
||||
run,
|
||||
timestamp,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
|
@ -850,7 +844,7 @@ fn process_aux_packet(
|
|||
// cannot run kernel while DDMA is running
|
||||
succeeded = false;
|
||||
} else {
|
||||
succeeded |= kernel_manager.run(source, id, timestamp).is_ok();
|
||||
succeeded |= kernel_manager.run(source, id).is_ok();
|
||||
}
|
||||
}
|
||||
router.send(
|
||||
|
@ -1016,335 +1010,6 @@ fn process_aux_packet(
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtGetLogRequest {
|
||||
destination: _destination,
|
||||
clear,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
let mut data_slice = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
let meta = core_manager.log_get_slice(&mut data_slice, clear);
|
||||
drtioaux::send(
|
||||
0,
|
||||
&drtioaux::Packet::CoreMgmtGetLogReply {
|
||||
last: meta.status.is_last(),
|
||||
length: meta.len as u16,
|
||||
data: data_slice,
|
||||
},
|
||||
)
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtClearLogRequest {
|
||||
destination: _destination,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
mgmt::clear_log();
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtSetLogLevelRequest {
|
||||
destination: _destination,
|
||||
log_level,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
if let Ok(level_filter) = mgmt::byte_to_level_filter(log_level) {
|
||||
info!("Changing log level to {}", level_filter);
|
||||
log::set_max_level(level_filter);
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtSetUartLogLevelRequest {
|
||||
destination: _destination,
|
||||
log_level,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
if let Ok(level_filter) = mgmt::byte_to_level_filter(log_level) {
|
||||
info!("Changing UART log level to {}", level_filter);
|
||||
unsafe {
|
||||
logger::BufferLogger::get_logger()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_uart_log_level(level_filter);
|
||||
}
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigReadRequest {
|
||||
destination: _destination,
|
||||
length,
|
||||
key,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
let mut value_slice = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
|
||||
let key_slice = &key[..length as usize];
|
||||
if !key_slice.is_ascii() {
|
||||
error!("invalid key");
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
} else {
|
||||
let key = core::str::from_utf8(key_slice).unwrap();
|
||||
if core_manager.fetch_config_value(key).is_ok() {
|
||||
let meta = core_manager.get_config_value_slice(&mut value_slice);
|
||||
drtioaux::send(
|
||||
0,
|
||||
&drtioaux::Packet::CoreMgmtConfigReadReply {
|
||||
last: meta.status.is_last(),
|
||||
length: meta.len as u16,
|
||||
value: value_slice,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigReadContinue {
|
||||
destination: _destination,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
let mut value_slice = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
let meta = core_manager.get_config_value_slice(&mut value_slice);
|
||||
drtioaux::send(
|
||||
0,
|
||||
&drtioaux::Packet::CoreMgmtConfigReadReply {
|
||||
last: meta.status.is_last(),
|
||||
length: meta.len as u16,
|
||||
value: value_slice,
|
||||
},
|
||||
)
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigWriteRequest {
|
||||
destination: _destination,
|
||||
last,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
core_manager.add_config_data(&data, length as usize);
|
||||
|
||||
let mut succeeded = true;
|
||||
if last {
|
||||
succeeded = core_manager.write_config().is_ok();
|
||||
core_manager.clear_config_data();
|
||||
}
|
||||
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigRemoveRequest {
|
||||
destination: _destination,
|
||||
length,
|
||||
key,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
let key_slice = &key[..length as usize];
|
||||
if !key_slice.is_ascii() {
|
||||
error!("invalid key");
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
} else {
|
||||
let key = core::str::from_utf8(key_slice).unwrap();
|
||||
let succeeded = core_manager.remove_config(key).is_ok();
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigEraseRequest {
|
||||
destination: _destination,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
error!("config erase not supported on zynq device");
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtRebootRequest {
|
||||
destination: _destination,
|
||||
} => {
|
||||
info!("received reboot request");
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })?;
|
||||
info!("reboot imminent");
|
||||
slcr::reboot();
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtAllocatorDebugRequest {
|
||||
destination: _destination,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
error!("debug allocator not supported on zynq device");
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtFlashRequest {
|
||||
destination: _destination,
|
||||
payload_length,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
core_manager.allocate_image_buffer(payload_length as usize);
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtFlashAddDataRequest {
|
||||
destination: _destination,
|
||||
last,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
core_manager.add_image_data(&data, length as usize);
|
||||
|
||||
if last {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtDropLink)
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtDropLinkAck {
|
||||
destination: _destination,
|
||||
} => {
|
||||
forward!(
|
||||
router,
|
||||
_routing_table,
|
||||
_destination,
|
||||
*rank,
|
||||
*self_destination,
|
||||
_repeaters,
|
||||
&packet,
|
||||
timer
|
||||
);
|
||||
|
||||
unsafe {
|
||||
csr::gt_drtio::txenable_write(0);
|
||||
}
|
||||
core_manager.write_image();
|
||||
info!("reboot imminent");
|
||||
slcr::reboot();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
p => {
|
||||
warn!("received unexpected aux packet: {:?}", p);
|
||||
|
@ -1363,7 +1028,6 @@ fn process_aux_packets(
|
|||
dma_manager: &mut DmaManager,
|
||||
analyzer: &mut Analyzer,
|
||||
kernel_manager: &mut KernelManager,
|
||||
core_manager: &mut CoreManager,
|
||||
router: &mut Router,
|
||||
) {
|
||||
let result = drtioaux::recv(0).and_then(|packet| {
|
||||
|
@ -1379,7 +1043,6 @@ fn process_aux_packets(
|
|||
dma_manager,
|
||||
analyzer,
|
||||
kernel_manager,
|
||||
core_manager,
|
||||
router,
|
||||
)
|
||||
} else {
|
||||
|
@ -1576,7 +1239,7 @@ pub extern "C" fn main_core0() -> i32 {
|
|||
#[cfg(has_si549)]
|
||||
si549::helper_setup(&mut timer, &SI549_SETTINGS).expect("cannot initialize helper Si549");
|
||||
|
||||
let mut cfg = match Config::new() {
|
||||
let cfg = match Config::new() {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
warn!("config initialization failed: {}", err);
|
||||
|
@ -1651,7 +1314,6 @@ pub extern "C" fn main_core0() -> i32 {
|
|||
let mut dma_manager = DmaManager::new();
|
||||
let mut analyzer = Analyzer::new();
|
||||
let mut kernel_manager = KernelManager::new(&mut control);
|
||||
let mut core_manager = CoreManager::new(&mut cfg);
|
||||
|
||||
drtioaux::reset(0);
|
||||
drtiosat_reset(false);
|
||||
|
@ -1669,7 +1331,6 @@ pub extern "C" fn main_core0() -> i32 {
|
|||
&mut dma_manager,
|
||||
&mut analyzer,
|
||||
&mut kernel_manager,
|
||||
&mut core_manager,
|
||||
&mut router,
|
||||
);
|
||||
#[allow(unused_mut)]
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
use byteorder::{ByteOrder, NativeEndian};
|
||||
use crc::crc32;
|
||||
use io::{ProtoRead, ProtoWrite};
|
||||
use libboard_artiq::{drtioaux_proto::SAT_PAYLOAD_MAX_SIZE,
|
||||
logger::{BufferLogger, LogBufferRef}};
|
||||
use libconfig::Config;
|
||||
use log::{debug, error, info, warn, LevelFilter};
|
||||
|
||||
use crate::routing::{SliceMeta, Sliceable};
|
||||
|
||||
type Result<T> = core::result::Result<T, ()>;
|
||||
|
||||
pub fn byte_to_level_filter(level_byte: u8) -> Result<LevelFilter> {
|
||||
Ok(match level_byte {
|
||||
0 => LevelFilter::Off,
|
||||
1 => LevelFilter::Error,
|
||||
2 => LevelFilter::Warn,
|
||||
3 => LevelFilter::Info,
|
||||
4 => LevelFilter::Debug,
|
||||
5 => LevelFilter::Trace,
|
||||
lv => {
|
||||
error!("unknown log level: {}", lv);
|
||||
return Err(());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_logger_buffer() -> LogBufferRef<'static> {
|
||||
let logger = unsafe { BufferLogger::get_logger().as_mut().unwrap() };
|
||||
loop {
|
||||
if let Some(buffer_ref) = logger.buffer() {
|
||||
return buffer_ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_log() {
|
||||
let mut buffer = get_logger_buffer();
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
pub struct Manager<'a> {
|
||||
cfg: &'a mut Config,
|
||||
last_log: Sliceable,
|
||||
config_payload: Vec<u8>,
|
||||
last_value: Sliceable,
|
||||
image_payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> Manager<'_> {
|
||||
pub fn new(cfg: &mut Config) -> Manager {
|
||||
Manager {
|
||||
cfg: cfg,
|
||||
last_log: Sliceable::new(0, Vec::new()),
|
||||
config_payload: Vec::new(),
|
||||
last_value: Sliceable::new(0, Vec::new()),
|
||||
image_payload: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE], consume: bool) -> SliceMeta {
|
||||
// Populate buffer if depleted
|
||||
if self.last_log.at_end() {
|
||||
let mut buffer = get_logger_buffer();
|
||||
self.last_log.extend(buffer.extract().as_bytes());
|
||||
if consume {
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
self.last_log.get_slice_satellite(data_slice)
|
||||
}
|
||||
|
||||
pub fn fetch_config_value(&mut self, key: &str) -> Result<()> {
|
||||
self.cfg
|
||||
.read(&key)
|
||||
.map(|value| {
|
||||
debug!("got value");
|
||||
self.last_value = Sliceable::new(0, value)
|
||||
})
|
||||
.map_err(|_| warn!("read error: no such key"))
|
||||
}
|
||||
|
||||
pub fn get_config_value_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta {
|
||||
self.last_value.get_slice_satellite(data_slice)
|
||||
}
|
||||
|
||||
pub fn add_config_data(&mut self, data: &[u8], data_len: usize) {
|
||||
self.config_payload.write_all(&data[..data_len]).unwrap();
|
||||
}
|
||||
|
||||
pub fn clear_config_data(&mut self) {
|
||||
self.config_payload.clear();
|
||||
}
|
||||
|
||||
pub fn write_config(&mut self) -> Result<()> {
|
||||
let mut payload = &self.config_payload[..];
|
||||
let key = payload.read_string().map_err(|_err| error!("error on reading key"))?;
|
||||
debug!("write key: {}", key);
|
||||
let value = payload.read_bytes().unwrap();
|
||||
|
||||
self.cfg
|
||||
.write(&key, value)
|
||||
.map(|()| debug!("write success"))
|
||||
.map_err(|err| error!("failed to write: {:?}", err))
|
||||
}
|
||||
|
||||
pub fn remove_config(&mut self, key: &str) -> Result<()> {
|
||||
debug!("erase key: {}", key);
|
||||
self.cfg
|
||||
.remove(&key)
|
||||
.map(|()| debug!("erase success"))
|
||||
.map_err(|err| warn!("failed to erase: {:?}", err))
|
||||
}
|
||||
|
||||
pub fn allocate_image_buffer(&mut self, image_size: usize) {
|
||||
self.image_payload = Vec::with_capacity(image_size);
|
||||
}
|
||||
|
||||
pub fn add_image_data(&mut self, data: &[u8], data_len: usize) {
|
||||
self.image_payload.extend(&data[..data_len]);
|
||||
}
|
||||
|
||||
pub fn write_image(&self) {
|
||||
let mut image = self.image_payload.clone();
|
||||
let image_ref = &image[..];
|
||||
let bin_len = image.len() - 4;
|
||||
|
||||
let (image_ref, expected_crc) = {
|
||||
let (image_ref, crc_slice) = image_ref.split_at(bin_len);
|
||||
(image_ref, NativeEndian::read_u32(crc_slice))
|
||||
};
|
||||
|
||||
let actual_crc = crc32::checksum_ieee(image_ref);
|
||||
|
||||
if actual_crc == expected_crc {
|
||||
info!("CRC passed. Writing boot image to SD card...");
|
||||
image.truncate(bin_len);
|
||||
self.cfg.write("boot", image).expect("failed to write boot image");
|
||||
} else {
|
||||
panic!(
|
||||
"CRC failed, images have not been written to flash.\n(actual {:08x}, expected {:08x})",
|
||||
actual_crc, expected_crc
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use core::cmp::min;
|
|||
#[cfg(has_drtio_routing)]
|
||||
use libboard_artiq::pl::csr;
|
||||
use libboard_artiq::{drtio_routing, drtioaux,
|
||||
drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE}};
|
||||
drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE}};
|
||||
|
||||
pub struct SliceMeta {
|
||||
pub destination: u8,
|
||||
|
@ -58,7 +58,6 @@ impl Sliceable {
|
|||
}
|
||||
|
||||
get_slice_fn!(get_slice_master, MASTER_PAYLOAD_MAX_SIZE);
|
||||
get_slice_fn!(get_slice_satellite, SAT_PAYLOAD_MAX_SIZE);
|
||||
}
|
||||
|
||||
// Packets from downstream (further satellites) are received and routed appropriately.
|
||||
|
|
|
@ -8,7 +8,7 @@ use core_io::{Error as IoError, Write};
|
|||
use cslice::AsCSlice;
|
||||
use dma::{Error as DmaError, Manager as DmaManager};
|
||||
use io::{Cursor, ProtoWrite};
|
||||
use ksupport::{eh_artiq, kernel, rpc, rtio};
|
||||
use ksupport::{eh_artiq, kernel, rpc};
|
||||
use libboard_artiq::{drtio_routing::RoutingTable,
|
||||
drtioaux,
|
||||
drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE},
|
||||
|
@ -349,7 +349,7 @@ impl<'a> Manager<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, source: u8, id: u32, timestamp: u64) -> Result<(), Error> {
|
||||
pub fn run(&mut self, source: u8, id: u32) -> Result<(), Error> {
|
||||
if self.session.kernel_state != KernelState::Loaded || self.session.id != id {
|
||||
self.load(id)?;
|
||||
}
|
||||
|
@ -359,7 +359,6 @@ impl<'a> Manager<'_> {
|
|||
csr::cri_con::selected_write(2);
|
||||
}
|
||||
|
||||
rtio::at_mu(timestamp as i64);
|
||||
self.control.tx.send(kernel::Message::StartRequest);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -813,7 +812,6 @@ impl<'a> Manager<'_> {
|
|||
id,
|
||||
destination: sk_destination,
|
||||
run,
|
||||
timestamp,
|
||||
} => {
|
||||
self.session.kernel_state = KernelState::SubkernelAwaitLoad;
|
||||
router.route(
|
||||
|
@ -822,7 +820,6 @@ impl<'a> Manager<'_> {
|
|||
destination: sk_destination,
|
||||
id: id,
|
||||
run: run,
|
||||
timestamp,
|
||||
},
|
||||
routing_table,
|
||||
rank,
|
||||
|
|
Loading…
Reference in New Issue