forked from M-Labs/artiq-zynq
Compare commits
167 Commits
Author | SHA1 | Date |
---|---|---|
Sébastien Bourdeauducq | e1f493f3ca | |
Sebastien Bourdeauducq | 1f5ea41934 | |
Sebastien Bourdeauducq | 7f83d56ef5 | |
Jonathan Coates | 1d431456f4 | |
Sebastien Bourdeauducq | b03e380c1e | |
occheung | 47fc53c4bf | |
occheung | 42eaecf9e1 | |
occheung | beb7e6f994 | |
occheung | 4502a47aa6 | |
occheung | ac6b7d5cf0 | |
occheung | 3019bc6123 | |
occheung | 95b8562812 | |
occheung | a13f5d02fa | |
occheung | e52aa77068 | |
occheung | 8e28d12ad0 | |
occheung | 47cddae04f | |
occheung | 27a65df40e | |
occheung | 759cca3bfd | |
occheung | aadb6fc22d | |
occheung | ae4d5a4228 | |
occheung | 6f1d727ca2 | |
occheung | 7da5061f7e | |
occheung | 47d418c69e | |
occheung | d2979e8894 | |
occheung | 3884c14a19 | |
occheung | c5b00d8e4e | |
occheung | 2985875f9a | |
occheung | 5cb565a7e0 | |
occheung | 59954829a2 | |
occheung | 960864c847 | |
occheung | bdc29e5709 | |
occheung | 332732dc44 | |
occheung | 244c7396d9 | |
newell | 2c633409b8 | |
Sebastien Bourdeauducq | 9774b39fd8 | |
Sebastien Bourdeauducq | 9054e4a7cb | |
newell | d79bf8d54a | |
Sebastien Bourdeauducq | 75e7fc55a3 | |
architeuthidae | 24a4d79f0f | |
Sébastien Bourdeauducq | 9ce3aadb15 | |
mwojcik | 3390abd5a1 | |
newell | a410c40b50 | |
newell | 030247be18 | |
newell | 61df939c87 | |
newell | aba97175c6 | |
newell | 81790257a5 | |
Sebastien Bourdeauducq | 1f81d038e0 | |
Sebastien Bourdeauducq | 1e42228aac | |
Sebastien Bourdeauducq | c84653b500 | |
Sebastien Bourdeauducq | 6585b9b441 | |
Simon Renblad | 873dd86b4d | |
Simon Renblad | e7614d2e8e | |
Simon Renblad | 491e426222 | |
Simon Renblad | ccd3bf3003 | |
Sebastien Bourdeauducq | 3fdb7e80a8 | |
abdul124 | bd1de933fb | |
abdul124 | e8d77fca3e | |
abdul124 | 85e8a3fc44 | |
Sebastien Bourdeauducq | 04078b3d89 | |
abdul124 | d508c5c6f8 | |
abdul124 | bae41253e4 | |
abdul124 | 20181e9915 | |
abdul124 | a835149619 | |
Sebastien Bourdeauducq | 78d6b7ddcf | |
Simon Renblad | fad1db9796 | |
Simon Renblad | fee30033ec | |
abdul124 | fe6f259d48 | |
Sebastien Bourdeauducq | e4d7ce114f | |
mwojcik | 63f4783687 | |
mwojcik | 69a0b1bfb7 | |
Sebastien Bourdeauducq | f6bff80105 | |
Sébastien Bourdeauducq | 57fd327ecb | |
abdul124 | 69d5b11ebf | |
abdul124 | bab938c563 | |
mwojcik | d51e5e60c3 | |
mwojcik | 23857eef63 | |
Sebastien Bourdeauducq | d0615bf965 | |
abdul124 | 3a789889cf | |
mwojcik | 72b814f7fd | |
Sebastien Bourdeauducq | ead20a66a5 | |
morgan | 586fd2f17e | |
morgan | 377f8779a0 | |
morgan | 1fbaacfc43 | |
Sebastien Bourdeauducq | 127ea9ea4d | |
Simon Renblad | 174c301d7d | |
Sebastien Bourdeauducq | 52defff000 | |
mwojcik | 2b2ebb5354 | |
Sebastien Bourdeauducq | 4341d2d2a5 | |
Sebastien Bourdeauducq | 57b885ed99 | |
Sebastien Bourdeauducq | e922543855 | |
morgan | 35ea0ed2ca | |
morgan | cdf4ff24c0 | |
morgan | 285b02c4b1 | |
morgan | 53cb592d19 | |
Florian Agbuya | c261897658 | |
morgan | 1d603c73b7 | |
morgan | 61315c29b9 | |
morgan | 3f57de6ec7 | |
morgan | cca23aa2a5 | |
morgan | 2bbaea3ad5 | |
Sébastien Bourdeauducq | 5abd274060 | |
mwojcik | 3abe9caadb | |
mwojcik | 0a19f8fb89 | |
mwojcik | a30c7d1f3a | |
mwojcik | 2d10503c20 | |
mwojcik | 92a29051f7 | |
morgan | 14fa038118 | |
morgan | b81323af30 | |
morgan | 291777f764 | |
morgan | a1d80fb93b | |
morgan | 7827c7b803 | |
morgan | e4d8d44c7c | |
morgan | 4f34a7c6d0 | |
morgan | 1f7c53b8d0 | |
morgan | 4455f740d2 | |
morgan | 63bf1c81d4 | |
Sebastien Bourdeauducq | cf0b83c3f9 | |
mwojcik | bfb582f99b | |
mwojcik | 52e64fb2f9 | |
mwojcik | facc98058c | |
mwojcik | f0f81dbf8a | |
mwojcik | 30e6bf4a3a | |
mwojcik | 8f4e30dd9c | |
mwojcik | e31a31c4ff | |
Sebastien Bourdeauducq | d044bbd8bb | |
Sebastien Bourdeauducq | 33cf924805 | |
Sebastien Bourdeauducq | f7887b14f6 | |
Sebastien Bourdeauducq | 3e3e23918e | |
mwojcik | 6ca1719033 | |
mwojcik | aebc739c1e | |
linuswck | e1b2c45813 | |
linuswck | e6372b9766 | |
linuswck | 07044752b6 | |
linuswck | 79fc5a7789 | |
Sebastien Bourdeauducq | d3f4602361 | |
mwojcik | 6c8346ca5f | |
mwojcik | b76f634686 | |
morgan | 4a34777b97 | |
morgan | 43e4527392 | |
Sebastien Bourdeauducq | a08a42c954 | |
mwojcik | 0a3bfc9a61 | |
Egor Savkin | d3fbfd75b0 | |
Egor Savkin | b768d5648c | |
Sebastien Bourdeauducq | 812aea33b3 | |
linuswck | 136e24f597 | |
Sebastien Bourdeauducq | 0f050844cf | |
linuswck | a4d1be00c0 | |
linuswck | b15322b6ba | |
linuswck | 8fd1306145 | |
Sebastien Bourdeauducq | a28a819b18 | |
Sebastien Bourdeauducq | 3f414278e2 | |
Sebastien Bourdeauducq | e5aafad60d | |
mwojcik | b9a0bcabeb | |
mwojcik | 8eb359ee42 | |
mwojcik | 7263862fd8 | |
mwojcik | 29cc0a6e28 | |
mwojcik | 616c40429e | |
mwojcik | 3ea8147966 | |
mwojcik | cb79c12284 | |
mwojcik | 623cc7b79e | |
mwojcik | 49205eea17 | |
mwojcik | 6885c618b5 | |
mwojcik | c696fd826f | |
mwojcik | 4b3c9a3d08 | |
mwojcik | 779aea7c6a | |
mwojcik | 6785ca2c85 | |
Sebastien Bourdeauducq | cded04e2d6 |
|
@ -3,3 +3,11 @@ examples/*.elf
|
||||||
__pycache__
|
__pycache__
|
||||||
|
|
||||||
build
|
build
|
||||||
|
|
||||||
|
src/libboard_artiq/Cargo.toml
|
||||||
|
src/libc/Cargo.toml
|
||||||
|
src/libdyld/Cargo.toml
|
||||||
|
src/libio/Cargo.toml
|
||||||
|
src/libksupport/Cargo.toml
|
||||||
|
src/runtime/Cargo.toml
|
||||||
|
src/satman/Cargo.toml
|
||||||
|
|
93
README.md
93
README.md
|
@ -4,66 +4,107 @@ ARTIQ on Zynq
|
||||||
How to use
|
How to use
|
||||||
----------
|
----------
|
||||||
|
|
||||||
1. Install the ARTIQ version that corresponds to the artiq-zynq version you are targeting.
|
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, 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``).
|
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 the ``boot.bin`` file, obtained from Hydra's "binary distribution" download link or from AFWS, at the root of a FAT-formatted SD card.
|
3. Place ``boot.bin`` file 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.
|
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 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.
|
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 the firmware starts successfully, it should respond to ping at its IP addresses, and boot messages can be observed from its UART at 115200bps.
|
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, but set ``"target": "cortexa9"`` in the arguments of the core device.
|
7. Create and use an ARTIQ device database as usual.
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
The following configuration keys are available:
|
The following configuration keys are available among others:
|
||||||
|
|
||||||
- ``mac``: Ethernet MAC address.
|
- ``mac``: Ethernet MAC address.
|
||||||
- ``ip``: IPv4 address.
|
- ``ip``: IPv4 address.
|
||||||
- ``ip6``: IPv6 address.
|
- ``ip6``: IPv6 address.
|
||||||
- ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``).
|
- ``idle_kernel``: idle kernel in ELF format (as produced by ``artiq_compile``).
|
||||||
|
- ``startup_kernel``: startup kernel in ELF format (as produced by ``artiq_compile``).
|
||||||
- ``rtio_clock``: source of RTIO clock; valid values are ``ext0_bypass`` and ``int_125``.
|
- ``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.
|
|
||||||
|
|
||||||
Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is
|
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.
|
||||||
not implemented as it seems not very 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.
|
||||||
|
|
||||||
Development instructions
|
Development instructions
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
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``).
|
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``).
|
||||||
|
|
||||||
Pure build with Nix and execution on a remote JTAG server:
|
**Pure build with Nix:**
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock_satellite-jtag etc.
|
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock-sd or etc
|
||||||
./remote_run.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Impure incremental build and execution on a remote JTAG server:
|
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. :
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
nix develop
|
nix develop
|
||||||
cd src
|
cd src
|
||||||
gateware/zc706.py -g ../build/gateware -V <variant> # build gateware
|
gateware/<board>.py -g ../build/gateware -V <variant> # gateware
|
||||||
make GWARGS="-V <variant>" <runtime/satman> # build firmware
|
make GWARGS="-V <variant>" <runtime/satman> # firmware
|
||||||
cd ..
|
```
|
||||||
./remote_run.sh -i
|
|
||||||
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- The impure build process is also compatible with non-Nix systems.
|
- 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.
|
- 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, use the ``local_run.sh`` script.
|
- If the board is connected to the local machine by JTAG, use the ``local_run.sh`` script.
|
||||||
- To update ``zynq-rs``, update the cargo files as per usual for Rust projects, but also keep ``flake.lock`` in sync.
|
- 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.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Copyright (C) 2019-2023 M-Labs Limited.
|
Copyright (C) 2019-2024 M-Labs Limited.
|
||||||
|
|
||||||
ARTIQ is free software: you can redistribute it and/or modify
|
ARTIQ is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
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"},
|
||||||
|
},
|
||||||
|
)
|
155
flake.lock
155
flake.lock
|
@ -3,19 +3,19 @@
|
||||||
"artiq": {
|
"artiq": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"artiq-comtools": "artiq-comtools",
|
"artiq-comtools": "artiq-comtools",
|
||||||
"mozilla-overlay": "mozilla-overlay",
|
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
|
"rust-overlay": "rust-overlay",
|
||||||
"sipyco": "sipyco",
|
"sipyco": "sipyco",
|
||||||
"src-migen": "src-migen",
|
"src-migen": "src-migen",
|
||||||
"src-misoc": "src-misoc",
|
"src-misoc": "src-misoc",
|
||||||
"src-pythonparser": "src-pythonparser"
|
"src-pythonparser": "src-pythonparser"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693998161,
|
"lastModified": 1732066716,
|
||||||
"narHash": "sha256-GbJbBQF8i0FrBOecaAGJFv+0i37wLPgy0uoeyuiq4IM=",
|
"narHash": "sha256-krjvt9+RccnAxSEZcFhRpjA2S3CoqE4MSa1JUg421b4=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "2f3329181c2579ed77334e5875e8698a804be91d",
|
"rev": "270a417a28b516d36983779a1adb6d33a3c55a4a",
|
||||||
"revCount": 8495,
|
"revCount": 9102,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/m-labs/artiq.git"
|
"url": "https://github.com/m-labs/artiq.git"
|
||||||
},
|
},
|
||||||
|
@ -37,11 +37,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693473687,
|
"lastModified": 1720768567,
|
||||||
"narHash": "sha256-BdLddCWbvoEyakcGwhph9b5dIU1iA0hCQV7KYgU8nos=",
|
"narHash": "sha256-3VoK7o5MtHtbHLrc6Pv+eQWFtaz5Gd/YWyV5TD3c5Ss=",
|
||||||
"owner": "m-labs",
|
"owner": "m-labs",
|
||||||
"repo": "artiq-comtools",
|
"repo": "artiq-comtools",
|
||||||
"rev": "f522ef3dbc65961f17b2d3d41e927409d970fd79",
|
"rev": "f93570d8f2ed5a3cfb3e1c16ab00f2540551e994",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -55,11 +55,11 @@
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1692799911,
|
"lastModified": 1710146030,
|
||||||
"narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -68,66 +68,18 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mozilla-overlay": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1690536331,
|
|
||||||
"narHash": "sha256-aRIf2FB2GTdfF7gl13WyETmiV/J7EhBGkSWXfZvlxcA=",
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"rev": "db89c8707edcffefcd8e738459d511543a339ff5",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mozilla-overlay_2": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1690536331,
|
|
||||||
"narHash": "sha256-aRIf2FB2GTdfF7gl13WyETmiV/J7EhBGkSWXfZvlxcA=",
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"rev": "db89c8707edcffefcd8e738459d511543a339ff5",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mozilla-overlay_3": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1690536331,
|
|
||||||
"narHash": "sha256-aRIf2FB2GTdfF7gl13WyETmiV/J7EhBGkSWXfZvlxcA=",
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"rev": "db89c8707edcffefcd8e738459d511543a339ff5",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693771906,
|
"lastModified": 1731319897,
|
||||||
"narHash": "sha256-32EnPCaVjOiEERZ+o/2Ir7JH9pkfwJZJ27SKHNvt4yk=",
|
"narHash": "sha256-PbABj4tnbWFMfBp6OcUK5iGy1QY+/Z96ZcLpooIbuEI=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "da5adce0ffaff10f6d0fee72a02a5ed9d01b52fc",
|
"rev": "dc460ec76cbff0e66e269457d7b728432263166c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-23.05",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
@ -135,10 +87,53 @@
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"artiq": "artiq",
|
"artiq": "artiq",
|
||||||
"mozilla-overlay": "mozilla-overlay_2",
|
|
||||||
"zynq-rs": "zynq-rs"
|
"zynq-rs": "zynq-rs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"artiq",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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": {
|
"sipyco": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
@ -147,11 +142,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693473454,
|
"lastModified": 1728371104,
|
||||||
"narHash": "sha256-kr8Ur6JNW/xVRHdPn3ou980IAxg/n+f3ZQBHuJ1uaC4=",
|
"narHash": "sha256-PPnAyDedUQ7Og/Cby9x5OT9wMkNGTP8GS53V6N/dk4w=",
|
||||||
"owner": "m-labs",
|
"owner": "m-labs",
|
||||||
"repo": "sipyco",
|
"repo": "sipyco",
|
||||||
"rev": "5467dcf9738673ab9a49e6f2377bda7c551b5f90",
|
"rev": "094a6cd63ffa980ef63698920170e50dc9ba77fd",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -163,11 +158,11 @@
|
||||||
"src-migen": {
|
"src-migen": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693990700,
|
"lastModified": 1727677091,
|
||||||
"narHash": "sha256-qJLA03QcZ5S9DrqrseuzIQBTWS7rjAbYJxLYZEQ8rxA=",
|
"narHash": "sha256-Zg3SQnTwMM/VkOGKogbPyuCC2NhLy8HB2SPEUWWNgCU=",
|
||||||
"owner": "m-labs",
|
"owner": "m-labs",
|
||||||
"repo": "migen",
|
"repo": "migen",
|
||||||
"rev": "2cfee3e0db6fdca9b5918686ea77c93252e7cebd",
|
"rev": "c19ae9f8ae162ffe2d310a92bfce53ac2a821bc8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -179,11 +174,11 @@
|
||||||
"src-misoc": {
|
"src-misoc": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693709836,
|
"lastModified": 1729234629,
|
||||||
"narHash": "sha256-YiCk05RYLzZu1CYkQ2r7XtjwVEqkUGTQn388uOls9tI=",
|
"narHash": "sha256-TLsTCXV5AC2xh+bS7EhBVBKqdqIU3eKrnlWcFF9LtAM=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "58dc4ee60d165ce9145cf3d904241fc154b6407f",
|
"rev": "6085a312bca26adeca6584e37d08c8ba2e1d6e38",
|
||||||
"revCount": 2448,
|
"revCount": 2460,
|
||||||
"submodules": true,
|
"submodules": true,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/m-labs/misoc.git"
|
"url": "https://github.com/m-labs/misoc.git"
|
||||||
|
@ -227,18 +222,18 @@
|
||||||
},
|
},
|
||||||
"zynq-rs": {
|
"zynq-rs": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"mozilla-overlay": "mozilla-overlay_3",
|
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"artiq",
|
"artiq",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
],
|
||||||
|
"rust-overlay": "rust-overlay_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693479539,
|
"lastModified": 1731749494,
|
||||||
"narHash": "sha256-MppR7yxs3cjG7tQc82vX0MhyN71CJL2QWkM65F5hrFU=",
|
"narHash": "sha256-WGigAhvVCGN5YZ1dHPyvoqAh47W1Gtph036O1aKFlLE=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "c15b54f92b3d4e125ae47a0dce7abe4b2bc9e054",
|
"rev": "12975de2e110d7948bf47b768559f727d0abc8fc",
|
||||||
"revCount": 628,
|
"revCount": 655,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
|
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
|
||||||
},
|
},
|
||||||
|
|
98
flake.nix
98
flake.nix
|
@ -2,26 +2,26 @@
|
||||||
description = "ARTIQ port to the Zynq-7000 platform";
|
description = "ARTIQ port to the Zynq-7000 platform";
|
||||||
|
|
||||||
inputs.artiq.url = git+https://github.com/m-labs/artiq.git;
|
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.url = git+https://git.m-labs.hk/m-labs/zynq-rs;
|
||||||
inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs";
|
inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs";
|
||||||
|
|
||||||
outputs = { self, mozilla-overlay, zynq-rs, artiq }:
|
outputs = { self, zynq-rs, artiq }:
|
||||||
let
|
let
|
||||||
pkgs = import artiq.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
|
pkgs = import artiq.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import zynq-rs.inputs.rust-overlay) ]; };
|
||||||
zynqpkgs = zynq-rs.packages.x86_64-linux;
|
zynqpkgs = zynq-rs.packages.x86_64-linux;
|
||||||
artiqpkgs = artiq.packages.x86_64-linux;
|
artiqpkgs = artiq.packages.x86_64-linux;
|
||||||
|
llvmPackages_11 = zynq-rs.llvmPackages_11;
|
||||||
|
|
||||||
rust = zynq-rs.rust;
|
rust = zynq-rs.rust;
|
||||||
rustPlatform = zynq-rs.rustPlatform;
|
rustPlatform = zynq-rs.rustPlatform;
|
||||||
|
|
||||||
fastnumbers = pkgs.python3Packages.buildPythonPackage rec {
|
fastnumbers = pkgs.python3Packages.buildPythonPackage rec {
|
||||||
pname = "fastnumbers";
|
pname = "fastnumbers";
|
||||||
version = "2.2.1";
|
version = "5.1.0";
|
||||||
|
|
||||||
src = pkgs.python3Packages.fetchPypi {
|
src = pkgs.python3Packages.fetchPypi {
|
||||||
inherit pname version;
|
inherit pname version;
|
||||||
sha256 = "0j15i54p7nri6hkzn1wal9pxri4pgql01wgjccig6ar0v5jjbvsy";
|
sha256 = "sha256-4JLTP4uVwxcaL7NOV57+DFSwKQ3X+W/6onYkN2AdkKc=";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,10 +74,11 @@
|
||||||
|
|
||||||
propagatedBuildInputs = with pkgs.python3Packages; [ setuptools click numpy toolz jinja2 ramda artiqpkgs.migen artiqpkgs.misoc ];
|
propagatedBuildInputs = with pkgs.python3Packages; [ setuptools click numpy toolz jinja2 ramda artiqpkgs.migen artiqpkgs.misoc ];
|
||||||
|
|
||||||
checkInputs = with pkgs.python3Packages; [ pytest-runner pytestCheckHook pytest-timeout ];
|
checkInputs = with pkgs.python3Packages; [ pytestCheckHook pytest-timeout ];
|
||||||
|
|
||||||
# migen/misoc version checks are broken with pyproject for some reason
|
# migen/misoc version checks are broken with pyproject for some reason
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
|
sed -i "1,4d" pyproject.toml
|
||||||
substituteInPlace pyproject.toml \
|
substituteInPlace pyproject.toml \
|
||||||
--replace '"migen@git+https://github.com/m-labs/migen",' ""
|
--replace '"migen@git+https://github.com/m-labs/migen",' ""
|
||||||
substituteInPlace pyproject.toml \
|
substituteInPlace pyproject.toml \
|
||||||
|
@ -112,7 +113,7 @@
|
||||||
"nist_clock_satellite" "nist_qc2_satellite" "acpki_nist_clock_satellite" "acpki_nist_qc2_satellite"
|
"nist_clock_satellite" "nist_qc2_satellite" "acpki_nist_clock_satellite" "acpki_nist_qc2_satellite"
|
||||||
"nist_clock_satellite_100mhz" "nist_qc2_satellite_100mhz" "acpki_nist_clock_satellite_100mhz" "acpki_nist_qc2_satellite_100mhz"
|
"nist_clock_satellite_100mhz" "nist_qc2_satellite_100mhz" "acpki_nist_clock_satellite_100mhz" "acpki_nist_qc2_satellite_100mhz"
|
||||||
];
|
];
|
||||||
build = { target, variant, json ? null }: let
|
board-package-set = { target, variant, json ? null }: let
|
||||||
szl = zynqpkgs."${target}-szl";
|
szl = zynqpkgs."${target}-szl";
|
||||||
fsbl = zynqpkgs."${target}-fsbl";
|
fsbl = zynqpkgs."${target}-fsbl";
|
||||||
fwtype = if builtins.elem variant sat_variants then "satman" else "runtime";
|
fwtype = if builtins.elem variant sat_variants then "satman" else "runtime";
|
||||||
|
@ -123,7 +124,8 @@
|
||||||
cargoLock = {
|
cargoLock = {
|
||||||
lockFile = src/Cargo.lock;
|
lockFile = src/Cargo.lock;
|
||||||
outputHashes = {
|
outputHashes = {
|
||||||
"libasync-0.0.0" = "sha256-MppR7yxs3cjG7tQc82vX0MhyN71CJL2QWkM65F5hrFU=";
|
"tar-no-std-0.1.8" = "sha256-xm17108v4smXOqxdLvHl9CxTCJslmeogjm4Y87IXFuM=";
|
||||||
|
"nalgebra-0.32.6" = "sha256-L/YudkVOtfGYoNQKBD7LMk/sMYgRDzPDdpGL5rO7G2I=";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -131,13 +133,14 @@
|
||||||
pkgs.gnumake
|
pkgs.gnumake
|
||||||
(pkgs.python3.withPackages(ps: [ ps.jsonschema artiqpkgs.migen migen-axi artiqpkgs.misoc artiqpkgs.artiq ]))
|
(pkgs.python3.withPackages(ps: [ ps.jsonschema artiqpkgs.migen migen-axi artiqpkgs.misoc artiqpkgs.artiq ]))
|
||||||
zynqpkgs.cargo-xbuild
|
zynqpkgs.cargo-xbuild
|
||||||
pkgs.llvmPackages_9.llvm
|
llvmPackages_11.llvm
|
||||||
pkgs.llvmPackages_9.clang-unwrapped
|
llvmPackages_11.clang-unwrapped
|
||||||
];
|
];
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
export XARGO_RUST_SRC="${rust}/lib/rustlib/src/rust/library"
|
export XARGO_RUST_SRC="${rust}/lib/rustlib/src/rust/library"
|
||||||
export CLANG_EXTRA_INCLUDE_DIR="${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include"
|
export CLANG_EXTRA_INCLUDE_DIR="${llvmPackages_11.clang-unwrapped.lib}/lib/clang/11.1.0/include"
|
||||||
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
|
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
|
||||||
|
export ZYNQ_RS=${zynq-rs}
|
||||||
make TARGET=${target} GWARGS="${if json == null then "-V ${variant}" else json}" ${fwtype}
|
make TARGET=${target} GWARGS="${if json == null then "-V ${variant}" else json}" ${fwtype}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@ -151,6 +154,7 @@
|
||||||
|
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
dontFixup = true;
|
dontFixup = true;
|
||||||
|
auditable = false;
|
||||||
};
|
};
|
||||||
gateware = pkgs.runCommand "${target}-${variant}-gateware"
|
gateware = pkgs.runCommand "${target}-${variant}-gateware"
|
||||||
{
|
{
|
||||||
|
@ -251,25 +255,26 @@
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
fmt-check = pkgs.stdenv.mkDerivation {
|
fmt-check = pkgs.stdenvNoCC.mkDerivation {
|
||||||
name = "fmt-check";
|
name = "fmt-check";
|
||||||
|
|
||||||
nativeBuildInputs = [
|
src = ./src;
|
||||||
rust
|
|
||||||
];
|
|
||||||
|
|
||||||
phases = [ "buildPhase" ];
|
nativeBuildInputs = [ rust pkgs.gnumake ];
|
||||||
|
|
||||||
|
phases = [ "unpackPhase" "buildPhase" ];
|
||||||
|
|
||||||
buildPhase =
|
buildPhase =
|
||||||
''
|
''
|
||||||
cd ${self}/src
|
export ZYNQ_RS=${zynq-rs}
|
||||||
|
make manifests
|
||||||
cargo fmt -- --check
|
cargo fmt -- --check
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# for hitl-tests
|
# for hitl-tests
|
||||||
zc706-nist_qc2 = (build { target = "zc706"; variant = "nist_qc2"; });
|
zc706-nist_qc2 = (board-package-set { target = "zc706"; variant = "nist_qc2"; });
|
||||||
zc706-hitl-tests = pkgs.stdenv.mkDerivation {
|
zc706-hitl-tests = pkgs.stdenv.mkDerivation {
|
||||||
name = "zc706-hitl-tests";
|
name = "zc706-hitl-tests";
|
||||||
|
|
||||||
|
@ -336,29 +341,30 @@
|
||||||
{
|
{
|
||||||
inherit fastnumbers artiq-netboot ramda migen-axi binutils-arm;
|
inherit fastnumbers artiq-netboot ramda migen-axi binutils-arm;
|
||||||
} //
|
} //
|
||||||
(build { target = "zc706"; variant = "nist_clock"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_clock"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_clock_master"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_clock_master"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_clock_master_100mhz"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_clock_master_100mhz"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_clock_satellite"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_clock_satellite"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_clock_satellite_100mhz"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_clock_satellite_100mhz"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_qc2"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_qc2"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_qc2_master"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_qc2_master"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_qc2_master_100mhz"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_qc2_master_100mhz"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_qc2_satellite"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_qc2_satellite"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_qc2_satellite_100mhz"; }) //
|
(board-package-set { target = "zc706"; variant = "nist_qc2_satellite_100mhz"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_clock"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_clock"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_clock_master"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_clock_master"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_clock_master_100mhz"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_clock_master_100mhz"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_clock_satellite"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_clock_satellite"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_clock_satellite_100mhz"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_clock_satellite_100mhz"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_qc2"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_qc2"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_qc2_master"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_qc2_master"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_qc2_master_100mhz"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_qc2_master_100mhz"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_qc2_satellite"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) //
|
(board-package-set { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) //
|
||||||
(build { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) //
|
(board-package-set { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) //
|
||||||
(build { target = "kasli_soc"; variant = "master"; json = ./kasli-soc-master.json; }) //
|
(board-package-set { target = "kasli_soc"; variant = "master"; json = ./kasli-soc-master.json; }) //
|
||||||
(build { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; });
|
(board-package-set { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; }) //
|
||||||
|
(board-package-set { target = "ebaz4205"; variant = "base"; });
|
||||||
|
|
||||||
hydraJobs = packages.x86_64-linux // { inherit zc706-hitl-tests; inherit gateware-sim; inherit fmt-check; };
|
hydraJobs = packages.x86_64-linux // { inherit zc706-hitl-tests; inherit gateware-sim; inherit fmt-check; };
|
||||||
|
|
||||||
|
@ -366,8 +372,8 @@
|
||||||
name = "artiq-zynq-dev-shell";
|
name = "artiq-zynq-dev-shell";
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
rust
|
rust
|
||||||
llvmPackages_9.llvm
|
llvmPackages_11.llvm
|
||||||
llvmPackages_9.clang-unwrapped
|
llvmPackages_11.clang-unwrapped
|
||||||
gnumake
|
gnumake
|
||||||
cacert
|
cacert
|
||||||
zynqpkgs.cargo-xbuild
|
zynqpkgs.cargo-xbuild
|
||||||
|
@ -378,14 +384,16 @@
|
||||||
artiqpkgs.artiq
|
artiqpkgs.artiq
|
||||||
artiqpkgs.vivado
|
artiqpkgs.vivado
|
||||||
binutils-arm
|
binutils-arm
|
||||||
|
pre-commit
|
||||||
];
|
];
|
||||||
XARGO_RUST_SRC = "${rust}/lib/rustlib/src/rust/library";
|
XARGO_RUST_SRC = "${rust}/lib/rustlib/src/rust/library";
|
||||||
CLANG_EXTRA_INCLUDE_DIR = "${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include";
|
CLANG_EXTRA_INCLUDE_DIR = "${llvmPackages_11.clang-unwrapped.lib}/lib/clang/11.1.0/include";
|
||||||
|
ZYNQ_RS = "${zynq-rs}";
|
||||||
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
|
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
|
||||||
SZL = "${zynqpkgs.szl}";
|
SZL = "${zynqpkgs.szl}";
|
||||||
};
|
};
|
||||||
|
|
||||||
makeArtiqZynqPackage = build;
|
makeArtiqZynqPackage = board-package-set;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
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
|
|
@ -0,0 +1 @@
|
||||||
|
doc-valid-idents = ["CPython", "NumPy", ".."]
|
|
@ -0,0 +1,32 @@
|
||||||
|
# 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]
|
|
@ -2,6 +2,21 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "approx"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-recursion"
|
name = "async-recursion"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -218,10 +233,38 @@ dependencies = [
|
||||||
"libsupport_zynq",
|
"libsupport_zynq",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ksupport"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"build_zynq",
|
||||||
|
"byteorder",
|
||||||
|
"core_io",
|
||||||
|
"cslice",
|
||||||
|
"dwarf",
|
||||||
|
"dyld",
|
||||||
|
"io",
|
||||||
|
"libasync",
|
||||||
|
"libboard_artiq",
|
||||||
|
"libboard_zynq",
|
||||||
|
"libc",
|
||||||
|
"libconfig",
|
||||||
|
"libcortex_a9",
|
||||||
|
"libm",
|
||||||
|
"libregister",
|
||||||
|
"libsupport_zynq",
|
||||||
|
"log",
|
||||||
|
"log_buffer",
|
||||||
|
"nalgebra",
|
||||||
|
"nb 0.1.3",
|
||||||
|
"unwind",
|
||||||
|
"vcell",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libasync"
|
name = "libasync"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#c15b54f92b3d4e125ae47a0dce7abe4b2bc9e054"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"libcortex_a9",
|
"libcortex_a9",
|
||||||
|
@ -244,6 +287,7 @@ dependencies = [
|
||||||
"libconfig",
|
"libconfig",
|
||||||
"libcortex_a9",
|
"libcortex_a9",
|
||||||
"libregister",
|
"libregister",
|
||||||
|
"libsupport_zynq",
|
||||||
"log",
|
"log",
|
||||||
"log_buffer",
|
"log_buffer",
|
||||||
"nb 1.0.0",
|
"nb 1.0.0",
|
||||||
|
@ -253,7 +297,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libboard_zynq"
|
name = "libboard_zynq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#c15b54f92b3d4e125ae47a0dce7abe4b2bc9e054"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
|
@ -278,7 +321,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libconfig"
|
name = "libconfig"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#c15b54f92b3d4e125ae47a0dce7abe4b2bc9e054"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core_io",
|
"core_io",
|
||||||
"fatfs",
|
"fatfs",
|
||||||
|
@ -289,7 +331,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libcortex_a9"
|
name = "libcortex_a9"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#c15b54f92b3d4e125ae47a0dce7abe4b2bc9e054"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"libregister",
|
"libregister",
|
||||||
|
@ -305,7 +346,6 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libregister"
|
name = "libregister"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#c15b54f92b3d4e125ae47a0dce7abe4b2bc9e054"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"vcell",
|
"vcell",
|
||||||
|
@ -315,7 +355,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsupport_zynq"
|
name = "libsupport_zynq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#c15b54f92b3d4e125ae47a0dce7abe4b2bc9e054"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"compiler_builtins",
|
"compiler_builtins",
|
||||||
|
@ -353,6 +392,19 @@ version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
|
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nalgebra"
|
||||||
|
version = "0.32.6"
|
||||||
|
source = "git+https://git.m-labs.hk/M-Labs/nalgebra.git?rev=dd00f9b#dd00f9b46046e0b931d1b470166db02fd29591be"
|
||||||
|
dependencies = [
|
||||||
|
"approx",
|
||||||
|
"num-complex",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
"simba",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb"
|
name = "nb"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -368,6 +420,15 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-derive"
|
name = "num-derive"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
@ -379,6 +440,26 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -386,8 +467,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
@ -438,20 +526,20 @@ dependencies = [
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"futures",
|
"futures",
|
||||||
"io",
|
"io",
|
||||||
|
"ksupport",
|
||||||
"libasync",
|
"libasync",
|
||||||
"libboard_artiq",
|
"libboard_artiq",
|
||||||
"libboard_zynq",
|
"libboard_zynq",
|
||||||
"libc",
|
"libc",
|
||||||
"libconfig",
|
"libconfig",
|
||||||
"libcortex_a9",
|
"libcortex_a9",
|
||||||
"libm",
|
|
||||||
"libregister",
|
"libregister",
|
||||||
"libsupport_zynq",
|
"libsupport_zynq",
|
||||||
"log",
|
"log",
|
||||||
"log_buffer",
|
"log_buffer",
|
||||||
"nb 0.1.3",
|
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"tar-no-std",
|
||||||
"unwind",
|
"unwind",
|
||||||
"vcell",
|
"vcell",
|
||||||
"void",
|
"void",
|
||||||
|
@ -471,7 +559,13 @@ name = "satman"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"build_zynq",
|
"build_zynq",
|
||||||
|
"byteorder",
|
||||||
|
"core_io",
|
||||||
|
"crc",
|
||||||
|
"cslice",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
|
"io",
|
||||||
|
"ksupport",
|
||||||
"libasync",
|
"libasync",
|
||||||
"libboard_artiq",
|
"libboard_artiq",
|
||||||
"libboard_zynq",
|
"libboard_zynq",
|
||||||
|
@ -490,6 +584,18 @@ version = "0.1.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simba"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4"
|
||||||
|
dependencies = [
|
||||||
|
"approx",
|
||||||
|
"num-complex",
|
||||||
|
"num-traits",
|
||||||
|
"paste",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smoltcp"
|
name = "smoltcp"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
|
@ -512,6 +618,22 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tar-no-std"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "git+https://git.m-labs.hk/M-Labs/tar-no-std?rev=2ab6dc5#2ab6dc58e5249c59c4eb03eaf3a119bcdd678d32"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"bitflags",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
|
|
@ -5,6 +5,7 @@ members = [
|
||||||
"libdwarf",
|
"libdwarf",
|
||||||
"libio",
|
"libio",
|
||||||
"libunwind",
|
"libunwind",
|
||||||
|
"libksupport",
|
||||||
"runtime",
|
"runtime",
|
||||||
"satman"
|
"satman"
|
||||||
]
|
]
|
||||||
|
|
13
src/Makefile
13
src/Makefile
|
@ -7,13 +7,20 @@ runtime: ../build/runtime.bin
|
||||||
|
|
||||||
satman: ../build/satman.bin
|
satman: ../build/satman.bin
|
||||||
|
|
||||||
.PHONY: all runtime_target satman_target
|
.PHONY: all manifests
|
||||||
|
|
||||||
|
manifests = libboard_artiq/Cargo.toml libc/Cargo.toml libdyld/Cargo.toml libio/Cargo.toml libksupport/Cargo.toml runtime/Cargo.toml satman/Cargo.toml
|
||||||
|
|
||||||
|
$(manifests): %.toml: %.toml.tpl
|
||||||
|
sed s+@@ZYNQ_RS@@+$(ZYNQ_RS)+g $< > $@
|
||||||
|
|
||||||
|
manifests: $(manifests)
|
||||||
|
|
||||||
../build/pl.rs ../build/rustc-cfg ../build/mem.rs: gateware/*
|
../build/pl.rs ../build/rustc-cfg ../build/mem.rs: gateware/*
|
||||||
mkdir -p ../build
|
mkdir -p ../build
|
||||||
python gateware/$(TARGET).py -r ../build/pl.rs -c ../build/rustc-cfg -m ../build/mem.rs $(GWARGS)
|
python gateware/$(TARGET).py -r ../build/pl.rs -c ../build/rustc-cfg -m ../build/mem.rs $(GWARGS)
|
||||||
|
|
||||||
../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
|
../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(manifests) $(shell find . -type f -not -name Cargo.toml -print)
|
||||||
cd runtime && \
|
cd runtime && \
|
||||||
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
||||||
cargo xbuild --release \
|
cargo xbuild --release \
|
||||||
|
@ -23,7 +30,7 @@ satman: ../build/satman.bin
|
||||||
../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime
|
../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime
|
||||||
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
|
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
|
||||||
|
|
||||||
../build/firmware/armv7-none-eabihf/release/satman: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
|
../build/firmware/armv7-none-eabihf/release/satman: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(manifests) $(shell find . -type f -not -name Cargo.toml -print)
|
||||||
cd satman && \
|
cd satman && \
|
||||||
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
||||||
cargo xbuild --release \
|
cargo xbuild --release \
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
from migen import *
|
||||||
|
from migen.genlib.cdc import PulseSynchronizer, MultiReg
|
||||||
|
from misoc.interconnect.csr import *
|
||||||
|
|
||||||
|
|
||||||
|
class DDMTDSampler(Module):
|
||||||
|
def __init__(self, cd_ref, main_clk_se):
|
||||||
|
self.ref_beating = Signal()
|
||||||
|
self.main_beating = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
ref_clk = Signal()
|
||||||
|
self.specials +=[
|
||||||
|
# ISERDESE2 can only be driven from fabric via IDELAYE2 (see UG471)
|
||||||
|
Instance("IDELAYE2",
|
||||||
|
p_DELAY_SRC="DATAIN",
|
||||||
|
p_HIGH_PERFORMANCE_MODE="TRUE",
|
||||||
|
p_REFCLK_FREQUENCY=208.3, # REFCLK frequency from IDELAYCTRL
|
||||||
|
p_IDELAY_VALUE=0,
|
||||||
|
|
||||||
|
i_DATAIN=cd_ref.clk,
|
||||||
|
|
||||||
|
o_DATAOUT=ref_clk
|
||||||
|
),
|
||||||
|
Instance("ISERDESE2",
|
||||||
|
p_IOBDELAY="IFD", # use DDLY as input
|
||||||
|
p_DATA_RATE="SDR",
|
||||||
|
p_DATA_WIDTH=2, # min is 2
|
||||||
|
p_NUM_CE=1,
|
||||||
|
|
||||||
|
i_DDLY=ref_clk,
|
||||||
|
i_CE1=1,
|
||||||
|
i_CLK=ClockSignal("helper"),
|
||||||
|
i_CLKDIV=ClockSignal("helper"),
|
||||||
|
|
||||||
|
o_Q1=self.ref_beating
|
||||||
|
),
|
||||||
|
Instance("ISERDESE2",
|
||||||
|
p_DATA_RATE="SDR",
|
||||||
|
p_DATA_WIDTH=2, # min is 2
|
||||||
|
p_NUM_CE=1,
|
||||||
|
|
||||||
|
i_D=main_clk_se,
|
||||||
|
i_CE1=1,
|
||||||
|
i_CLK=ClockSignal("helper"),
|
||||||
|
i_CLKDIV=ClockSignal("helper"),
|
||||||
|
|
||||||
|
o_Q1=self.main_beating,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DDMTDDeglitcherMedianEdge(Module):
|
||||||
|
def __init__(self, counter, input_signal, stable_0_period=100, stable_1_period=100):
|
||||||
|
self.tag = Signal(len(counter))
|
||||||
|
self.detect = Signal()
|
||||||
|
|
||||||
|
stable_0_counter = Signal(reset=stable_0_period - 1, max=stable_0_period)
|
||||||
|
stable_1_counter = Signal(reset=stable_1_period - 1, max=stable_1_period)
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# Based on CERN's median edge deglitcher FSM
|
||||||
|
# https://white-rabbit.web.cern.ch/documents/Precise_time_and_frequency_transfer_in_a_White_Rabbit_network.pdf (p.72)
|
||||||
|
fsm = ClockDomainsRenamer("helper")(FSM(reset_state="WAIT_STABLE_0"))
|
||||||
|
self.submodules += fsm
|
||||||
|
|
||||||
|
fsm.act("WAIT_STABLE_0",
|
||||||
|
If(stable_0_counter != 0,
|
||||||
|
NextValue(stable_0_counter, stable_0_counter - 1)
|
||||||
|
).Else(
|
||||||
|
NextValue(stable_0_counter, stable_0_period - 1),
|
||||||
|
NextState("WAIT_EDGE")
|
||||||
|
),
|
||||||
|
If(input_signal,
|
||||||
|
NextValue(stable_0_counter, stable_0_period - 1)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
fsm.act("WAIT_EDGE",
|
||||||
|
If(input_signal,
|
||||||
|
NextValue(self.tag, counter),
|
||||||
|
NextState("GOT_EDGE")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("GOT_EDGE",
|
||||||
|
If(stable_1_counter != 0,
|
||||||
|
NextValue(stable_1_counter, stable_1_counter - 1)
|
||||||
|
).Else(
|
||||||
|
NextValue(stable_1_counter, stable_1_period - 1),
|
||||||
|
self.detect.eq(1),
|
||||||
|
NextState("WAIT_STABLE_0")
|
||||||
|
),
|
||||||
|
If(~input_signal,
|
||||||
|
NextValue(self.tag, self.tag + 1),
|
||||||
|
NextValue(stable_1_counter, stable_1_period - 1)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DDMTD(Module):
|
||||||
|
def __init__(self, counter, input_signal):
|
||||||
|
|
||||||
|
# in helper clock domain
|
||||||
|
self.h_tag = Signal(len(counter))
|
||||||
|
self.h_tag_update = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
deglitcher = DDMTDDeglitcherMedianEdge(counter, input_signal)
|
||||||
|
self.submodules += deglitcher
|
||||||
|
|
||||||
|
self.sync.helper += [
|
||||||
|
self.h_tag_update.eq(0),
|
||||||
|
If(deglitcher.detect,
|
||||||
|
self.h_tag_update.eq(1),
|
||||||
|
self.h_tag.eq(deglitcher.tag)
|
||||||
|
)
|
||||||
|
]
|
|
@ -1,12 +1,12 @@
|
||||||
"""Auxiliary controller, common to satellite and master"""
|
"""Auxiliary controller, common to satellite and master"""
|
||||||
|
|
||||||
from artiq.gateware.drtio.aux_controller import Transmitter, Receiver
|
from artiq.gateware.drtio.aux_controller import (max_packet, aux_buffer_count,
|
||||||
|
Transmitter, Receiver)
|
||||||
from migen.fhdl.simplify import FullMemoryWE
|
from migen.fhdl.simplify import FullMemoryWE
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
from migen_axi.interconnect.sram import SRAM
|
from migen_axi.interconnect.sram import SRAM
|
||||||
from migen_axi.interconnect import axi
|
from migen_axi.interconnect import axi
|
||||||
|
|
||||||
max_packet = 1024
|
|
||||||
|
|
||||||
class _DRTIOAuxControllerBase(Module):
|
class _DRTIOAuxControllerBase(Module):
|
||||||
def __init__(self, link_layer):
|
def __init__(self, link_layer):
|
||||||
|
@ -27,12 +27,12 @@ class DRTIOAuxControllerAxi(_DRTIOAuxControllerBase):
|
||||||
tx_sdram_if = SRAM(self.transmitter.mem, read_only=False)
|
tx_sdram_if = SRAM(self.transmitter.mem, read_only=False)
|
||||||
rx_sdram_if = SRAM(self.receiver.mem, read_only=True)
|
rx_sdram_if = SRAM(self.receiver.mem, read_only=True)
|
||||||
aw_decoder = axi.AddressDecoder(self.bus.aw,
|
aw_decoder = axi.AddressDecoder(self.bus.aw,
|
||||||
[(lambda a: a[log2_int(max_packet)] == 0, tx_sdram_if.bus.aw),
|
[(lambda a: a[log2_int(max_packet*aux_buffer_count)] == 0, tx_sdram_if.bus.aw),
|
||||||
(lambda a: a[log2_int(max_packet)] == 1, rx_sdram_if.bus.aw)],
|
(lambda a: a[log2_int(max_packet*aux_buffer_count)] == 1, rx_sdram_if.bus.aw)],
|
||||||
register=True)
|
register=True)
|
||||||
ar_decoder = axi.AddressDecoder(self.bus.ar,
|
ar_decoder = axi.AddressDecoder(self.bus.ar,
|
||||||
[(lambda a: a[log2_int(max_packet)] == 0, tx_sdram_if.bus.ar),
|
[(lambda a: a[log2_int(max_packet*aux_buffer_count)] == 0, tx_sdram_if.bus.ar),
|
||||||
(lambda a: a[log2_int(max_packet)] == 1, rx_sdram_if.bus.ar)],
|
(lambda a: a[log2_int(max_packet*aux_buffer_count)] == 1, rx_sdram_if.bus.ar)],
|
||||||
register=True)
|
register=True)
|
||||||
# unlike wb, axi address decoder only connects ar/aw lanes,
|
# unlike wb, axi address decoder only connects ar/aw lanes,
|
||||||
# the rest must also be connected!
|
# the rest must also be connected!
|
||||||
|
@ -82,4 +82,4 @@ class DRTIOAuxControllerBare(_DRTIOAuxControllerBase):
|
||||||
return self.receiver.mem.get_port(write_capable=False)
|
return self.receiver.mem.get_port(write_capable=False)
|
||||||
|
|
||||||
def get_mem_size(self):
|
def get_mem_size(self):
|
||||||
return max_packet
|
return max_packet*aux_buffer_count
|
||||||
|
|
|
@ -0,0 +1,307 @@
|
||||||
|
#!/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()
|
|
@ -16,10 +16,11 @@ from artiq.coredevice import jsondesc
|
||||||
from artiq.gateware import rtio, eem_7series
|
from artiq.gateware import rtio, eem_7series
|
||||||
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
||||||
from artiq.gateware.rtio.phy import ttl_simple
|
from artiq.gateware.rtio.phy import ttl_simple
|
||||||
from artiq.gateware.drtio.transceiver import gtx_7series
|
from artiq.gateware.drtio.transceiver import gtx_7series, eem_serdes
|
||||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
||||||
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
||||||
from artiq.gateware.drtio import *
|
from artiq.gateware.drtio import *
|
||||||
|
from artiq.gateware.wrpll import wrpll
|
||||||
|
|
||||||
import dma
|
import dma
|
||||||
import analyzer
|
import analyzer
|
||||||
|
@ -107,6 +108,8 @@ class GTPBootstrapClock(Module):
|
||||||
class GenericStandalone(SoCCore):
|
class GenericStandalone(SoCCore):
|
||||||
def __init__(self, description, acpki=False):
|
def __init__(self, description, acpki=False):
|
||||||
self.acpki = acpki
|
self.acpki = acpki
|
||||||
|
clk_freq = description["rtio_frequency"]
|
||||||
|
with_wrpll = description["enable_wrpll"]
|
||||||
|
|
||||||
platform = kasli_soc.Platform()
|
platform = kasli_soc.Platform()
|
||||||
platform.toolchain.bitstream_commands.extend([
|
platform.toolchain.bitstream_commands.extend([
|
||||||
|
@ -118,30 +121,47 @@ class GenericStandalone(SoCCore):
|
||||||
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident, ps_cd_sys=False)
|
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident, ps_cd_sys=False)
|
||||||
|
|
||||||
self.config["HW_REV"] = description["hw_rev"]
|
self.config["HW_REV"] = description["hw_rev"]
|
||||||
|
|
||||||
|
|
||||||
self.submodules += SMAClkinForward(self.platform)
|
|
||||||
|
|
||||||
self.config["HAS_SI5324"] = None
|
|
||||||
self.config["SI5324_SOFT_RESET"] = None
|
|
||||||
|
|
||||||
clk_synth = platform.request("cdr_clk_clean_fabric")
|
clk_synth = platform.request("cdr_clk_clean_fabric")
|
||||||
clk_synth_se = Signal()
|
clk_synth_se = Signal()
|
||||||
|
clk_synth_se_buf = Signal()
|
||||||
platform.add_period_constraint(clk_synth.p, 8.0)
|
platform.add_period_constraint(clk_synth.p, 8.0)
|
||||||
|
|
||||||
self.specials += Instance("IBUFGDS",
|
self.specials += [
|
||||||
|
Instance("IBUFGDS",
|
||||||
p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE",
|
p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE",
|
||||||
i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se)
|
i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se
|
||||||
|
),
|
||||||
|
Instance("BUFG", i_I=clk_synth_se, o_O=clk_synth_se_buf),
|
||||||
|
]
|
||||||
fix_serdes_timing_path(platform)
|
fix_serdes_timing_path(platform)
|
||||||
self.submodules.bootstrap = GTPBootstrapClock(self.platform, description["rtio_frequency"])
|
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq)
|
||||||
|
self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6)
|
||||||
|
self.config["CLOCK_FREQUENCY"] = int(clk_freq)
|
||||||
|
|
||||||
self.submodules.sys_crg = zynq_clocking.SYSCRG(self.platform, self.ps7, clk_synth_se)
|
self.submodules.sys_crg = zynq_clocking.SYSCRG(self.platform, self.ps7, clk_synth_se_buf)
|
||||||
platform.add_false_path_constraints(
|
platform.add_false_path_constraints(
|
||||||
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
||||||
self.csr_devices.append("sys_crg")
|
self.csr_devices.append("sys_crg")
|
||||||
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
||||||
self.crg.cd_sys = self.sys_crg.cd_sys
|
self.crg.cd_sys = self.sys_crg.cd_sys
|
||||||
|
|
||||||
|
if with_wrpll:
|
||||||
|
self.submodules.wrpll_refclk = wrpll.FrequencyMultiplier(platform.request("sma_clkin"))
|
||||||
|
self.submodules.wrpll = wrpll.WRPLL(
|
||||||
|
platform=self.platform,
|
||||||
|
cd_ref=self.wrpll_refclk.cd_ref,
|
||||||
|
main_clk_se=clk_synth_se)
|
||||||
|
self.csr_devices.append("wrpll_refclk")
|
||||||
|
self.csr_devices.append("wrpll")
|
||||||
|
self.comb += self.ps7.core.core0.nfiq.eq(self.wrpll.ev.irq)
|
||||||
|
self.config["HAS_SI549"] = None
|
||||||
|
self.config["WRPLL_REF_CLK"] = "SMA_CLKIN"
|
||||||
|
else:
|
||||||
|
self.submodules += SMAClkinForward(self.platform)
|
||||||
|
self.config["HAS_SI5324"] = None
|
||||||
|
self.config["SI5324_SOFT_RESET"] = None
|
||||||
|
|
||||||
|
|
||||||
self.rtio_channels = []
|
self.rtio_channels = []
|
||||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||||
if has_grabber:
|
if has_grabber:
|
||||||
|
@ -200,7 +220,9 @@ class GenericStandalone(SoCCore):
|
||||||
class GenericMaster(SoCCore):
|
class GenericMaster(SoCCore):
|
||||||
def __init__(self, description, acpki=False):
|
def __init__(self, description, acpki=False):
|
||||||
clk_freq = description["rtio_frequency"]
|
clk_freq = description["rtio_frequency"]
|
||||||
|
with_wrpll = description["enable_wrpll"]
|
||||||
|
|
||||||
|
has_drtio_over_eem = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"])
|
||||||
self.acpki = acpki
|
self.acpki = acpki
|
||||||
|
|
||||||
platform = kasli_soc.Platform()
|
platform = kasli_soc.Platform()
|
||||||
|
@ -214,8 +236,6 @@ class GenericMaster(SoCCore):
|
||||||
|
|
||||||
self.config["HW_REV"] = description["hw_rev"]
|
self.config["HW_REV"] = description["hw_rev"]
|
||||||
|
|
||||||
self.submodules += SMAClkinForward(self.platform)
|
|
||||||
|
|
||||||
data_pads = [platform.request("sfp", i) for i in range(4)]
|
data_pads = [platform.request("sfp", i) for i in range(4)]
|
||||||
|
|
||||||
self.submodules.gt_drtio = gtx_7series.GTX(
|
self.submodules.gt_drtio = gtx_7series.GTX(
|
||||||
|
@ -223,17 +243,23 @@ class GenericMaster(SoCCore):
|
||||||
pads=data_pads,
|
pads=data_pads,
|
||||||
clk_freq=clk_freq)
|
clk_freq=clk_freq)
|
||||||
self.csr_devices.append("gt_drtio")
|
self.csr_devices.append("gt_drtio")
|
||||||
|
self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6)
|
||||||
|
self.config["CLOCK_FREQUENCY"] = int(clk_freq)
|
||||||
|
|
||||||
txout_buf = Signal()
|
txout_buf = Signal()
|
||||||
gtx0 = self.gt_drtio.gtxs[0]
|
gtx0 = self.gt_drtio.gtxs[0]
|
||||||
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
|
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
|
||||||
|
|
||||||
|
ext_async_rst = Signal()
|
||||||
|
|
||||||
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq)
|
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq)
|
||||||
self.submodules.sys_crg = zynq_clocking.SYSCRG(
|
self.submodules.sys_crg = zynq_clocking.SYSCRG(
|
||||||
self.platform,
|
self.platform,
|
||||||
self.ps7,
|
self.ps7,
|
||||||
txout_buf,
|
txout_buf,
|
||||||
clk_sw=gtx0.tx_init.done)
|
clk_sw=self.gt_drtio.stable_clkin.storage,
|
||||||
|
clk_sw_status=gtx0.tx_init.done,
|
||||||
|
ext_async_rst=ext_async_rst)
|
||||||
self.csr_devices.append("sys_crg")
|
self.csr_devices.append("sys_crg")
|
||||||
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
||||||
self.crg.cd_sys = self.sys_crg.cd_sys
|
self.crg.cd_sys = self.sys_crg.cd_sys
|
||||||
|
@ -241,11 +267,33 @@ class GenericMaster(SoCCore):
|
||||||
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
||||||
fix_serdes_timing_path(platform)
|
fix_serdes_timing_path(platform)
|
||||||
|
|
||||||
|
self.comb += ext_async_rst.eq(self.sys_crg.clk_sw_fsm.o_clk_sw & ~gtx0.tx_init.done)
|
||||||
|
self.specials += MultiReg(self.sys_crg.clk_sw_fsm.o_clk_sw & self.sys_crg.mmcm_locked, self.gt_drtio.clk_path_ready, odomain="bootstrap")
|
||||||
|
|
||||||
|
if with_wrpll:
|
||||||
|
clk_synth = platform.request("cdr_clk_clean_fabric")
|
||||||
|
clk_synth_se = Signal()
|
||||||
|
platform.add_period_constraint(clk_synth.p, 8.0)
|
||||||
|
self.specials += Instance("IBUFGDS", p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE", i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se)
|
||||||
|
self.submodules.wrpll_refclk = wrpll.FrequencyMultiplier(platform.request("sma_clkin"))
|
||||||
|
self.submodules.wrpll = wrpll.WRPLL(
|
||||||
|
platform=self.platform,
|
||||||
|
cd_ref=self.wrpll_refclk.cd_ref,
|
||||||
|
main_clk_se=clk_synth_se)
|
||||||
|
self.csr_devices.append("wrpll_refclk")
|
||||||
|
self.csr_devices.append("wrpll")
|
||||||
|
self.comb += self.ps7.core.core0.nfiq.eq(self.wrpll.ev.irq)
|
||||||
|
self.config["HAS_SI549"] = None
|
||||||
|
self.config["WRPLL_REF_CLK"] = "SMA_CLKIN"
|
||||||
|
else:
|
||||||
|
self.submodules += SMAClkinForward(self.platform)
|
||||||
self.config["HAS_SI5324"] = None
|
self.config["HAS_SI5324"] = None
|
||||||
self.config["SI5324_SOFT_RESET"] = None
|
self.config["SI5324_SOFT_RESET"] = None
|
||||||
|
|
||||||
self.rtio_channels = []
|
self.rtio_channels = []
|
||||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||||
|
if has_drtio_over_eem:
|
||||||
|
self.eem_drtio_channels = []
|
||||||
if has_grabber:
|
if has_grabber:
|
||||||
self.grabber_csr_group = []
|
self.grabber_csr_group = []
|
||||||
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
|
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
|
||||||
|
@ -260,17 +308,17 @@ class GenericMaster(SoCCore):
|
||||||
|
|
||||||
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
||||||
|
|
||||||
drtio_csr_group = []
|
self.drtio_csr_group = []
|
||||||
drtioaux_csr_group = []
|
self.drtioaux_csr_group = []
|
||||||
drtioaux_memory_group = []
|
self.drtioaux_memory_group = []
|
||||||
self.drtio_cri = []
|
self.drtio_cri = []
|
||||||
for i in range(len(self.gt_drtio.channels)):
|
for i in range(len(self.gt_drtio.channels)):
|
||||||
core_name = "drtio" + str(i)
|
core_name = "drtio" + str(i)
|
||||||
coreaux_name = "drtioaux" + str(i)
|
coreaux_name = "drtioaux" + str(i)
|
||||||
memory_name = "drtioaux" + str(i) + "_mem"
|
memory_name = "drtioaux" + str(i) + "_mem"
|
||||||
drtio_csr_group.append(core_name)
|
self.drtio_csr_group.append(core_name)
|
||||||
drtioaux_csr_group.append(coreaux_name)
|
self.drtioaux_csr_group.append(coreaux_name)
|
||||||
drtioaux_memory_group.append(memory_name)
|
self.drtioaux_memory_group.append(memory_name)
|
||||||
|
|
||||||
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
|
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
|
||||||
|
|
||||||
|
@ -289,9 +337,10 @@ class GenericMaster(SoCCore):
|
||||||
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, size * 2)
|
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, size * 2)
|
||||||
self.config["HAS_DRTIO"] = None
|
self.config["HAS_DRTIO"] = None
|
||||||
self.config["HAS_DRTIO_ROUTING"] = None
|
self.config["HAS_DRTIO_ROUTING"] = None
|
||||||
self.add_csr_group("drtio", drtio_csr_group)
|
|
||||||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
if has_drtio_over_eem:
|
||||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
self.add_eem_drtio(self.eem_drtio_channels)
|
||||||
|
self.add_drtio_cpuif_groups()
|
||||||
|
|
||||||
self.submodules.rtio_core = rtio.Core(
|
self.submodules.rtio_core = rtio.Core(
|
||||||
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
|
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
|
||||||
|
@ -340,11 +389,48 @@ class GenericMaster(SoCCore):
|
||||||
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
|
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
|
||||||
for i, channel in enumerate(self.gt_drtio.channels)]
|
for i, channel in enumerate(self.gt_drtio.channels)]
|
||||||
|
|
||||||
|
def add_eem_drtio(self, eem_drtio_channels):
|
||||||
|
# Must be called before invoking add_rtio() to construct the CRI
|
||||||
|
# interconnect properly
|
||||||
|
self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, eem_drtio_channels)
|
||||||
|
self.csr_devices.append("eem_transceiver")
|
||||||
|
self.config["HAS_DRTIO_EEM"] = None
|
||||||
|
self.config["EEM_DRTIO_COUNT"] = len(eem_drtio_channels)
|
||||||
|
|
||||||
|
cdr = ClockDomainsRenamer({"rtio_rx": "sys"})
|
||||||
|
for i in range(len(self.eem_transceiver.channels)):
|
||||||
|
channel = i + len(self.gt_drtio.channels)
|
||||||
|
core_name = "drtio" + str(channel)
|
||||||
|
coreaux_name = "drtioaux" + str(channel)
|
||||||
|
memory_name = "drtioaux" + str(channel) + "_mem"
|
||||||
|
self.drtio_csr_group.append(core_name)
|
||||||
|
self.drtioaux_csr_group.append(coreaux_name)
|
||||||
|
self.drtioaux_memory_group.append(memory_name)
|
||||||
|
|
||||||
|
core = cdr(DRTIOMaster(self.rtio_tsc, self.eem_transceiver.channels[i]))
|
||||||
|
setattr(self.submodules, core_name, core)
|
||||||
|
self.drtio_cri.append(core.cri)
|
||||||
|
self.csr_devices.append(core_name)
|
||||||
|
|
||||||
|
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
|
||||||
|
setattr(self.submodules, coreaux_name, coreaux)
|
||||||
|
self.csr_devices.append(coreaux_name)
|
||||||
|
|
||||||
|
size = coreaux.get_mem_size()
|
||||||
|
memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), size)
|
||||||
|
self.axi2csr.register_port(coreaux.get_rx_port(), size)
|
||||||
|
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, size * 2)
|
||||||
|
|
||||||
|
def add_drtio_cpuif_groups(self):
|
||||||
|
self.add_csr_group("drtio", self.drtio_csr_group)
|
||||||
|
self.add_csr_group("drtioaux", self.drtioaux_csr_group)
|
||||||
|
self.add_memory_group("drtioaux_mem", self.drtioaux_memory_group)
|
||||||
|
|
||||||
|
|
||||||
class GenericSatellite(SoCCore):
|
class GenericSatellite(SoCCore):
|
||||||
def __init__(self, description, acpki=False):
|
def __init__(self, description, acpki=False):
|
||||||
clk_freq = description["rtio_frequency"]
|
clk_freq = description["rtio_frequency"]
|
||||||
|
with_wrpll = description["enable_wrpll"]
|
||||||
|
|
||||||
self.acpki = acpki
|
self.acpki = acpki
|
||||||
|
|
||||||
|
@ -371,12 +457,16 @@ class GenericSatellite(SoCCore):
|
||||||
gtx0 = self.gt_drtio.gtxs[0]
|
gtx0 = self.gt_drtio.gtxs[0]
|
||||||
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
|
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
|
||||||
|
|
||||||
|
ext_async_rst = Signal()
|
||||||
|
|
||||||
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq)
|
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq)
|
||||||
self.submodules.sys_crg = zynq_clocking.SYSCRG(
|
self.submodules.sys_crg = zynq_clocking.SYSCRG(
|
||||||
self.platform,
|
self.platform,
|
||||||
self.ps7,
|
self.ps7,
|
||||||
txout_buf,
|
txout_buf,
|
||||||
clk_sw=gtx0.tx_init.done)
|
clk_sw=self.gt_drtio.stable_clkin.storage,
|
||||||
|
clk_sw_status=gtx0.tx_init.done,
|
||||||
|
ext_async_rst=ext_async_rst)
|
||||||
platform.add_false_path_constraints(
|
platform.add_false_path_constraints(
|
||||||
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
||||||
self.csr_devices.append("sys_crg")
|
self.csr_devices.append("sys_crg")
|
||||||
|
@ -385,6 +475,9 @@ class GenericSatellite(SoCCore):
|
||||||
|
|
||||||
fix_serdes_timing_path(platform)
|
fix_serdes_timing_path(platform)
|
||||||
|
|
||||||
|
self.comb += ext_async_rst.eq(self.sys_crg.clk_sw_fsm.o_clk_sw & ~gtx0.tx_init.done)
|
||||||
|
self.specials += MultiReg(self.sys_crg.clk_sw_fsm.o_clk_sw & self.sys_crg.mmcm_locked, self.gt_drtio.clk_path_ready, odomain="bootstrap")
|
||||||
|
|
||||||
self.rtio_channels = []
|
self.rtio_channels = []
|
||||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||||
if has_grabber:
|
if has_grabber:
|
||||||
|
@ -467,10 +560,13 @@ class GenericSatellite(SoCCore):
|
||||||
self.submodules.local_io = SyncRTIO(
|
self.submodules.local_io = SyncRTIO(
|
||||||
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
|
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
|
||||||
)
|
)
|
||||||
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
self.comb += [
|
||||||
|
self.drtiosat.async_errors.eq(self.local_io.async_errors),
|
||||||
|
self.local_io.sed_spread_enable.eq(self.drtiosat.sed_spread_enable.storage)
|
||||||
|
]
|
||||||
|
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
[self.drtiosat.cri, self.rtio_dma.cri],
|
[self.drtiosat.cri, self.rtio_dma.cri, self.rtio.cri],
|
||||||
[self.local_io.cri] + self.drtio_cri,
|
[self.local_io.cri] + self.drtio_cri,
|
||||||
enable_routing=True)
|
enable_routing=True)
|
||||||
self.csr_devices.append("cri_con")
|
self.csr_devices.append("cri_con")
|
||||||
|
@ -487,7 +583,24 @@ class GenericSatellite(SoCCore):
|
||||||
|
|
||||||
rtio_clk_period = 1e9/clk_freq
|
rtio_clk_period = 1e9/clk_freq
|
||||||
self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6)
|
self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6)
|
||||||
|
self.config["CLOCK_FREQUENCY"] = int(clk_freq)
|
||||||
|
|
||||||
|
if with_wrpll:
|
||||||
|
clk_synth = platform.request("cdr_clk_clean_fabric")
|
||||||
|
clk_synth_se = Signal()
|
||||||
|
platform.add_period_constraint(clk_synth.p, 8.0)
|
||||||
|
self.specials += Instance("IBUFGDS", p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE", i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se)
|
||||||
|
self.submodules.wrpll = wrpll.WRPLL(
|
||||||
|
platform=self.platform,
|
||||||
|
cd_ref=self.gt_drtio.cd_rtio_rx0,
|
||||||
|
main_clk_se=clk_synth_se)
|
||||||
|
self.submodules.wrpll_skewtester = wrpll.SkewTester(self.rx_synchronizer)
|
||||||
|
self.csr_devices.append("wrpll_skewtester")
|
||||||
|
self.csr_devices.append("wrpll")
|
||||||
|
self.comb += self.ps7.core.core0.nfiq.eq(self.wrpll.ev.irq)
|
||||||
|
self.config["HAS_SI549"] = None
|
||||||
|
self.config["WRPLL_REF_CLK"] = "GT_CDR"
|
||||||
|
else:
|
||||||
self.submodules.siphaser = SiPhaser7Series(
|
self.submodules.siphaser = SiPhaser7Series(
|
||||||
si5324_clkin=platform.request("cdr_clk"),
|
si5324_clkin=platform.request("cdr_clk"),
|
||||||
rx_synchronizer=self.rx_synchronizer,
|
rx_synchronizer=self.rx_synchronizer,
|
||||||
|
|
|
@ -226,6 +226,7 @@ class _MasterBase(SoCCore):
|
||||||
self.csr_devices.append("gt_drtio")
|
self.csr_devices.append("gt_drtio")
|
||||||
|
|
||||||
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
||||||
|
ext_async_rst = Signal()
|
||||||
txout_buf = Signal()
|
txout_buf = Signal()
|
||||||
gtx0 = self.gt_drtio.gtxs[0]
|
gtx0 = self.gt_drtio.gtxs[0]
|
||||||
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
|
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
|
||||||
|
@ -234,12 +235,17 @@ class _MasterBase(SoCCore):
|
||||||
self.platform,
|
self.platform,
|
||||||
self.ps7,
|
self.ps7,
|
||||||
txout_buf,
|
txout_buf,
|
||||||
clk_sw=gtx0.tx_init.done,
|
clk_sw=self.gt_drtio.stable_clkin.storage,
|
||||||
|
clk_sw_status=gtx0.tx_init.done,
|
||||||
|
ext_async_rst=ext_async_rst,
|
||||||
freq=clk_freq)
|
freq=clk_freq)
|
||||||
platform.add_false_path_constraints(
|
platform.add_false_path_constraints(
|
||||||
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
||||||
self.csr_devices.append("sys_crg")
|
self.csr_devices.append("sys_crg")
|
||||||
|
|
||||||
|
self.comb += ext_async_rst.eq(self.sys_crg.clk_sw_fsm.o_clk_sw & ~gtx0.tx_init.done)
|
||||||
|
self.specials += MultiReg(self.sys_crg.clk_sw_fsm.o_clk_sw & self.sys_crg.mmcm_locked, self.gt_drtio.clk_path_ready, odomain="bootstrap")
|
||||||
|
|
||||||
drtio_csr_group = []
|
drtio_csr_group = []
|
||||||
drtioaux_csr_group = []
|
drtioaux_csr_group = []
|
||||||
drtioaux_memory_group = []
|
drtioaux_memory_group = []
|
||||||
|
@ -361,6 +367,7 @@ class _SatelliteBase(SoCCore):
|
||||||
clk_freq=clk_freq)
|
clk_freq=clk_freq)
|
||||||
self.csr_devices.append("gt_drtio")
|
self.csr_devices.append("gt_drtio")
|
||||||
|
|
||||||
|
ext_async_rst = Signal()
|
||||||
txout_buf = Signal()
|
txout_buf = Signal()
|
||||||
txout_buf.attr.add("keep")
|
txout_buf.attr.add("keep")
|
||||||
gtx0 = self.gt_drtio.gtxs[0]
|
gtx0 = self.gt_drtio.gtxs[0]
|
||||||
|
@ -373,12 +380,17 @@ class _SatelliteBase(SoCCore):
|
||||||
self.platform,
|
self.platform,
|
||||||
self.ps7,
|
self.ps7,
|
||||||
txout_buf,
|
txout_buf,
|
||||||
clk_sw=gtx0.tx_init.done,
|
clk_sw=self.gt_drtio.stable_clkin.storage,
|
||||||
|
clk_sw_status=gtx0.tx_init.done,
|
||||||
|
ext_async_rst=ext_async_rst,
|
||||||
freq=clk_freq)
|
freq=clk_freq)
|
||||||
platform.add_false_path_constraints(
|
platform.add_false_path_constraints(
|
||||||
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk)
|
||||||
self.csr_devices.append("sys_crg")
|
self.csr_devices.append("sys_crg")
|
||||||
|
|
||||||
|
self.comb += ext_async_rst.eq(self.sys_crg.clk_sw_fsm.o_clk_sw & ~gtx0.tx_init.done)
|
||||||
|
self.specials += MultiReg(self.sys_crg.clk_sw_fsm.o_clk_sw & self.sys_crg.mmcm_locked, self.gt_drtio.clk_path_ready, odomain="bootstrap")
|
||||||
|
|
||||||
drtioaux_csr_group = []
|
drtioaux_csr_group = []
|
||||||
drtioaux_memory_group = []
|
drtioaux_memory_group = []
|
||||||
drtiorep_csr_group = []
|
drtiorep_csr_group = []
|
||||||
|
@ -475,8 +487,12 @@ class _SatelliteBase(SoCCore):
|
||||||
self.csr_devices.append("rtio_dma")
|
self.csr_devices.append("rtio_dma")
|
||||||
|
|
||||||
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
|
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
|
||||||
|
self.comb += [
|
||||||
|
self.drtiosat.async_errors.eq(self.local_io.async_errors),
|
||||||
|
self.local_io.sed_spread_enable.eq(self.drtiosat.sed_spread_enable.storage)
|
||||||
|
]
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
[self.drtiosat.cri, self.rtio_dma.cri],
|
[self.drtiosat.cri, self.rtio_dma.cri, self.rtio.cri],
|
||||||
[self.local_io.cri] + self.drtio_cri,
|
[self.local_io.cri] + self.drtio_cri,
|
||||||
enable_routing=True)
|
enable_routing=True)
|
||||||
self.csr_devices.append("cri_con")
|
self.csr_devices.append("cri_con")
|
||||||
|
|
|
@ -65,10 +65,12 @@ class ClockSwitchFSM(Module):
|
||||||
|
|
||||||
|
|
||||||
class SYSCRG(Module, AutoCSR):
|
class SYSCRG(Module, AutoCSR):
|
||||||
def __init__(self, platform, ps7, main_clk, clk_sw=None, freq=125e6):
|
def __init__(self, platform, ps7, main_clk, clk_sw=None, clk_sw_status=None, freq=125e6, ext_async_rst=None, ):
|
||||||
# assumes bootstrap clock is same freq as main and sys output
|
# assumes bootstrap clock is same freq as main and sys output
|
||||||
self.clock_domains.cd_sys = ClockDomain()
|
self.clock_domains.cd_sys = ClockDomain()
|
||||||
self.clock_domains.cd_sys4x = ClockDomain(reset_less=True)
|
self.clock_domains.cd_sys4x = ClockDomain(reset_less=True)
|
||||||
|
self.clock_domains.cd_sys5x = ClockDomain(reset_less=True)
|
||||||
|
self.clock_domains.cd_clk200 = ClockDomain()
|
||||||
|
|
||||||
self.current_clock = CSRStatus()
|
self.current_clock = CSRStatus()
|
||||||
|
|
||||||
|
@ -78,11 +80,6 @@ class SYSCRG(Module, AutoCSR):
|
||||||
|
|
||||||
period = 1e9/freq
|
period = 1e9/freq
|
||||||
|
|
||||||
pll_locked = Signal()
|
|
||||||
pll_sys = Signal()
|
|
||||||
pll_sys4x = Signal()
|
|
||||||
fb_clk = Signal()
|
|
||||||
|
|
||||||
self.submodules.clk_sw_fsm = ClockSwitchFSM()
|
self.submodules.clk_sw_fsm = ClockSwitchFSM()
|
||||||
|
|
||||||
if clk_sw is None:
|
if clk_sw is None:
|
||||||
|
@ -91,32 +88,67 @@ class SYSCRG(Module, AutoCSR):
|
||||||
else:
|
else:
|
||||||
self.comb += self.clk_sw_fsm.i_clk_sw.eq(clk_sw)
|
self.comb += self.clk_sw_fsm.i_clk_sw.eq(clk_sw)
|
||||||
|
|
||||||
|
self.mmcm_locked = Signal()
|
||||||
|
mmcm_sys = Signal()
|
||||||
|
mmcm_sys4x = Signal()
|
||||||
|
mmcm_sys5x = Signal()
|
||||||
|
mmcm_clk208 = Signal()
|
||||||
|
mmcm_fb_clk = Signal()
|
||||||
self.specials += [
|
self.specials += [
|
||||||
Instance("PLLE2_ADV",
|
Instance("MMCME2_ADV",
|
||||||
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
|
p_STARTUP_WAIT="FALSE", o_LOCKED=self.mmcm_locked,
|
||||||
p_BANDWIDTH="HIGH",
|
p_BANDWIDTH="HIGH",
|
||||||
p_REF_JITTER1=0.001,
|
p_REF_JITTER1=0.001,
|
||||||
p_CLKIN1_PERIOD=period, i_CLKIN1=main_clk,
|
p_CLKIN1_PERIOD=period, i_CLKIN1=main_clk,
|
||||||
p_CLKIN2_PERIOD=period, i_CLKIN2=bootstrap_clk,
|
p_CLKIN2_PERIOD=period, i_CLKIN2=bootstrap_clk,
|
||||||
i_CLKINSEL=self.clk_sw_fsm.o_clk_sw,
|
i_CLKINSEL=self.clk_sw_fsm.o_clk_sw,
|
||||||
|
|
||||||
# VCO @ 1.5GHz when using 125MHz input
|
# VCO @ 1.25GHz
|
||||||
# 1.2GHz for 100MHz (zc706)
|
p_CLKFBOUT_MULT_F=10, p_DIVCLK_DIVIDE=1,
|
||||||
p_CLKFBOUT_MULT=12, p_DIVCLK_DIVIDE=1,
|
i_CLKFBIN=mmcm_fb_clk,
|
||||||
i_CLKFBIN=fb_clk,
|
|
||||||
i_RST=self.clk_sw_fsm.o_reset,
|
i_RST=self.clk_sw_fsm.o_reset,
|
||||||
|
|
||||||
o_CLKFBOUT=fb_clk,
|
o_CLKFBOUT=mmcm_fb_clk,
|
||||||
|
|
||||||
p_CLKOUT0_DIVIDE=3, p_CLKOUT0_PHASE=0.0,
|
p_CLKOUT0_DIVIDE_F=2.5, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=mmcm_sys4x,
|
||||||
o_CLKOUT0=pll_sys4x,
|
|
||||||
|
|
||||||
p_CLKOUT1_DIVIDE=12, p_CLKOUT1_PHASE=0.0,
|
# 125MHz
|
||||||
o_CLKOUT1=pll_sys),
|
p_CLKOUT1_DIVIDE=10, p_CLKOUT1_PHASE=0.0, o_CLKOUT1=mmcm_sys,
|
||||||
Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk),
|
|
||||||
Instance("BUFG", i_I=pll_sys4x, o_O=self.cd_sys4x.clk),
|
|
||||||
|
|
||||||
AsyncResetSynchronizer(self.cd_sys, ~pll_locked),
|
# 625MHz
|
||||||
|
p_CLKOUT2_DIVIDE=2, p_CLKOUT2_PHASE=0.0, o_CLKOUT2=mmcm_sys5x,
|
||||||
|
|
||||||
|
# 208MHz
|
||||||
|
p_CLKOUT3_DIVIDE=6, p_CLKOUT3_PHASE=0.0, o_CLKOUT3=mmcm_clk208,
|
||||||
|
),
|
||||||
|
Instance("BUFG", i_I=mmcm_sys5x, o_O=self.cd_sys5x.clk),
|
||||||
|
Instance("BUFG", i_I=mmcm_sys, o_O=self.cd_sys.clk),
|
||||||
|
Instance("BUFG", i_I=mmcm_sys4x, o_O=self.cd_sys4x.clk),
|
||||||
|
Instance("BUFG", i_I=mmcm_clk208, o_O=self.cd_clk200.clk),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if ext_async_rst is not None:
|
||||||
|
self.specials += [
|
||||||
|
AsyncResetSynchronizer(self.cd_sys, ~self.mmcm_locked | ext_async_rst),
|
||||||
|
AsyncResetSynchronizer(self.cd_clk200, ~self.mmcm_locked | ext_async_rst),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
self.specials += [
|
||||||
|
AsyncResetSynchronizer(self.cd_sys, ~self.mmcm_locked),
|
||||||
|
AsyncResetSynchronizer(self.cd_clk200, ~self.mmcm_locked),
|
||||||
|
]
|
||||||
|
|
||||||
|
reset_counter = Signal(4, reset=15)
|
||||||
|
ic_reset = Signal(reset=1)
|
||||||
|
self.sync.clk200 += \
|
||||||
|
If(reset_counter != 0,
|
||||||
|
reset_counter.eq(reset_counter - 1)
|
||||||
|
).Else(
|
||||||
|
ic_reset.eq(0)
|
||||||
|
)
|
||||||
|
self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset)
|
||||||
|
|
||||||
|
if clk_sw_status is None:
|
||||||
self.comb += self.current_clock.status.eq(self.clk_sw_fsm.o_clk_sw)
|
self.comb += self.current_clock.status.eq(self.clk_sw_fsm.o_clk_sw)
|
||||||
|
else:
|
||||||
|
self.comb += self.current_clock.status.eq(clk_sw_status)
|
||||||
|
|
|
@ -10,6 +10,8 @@ name = "libboard_artiq"
|
||||||
[features]
|
[features]
|
||||||
target_zc706 = ["libboard_zynq/target_zc706", "libconfig/target_zc706"]
|
target_zc706 = ["libboard_zynq/target_zc706", "libconfig/target_zc706"]
|
||||||
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
|
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]
|
[build-dependencies]
|
||||||
build_zynq = { path = "../libbuild_zynq" }
|
build_zynq = { path = "../libbuild_zynq" }
|
||||||
|
@ -24,8 +26,9 @@ nb = "1.0"
|
||||||
void = { version = "1", default-features = false }
|
void = { version = "1", default-features = false }
|
||||||
|
|
||||||
io = { path = "../libio", features = ["byteorder"] }
|
io = { path = "../libio", features = ["byteorder"] }
|
||||||
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"}
|
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq" }
|
||||||
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] }
|
||||||
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn"] }
|
libregister = { path = "@@ZYNQ_RS@@/libregister" }
|
||||||
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn"] }
|
||||||
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" }
|
||||||
|
libasync = { path = "@@ZYNQ_RS@@/libasync" }
|
|
@ -0,0 +1,245 @@
|
||||||
|
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
|
||||||
|
use libboard_zynq::timer::GlobalTimer;
|
||||||
|
use libconfig::Config;
|
||||||
|
use libsupport_zynq::alloc::format;
|
||||||
|
use log::{debug, error, info};
|
||||||
|
|
||||||
|
use crate::pl;
|
||||||
|
|
||||||
|
struct SerdesConfig {
|
||||||
|
pub delay: [u8; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerdesConfig {
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
core::slice::from_raw_parts(
|
||||||
|
(self as *const SerdesConfig) as *const u8,
|
||||||
|
core::mem::size_of::<SerdesConfig>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_lane(lane_no: u8) {
|
||||||
|
unsafe {
|
||||||
|
pl::csr::eem_transceiver::lane_sel_write(lane_no);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_delay(tap: u8, timer: &mut GlobalTimer) {
|
||||||
|
unsafe {
|
||||||
|
pl::csr::eem_transceiver::dly_cnt_in_write(tap);
|
||||||
|
pl::csr::eem_transceiver::dly_ld_write(1);
|
||||||
|
timer.delay_us(1);
|
||||||
|
assert!(tap as u8 == pl::csr::eem_transceiver::dly_cnt_out_read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_config(config: &SerdesConfig, timer: &mut GlobalTimer) {
|
||||||
|
for lane_no in 0..4 {
|
||||||
|
select_lane(lane_no as u8);
|
||||||
|
apply_delay(config.delay[lane_no], timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn assign_delay(timer: &mut GlobalTimer) -> SerdesConfig {
|
||||||
|
// Select an appropriate delay for lane 0
|
||||||
|
select_lane(0);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
let mut best_dly = None;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut prev = None;
|
||||||
|
for curr_dly in 0..32 {
|
||||||
|
//let read_align = read_align_fn(curr_dly, timer);
|
||||||
|
let curr_low_rate = read_align(curr_dly, timer);
|
||||||
|
|
||||||
|
if let Some(prev_low_rate) = prev {
|
||||||
|
// This is potentially a crossover position
|
||||||
|
if prev_low_rate <= curr_low_rate && curr_low_rate >= 0.5 {
|
||||||
|
let prev_dev = 0.5 - prev_low_rate;
|
||||||
|
let curr_dev = curr_low_rate - 0.5;
|
||||||
|
let selected_idx = if prev_dev < curr_dev { curr_dly - 1 } else { curr_dly };
|
||||||
|
|
||||||
|
// The setup setup/hold calibration timing (even with
|
||||||
|
// tolerance) might be invalid in other lanes due to skew.
|
||||||
|
// 5 taps is very conservative, generally it is 1 or 2
|
||||||
|
if selected_idx < 5 {
|
||||||
|
prev = None;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
best_dly = Some(selected_idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only rising slope from <= 0.5 can result in a rising low rate
|
||||||
|
// crossover at 50%.
|
||||||
|
if curr_low_rate <= 0.5 {
|
||||||
|
prev = Some(curr_low_rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if best_dly.is_none() {
|
||||||
|
error!("setup/hold timing calibration failed, retry in 1s...");
|
||||||
|
timer.delay_us(1_000_000);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let best_dly = best_dly.unwrap();
|
||||||
|
|
||||||
|
apply_delay(best_dly, timer);
|
||||||
|
let mut delay_list = [best_dly; 4];
|
||||||
|
|
||||||
|
// Assign delay for other lanes
|
||||||
|
for lane_no in 1..=3 {
|
||||||
|
select_lane(lane_no as u8);
|
||||||
|
|
||||||
|
let mut min_deviation = 0.5;
|
||||||
|
let mut min_idx = 0;
|
||||||
|
for dly_delta in -3..=3 {
|
||||||
|
let index = (best_dly as isize + dly_delta) as u8;
|
||||||
|
let low_rate = read_align(index, timer);
|
||||||
|
// abs() from f32 is not available in core library
|
||||||
|
let deviation = if low_rate < 0.5 { 0.5 - low_rate } else { low_rate - 0.5 };
|
||||||
|
|
||||||
|
if deviation < min_deviation {
|
||||||
|
min_deviation = deviation;
|
||||||
|
min_idx = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_delay(min_idx, timer);
|
||||||
|
delay_list[lane_no] = min_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("setup/hold timing calibration: {:?}", delay_list);
|
||||||
|
|
||||||
|
SerdesConfig { delay: delay_list }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_align(dly: u8, timer: &mut GlobalTimer) -> f32 {
|
||||||
|
unsafe {
|
||||||
|
apply_delay(dly, timer);
|
||||||
|
pl::csr::eem_transceiver::counter_reset_write(1);
|
||||||
|
|
||||||
|
pl::csr::eem_transceiver::counter_enable_write(1);
|
||||||
|
timer.delay_us(2000);
|
||||||
|
pl::csr::eem_transceiver::counter_enable_write(0);
|
||||||
|
|
||||||
|
let (high, low) = (
|
||||||
|
pl::csr::eem_transceiver::counter_high_count_read(),
|
||||||
|
pl::csr::eem_transceiver::counter_low_count_read(),
|
||||||
|
);
|
||||||
|
if pl::csr::eem_transceiver::counter_overflow_read() == 1 {
|
||||||
|
panic!("Unexpected phase detector counter overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
low as f32 / (low + high) as f32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn align_comma(timer: &mut GlobalTimer) {
|
||||||
|
loop {
|
||||||
|
for slip in 1..=10 {
|
||||||
|
// The soft transceiver has 2 8b10b decoders, which receives lane
|
||||||
|
// 0/1 and lane 2/3 respectively. The decoder are time-multiplexed
|
||||||
|
// to decode exactly 1 lane each sysclk cycle.
|
||||||
|
//
|
||||||
|
// The decoder decodes lane 0/2 data on odd sysclk cycles, buffer
|
||||||
|
// on even cycles, and vice versa for lane 1/3. Data/Clock latency
|
||||||
|
// could change timing. The extend bit flips the decoding timing,
|
||||||
|
// so lane 0/2 data are decoded on even cycles, and lane 1/3 data
|
||||||
|
// are decoded on odd cycles.
|
||||||
|
//
|
||||||
|
// This is needed because transmitting/receiving a 8b10b character
|
||||||
|
// takes 2 sysclk cycles. Adjusting bitslip only via ISERDES
|
||||||
|
// limits the range to 1 cycle. The wordslip bit extends the range
|
||||||
|
// to 2 sysclk cycles.
|
||||||
|
pl::csr::eem_transceiver::wordslip_write((slip > 5) as u8);
|
||||||
|
|
||||||
|
// Apply a double bitslip since the ISERDES is 2x oversampled.
|
||||||
|
// Bitslip is used for comma alignment purposes once setup/hold
|
||||||
|
// timing is met.
|
||||||
|
pl::csr::eem_transceiver::bitslip_write(1);
|
||||||
|
pl::csr::eem_transceiver::bitslip_write(1);
|
||||||
|
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 after {} bitslips", slip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error!("comma alignment failed, retrying in 1s...");
|
||||||
|
timer.delay_us(1_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
pl::csr::eem_transceiver::transceiver_sel_write(trx_no as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = format!("eem_drtio_delay{}", trx_no);
|
||||||
|
|
||||||
|
let cfg_read = cfg.read(&key);
|
||||||
|
match cfg_read {
|
||||||
|
Ok(record) => {
|
||||||
|
info!("loading calibrated timing values from sd card");
|
||||||
|
unsafe {
|
||||||
|
apply_config(&*(record.as_ptr() as *const SerdesConfig), timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
info!("calibrating...");
|
||||||
|
let config = unsafe { assign_delay(timer) };
|
||||||
|
|
||||||
|
match cfg.write(&key, config.as_bytes().to_vec()) {
|
||||||
|
Ok(()) => {
|
||||||
|
info!("storing calibration timing values into sd card");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"calibration successful but calibration timing values cannot be stored into sd card. \
|
||||||
|
Error:{}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
align_comma(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
|
use core::slice;
|
||||||
|
|
||||||
use core_io::{Error as IoError, ErrorKind as IoErrorKind};
|
use core_io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||||
use crc;
|
use crc;
|
||||||
use io::{proto::{ProtoRead, ProtoWrite},
|
use io::{proto::{ProtoRead, ProtoWrite},
|
||||||
Cursor};
|
Cursor};
|
||||||
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
|
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
|
||||||
use libcortex_a9::asm::dmb;
|
|
||||||
|
|
||||||
pub use crate::drtioaux_proto::Packet;
|
pub use crate::drtioaux_proto::Packet;
|
||||||
use crate::{drtioaux_proto::Error as ProtocolError, mem::mem::DRTIOAUX_MEM, pl::csr::DRTIOAUX};
|
use crate::{drtioaux_proto::Error as ProtocolError, mem::mem::DRTIOAUX_MEM, pl::csr::DRTIOAUX};
|
||||||
|
@ -56,30 +57,14 @@ pub fn has_rx_error(linkno: u8) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_work_buffer(src: *mut u32, dst: *mut u32, len: isize) {
|
|
||||||
// AXI writes must be 4-byte aligned (drtio proto doesn't care for that),
|
|
||||||
// and AXI burst reads/writes are not implemented yet in gateware
|
|
||||||
// thus the need for a work buffer for transmitting and copying it over
|
|
||||||
unsafe {
|
|
||||||
for i in 0..(len / 4) {
|
|
||||||
*dst.offset(i) = *src.offset(i);
|
|
||||||
//data memory barrier to prevent bursts
|
|
||||||
dmb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
|
fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
|
||||||
where F: FnOnce(&[u8]) -> Result<T, Error> {
|
where F: FnOnce(&[u8]) -> Result<T, Error> {
|
||||||
let linkidx = linkno as usize;
|
let linkidx = linkno as usize;
|
||||||
unsafe {
|
unsafe {
|
||||||
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
|
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
|
||||||
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u32;
|
let read_ptr = (DRTIOAUX[linkidx].aux_read_pointer_read)() as usize;
|
||||||
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize;
|
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2 + read_ptr * 0x400) as *mut u32;
|
||||||
// work buffer to accomodate axi burst reads
|
let result = f(slice::from_raw_parts(ptr as *mut u8, 0x400 as usize));
|
||||||
let mut buf: [u8; 1024] = [0; 1024];
|
|
||||||
copy_work_buffer(ptr, buf.as_mut_ptr() as *mut u32, len as isize);
|
|
||||||
let result = f(&buf[0..len]);
|
|
||||||
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
|
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
|
||||||
Ok(Some(result?))
|
Ok(Some(result?))
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,15 +85,15 @@ pub fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
|
||||||
|
|
||||||
let mut reader = Cursor::new(buffer);
|
let mut reader = Cursor::new(buffer);
|
||||||
|
|
||||||
let checksum_at = buffer.len() - 4;
|
let packet = Packet::read_from(&mut reader)?;
|
||||||
|
let padding = (12 - (reader.position() % 8)) % 8;
|
||||||
|
let checksum_at = reader.position() + padding;
|
||||||
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
|
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
|
||||||
reader.set_position(checksum_at);
|
reader.set_position(checksum_at);
|
||||||
if reader.read_u32()? != checksum {
|
if reader.read_u32()? != checksum {
|
||||||
return Err(Error::CorruptedPacket);
|
return Err(Error::CorruptedPacket);
|
||||||
}
|
}
|
||||||
reader.set_position(0);
|
Ok(packet)
|
||||||
|
|
||||||
Ok(Packet::read_from(&mut reader)?)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,11 +115,7 @@ where F: FnOnce(&mut [u8]) -> Result<usize, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
while (DRTIOAUX[linkno].aux_tx_read)() != 0 {}
|
while (DRTIOAUX[linkno].aux_tx_read)() != 0 {}
|
||||||
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
|
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
|
||||||
let len = DRTIOAUX_MEM[linkno].size / 2;
|
let len = f(slice::from_raw_parts_mut(ptr as *mut u8, 0x400 as usize))?;
|
||||||
// work buffer, works with unaligned mem access
|
|
||||||
let mut buf: [u8; 1024] = [0; 1024];
|
|
||||||
let len = f(&mut buf[0..len])?;
|
|
||||||
copy_work_buffer(buf.as_mut_ptr() as *mut u32, ptr, len as isize);
|
|
||||||
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
|
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
|
||||||
(DRTIOAUX[linkno].aux_tx_write)(1);
|
(DRTIOAUX[linkno].aux_tx_write)(1);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use core::slice;
|
||||||
|
|
||||||
use core_io::{Error as IoError, ErrorKind as IoErrorKind};
|
use core_io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||||
use crc;
|
use crc;
|
||||||
use io::{proto::{ProtoRead, ProtoWrite},
|
use io::{proto::{ProtoRead, ProtoWrite},
|
||||||
|
@ -8,7 +10,7 @@ use nb;
|
||||||
use void::Void;
|
use void::Void;
|
||||||
|
|
||||||
pub use crate::drtioaux_proto::Packet;
|
pub use crate::drtioaux_proto::Packet;
|
||||||
use crate::{drtioaux::{copy_work_buffer, has_rx_error, Error},
|
use crate::{drtioaux::{has_rx_error, Error},
|
||||||
mem::mem::DRTIOAUX_MEM,
|
mem::mem::DRTIOAUX_MEM,
|
||||||
pl::csr::DRTIOAUX};
|
pl::csr::DRTIOAUX};
|
||||||
|
|
||||||
|
@ -38,12 +40,9 @@ where F: FnOnce(&[u8]) -> Result<T, Error> {
|
||||||
let linkidx = linkno as usize;
|
let linkidx = linkno as usize;
|
||||||
unsafe {
|
unsafe {
|
||||||
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
|
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
|
||||||
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u32;
|
let read_ptr = (DRTIOAUX[linkidx].aux_read_pointer_read)() as usize;
|
||||||
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize;
|
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2 + read_ptr * 0x400) as *mut u32;
|
||||||
// work buffer to accomodate axi burst reads
|
let result = f(slice::from_raw_parts(ptr as *mut u8, 0x400 as usize));
|
||||||
let mut buf: [u8; 1024] = [0; 1024];
|
|
||||||
copy_work_buffer(ptr, buf.as_mut_ptr() as *mut u32, len as isize);
|
|
||||||
let result = f(&buf[0..len]);
|
|
||||||
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
|
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
|
||||||
Ok(Some(result?))
|
Ok(Some(result?))
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,15 +63,15 @@ pub async fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
|
||||||
|
|
||||||
let mut reader = Cursor::new(buffer);
|
let mut reader = Cursor::new(buffer);
|
||||||
|
|
||||||
let checksum_at = buffer.len() - 4;
|
let packet = Packet::read_from(&mut reader)?;
|
||||||
|
let padding = (12 - (reader.position() % 8)) % 8;
|
||||||
|
let checksum_at = reader.position() + padding;
|
||||||
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
|
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
|
||||||
reader.set_position(checksum_at);
|
reader.set_position(checksum_at);
|
||||||
if reader.read_u32()? != checksum {
|
if reader.read_u32()? != checksum {
|
||||||
return Err(Error::CorruptedPacket);
|
return Err(Error::CorruptedPacket);
|
||||||
}
|
}
|
||||||
reader.set_position(0);
|
Ok(packet)
|
||||||
|
|
||||||
Ok(Packet::read_from(&mut reader)?)
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -103,11 +102,7 @@ where F: FnOnce(&mut [u8]) -> Result<usize, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = block_async!(tx_ready(linkno)).await;
|
let _ = block_async!(tx_ready(linkno)).await;
|
||||||
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
|
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
|
||||||
let len = DRTIOAUX_MEM[linkno].size / 2;
|
let len = f(slice::from_raw_parts_mut(ptr as *mut u8, 0x400 as usize))?;
|
||||||
// work buffer, works with unaligned mem access
|
|
||||||
let mut buf: [u8; 1024] = [0; 1024];
|
|
||||||
let len = f(&mut buf[0..len])?;
|
|
||||||
copy_work_buffer(buf.as_mut_ptr() as *mut u32, ptr, len as isize);
|
|
||||||
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
|
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
|
||||||
(DRTIOAUX[linkno].aux_tx_write)(1);
|
(DRTIOAUX[linkno].aux_tx_write)(1);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
use core_io::{Error as IoError, Read, Write};
|
use core_io::{Error as IoError, Read, Write};
|
||||||
use io::proto::{ProtoRead, ProtoWrite};
|
use io::proto::{ProtoRead, ProtoWrite};
|
||||||
|
|
||||||
pub const DMA_TRACE_MAX_SIZE: usize = /*max size*/512 - /*CRC*/4 - /*packet ID*/1 - /*trace ID*/4 - /*last*/1 -/*length*/2;
|
const MAX_PACKET: usize = 1024;
|
||||||
pub const ANALYZER_MAX_SIZE: usize = /*max size*/512 - /*CRC*/4 - /*packet ID*/1 - /*last*/1 - /*length*/2;
|
|
||||||
|
// maximum size of arbitrary payloads
|
||||||
|
// used by satellite -> master analyzer, subkernel exceptions
|
||||||
|
pub const SAT_PAYLOAD_MAX_SIZE: usize = /*max size*/MAX_PACKET - /*CRC*/4 - /*packet ID*/1 - /*last*/1 - /*length*/2;
|
||||||
|
// used by DDMA, subkernel program data (need to provide extra ID and destination)
|
||||||
|
pub const MASTER_PAYLOAD_MAX_SIZE: usize = SAT_PAYLOAD_MAX_SIZE - /*source*/1 - /*destination*/1 - /*ID*/4;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -16,6 +21,46 @@ impl From<IoError> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum PayloadStatus {
|
||||||
|
Middle = 0,
|
||||||
|
First = 1,
|
||||||
|
Last = 2,
|
||||||
|
FirstAndLast = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for PayloadStatus {
|
||||||
|
fn from(value: u8) -> PayloadStatus {
|
||||||
|
match value {
|
||||||
|
0 => PayloadStatus::Middle,
|
||||||
|
1 => PayloadStatus::First,
|
||||||
|
2 => PayloadStatus::Last,
|
||||||
|
3 => PayloadStatus::FirstAndLast,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PayloadStatus {
|
||||||
|
pub fn is_first(self) -> bool {
|
||||||
|
self == PayloadStatus::First || self == PayloadStatus::FirstAndLast
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_last(self) -> bool {
|
||||||
|
self == PayloadStatus::Last || self == PayloadStatus::FirstAndLast
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_status(first: bool, last: bool) -> PayloadStatus {
|
||||||
|
match (first, last) {
|
||||||
|
(true, true) => PayloadStatus::FirstAndLast,
|
||||||
|
(true, false) => PayloadStatus::First,
|
||||||
|
(false, true) => PayloadStatus::Last,
|
||||||
|
(false, false) => PayloadStatus::Middle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum Packet {
|
pub enum Packet {
|
||||||
EchoRequest,
|
EchoRequest,
|
||||||
|
@ -150,41 +195,170 @@ pub enum Packet {
|
||||||
AnalyzerData {
|
AnalyzerData {
|
||||||
last: bool,
|
last: bool,
|
||||||
length: u16,
|
length: u16,
|
||||||
data: [u8; ANALYZER_MAX_SIZE],
|
data: [u8; SAT_PAYLOAD_MAX_SIZE],
|
||||||
},
|
},
|
||||||
|
|
||||||
DmaAddTraceRequest {
|
DmaAddTraceRequest {
|
||||||
|
source: u8,
|
||||||
destination: u8,
|
destination: u8,
|
||||||
id: u32,
|
id: u32,
|
||||||
last: bool,
|
status: PayloadStatus,
|
||||||
length: u16,
|
length: u16,
|
||||||
trace: [u8; DMA_TRACE_MAX_SIZE],
|
trace: [u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||||
},
|
},
|
||||||
DmaAddTraceReply {
|
DmaAddTraceReply {
|
||||||
|
source: u8,
|
||||||
|
destination: u8,
|
||||||
|
id: u32,
|
||||||
succeeded: bool,
|
succeeded: bool,
|
||||||
},
|
},
|
||||||
DmaRemoveTraceRequest {
|
DmaRemoveTraceRequest {
|
||||||
|
source: u8,
|
||||||
destination: u8,
|
destination: u8,
|
||||||
id: u32,
|
id: u32,
|
||||||
},
|
},
|
||||||
DmaRemoveTraceReply {
|
DmaRemoveTraceReply {
|
||||||
|
destination: u8,
|
||||||
succeeded: bool,
|
succeeded: bool,
|
||||||
},
|
},
|
||||||
DmaPlaybackRequest {
|
DmaPlaybackRequest {
|
||||||
|
source: u8,
|
||||||
destination: u8,
|
destination: u8,
|
||||||
id: u32,
|
id: u32,
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
},
|
},
|
||||||
DmaPlaybackReply {
|
DmaPlaybackReply {
|
||||||
|
destination: u8,
|
||||||
succeeded: bool,
|
succeeded: bool,
|
||||||
},
|
},
|
||||||
DmaPlaybackStatus {
|
DmaPlaybackStatus {
|
||||||
|
source: u8,
|
||||||
destination: u8,
|
destination: u8,
|
||||||
id: u32,
|
id: u32,
|
||||||
error: u8,
|
error: u8,
|
||||||
channel: u32,
|
channel: u32,
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
SubkernelAddDataRequest {
|
||||||
|
destination: u8,
|
||||||
|
id: u32,
|
||||||
|
status: PayloadStatus,
|
||||||
|
length: u16,
|
||||||
|
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||||
|
},
|
||||||
|
SubkernelAddDataReply {
|
||||||
|
succeeded: bool,
|
||||||
|
},
|
||||||
|
SubkernelLoadRunRequest {
|
||||||
|
source: u8,
|
||||||
|
destination: u8,
|
||||||
|
id: u32,
|
||||||
|
run: bool,
|
||||||
|
timestamp: u64,
|
||||||
|
},
|
||||||
|
SubkernelLoadRunReply {
|
||||||
|
destination: u8,
|
||||||
|
succeeded: bool,
|
||||||
|
},
|
||||||
|
SubkernelFinished {
|
||||||
|
destination: u8,
|
||||||
|
id: u32,
|
||||||
|
with_exception: bool,
|
||||||
|
exception_src: u8,
|
||||||
|
},
|
||||||
|
SubkernelExceptionRequest {
|
||||||
|
source: u8,
|
||||||
|
destination: u8,
|
||||||
|
},
|
||||||
|
SubkernelException {
|
||||||
|
destination: u8,
|
||||||
|
last: bool,
|
||||||
|
length: u16,
|
||||||
|
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||||
|
},
|
||||||
|
SubkernelMessage {
|
||||||
|
source: u8,
|
||||||
|
destination: u8,
|
||||||
|
id: u32,
|
||||||
|
status: PayloadStatus,
|
||||||
|
length: u16,
|
||||||
|
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||||
|
},
|
||||||
|
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 {
|
impl Packet {
|
||||||
|
@ -329,7 +503,7 @@ impl Packet {
|
||||||
0xa3 => {
|
0xa3 => {
|
||||||
let last = reader.read_bool()?;
|
let last = reader.read_bool()?;
|
||||||
let length = reader.read_u16()?;
|
let length = reader.read_u16()?;
|
||||||
let mut data: [u8; ANALYZER_MAX_SIZE] = [0; ANALYZER_MAX_SIZE];
|
let mut data: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||||
reader.read_exact(&mut data[0..length as usize])?;
|
reader.read_exact(&mut data[0..length as usize])?;
|
||||||
Packet::AnalyzerData {
|
Packet::AnalyzerData {
|
||||||
last: last,
|
last: last,
|
||||||
|
@ -339,39 +513,49 @@ impl Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
0xb0 => {
|
0xb0 => {
|
||||||
|
let source = reader.read_u8()?;
|
||||||
let destination = reader.read_u8()?;
|
let destination = reader.read_u8()?;
|
||||||
let id = reader.read_u32()?;
|
let id = reader.read_u32()?;
|
||||||
let last = reader.read_bool()?;
|
let status = reader.read_u8()?;
|
||||||
let length = reader.read_u16()?;
|
let length = reader.read_u16()?;
|
||||||
let mut trace: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
|
let mut trace: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||||
reader.read_exact(&mut trace[0..length as usize])?;
|
reader.read_exact(&mut trace[0..length as usize])?;
|
||||||
Packet::DmaAddTraceRequest {
|
Packet::DmaAddTraceRequest {
|
||||||
|
source: source,
|
||||||
destination: destination,
|
destination: destination,
|
||||||
id: id,
|
id: id,
|
||||||
last: last,
|
status: PayloadStatus::from(status),
|
||||||
length: length as u16,
|
length: length as u16,
|
||||||
trace: trace,
|
trace: trace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xb1 => Packet::DmaAddTraceReply {
|
0xb1 => Packet::DmaAddTraceReply {
|
||||||
|
source: reader.read_u8()?,
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
id: reader.read_u32()?,
|
||||||
succeeded: reader.read_bool()?,
|
succeeded: reader.read_bool()?,
|
||||||
},
|
},
|
||||||
0xb2 => Packet::DmaRemoveTraceRequest {
|
0xb2 => Packet::DmaRemoveTraceRequest {
|
||||||
|
source: reader.read_u8()?,
|
||||||
destination: reader.read_u8()?,
|
destination: reader.read_u8()?,
|
||||||
id: reader.read_u32()?,
|
id: reader.read_u32()?,
|
||||||
},
|
},
|
||||||
0xb3 => Packet::DmaRemoveTraceReply {
|
0xb3 => Packet::DmaRemoveTraceReply {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
succeeded: reader.read_bool()?,
|
succeeded: reader.read_bool()?,
|
||||||
},
|
},
|
||||||
0xb4 => Packet::DmaPlaybackRequest {
|
0xb4 => Packet::DmaPlaybackRequest {
|
||||||
|
source: reader.read_u8()?,
|
||||||
destination: reader.read_u8()?,
|
destination: reader.read_u8()?,
|
||||||
id: reader.read_u32()?,
|
id: reader.read_u32()?,
|
||||||
timestamp: reader.read_u64()?,
|
timestamp: reader.read_u64()?,
|
||||||
},
|
},
|
||||||
0xb5 => Packet::DmaPlaybackReply {
|
0xb5 => Packet::DmaPlaybackReply {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
succeeded: reader.read_bool()?,
|
succeeded: reader.read_bool()?,
|
||||||
},
|
},
|
||||||
0xb6 => Packet::DmaPlaybackStatus {
|
0xb6 => Packet::DmaPlaybackStatus {
|
||||||
|
source: reader.read_u8()?,
|
||||||
destination: reader.read_u8()?,
|
destination: reader.read_u8()?,
|
||||||
id: reader.read_u32()?,
|
id: reader.read_u32()?,
|
||||||
error: reader.read_u8()?,
|
error: reader.read_u8()?,
|
||||||
|
@ -379,6 +563,188 @@ impl Packet {
|
||||||
timestamp: reader.read_u64()?,
|
timestamp: reader.read_u64()?,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
0xc0 => {
|
||||||
|
let destination = reader.read_u8()?;
|
||||||
|
let id = reader.read_u32()?;
|
||||||
|
let status = PayloadStatus::from(reader.read_u8()?);
|
||||||
|
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::SubkernelAddDataRequest {
|
||||||
|
destination: destination,
|
||||||
|
id: id,
|
||||||
|
status: status,
|
||||||
|
length: length as u16,
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0xc1 => Packet::SubkernelAddDataReply {
|
||||||
|
succeeded: reader.read_bool()?,
|
||||||
|
},
|
||||||
|
0xc4 => Packet::SubkernelLoadRunRequest {
|
||||||
|
source: reader.read_u8()?,
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
id: reader.read_u32()?,
|
||||||
|
run: reader.read_bool()?,
|
||||||
|
timestamp: reader.read_u64()?,
|
||||||
|
},
|
||||||
|
0xc5 => Packet::SubkernelLoadRunReply {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
succeeded: reader.read_bool()?,
|
||||||
|
},
|
||||||
|
0xc8 => Packet::SubkernelFinished {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
id: reader.read_u32()?,
|
||||||
|
with_exception: reader.read_bool()?,
|
||||||
|
exception_src: reader.read_u8()?,
|
||||||
|
},
|
||||||
|
0xc9 => Packet::SubkernelExceptionRequest {
|
||||||
|
source: reader.read_u8()?,
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
},
|
||||||
|
0xca => {
|
||||||
|
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::SubkernelException {
|
||||||
|
destination: destination,
|
||||||
|
last: last,
|
||||||
|
length: length,
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0xcb => {
|
||||||
|
let source = reader.read_u8()?;
|
||||||
|
let destination = reader.read_u8()?;
|
||||||
|
let id = reader.read_u32()?;
|
||||||
|
let status = reader.read_u8()?;
|
||||||
|
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::SubkernelMessage {
|
||||||
|
source: source,
|
||||||
|
destination: destination,
|
||||||
|
id: id,
|
||||||
|
status: PayloadStatus::from(status),
|
||||||
|
length: length as u16,
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0xcc => Packet::SubkernelMessageAck {
|
||||||
|
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)),
|
ty => return Err(Error::UnknownPacket(ty)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -592,49 +958,69 @@ impl Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet::DmaAddTraceRequest {
|
Packet::DmaAddTraceRequest {
|
||||||
|
source,
|
||||||
destination,
|
destination,
|
||||||
id,
|
id,
|
||||||
last,
|
status,
|
||||||
trace,
|
trace,
|
||||||
length,
|
length,
|
||||||
} => {
|
} => {
|
||||||
writer.write_u8(0xb0)?;
|
writer.write_u8(0xb0)?;
|
||||||
|
writer.write_u8(source)?;
|
||||||
writer.write_u8(destination)?;
|
writer.write_u8(destination)?;
|
||||||
writer.write_u32(id)?;
|
writer.write_u32(id)?;
|
||||||
writer.write_bool(last)?;
|
writer.write_u8(status as u8)?;
|
||||||
// trace may be broken down to fit within drtio aux memory limit
|
// trace may be broken down to fit within drtio aux memory limit
|
||||||
// will be reconstructed by satellite
|
// will be reconstructed by satellite
|
||||||
writer.write_u16(length)?;
|
writer.write_u16(length)?;
|
||||||
writer.write_all(&trace[0..length as usize])?;
|
writer.write_all(&trace[0..length as usize])?;
|
||||||
}
|
}
|
||||||
Packet::DmaAddTraceReply { succeeded } => {
|
Packet::DmaAddTraceReply {
|
||||||
|
source,
|
||||||
|
destination,
|
||||||
|
id,
|
||||||
|
succeeded,
|
||||||
|
} => {
|
||||||
writer.write_u8(0xb1)?;
|
writer.write_u8(0xb1)?;
|
||||||
|
writer.write_u8(source)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u32(id)?;
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
}
|
}
|
||||||
Packet::DmaRemoveTraceRequest { destination, id } => {
|
Packet::DmaRemoveTraceRequest {
|
||||||
|
source,
|
||||||
|
destination,
|
||||||
|
id,
|
||||||
|
} => {
|
||||||
writer.write_u8(0xb2)?;
|
writer.write_u8(0xb2)?;
|
||||||
|
writer.write_u8(source)?;
|
||||||
writer.write_u8(destination)?;
|
writer.write_u8(destination)?;
|
||||||
writer.write_u32(id)?;
|
writer.write_u32(id)?;
|
||||||
}
|
}
|
||||||
Packet::DmaRemoveTraceReply { succeeded } => {
|
Packet::DmaRemoveTraceReply { destination, succeeded } => {
|
||||||
writer.write_u8(0xb3)?;
|
writer.write_u8(0xb3)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
}
|
}
|
||||||
Packet::DmaPlaybackRequest {
|
Packet::DmaPlaybackRequest {
|
||||||
|
source,
|
||||||
destination,
|
destination,
|
||||||
id,
|
id,
|
||||||
timestamp,
|
timestamp,
|
||||||
} => {
|
} => {
|
||||||
writer.write_u8(0xb4)?;
|
writer.write_u8(0xb4)?;
|
||||||
|
writer.write_u8(source)?;
|
||||||
writer.write_u8(destination)?;
|
writer.write_u8(destination)?;
|
||||||
writer.write_u32(id)?;
|
writer.write_u32(id)?;
|
||||||
writer.write_u64(timestamp)?;
|
writer.write_u64(timestamp)?;
|
||||||
}
|
}
|
||||||
Packet::DmaPlaybackReply { succeeded } => {
|
Packet::DmaPlaybackReply { destination, succeeded } => {
|
||||||
writer.write_u8(0xb5)?;
|
writer.write_u8(0xb5)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
}
|
}
|
||||||
Packet::DmaPlaybackStatus {
|
Packet::DmaPlaybackStatus {
|
||||||
|
source,
|
||||||
destination,
|
destination,
|
||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
|
@ -642,13 +1028,249 @@ impl Packet {
|
||||||
timestamp,
|
timestamp,
|
||||||
} => {
|
} => {
|
||||||
writer.write_u8(0xb6)?;
|
writer.write_u8(0xb6)?;
|
||||||
|
writer.write_u8(source)?;
|
||||||
writer.write_u8(destination)?;
|
writer.write_u8(destination)?;
|
||||||
writer.write_u32(id)?;
|
writer.write_u32(id)?;
|
||||||
writer.write_u8(error)?;
|
writer.write_u8(error)?;
|
||||||
writer.write_u32(channel)?;
|
writer.write_u32(channel)?;
|
||||||
writer.write_u64(timestamp)?;
|
writer.write_u64(timestamp)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Packet::SubkernelAddDataRequest {
|
||||||
|
destination,
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
data,
|
||||||
|
length,
|
||||||
|
} => {
|
||||||
|
writer.write_u8(0xc0)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u32(id)?;
|
||||||
|
writer.write_u8(status as u8)?;
|
||||||
|
writer.write_u16(length)?;
|
||||||
|
writer.write_all(&data[0..length as usize])?;
|
||||||
|
}
|
||||||
|
Packet::SubkernelAddDataReply { succeeded } => {
|
||||||
|
writer.write_u8(0xc1)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
}
|
||||||
|
Packet::SubkernelLoadRunRequest {
|
||||||
|
source,
|
||||||
|
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)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
}
|
||||||
|
Packet::SubkernelFinished {
|
||||||
|
destination,
|
||||||
|
id,
|
||||||
|
with_exception,
|
||||||
|
exception_src,
|
||||||
|
} => {
|
||||||
|
writer.write_u8(0xc8)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u32(id)?;
|
||||||
|
writer.write_bool(with_exception)?;
|
||||||
|
writer.write_u8(exception_src)?;
|
||||||
|
}
|
||||||
|
Packet::SubkernelExceptionRequest { source, destination } => {
|
||||||
|
writer.write_u8(0xc9)?;
|
||||||
|
writer.write_u8(source)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
}
|
||||||
|
Packet::SubkernelException {
|
||||||
|
destination,
|
||||||
|
last,
|
||||||
|
length,
|
||||||
|
data,
|
||||||
|
} => {
|
||||||
|
writer.write_u8(0xca)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_bool(last)?;
|
||||||
|
writer.write_u16(length)?;
|
||||||
|
writer.write_all(&data[0..length as usize])?;
|
||||||
|
}
|
||||||
|
Packet::SubkernelMessage {
|
||||||
|
source,
|
||||||
|
destination,
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
data,
|
||||||
|
length,
|
||||||
|
} => {
|
||||||
|
writer.write_u8(0xcb)?;
|
||||||
|
writer.write_u8(source)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u32(id)?;
|
||||||
|
writer.write_u8(status as u8)?;
|
||||||
|
writer.write_u16(length)?;
|
||||||
|
writer.write_all(&data[0..length as usize])?;
|
||||||
|
}
|
||||||
|
Packet::SubkernelMessageAck { destination } => {
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn routable_destination(&self) -> Option<u8> {
|
||||||
|
// only for packets that could be re-routed, not only forwarded
|
||||||
|
match self {
|
||||||
|
Packet::DmaAddTraceRequest { destination, .. } => Some(*destination),
|
||||||
|
Packet::DmaAddTraceReply { destination, .. } => Some(*destination),
|
||||||
|
Packet::DmaRemoveTraceRequest { destination, .. } => Some(*destination),
|
||||||
|
Packet::DmaRemoveTraceReply { destination, .. } => Some(*destination),
|
||||||
|
Packet::DmaPlaybackRequest { destination, .. } => Some(*destination),
|
||||||
|
Packet::DmaPlaybackReply { destination, .. } => Some(*destination),
|
||||||
|
Packet::SubkernelLoadRunRequest { destination, .. } => Some(*destination),
|
||||||
|
Packet::SubkernelLoadRunReply { destination, .. } => Some(*destination),
|
||||||
|
Packet::SubkernelMessage { destination, .. } => Some(*destination),
|
||||||
|
Packet::SubkernelMessageAck { destination } => Some(*destination),
|
||||||
|
Packet::SubkernelExceptionRequest { destination, .. } => Some(*destination),
|
||||||
|
Packet::SubkernelException { destination, .. } => Some(*destination),
|
||||||
|
Packet::DmaPlaybackStatus { destination, .. } => Some(*destination),
|
||||||
|
Packet::SubkernelFinished { destination, .. } => Some(*destination),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expects_response(&self) -> bool {
|
||||||
|
// returns true if the routable packet should elicit a response
|
||||||
|
// e.g. reply, ACK packets end a conversation,
|
||||||
|
// and firmware should not wait for response
|
||||||
|
match self {
|
||||||
|
Packet::DmaAddTraceReply { .. }
|
||||||
|
| Packet::DmaRemoveTraceReply { .. }
|
||||||
|
| Packet::DmaPlaybackReply { .. }
|
||||||
|
| Packet::SubkernelLoadRunReply { .. }
|
||||||
|
| Packet::SubkernelMessageAck { .. }
|
||||||
|
| Packet::DmaPlaybackStatus { .. }
|
||||||
|
| Packet::SubkernelFinished { .. }
|
||||||
|
| Packet::CoreMgmtDropLinkAck { .. }
|
||||||
|
| Packet::InjectionRequest { .. } => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
use libboard_zynq::{println, stdio};
|
||||||
|
use libcortex_a9::{interrupt_handler, regs::MPIDR};
|
||||||
|
use libregister::RegisterR;
|
||||||
|
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
use crate::si549;
|
||||||
|
|
||||||
|
interrupt_handler!(FIQ, fiq, __irq_stack0_start, __irq_stack1_start, {
|
||||||
|
match MPIDR.read().cpu_id() {
|
||||||
|
0 => {
|
||||||
|
// nFIQ is driven directly and bypass GIC
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
si549::wrpll::interrupt_handler();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("FIQ");
|
||||||
|
loop {}
|
||||||
|
});
|
|
@ -0,0 +1,163 @@
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::pl::csr;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
|
enum State {
|
||||||
|
Reset,
|
||||||
|
ExitReset,
|
||||||
|
Lock,
|
||||||
|
Align,
|
||||||
|
Watch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Info {
|
||||||
|
state: State,
|
||||||
|
frame_size: (u16, u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut INFO: [Info; csr::GRABBER_LEN] = [Info {
|
||||||
|
state: State::Reset,
|
||||||
|
frame_size: (0, 0),
|
||||||
|
}; csr::GRABBER_LEN];
|
||||||
|
|
||||||
|
fn get_pll_reset(g: usize) -> bool {
|
||||||
|
unsafe { (csr::GRABBER[g].pll_reset_read)() != 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_pll_reset(g: usize, reset: bool) {
|
||||||
|
let val = if reset { 1 } else { 0 };
|
||||||
|
unsafe { (csr::GRABBER[g].pll_reset_write)(val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pll_locked(g: usize) -> bool {
|
||||||
|
unsafe { (csr::GRABBER[g].pll_locked_read)() != 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_pattern_ok(g: usize) -> bool {
|
||||||
|
unsafe { (csr::GRABBER[g].clk_sampled_read)() == 0b1100011 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_pattern_ok_filter(g: usize) -> bool {
|
||||||
|
for _ in 0..128 {
|
||||||
|
if !clock_pattern_ok(g) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phase_shift(g: usize, direction: u8) {
|
||||||
|
unsafe {
|
||||||
|
(csr::GRABBER[g].phase_shift_write)(direction);
|
||||||
|
while (csr::GRABBER[g].phase_shift_done_read)() == 0 {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_align(g: usize) -> bool {
|
||||||
|
while clock_pattern_ok_filter(g) {
|
||||||
|
phase_shift(g, 1);
|
||||||
|
}
|
||||||
|
phase_shift(g, 1);
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
while !clock_pattern_ok_filter(g) {
|
||||||
|
phase_shift(g, 1);
|
||||||
|
count += 1;
|
||||||
|
if count > 1024 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut window = 1;
|
||||||
|
phase_shift(g, 1);
|
||||||
|
while clock_pattern_ok_filter(g) {
|
||||||
|
phase_shift(g, 1);
|
||||||
|
window += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..window / 2 {
|
||||||
|
phase_shift(g, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_last_pixels(g: usize) -> (u16, u16) {
|
||||||
|
unsafe { ((csr::GRABBER[g].last_x_read)(), (csr::GRABBER[g].last_y_read)()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_video_clock(g: usize) -> u32 {
|
||||||
|
let freq_count = unsafe { (csr::GRABBER[g].freq_count_read)() } as u32;
|
||||||
|
2 * freq_count * (csr::CONFIG_CLOCK_FREQUENCY / 1000) / (511 * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick() {
|
||||||
|
for g in 0..csr::GRABBER.len() {
|
||||||
|
let next = match unsafe { INFO[g].state } {
|
||||||
|
State::Reset => {
|
||||||
|
set_pll_reset(g, true);
|
||||||
|
unsafe {
|
||||||
|
INFO[g].frame_size = (0, 0);
|
||||||
|
}
|
||||||
|
State::ExitReset
|
||||||
|
}
|
||||||
|
State::ExitReset => {
|
||||||
|
if get_pll_reset(g) {
|
||||||
|
set_pll_reset(g, false);
|
||||||
|
State::Lock
|
||||||
|
} else {
|
||||||
|
State::ExitReset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Lock => {
|
||||||
|
if pll_locked(g) {
|
||||||
|
info!("grabber{} locked: {}MHz", g, get_video_clock(g));
|
||||||
|
State::Align
|
||||||
|
} else {
|
||||||
|
State::Lock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Align => {
|
||||||
|
if pll_locked(g) {
|
||||||
|
if clock_align(g) {
|
||||||
|
info!("grabber{} alignment success", g);
|
||||||
|
State::Watch
|
||||||
|
} else {
|
||||||
|
info!("grabber{} alignment failure", g);
|
||||||
|
State::Reset
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("grabber{} lock lost", g);
|
||||||
|
State::Reset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Watch => {
|
||||||
|
if pll_locked(g) {
|
||||||
|
if clock_pattern_ok(g) {
|
||||||
|
let last_xy = get_last_pixels(g);
|
||||||
|
if last_xy != unsafe { INFO[g].frame_size } {
|
||||||
|
// x capture is on ~LVAL which is after
|
||||||
|
// the last increment on DVAL
|
||||||
|
// y capture is on ~FVAL which coincides with the
|
||||||
|
// last increment on ~LVAL
|
||||||
|
info!("grabber{} frame size: {}x{}", g, last_xy.0, last_xy.1 + 1);
|
||||||
|
unsafe { INFO[g].frame_size = last_xy }
|
||||||
|
}
|
||||||
|
State::Watch
|
||||||
|
} else {
|
||||||
|
info!("grabber{} alignment lost", g);
|
||||||
|
State::Reset
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("grabber{} lock lost", g);
|
||||||
|
State::Reset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
INFO[g].state = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use libboard_zynq::i2c;
|
use libboard_zynq::i2c;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
use crate::pl::csr;
|
use crate::pl::csr;
|
||||||
|
|
||||||
// Only the bare minimum registers. Bits/IO connections equivalent between IC types.
|
// Only the bare minimum registers. Bits/IO connections equivalent between IC types.
|
||||||
|
@ -19,11 +20,15 @@ const IODIR_OUT_SFP_LED: u8 = 0x40;
|
||||||
const IODIR_OUT_SFP0_LED: u8 = 0x40;
|
const IODIR_OUT_SFP0_LED: u8 = 0x40;
|
||||||
#[cfg(hw_rev = "v1.1")]
|
#[cfg(hw_rev = "v1.1")]
|
||||||
const IODIR_OUT_SFP0_LED: u8 = 0x80;
|
const IODIR_OUT_SFP0_LED: u8 = 0x80;
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
const IODIR_CLK_SEL: u8 = 0x80; // out
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
const IODIR_CLK_SEL: u8 = 0x00; // in
|
||||||
|
|
||||||
//IO expander port direction
|
//IO expander port direction
|
||||||
const IODIR0: [u8; 2] = [
|
const IODIR0: [u8; 2] = [
|
||||||
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP0_LED,
|
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP0_LED,
|
||||||
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED,
|
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED & !IODIR_CLK_SEL,
|
||||||
];
|
];
|
||||||
|
|
||||||
const IODIR1: [u8; 2] = [
|
const IODIR1: [u8; 2] = [
|
||||||
|
@ -33,6 +38,7 @@ const IODIR1: [u8; 2] = [
|
||||||
|
|
||||||
pub struct IoExpander {
|
pub struct IoExpander {
|
||||||
address: u8,
|
address: u8,
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
virtual_led_mapping: &'static [(u8, u8, u8)],
|
virtual_led_mapping: &'static [(u8, u8, u8)],
|
||||||
iodir: [u8; 2],
|
iodir: [u8; 2],
|
||||||
out_current: [u8; 2],
|
out_current: [u8; 2],
|
||||||
|
@ -42,17 +48,18 @@ pub struct IoExpander {
|
||||||
|
|
||||||
impl IoExpander {
|
impl IoExpander {
|
||||||
pub fn new(i2c: &mut i2c::I2c, index: u8) -> Result<Self, &'static str> {
|
pub fn new(i2c: &mut i2c::I2c, index: u8) -> Result<Self, &'static str> {
|
||||||
#[cfg(hw_rev = "v1.0")]
|
#[cfg(all(hw_rev = "v1.0", has_virtual_leds))]
|
||||||
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)];
|
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)];
|
||||||
#[cfg(hw_rev = "v1.1")]
|
#[cfg(all(hw_rev = "v1.1", has_virtual_leds))]
|
||||||
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 7), (1, 1, 6)];
|
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 7), (1, 1, 6)];
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)];
|
const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)];
|
||||||
|
|
||||||
// Both expanders on SHARED I2C bus
|
// Both expanders on SHARED I2C bus
|
||||||
let mut io_expander = match index {
|
let mut io_expander = match index {
|
||||||
0 => IoExpander {
|
0 => IoExpander {
|
||||||
address: 0x40,
|
address: 0x40,
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
virtual_led_mapping: &VIRTUAL_LED_MAPPING0,
|
virtual_led_mapping: &VIRTUAL_LED_MAPPING0,
|
||||||
iodir: IODIR0,
|
iodir: IODIR0,
|
||||||
out_current: [0; 2],
|
out_current: [0; 2],
|
||||||
|
@ -66,6 +73,7 @@ impl IoExpander {
|
||||||
},
|
},
|
||||||
1 => IoExpander {
|
1 => IoExpander {
|
||||||
address: 0x42,
|
address: 0x42,
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
virtual_led_mapping: &VIRTUAL_LED_MAPPING1,
|
virtual_led_mapping: &VIRTUAL_LED_MAPPING1,
|
||||||
iodir: IODIR1,
|
iodir: IODIR1,
|
||||||
out_current: [0; 2],
|
out_current: [0; 2],
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
|
#![feature(naked_functions)]
|
||||||
|
#![feature(asm)]
|
||||||
|
|
||||||
extern crate core_io;
|
extern crate core_io;
|
||||||
extern crate crc;
|
extern crate crc;
|
||||||
|
@ -19,6 +21,7 @@ pub mod drtioaux;
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
pub mod drtioaux_async;
|
pub mod drtioaux_async;
|
||||||
pub mod drtioaux_proto;
|
pub mod drtioaux_proto;
|
||||||
|
pub mod fiq;
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
pub mod io_expander;
|
pub mod io_expander;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
|
@ -29,9 +32,14 @@ pub mod mem;
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[path = "../../../build/pl.rs"]
|
#[path = "../../../build/pl.rs"]
|
||||||
pub mod pl;
|
pub mod pl;
|
||||||
|
#[cfg(has_drtio_eem)]
|
||||||
|
pub mod drtio_eem;
|
||||||
|
#[cfg(has_grabber)]
|
||||||
|
pub mod grabber;
|
||||||
#[cfg(has_si5324)]
|
#[cfg(has_si5324)]
|
||||||
pub mod si5324;
|
pub mod si5324;
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
pub mod si549;
|
||||||
use core::{cmp, str};
|
use core::{cmp, str};
|
||||||
|
|
||||||
pub fn identifier_read(buf: &mut [u8]) -> &str {
|
pub fn identifier_read(buf: &mut [u8]) -> &str {
|
||||||
|
|
|
@ -0,0 +1,854 @@
|
||||||
|
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
|
||||||
|
use libboard_zynq::timer::GlobalTimer;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::pl::csr;
|
||||||
|
|
||||||
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
|
const ADDRESS: u8 = 0x67;
|
||||||
|
|
||||||
|
const ADPLL_MAX: i32 = (950.0 / 0.0001164) as i32;
|
||||||
|
|
||||||
|
pub struct DividerConfig {
|
||||||
|
pub hsdiv: u16,
|
||||||
|
pub lsdiv: u8,
|
||||||
|
pub fbdiv: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FrequencySetting {
|
||||||
|
pub main: DividerConfig,
|
||||||
|
pub helper: DividerConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod i2c {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum DCXO {
|
||||||
|
Main,
|
||||||
|
Helper,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn half_period(timer: &mut GlobalTimer) {
|
||||||
|
timer.delay_us(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sda_i(dcxo: DCXO) -> bool {
|
||||||
|
match dcxo {
|
||||||
|
DCXO::Main => unsafe { csr::wrpll::main_dcxo_sda_in_read() == 1 },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::helper_dcxo_sda_in_read() == 1 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sda_oe(dcxo: DCXO, oe: bool) {
|
||||||
|
let val = if oe { 1 } else { 0 };
|
||||||
|
match dcxo {
|
||||||
|
DCXO::Main => unsafe { csr::wrpll::main_dcxo_sda_oe_write(val) },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::helper_dcxo_sda_oe_write(val) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sda_o(dcxo: DCXO, o: bool) {
|
||||||
|
let val = if o { 1 } else { 0 };
|
||||||
|
match dcxo {
|
||||||
|
DCXO::Main => unsafe { csr::wrpll::main_dcxo_sda_out_write(val) },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::helper_dcxo_sda_out_write(val) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scl_oe(dcxo: DCXO, oe: bool) {
|
||||||
|
let val = if oe { 1 } else { 0 };
|
||||||
|
match dcxo {
|
||||||
|
DCXO::Main => unsafe { csr::wrpll::main_dcxo_scl_oe_write(val) },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::helper_dcxo_scl_oe_write(val) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scl_o(dcxo: DCXO, o: bool) {
|
||||||
|
let val = if o { 1 } else { 0 };
|
||||||
|
match dcxo {
|
||||||
|
DCXO::Main => unsafe { csr::wrpll::main_dcxo_scl_out_write(val) },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::helper_dcxo_scl_out_write(val) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(dcxo: DCXO, timer: &mut GlobalTimer) -> Result<(), &'static str> {
|
||||||
|
// Set SCL as output, and high level
|
||||||
|
scl_o(dcxo, true);
|
||||||
|
scl_oe(dcxo, true);
|
||||||
|
// Prepare a zero level on SDA so that sda_oe pulls it down
|
||||||
|
sda_o(dcxo, false);
|
||||||
|
// Release SDA
|
||||||
|
sda_oe(dcxo, false);
|
||||||
|
|
||||||
|
// Check the I2C bus is ready
|
||||||
|
half_period(timer);
|
||||||
|
half_period(timer);
|
||||||
|
if !sda_i(dcxo) {
|
||||||
|
// Try toggling SCL a few times
|
||||||
|
for _bit in 0..8 {
|
||||||
|
scl_o(dcxo, false);
|
||||||
|
half_period(timer);
|
||||||
|
scl_o(dcxo, true);
|
||||||
|
half_period(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sda_i(dcxo) {
|
||||||
|
return Err("SDA is stuck low and doesn't get unstuck");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(dcxo: DCXO, timer: &mut GlobalTimer) {
|
||||||
|
// Set SCL high then SDA low
|
||||||
|
scl_o(dcxo, true);
|
||||||
|
half_period(timer);
|
||||||
|
sda_oe(dcxo, true);
|
||||||
|
half_period(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(dcxo: DCXO, timer: &mut GlobalTimer) {
|
||||||
|
// First, make sure SCL is low, so that the target releases the SDA line
|
||||||
|
scl_o(dcxo, false);
|
||||||
|
half_period(timer);
|
||||||
|
// Set SCL high then SDA high
|
||||||
|
sda_oe(dcxo, true);
|
||||||
|
scl_o(dcxo, true);
|
||||||
|
half_period(timer);
|
||||||
|
sda_oe(dcxo, false);
|
||||||
|
half_period(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(dcxo: DCXO, data: u8, timer: &mut GlobalTimer) -> bool {
|
||||||
|
// MSB first
|
||||||
|
for bit in (0..8).rev() {
|
||||||
|
// Set SCL low and set our bit on SDA
|
||||||
|
scl_o(dcxo, false);
|
||||||
|
sda_oe(dcxo, data & (1 << bit) == 0);
|
||||||
|
half_period(timer);
|
||||||
|
// Set SCL high ; data is shifted on the rising edge of SCL
|
||||||
|
scl_o(dcxo, true);
|
||||||
|
half_period(timer);
|
||||||
|
}
|
||||||
|
// Check ack
|
||||||
|
// Set SCL low, then release SDA so that the I2C target can respond
|
||||||
|
scl_o(dcxo, false);
|
||||||
|
half_period(timer);
|
||||||
|
sda_oe(dcxo, false);
|
||||||
|
// Set SCL high and check for ack
|
||||||
|
scl_o(dcxo, true);
|
||||||
|
half_period(timer);
|
||||||
|
// returns true if acked (I2C target pulled SDA low)
|
||||||
|
!sda_i(dcxo)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(dcxo: DCXO, ack: bool, timer: &mut GlobalTimer) -> u8 {
|
||||||
|
// Set SCL low first, otherwise setting SDA as input may cause a transition
|
||||||
|
// on SDA with SCL high which will be interpreted as START/STOP condition.
|
||||||
|
scl_o(dcxo, false);
|
||||||
|
half_period(timer); // make sure SCL has settled low
|
||||||
|
sda_oe(dcxo, false);
|
||||||
|
|
||||||
|
let mut data: u8 = 0;
|
||||||
|
|
||||||
|
// MSB first
|
||||||
|
for bit in (0..8).rev() {
|
||||||
|
scl_o(dcxo, false);
|
||||||
|
half_period(timer);
|
||||||
|
// Set SCL high and shift data
|
||||||
|
scl_o(dcxo, true);
|
||||||
|
half_period(timer);
|
||||||
|
if sda_i(dcxo) {
|
||||||
|
data |= 1 << bit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Send ack
|
||||||
|
// Set SCL low and pull SDA low when acking
|
||||||
|
scl_o(dcxo, false);
|
||||||
|
if ack {
|
||||||
|
sda_oe(dcxo, true)
|
||||||
|
}
|
||||||
|
half_period(timer);
|
||||||
|
// then set SCL high
|
||||||
|
scl_o(dcxo, true);
|
||||||
|
half_period(timer);
|
||||||
|
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(dcxo: i2c::DCXO, reg: u8, val: u8, timer: &mut GlobalTimer) -> Result<(), &'static str> {
|
||||||
|
i2c::start(dcxo, timer);
|
||||||
|
if !i2c::write(dcxo, ADDRESS << 1, timer) {
|
||||||
|
return Err("Si549 failed to ack write address");
|
||||||
|
}
|
||||||
|
if !i2c::write(dcxo, reg, timer) {
|
||||||
|
return Err("Si549 failed to ack register");
|
||||||
|
}
|
||||||
|
if !i2c::write(dcxo, val, timer) {
|
||||||
|
return Err("Si549 failed to ack value");
|
||||||
|
}
|
||||||
|
i2c::stop(dcxo, timer);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(dcxo: i2c::DCXO, reg: u8, timer: &mut GlobalTimer) -> Result<u8, &'static str> {
|
||||||
|
i2c::start(dcxo, timer);
|
||||||
|
if !i2c::write(dcxo, ADDRESS << 1, timer) {
|
||||||
|
return Err("Si549 failed to ack write address");
|
||||||
|
}
|
||||||
|
if !i2c::write(dcxo, reg, timer) {
|
||||||
|
return Err("Si549 failed to ack register");
|
||||||
|
}
|
||||||
|
i2c::stop(dcxo, timer);
|
||||||
|
|
||||||
|
i2c::start(dcxo, timer);
|
||||||
|
if !i2c::write(dcxo, (ADDRESS << 1) | 1, timer) {
|
||||||
|
return Err("Si549 failed to ack read address");
|
||||||
|
}
|
||||||
|
let val = i2c::read(dcxo, false, timer);
|
||||||
|
i2c::stop(dcxo, timer);
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(dcxo: i2c::DCXO, config: &DividerConfig, timer: &mut GlobalTimer) -> Result<(), &'static str> {
|
||||||
|
i2c::init(dcxo, timer)?;
|
||||||
|
|
||||||
|
write(dcxo, 255, 0x00, timer)?; // PAGE
|
||||||
|
write(dcxo, 69, 0x00, timer)?; // Disable FCAL override.
|
||||||
|
write(dcxo, 17, 0x00, timer)?; // Synchronously disable output
|
||||||
|
|
||||||
|
// The Si549 has no ID register, so we check that it responds correctly
|
||||||
|
// by writing values to a RAM-like register and reading them back.
|
||||||
|
for test_value in 0..255 {
|
||||||
|
write(dcxo, 23, test_value, timer)?;
|
||||||
|
let readback = read(dcxo, 23, timer)?;
|
||||||
|
if readback != test_value {
|
||||||
|
return Err("Si549 detection failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write(dcxo, 23, config.hsdiv as u8, timer)?;
|
||||||
|
write(dcxo, 24, (config.hsdiv >> 8) as u8 | (config.lsdiv << 4), timer)?;
|
||||||
|
write(dcxo, 26, config.fbdiv as u8, timer)?;
|
||||||
|
write(dcxo, 27, (config.fbdiv >> 8) as u8, timer)?;
|
||||||
|
write(dcxo, 28, (config.fbdiv >> 16) as u8, timer)?;
|
||||||
|
write(dcxo, 29, (config.fbdiv >> 24) as u8, timer)?;
|
||||||
|
write(dcxo, 30, (config.fbdiv >> 32) as u8, timer)?;
|
||||||
|
write(dcxo, 31, (config.fbdiv >> 40) as u8, timer)?;
|
||||||
|
|
||||||
|
write(dcxo, 7, 0x08, timer)?; // Start FCAL
|
||||||
|
timer.delay_us(30_000); // Internal FCAL VCO calibration
|
||||||
|
write(dcxo, 17, 0x01, timer)?; // Synchronously enable output
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_setup(timer: &mut GlobalTimer, settings: &FrequencySetting) -> Result<(), &'static str> {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll::main_dcxo_bitbang_enable_write(1);
|
||||||
|
csr::wrpll::main_dcxo_i2c_address_write(ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(i2c::DCXO::Main, &settings.main, timer)?;
|
||||||
|
|
||||||
|
// Si549 maximum settling time for large frequency change.
|
||||||
|
timer.delay_us(40_000);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll::main_dcxo_bitbang_enable_write(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Main Si549 started");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn helper_setup(timer: &mut GlobalTimer, settings: &FrequencySetting) -> Result<(), &'static str> {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll::helper_reset_write(1);
|
||||||
|
csr::wrpll::helper_dcxo_bitbang_enable_write(1);
|
||||||
|
csr::wrpll::helper_dcxo_i2c_address_write(ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(i2c::DCXO::Helper, &settings.helper, timer)?;
|
||||||
|
|
||||||
|
// Si549 maximum settling time for large frequency change.
|
||||||
|
timer.delay_us(40_000);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll::helper_reset_write(0);
|
||||||
|
csr::wrpll::helper_dcxo_bitbang_enable_write(0);
|
||||||
|
}
|
||||||
|
info!("Helper Si549 started");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_adpll(dcxo: i2c::DCXO, adpll: i32) -> Result<(), &'static str> {
|
||||||
|
if adpll.abs() > ADPLL_MAX {
|
||||||
|
return Err("adpll is too large");
|
||||||
|
}
|
||||||
|
|
||||||
|
match dcxo {
|
||||||
|
i2c::DCXO::Main => unsafe {
|
||||||
|
if csr::wrpll::main_dcxo_bitbang_enable_read() == 1 {
|
||||||
|
return Err("Main si549 bitbang mode is active when using gateware i2c");
|
||||||
|
}
|
||||||
|
|
||||||
|
while csr::wrpll::main_dcxo_adpll_busy_read() == 1 {}
|
||||||
|
if csr::wrpll::main_dcxo_nack_read() == 1 {
|
||||||
|
return Err("Main si549 failed to ack adpll write");
|
||||||
|
}
|
||||||
|
|
||||||
|
csr::wrpll::main_dcxo_i2c_address_write(ADDRESS);
|
||||||
|
csr::wrpll::main_dcxo_adpll_write(adpll as u32);
|
||||||
|
|
||||||
|
csr::wrpll::main_dcxo_adpll_stb_write(1);
|
||||||
|
},
|
||||||
|
i2c::DCXO::Helper => unsafe {
|
||||||
|
if csr::wrpll::helper_dcxo_bitbang_enable_read() == 1 {
|
||||||
|
return Err("Helper si549 bitbang mode is active when using gateware i2c");
|
||||||
|
}
|
||||||
|
|
||||||
|
while csr::wrpll::helper_dcxo_adpll_busy_read() == 1 {}
|
||||||
|
if csr::wrpll::helper_dcxo_nack_read() == 1 {
|
||||||
|
return Err("Helper si549 failed to ack adpll write");
|
||||||
|
}
|
||||||
|
|
||||||
|
csr::wrpll::helper_dcxo_i2c_address_write(ADDRESS);
|
||||||
|
csr::wrpll::helper_dcxo_adpll_write(adpll as u32);
|
||||||
|
|
||||||
|
csr::wrpll::helper_dcxo_adpll_stb_write(1);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_wrpll)]
|
||||||
|
pub mod wrpll {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const BEATING_PERIOD: i32 = 0x8000;
|
||||||
|
const BEATING_HALFPERIOD: i32 = 0x4000;
|
||||||
|
const COUNTER_WIDTH: u32 = 24;
|
||||||
|
const DIV_WIDTH: u32 = 2;
|
||||||
|
|
||||||
|
// y[n] = b0*x[n] + b1*x[n-1] + b2*x[n-2] - a1*y[n-1] - a2*y[n-2]
|
||||||
|
struct FilterParameters {
|
||||||
|
pub b0: f64,
|
||||||
|
pub b1: f64,
|
||||||
|
pub b2: f64,
|
||||||
|
pub a1: f64,
|
||||||
|
pub a2: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(rtio_frequency = "100.0")]
|
||||||
|
const LPF: FilterParameters = FilterParameters {
|
||||||
|
b0: 0.03967479060647884,
|
||||||
|
b1: 0.07934958121295768,
|
||||||
|
b2: 0.03967479060647884,
|
||||||
|
a1: -1.3865593741228928,
|
||||||
|
a2: 0.5452585365488082,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(rtio_frequency = "125.0")]
|
||||||
|
const LPF: FilterParameters = FilterParameters {
|
||||||
|
b0: 0.07209205036273991,
|
||||||
|
b1: 0.14418410072547982,
|
||||||
|
b2: 0.07209205036273991,
|
||||||
|
a1: -0.6114078511562919,
|
||||||
|
a2: -0.10022394739274834,
|
||||||
|
};
|
||||||
|
|
||||||
|
static mut H_ADPLL1: i32 = 0;
|
||||||
|
static mut H_ADPLL2: i32 = 0;
|
||||||
|
static mut PERIOD_ERR1: i32 = 0;
|
||||||
|
static mut PERIOD_ERR2: i32 = 0;
|
||||||
|
|
||||||
|
static mut M_ADPLL1: i32 = 0;
|
||||||
|
static mut M_ADPLL2: i32 = 0;
|
||||||
|
static mut PHASE_ERR1: i32 = 0;
|
||||||
|
static mut PHASE_ERR2: i32 = 0;
|
||||||
|
|
||||||
|
static mut BASE_ADPLL: i32 = 0;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ISR {
|
||||||
|
RefTag,
|
||||||
|
MainTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tag_collector {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(wrpll_ref_clk = "GT_CDR")]
|
||||||
|
static mut TAG_OFFSET: u32 = 8382;
|
||||||
|
#[cfg(wrpll_ref_clk = "SMA_CLKIN")]
|
||||||
|
static mut TAG_OFFSET: u32 = 0;
|
||||||
|
static mut REF_TAG: u32 = 0;
|
||||||
|
static mut REF_TAG_READY: bool = false;
|
||||||
|
static mut MAIN_TAG: u32 = 0;
|
||||||
|
static mut MAIN_TAG_READY: bool = false;
|
||||||
|
|
||||||
|
pub fn reset() {
|
||||||
|
clear_phase_diff_ready();
|
||||||
|
unsafe {
|
||||||
|
REF_TAG = 0;
|
||||||
|
MAIN_TAG = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_phase_diff_ready() {
|
||||||
|
unsafe {
|
||||||
|
REF_TAG_READY = false;
|
||||||
|
MAIN_TAG_READY = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_tags(interrupt: ISR) {
|
||||||
|
match interrupt {
|
||||||
|
ISR::RefTag => unsafe {
|
||||||
|
REF_TAG = csr::wrpll::ref_tag_read();
|
||||||
|
REF_TAG_READY = true;
|
||||||
|
},
|
||||||
|
ISR::MainTag => unsafe {
|
||||||
|
MAIN_TAG = csr::wrpll::main_tag_read();
|
||||||
|
MAIN_TAG_READY = true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn phase_diff_ready() -> bool {
|
||||||
|
unsafe { REF_TAG_READY && MAIN_TAG_READY }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
pub fn set_tag_offset(offset: u32) {
|
||||||
|
unsafe {
|
||||||
|
TAG_OFFSET = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
pub fn get_tag_offset() -> u32 {
|
||||||
|
unsafe { TAG_OFFSET }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_period_error() -> i32 {
|
||||||
|
// n * BEATING_PERIOD - REF_TAG(n) mod BEATING_PERIOD
|
||||||
|
let mut period_error = unsafe { REF_TAG.overflowing_neg().0.rem_euclid(BEATING_PERIOD as u32) as i32 };
|
||||||
|
// mapping tags from [0, 2π] -> [-π, π]
|
||||||
|
if period_error > BEATING_HALFPERIOD {
|
||||||
|
period_error -= BEATING_PERIOD
|
||||||
|
}
|
||||||
|
period_error
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_phase_error() -> i32 {
|
||||||
|
// MAIN_TAG(n) - REF_TAG(n) - TAG_OFFSET mod BEATING_PERIOD
|
||||||
|
let mut phase_error = unsafe {
|
||||||
|
MAIN_TAG
|
||||||
|
.overflowing_sub(REF_TAG + TAG_OFFSET)
|
||||||
|
.0
|
||||||
|
.rem_euclid(BEATING_PERIOD as u32) as i32
|
||||||
|
};
|
||||||
|
|
||||||
|
// mapping tags from [0, 2π] -> [-π, π]
|
||||||
|
if phase_error > BEATING_HALFPERIOD {
|
||||||
|
phase_error -= BEATING_PERIOD
|
||||||
|
}
|
||||||
|
phase_error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_isr(en: bool) {
|
||||||
|
let val = if en { 1 } else { 0 };
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll::ref_tag_ev_enable_write(val);
|
||||||
|
csr::wrpll::main_tag_ev_enable_write(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_base_adpll() -> Result<(), &'static str> {
|
||||||
|
let count2adpll =
|
||||||
|
|error: i32| ((error as f64 * 1e6) / (0.0001164 * (1 << (COUNTER_WIDTH - DIV_WIDTH)) as f64)) as i32;
|
||||||
|
|
||||||
|
let (ref_count, main_count) = get_freq_counts();
|
||||||
|
unsafe {
|
||||||
|
BASE_ADPLL = count2adpll(ref_count as i32 - main_count as i32);
|
||||||
|
set_adpll(i2c::DCXO::Main, BASE_ADPLL)?;
|
||||||
|
set_adpll(i2c::DCXO::Helper, BASE_ADPLL)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_freq_counts() -> (u32, u32) {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll::frequency_counter_update_write(1);
|
||||||
|
while csr::wrpll::frequency_counter_busy_read() == 1 {}
|
||||||
|
#[cfg(wrpll_ref_clk = "GT_CDR")]
|
||||||
|
let ref_count = csr::wrpll::frequency_counter_counter_rtio_rx0_read();
|
||||||
|
#[cfg(wrpll_ref_clk = "SMA_CLKIN")]
|
||||||
|
let ref_count = csr::wrpll::frequency_counter_counter_ref_read();
|
||||||
|
let main_count = csr::wrpll::frequency_counter_counter_sys_read();
|
||||||
|
|
||||||
|
(ref_count, main_count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_plls(timer: &mut GlobalTimer) -> Result<(), &'static str> {
|
||||||
|
unsafe {
|
||||||
|
H_ADPLL1 = 0;
|
||||||
|
H_ADPLL2 = 0;
|
||||||
|
PERIOD_ERR1 = 0;
|
||||||
|
PERIOD_ERR2 = 0;
|
||||||
|
M_ADPLL1 = 0;
|
||||||
|
M_ADPLL2 = 0;
|
||||||
|
PHASE_ERR1 = 0;
|
||||||
|
PHASE_ERR2 = 0;
|
||||||
|
}
|
||||||
|
set_adpll(i2c::DCXO::Main, 0)?;
|
||||||
|
set_adpll(i2c::DCXO::Helper, 0)?;
|
||||||
|
// wait for adpll to transfer and DCXO to settle
|
||||||
|
timer.delay_us(200);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_pending(interrupt: ISR) {
|
||||||
|
match interrupt {
|
||||||
|
ISR::RefTag => unsafe { csr::wrpll::ref_tag_ev_pending_write(1) },
|
||||||
|
ISR::MainTag => unsafe { csr::wrpll::main_tag_ev_pending_write(1) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_pending(interrupt: ISR) -> bool {
|
||||||
|
match interrupt {
|
||||||
|
ISR::RefTag => unsafe { csr::wrpll::ref_tag_ev_pending_read() == 1 },
|
||||||
|
ISR::MainTag => unsafe { csr::wrpll::main_tag_ev_pending_read() == 1 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_handler() {
|
||||||
|
if is_pending(ISR::RefTag) {
|
||||||
|
tag_collector::collect_tags(ISR::RefTag);
|
||||||
|
clear_pending(ISR::RefTag);
|
||||||
|
helper_pll().expect("failed to run helper DCXO PLL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_pending(ISR::MainTag) {
|
||||||
|
tag_collector::collect_tags(ISR::MainTag);
|
||||||
|
clear_pending(ISR::MainTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag_collector::phase_diff_ready() {
|
||||||
|
main_pll().expect("failed to run main DCXO PLL");
|
||||||
|
tag_collector::clear_phase_diff_ready();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn helper_pll() -> Result<(), &'static str> {
|
||||||
|
let period_err = tag_collector::get_period_error();
|
||||||
|
unsafe {
|
||||||
|
let adpll = ((LPF.b0 * period_err as f64) + (LPF.b1 * PERIOD_ERR1 as f64) + (LPF.b2 * PERIOD_ERR2 as f64)
|
||||||
|
- (LPF.a1 * H_ADPLL1 as f64)
|
||||||
|
- (LPF.a2 * H_ADPLL2 as f64)) as i32;
|
||||||
|
set_adpll(i2c::DCXO::Helper, BASE_ADPLL + adpll)?;
|
||||||
|
H_ADPLL2 = H_ADPLL1;
|
||||||
|
PERIOD_ERR2 = PERIOD_ERR1;
|
||||||
|
H_ADPLL1 = adpll;
|
||||||
|
PERIOD_ERR1 = period_err;
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main_pll() -> Result<(), &'static str> {
|
||||||
|
let phase_err = tag_collector::get_phase_error();
|
||||||
|
unsafe {
|
||||||
|
let adpll = ((LPF.b0 * phase_err as f64) + (LPF.b1 * PHASE_ERR1 as f64) + (LPF.b2 * PHASE_ERR2 as f64)
|
||||||
|
- (LPF.a1 * M_ADPLL1 as f64)
|
||||||
|
- (LPF.a2 * M_ADPLL2 as f64)) as i32;
|
||||||
|
set_adpll(i2c::DCXO::Main, BASE_ADPLL + adpll)?;
|
||||||
|
M_ADPLL2 = M_ADPLL1;
|
||||||
|
PHASE_ERR2 = PHASE_ERR1;
|
||||||
|
M_ADPLL1 = adpll;
|
||||||
|
PHASE_ERR1 = phase_err;
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(wrpll_ref_clk = "GT_CDR")]
|
||||||
|
fn test_skew(timer: &mut GlobalTimer) -> Result<(), &'static str> {
|
||||||
|
// wait for PLL to stabilize
|
||||||
|
timer.delay_us(20_000);
|
||||||
|
|
||||||
|
info!("testing the skew of SYS CLK...");
|
||||||
|
if has_timing_error(timer) {
|
||||||
|
return Err("the skew cannot satisfy setup/hold time constraint of RX synchronizer");
|
||||||
|
}
|
||||||
|
info!("the skew of SYS CLK met the timing constraint");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(wrpll_ref_clk = "GT_CDR")]
|
||||||
|
fn has_timing_error(timer: &mut GlobalTimer) -> bool {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_skewtester::error_write(1);
|
||||||
|
}
|
||||||
|
timer.delay_us(5_000);
|
||||||
|
unsafe { csr::wrpll_skewtester::error_read() == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
fn find_edge(target: bool, timer: &mut GlobalTimer) -> Result<u32, &'static str> {
|
||||||
|
const STEP: u32 = 8;
|
||||||
|
const STABLE_THRESHOLD: u32 = 10;
|
||||||
|
|
||||||
|
enum FSM {
|
||||||
|
Init,
|
||||||
|
WaitEdge,
|
||||||
|
GotEdge,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state: FSM = FSM::Init;
|
||||||
|
let mut offset: u32 = tag_collector::get_tag_offset();
|
||||||
|
let mut median_edge: u32 = 0;
|
||||||
|
let mut stable_counter: u32 = 0;
|
||||||
|
|
||||||
|
for _ in 0..(BEATING_PERIOD as u32 / STEP) as usize {
|
||||||
|
tag_collector::set_tag_offset(offset);
|
||||||
|
offset += STEP;
|
||||||
|
// wait for PLL to stabilize
|
||||||
|
timer.delay_us(20_000);
|
||||||
|
|
||||||
|
let error = has_timing_error(timer);
|
||||||
|
// A median edge deglitcher
|
||||||
|
match state {
|
||||||
|
FSM::Init => {
|
||||||
|
if error != target {
|
||||||
|
stable_counter += 1;
|
||||||
|
} else {
|
||||||
|
stable_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stable_counter >= STABLE_THRESHOLD {
|
||||||
|
state = FSM::WaitEdge;
|
||||||
|
stable_counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FSM::WaitEdge => {
|
||||||
|
if error == target {
|
||||||
|
state = FSM::GotEdge;
|
||||||
|
median_edge = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FSM::GotEdge => {
|
||||||
|
if error != target {
|
||||||
|
median_edge += STEP;
|
||||||
|
stable_counter = 0;
|
||||||
|
} else {
|
||||||
|
stable_counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stable_counter >= STABLE_THRESHOLD {
|
||||||
|
return Ok(median_edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err("failed to find timing error edge");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
fn calibrate_skew(timer: &mut GlobalTimer) -> Result<(), &'static str> {
|
||||||
|
info!("calibrating skew to meet timing constraint...");
|
||||||
|
|
||||||
|
// clear calibrated value
|
||||||
|
tag_collector::set_tag_offset(0);
|
||||||
|
let rising = find_edge(true, timer)? as i32;
|
||||||
|
let falling = find_edge(false, timer)? as i32;
|
||||||
|
|
||||||
|
let width = BEATING_PERIOD - (falling - rising);
|
||||||
|
let result = falling + width / 2;
|
||||||
|
tag_collector::set_tag_offset(result as u32);
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"calibration successful, error zone: {} -> {}, width: {} ({}deg), middle of working region: {}",
|
||||||
|
rising,
|
||||||
|
falling,
|
||||||
|
width,
|
||||||
|
360 * width / BEATING_PERIOD,
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_recovered_clock(rc: bool, timer: &mut GlobalTimer) {
|
||||||
|
set_isr(false);
|
||||||
|
|
||||||
|
if rc {
|
||||||
|
tag_collector::reset();
|
||||||
|
reset_plls(timer).expect("failed to reset main and helper PLL");
|
||||||
|
|
||||||
|
// get within capture range
|
||||||
|
set_base_adpll().expect("failed to set base adpll");
|
||||||
|
|
||||||
|
// clear gateware pending flag
|
||||||
|
clear_pending(ISR::RefTag);
|
||||||
|
clear_pending(ISR::MainTag);
|
||||||
|
|
||||||
|
// use nFIQ to avoid IRQ being disabled by mutex lock and mess up PLL
|
||||||
|
set_isr(true);
|
||||||
|
info!("WRPLL interrupt enabled");
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
calibrate_skew(timer).expect("failed to set the correct skew");
|
||||||
|
|
||||||
|
#[cfg(wrpll_ref_clk = "GT_CDR")]
|
||||||
|
test_skew(timer).expect("skew test failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_wrpll_refclk)]
|
||||||
|
pub mod wrpll_refclk {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct MmcmSetting {
|
||||||
|
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() {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::mmcm_dclk_write(1);
|
||||||
|
csr::wrpll_refclk::mmcm_dclk_write(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_addr(address: u8) {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::mmcm_daddr_write(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_data(value: u16) {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::mmcm_din_write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_enable(en: bool) {
|
||||||
|
unsafe {
|
||||||
|
let val = if en { 1 } else { 0 };
|
||||||
|
csr::wrpll_refclk::mmcm_den_write(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_write_enable(en: bool) {
|
||||||
|
unsafe {
|
||||||
|
let val = if en { 1 } else { 0 };
|
||||||
|
csr::wrpll_refclk::mmcm_dwen_write(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_data() -> u16 {
|
||||||
|
unsafe { csr::wrpll_refclk::mmcm_dout_read() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drp_ready() -> bool {
|
||||||
|
unsafe { csr::wrpll_refclk::mmcm_dready_read() == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn read(address: u8) -> u16 {
|
||||||
|
set_addr(address);
|
||||||
|
set_enable(true);
|
||||||
|
// Set DADDR on the mmcm and assert DEN for one clock cycle
|
||||||
|
one_clock_cycle();
|
||||||
|
|
||||||
|
set_enable(false);
|
||||||
|
while !drp_ready() {
|
||||||
|
// keep the clock signal until data is ready
|
||||||
|
one_clock_cycle();
|
||||||
|
}
|
||||||
|
get_data()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(address: u8, value: u16) {
|
||||||
|
set_addr(address);
|
||||||
|
set_data(value);
|
||||||
|
set_write_enable(true);
|
||||||
|
set_enable(true);
|
||||||
|
// Set DADDR, DI on the mmcm and assert DWE, DEN for one clock cycle
|
||||||
|
one_clock_cycle();
|
||||||
|
|
||||||
|
set_write_enable(false);
|
||||||
|
set_enable(false);
|
||||||
|
while !drp_ready() {
|
||||||
|
// keep the clock signal until write is finished
|
||||||
|
one_clock_cycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(rst: bool) {
|
||||||
|
unsafe {
|
||||||
|
let val = if rst { 1 } else { 0 };
|
||||||
|
csr::wrpll_refclk::mmcm_reset_write(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(timer: &mut GlobalTimer, settings: MmcmSetting, mmcm_bypass: bool) -> Result<(), &'static str> {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::refclk_reset_write(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if mmcm_bypass {
|
||||||
|
info!("Bypassing mmcm");
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::mmcm_bypass_write(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Based on "DRP State Machine" from XAPP888
|
||||||
|
// hold reset HIGH during mmcm config
|
||||||
|
reset(true);
|
||||||
|
write(0x08, settings.clkout0_reg1);
|
||||||
|
write(0x09, settings.clkout0_reg2);
|
||||||
|
write(0x14, settings.clkfbout_reg1);
|
||||||
|
write(0x15, settings.clkfbout_reg2);
|
||||||
|
write(0x16, settings.div_reg);
|
||||||
|
write(0x18, settings.lock_reg1);
|
||||||
|
write(0x19, settings.lock_reg2);
|
||||||
|
write(0x1A, settings.lock_reg3);
|
||||||
|
write(0x28, settings.power_reg);
|
||||||
|
write(0x4E, settings.filt_reg1);
|
||||||
|
write(0x4F, settings.filt_reg2);
|
||||||
|
reset(false);
|
||||||
|
|
||||||
|
// wait for the mmcm to lock
|
||||||
|
timer.delay_us(100);
|
||||||
|
|
||||||
|
let locked = unsafe { csr::wrpll_refclk::mmcm_locked_read() == 1 };
|
||||||
|
if !locked {
|
||||||
|
return Err("mmcm failed to generate 125MHz ref clock from SMA CLKIN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::refclk_reset_write(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,9 @@ SECTIONS
|
||||||
__text_start = .;
|
__text_start = .;
|
||||||
.text :
|
.text :
|
||||||
{
|
{
|
||||||
|
__exceptions_start = .;
|
||||||
KEEP(*(.text.exceptions));
|
KEEP(*(.text.exceptions));
|
||||||
|
__exceptions_end = .;
|
||||||
*(.text.boot);
|
*(.text.boot);
|
||||||
*(.text .text.*);
|
*(.text .text.*);
|
||||||
} > SDRAM
|
} > SDRAM
|
||||||
|
|
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = { version = "1.0.1" }
|
cc = { version = "1.0.1" }
|
|
@ -85,10 +85,7 @@ unsafe fn get_ttype_entry(
|
||||||
encoding | DW_EH_PE_pcrel,
|
encoding | DW_EH_PE_pcrel,
|
||||||
ttype_base,
|
ttype_base,
|
||||||
)
|
)
|
||||||
.map(|v| match v {
|
.map(|v| (v != ttype_base).then(|| v as *const u8))
|
||||||
ttype_base => None,
|
|
||||||
ttype_entry => Some(ttype_entry as *const u8),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn find_eh_action(
|
pub unsafe fn find_eh_action(
|
||||||
|
|
|
@ -8,4 +8,4 @@ name = "dyld"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" }
|
|
@ -11,7 +11,7 @@ path = "lib.rs"
|
||||||
core_io = { version = "0.1", features = ["collections"] }
|
core_io = { version = "0.1", features = ["collections"] }
|
||||||
byteorder = { version = "1.0", default-features = false, optional = true }
|
byteorder = { version = "1.0", default-features = false, optional = true }
|
||||||
|
|
||||||
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = []
|
alloc = []
|
|
@ -1,3 +1,6 @@
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use core_io::{Error as IoError, Read, Write};
|
use core_io::{Error as IoError, Read, Write};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -42,7 +45,10 @@ impl<T: AsRef<[u8]>> Read for Cursor<T> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
|
||||||
let data = &self.inner.as_ref()[self.pos..];
|
let data = &self.inner.as_ref()[self.pos..];
|
||||||
let len = buf.len().min(data.len());
|
let len = buf.len().min(data.len());
|
||||||
buf[..len].copy_from_slice(&data[..len]);
|
// ``copy_from_slice`` generates AXI bursts, use a regular loop instead
|
||||||
|
for i in 0..len {
|
||||||
|
buf[i] = data[i];
|
||||||
|
}
|
||||||
self.pos += len;
|
self.pos += len;
|
||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
|
@ -52,7 +58,9 @@ impl Write for Cursor<&mut [u8]> {
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
|
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
|
||||||
let data = &mut self.inner[self.pos..];
|
let data = &mut self.inner[self.pos..];
|
||||||
let len = buf.len().min(data.len());
|
let len = buf.len().min(data.len());
|
||||||
data[..len].copy_from_slice(&buf[..len]);
|
for i in 0..len {
|
||||||
|
data[i] = buf[i];
|
||||||
|
}
|
||||||
self.pos += len;
|
self.pos += len;
|
||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
|
@ -64,8 +72,7 @@ impl Write for Cursor<&mut [u8]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
impl Write for Cursor<::alloc::Vec<u8>> {
|
impl Write for Cursor<Vec<u8>> {
|
||||||
#[inline]
|
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
|
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
|
||||||
self.inner.extend_from_slice(buf);
|
self.inner.extend_from_slice(buf);
|
||||||
Ok(buf.len())
|
Ok(buf.len())
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![cfg_attr(feature = "alloc", feature(alloc))]
|
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate core_io;
|
extern crate core_io;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
#[macro_use]
|
|
||||||
use alloc;
|
|
||||||
#[cfg(feature = "byteorder")]
|
#[cfg(feature = "byteorder")]
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::{string::String, vec};
|
use alloc::{string::String, vec};
|
||||||
use core::str::Utf8Error;
|
use core::str::Utf8Error;
|
||||||
|
|
||||||
|
@ -50,7 +51,8 @@ pub trait ProtoRead {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_bytes(&mut self) -> Result<::alloc::vec::Vec<u8>, Self::ReadError> {
|
#[cfg(feature = "alloc")]
|
||||||
|
fn read_bytes(&mut self) -> Result<vec::Vec<u8>, Self::ReadError> {
|
||||||
let length = self.read_u32()?;
|
let length = self.read_u32()?;
|
||||||
let mut value = vec![0; length as usize];
|
let mut value = vec![0; length as usize];
|
||||||
self.read_exact(&mut value)?;
|
self.read_exact(&mut value)?;
|
||||||
|
@ -58,7 +60,8 @@ pub trait ProtoRead {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_string(&mut self) -> Result<::alloc::string::String, ReadStringError<Self::ReadError>> {
|
#[cfg(feature = "alloc")]
|
||||||
|
fn read_string(&mut self) -> Result<String, ReadStringError<Self::ReadError>> {
|
||||||
let bytes = self.read_bytes().map_err(ReadStringError::Other)?;
|
let bytes = self.read_bytes().map_err(ReadStringError::Other)?;
|
||||||
String::from_utf8(bytes).map_err(|err| ReadStringError::Utf8(err.utf8_error()))
|
String::from_utf8(bytes).map_err(|err| ReadStringError::Utf8(err.utf8_error()))
|
||||||
}
|
}
|
||||||
|
@ -135,6 +138,7 @@ pub trait ProtoWrite {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
fn write_string(&mut self, value: &str) -> Result<(), Self::WriteError> {
|
fn write_string(&mut self, value: &str) -> Result<(), Self::WriteError> {
|
||||||
self.write_bytes(value.as_bytes())
|
self.write_bytes(value.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
[package]
|
||||||
|
name = "ksupport"
|
||||||
|
description = "Kernel support for Zynq-based platforms"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
build_zynq = { path = "../libbuild_zynq" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cslice = "0.3"
|
||||||
|
log = "0.4"
|
||||||
|
nb = "0.1"
|
||||||
|
core_io = { version = "0.1", features = ["collections"] }
|
||||||
|
byteorder = { version = "1.3", default-features = false }
|
||||||
|
void = { version = "1", default-features = false }
|
||||||
|
log_buffer = { version = "1.2" }
|
||||||
|
libm = { version = "0.2", features = ["unstable"] }
|
||||||
|
vcell = "0.1"
|
||||||
|
|
||||||
|
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq", features = ["ipv6"]}
|
||||||
|
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] }
|
||||||
|
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" }
|
||||||
|
libasync = { path = "@@ZYNQ_RS@@/libasync" }
|
||||||
|
libregister = { path = "@@ZYNQ_RS@@/libregister" }
|
||||||
|
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn", "ipv6"] }
|
||||||
|
|
||||||
|
dyld = { path = "../libdyld" }
|
||||||
|
dwarf = { path = "../libdwarf" }
|
||||||
|
unwind = { path = "../libunwind" }
|
||||||
|
libc = { path = "../libc" }
|
||||||
|
io = { path = "../libio" }
|
||||||
|
libboard_artiq = { path = "../libboard_artiq" }
|
||||||
|
|
||||||
|
[dependencies.nalgebra]
|
||||||
|
git = "https://git.m-labs.hk/M-Labs/nalgebra.git"
|
||||||
|
rev = "dd00f9b"
|
||||||
|
default-features = false
|
||||||
|
features = ["libm", "alloc"]
|
|
@ -0,0 +1,5 @@
|
||||||
|
extern crate build_zynq;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_zynq::cfg();
|
||||||
|
}
|
|
@ -14,8 +14,10 @@
|
||||||
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
use cslice::CSlice;
|
use core_io::Error as ReadError;
|
||||||
|
use cslice::{AsCSlice, CSlice};
|
||||||
use dwarf::eh::{self, EHAction, EHContext};
|
use dwarf::eh::{self, EHAction, EHContext};
|
||||||
|
use io::{Cursor, ProtoRead};
|
||||||
use libc::{c_int, c_void, uintptr_t};
|
use libc::{c_int, c_void, uintptr_t};
|
||||||
use log::{error, trace};
|
use log::{error, trace};
|
||||||
use unwind as uw;
|
use unwind as uw;
|
||||||
|
@ -220,8 +222,6 @@ pub unsafe fn artiq_personality(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn raise(exception: *const Exception) -> ! {
|
pub unsafe extern "C" fn raise(exception: *const Exception) -> ! {
|
||||||
use cslice::AsCSlice;
|
|
||||||
|
|
||||||
let count = EXCEPTION_BUFFER.exception_count;
|
let count = EXCEPTION_BUFFER.exception_count;
|
||||||
let stack = &mut EXCEPTION_BUFFER.exception_stack;
|
let stack = &mut EXCEPTION_BUFFER.exception_stack;
|
||||||
let diff = exception as isize - EXCEPTION_BUFFER.exceptions.as_ptr() as isize;
|
let diff = exception as isize - EXCEPTION_BUFFER.exceptions.as_ptr() as isize;
|
||||||
|
@ -295,6 +295,60 @@ pub unsafe extern "C" fn raise(exception: *const Exception) -> ! {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_exception_string<'a>(reader: &mut Cursor<&[u8]>) -> Result<CSlice<'a, u8>, ReadError> {
|
||||||
|
let len = reader.read_u32()? as usize;
|
||||||
|
if len == usize::MAX {
|
||||||
|
let data = reader.read_u32()?;
|
||||||
|
Ok(unsafe { CSlice::new(data as *const u8, len) })
|
||||||
|
} else {
|
||||||
|
let pos = reader.position();
|
||||||
|
let slice = unsafe {
|
||||||
|
let ptr = reader.get_ref().as_ptr().offset(pos as isize);
|
||||||
|
CSlice::new(ptr, len)
|
||||||
|
};
|
||||||
|
reader.set_position(pos + len);
|
||||||
|
Ok(slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_exception(raw_exception: &[u8]) -> Result<Exception, ReadError> {
|
||||||
|
let mut reader = Cursor::new(raw_exception);
|
||||||
|
|
||||||
|
let mut byte = reader.read_u8()?;
|
||||||
|
// to sync
|
||||||
|
while byte != 0x5a {
|
||||||
|
byte = reader.read_u8()?;
|
||||||
|
}
|
||||||
|
// skip sync bytes, 0x09 indicates exception
|
||||||
|
while byte != 0x09 {
|
||||||
|
byte = reader.read_u8()?;
|
||||||
|
}
|
||||||
|
let _len = reader.read_u32()?;
|
||||||
|
// ignore the remaining exceptions, stack traces etc. - unwinding from another device would be unwise anyway
|
||||||
|
Ok(Exception {
|
||||||
|
id: reader.read_u32()?,
|
||||||
|
message: read_exception_string(&mut reader)?,
|
||||||
|
param: [
|
||||||
|
reader.read_u64()? as i64,
|
||||||
|
reader.read_u64()? as i64,
|
||||||
|
reader.read_u64()? as i64,
|
||||||
|
],
|
||||||
|
file: read_exception_string(&mut reader)?,
|
||||||
|
line: reader.read_u32()?,
|
||||||
|
column: reader.read_u32()?,
|
||||||
|
function: read_exception_string(&mut reader)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raise_raw(raw_exception: &[u8]) -> ! {
|
||||||
|
use crate::artiq_raise;
|
||||||
|
if let Ok(exception) = read_exception(raw_exception) {
|
||||||
|
unsafe { raise(&exception) };
|
||||||
|
} else {
|
||||||
|
artiq_raise!("SubkernelError", "Error passing exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn resume() -> ! {
|
pub unsafe extern "C" fn resume() -> ! {
|
||||||
trace!("resume");
|
trace!("resume");
|
||||||
assert!(EXCEPTION_BUFFER.exception_count != 0);
|
assert!(EXCEPTION_BUFFER.exception_count != 0);
|
||||||
|
@ -421,19 +475,30 @@ extern "C" fn stop_fn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be kept in sync with preallocate_runtime_exception_names() in artiq/language/embedding_map.py
|
// Must be kept in sync with preallocate_runtime_exception_names() in `artiq.compiler.embedding`
|
||||||
static EXCEPTION_ID_LOOKUP: [(&str, u32); 11] = [
|
static EXCEPTION_ID_LOOKUP: [(&str, u32); 22] = [
|
||||||
("RuntimeError", 0),
|
("RTIOUnderflow", 0),
|
||||||
("RTIOUnderflow", 1),
|
("RTIOOverflow", 1),
|
||||||
("RTIOOverflow", 2),
|
("RTIODestinationUnreachable", 2),
|
||||||
("RTIODestinationUnreachable", 3),
|
("DMAError", 3),
|
||||||
("DMAError", 4),
|
("I2CError", 4),
|
||||||
("I2CError", 5),
|
("CacheError", 5),
|
||||||
("CacheError", 6),
|
("SPIError", 6),
|
||||||
("SPIError", 7),
|
("SubkernelError", 7),
|
||||||
("ZeroDivisionError", 8),
|
("AssertionError", 8),
|
||||||
("IndexError", 9),
|
("AttributeError", 9),
|
||||||
("UnwrapNoneError", 10),
|
("IndexError", 10),
|
||||||
|
("IOError", 11),
|
||||||
|
("KeyError", 12),
|
||||||
|
("NotImplementedError", 13),
|
||||||
|
("OverflowError", 14),
|
||||||
|
("RuntimeError", 15),
|
||||||
|
("TimeoutError", 16),
|
||||||
|
("TypeError", 17),
|
||||||
|
("ValueError", 18),
|
||||||
|
("ZeroDivisionError", 19),
|
||||||
|
("LinAlgError", 20),
|
||||||
|
("UnwrapNoneError", 21),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn get_exception_id(name: &str) -> u32 {
|
pub fn get_exception_id(name: &str) -> u32 {
|
||||||
|
@ -468,3 +533,29 @@ macro_rules! artiq_raise {
|
||||||
}};
|
}};
|
||||||
($name:expr, $message:expr) => {{ artiq_raise!($name, $message, 0, 0, 0) }};
|
($name:expr, $message:expr) => {{ artiq_raise!($name, $message, 0, 0, 0) }};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes as input exception id from host
|
||||||
|
/// Generates a new exception with:
|
||||||
|
/// * `id` set to `exn_id`
|
||||||
|
/// * `message` set to corresponding exception name from `EXCEPTION_ID_LOOKUP`
|
||||||
|
///
|
||||||
|
/// The message is matched on host to ensure correct exception is being referred
|
||||||
|
/// This test checks the synchronization of exception ids for runtime errors
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn test_exception_id_sync(exn_id: u32) {
|
||||||
|
let message = EXCEPTION_ID_LOOKUP
|
||||||
|
.iter()
|
||||||
|
.find_map(|&(name, id)| if id == exn_id { Some(name) } else { None })
|
||||||
|
.unwrap_or("unallocated internal exception id");
|
||||||
|
|
||||||
|
let exn = Exception {
|
||||||
|
id: exn_id,
|
||||||
|
file: file!().as_c_slice(),
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
function: "test_exception_id_sync".as_c_slice(),
|
||||||
|
message: message.as_c_slice(),
|
||||||
|
param: [0, 0, 0],
|
||||||
|
};
|
||||||
|
unsafe { raise(&exn) };
|
||||||
|
}
|
|
@ -5,9 +5,11 @@ use libc::{c_char, c_int, size_t};
|
||||||
use libm;
|
use libm;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use super::subkernel;
|
||||||
use super::{cache,
|
use super::{cache,
|
||||||
core1::rtio_get_destination_status,
|
core1::rtio_get_destination_status,
|
||||||
dma,
|
dma, linalg,
|
||||||
rpc::{rpc_recv, rpc_send, rpc_send_async}};
|
rpc::{rpc_recv, rpc_send, rpc_send_async}};
|
||||||
use crate::{eh_artiq, i2c, rtio};
|
use crate::{eh_artiq, i2c, rtio};
|
||||||
|
|
||||||
|
@ -114,6 +116,16 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
api!(i2c_read = i2c::read),
|
api!(i2c_read = i2c::read),
|
||||||
api!(i2c_switch_select = i2c::switch_select),
|
api!(i2c_switch_select = i2c::switch_select),
|
||||||
|
|
||||||
|
// subkernel
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
api!(subkernel_load_run = subkernel::load_run),
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
api!(subkernel_await_finish = subkernel::await_finish),
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
api!(subkernel_send_message = subkernel::send_message),
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
api!(subkernel_await_message = subkernel::await_message),
|
||||||
|
|
||||||
// Double-precision floating-point arithmetic helper functions
|
// Double-precision floating-point arithmetic helper functions
|
||||||
// RTABI chapter 4.1.2, Table 2
|
// RTABI chapter 4.1.2, Table 2
|
||||||
api!(__aeabi_dadd),
|
api!(__aeabi_dadd),
|
||||||
|
@ -291,6 +303,7 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
api_libm_f64f64f64!(nextafter),
|
api_libm_f64f64f64!(nextafter),
|
||||||
api_libm_f64f64f64!(pow),
|
api_libm_f64f64f64!(pow),
|
||||||
api_libm_f64f64!(round),
|
api_libm_f64f64!(round),
|
||||||
|
api_libm_f64f64!(rint),
|
||||||
api_libm_f64f64!(sin),
|
api_libm_f64f64!(sin),
|
||||||
api_libm_f64f64!(sinh),
|
api_libm_f64f64!(sinh),
|
||||||
api_libm_f64f64!(sqrt),
|
api_libm_f64f64!(sqrt),
|
||||||
|
@ -306,6 +319,26 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
}
|
}
|
||||||
api!(yn = yn)
|
api!(yn = yn)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// linalg
|
||||||
|
api!(np_linalg_cholesky = linalg::np_linalg_cholesky),
|
||||||
|
api!(np_linalg_qr = linalg::np_linalg_qr),
|
||||||
|
api!(np_linalg_svd = linalg::np_linalg_svd),
|
||||||
|
api!(np_linalg_inv = linalg::np_linalg_inv),
|
||||||
|
api!(np_linalg_pinv = linalg::np_linalg_pinv),
|
||||||
|
api!(np_linalg_matrix_power = linalg::np_linalg_matrix_power),
|
||||||
|
api!(np_linalg_det = linalg::np_linalg_det),
|
||||||
|
api!(sp_linalg_lu = linalg::sp_linalg_lu),
|
||||||
|
api!(sp_linalg_schur = linalg::sp_linalg_schur),
|
||||||
|
api!(sp_linalg_hessenberg = linalg::sp_linalg_hessenberg),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* syscall for unit tests
|
||||||
|
* Used in `artiq.tests.coredevice.test_exceptions.ExceptionTest.test_raise_exceptions_kernel`
|
||||||
|
* This syscall checks that the exception IDs used in the Python `EmbeddingMap` (in `artiq.language.embedding`)
|
||||||
|
* match the `EXCEPTION_ID_LOOKUP` defined in the firmware (`libksupport::src::eh_artiq`)
|
||||||
|
*/
|
||||||
|
api!(test_exception_id_sync = eh_artiq::test_exception_id_sync)
|
||||||
];
|
];
|
||||||
api.iter()
|
api.iter()
|
||||||
.find(|&&(exported, _)| exported.as_bytes() == required)
|
.find(|&&(exported, _)| exported.as_bytes() == required)
|
|
@ -170,6 +170,7 @@ pub extern "C" fn dma_playback(timestamp: i64, ptr: i32, _uses_ddma: bool) {
|
||||||
csr::rtio_dma::base_address_write(ptr as u32);
|
csr::rtio_dma::base_address_write(ptr as u32);
|
||||||
csr::rtio_dma::time_offset_write(timestamp as u64);
|
csr::rtio_dma::time_offset_write(timestamp as u64);
|
||||||
|
|
||||||
|
let old_cri_master = csr::cri_con::selected_read();
|
||||||
csr::cri_con::selected_write(1);
|
csr::cri_con::selected_write(1);
|
||||||
csr::rtio_dma::enable_write(1);
|
csr::rtio_dma::enable_write(1);
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
|
@ -183,7 +184,7 @@ pub extern "C" fn dma_playback(timestamp: i64, ptr: i32, _uses_ddma: bool) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
while csr::rtio_dma::enable_read() != 0 {}
|
while csr::rtio_dma::enable_read() != 0 {}
|
||||||
csr::cri_con::selected_write(0);
|
csr::cri_con::selected_write(old_cri_master);
|
||||||
|
|
||||||
let error = csr::rtio_dma::error_read();
|
let error = csr::rtio_dma::error_read();
|
||||||
if error != 0 {
|
if error != 0 {
|
|
@ -0,0 +1,440 @@
|
||||||
|
// Uses `nalgebra` crate to invoke `np_linalg` and `sp_linalg` functions
|
||||||
|
// When converting between `nalgebra::Matrix` and `NDArray` following considerations are necessary
|
||||||
|
//
|
||||||
|
// * Both `nalgebra::Matrix` and `NDArray` require their content to be stored in row-major order
|
||||||
|
// * `NDArray` data pointer can be directly read and converted to `nalgebra::Matrix` (row and column number must be known)
|
||||||
|
// * `nalgebra::Matrix::as_slice` returns the content of matrix in column-major order and initial data needs to be transposed before storing it in `NDArray` data pointer
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::slice;
|
||||||
|
|
||||||
|
use nalgebra::DMatrix;
|
||||||
|
|
||||||
|
use crate::artiq_raise;
|
||||||
|
|
||||||
|
pub struct InputMatrix {
|
||||||
|
pub ndims: usize,
|
||||||
|
pub dims: *const usize,
|
||||||
|
pub data: *mut f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputMatrix {
|
||||||
|
fn get_dims(&mut self) -> Vec<usize> {
|
||||||
|
let dims = unsafe { slice::from_raw_parts(self.dims, self.ndims) };
|
||||||
|
dims.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn np_linalg_cholesky(mat1: *mut InputMatrix, out: *mut InputMatrix) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let out = out.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
if dim1[0] != dim1[1] {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"last 2 dimensions of the array must be square: {1} != {2}",
|
||||||
|
0,
|
||||||
|
dim1[0] as i64,
|
||||||
|
dim1[1] as i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let outdim = out.get_dims();
|
||||||
|
let out_slice = slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]);
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
|
||||||
|
let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
let result = matrix1.cholesky();
|
||||||
|
match result {
|
||||||
|
Some(res) => {
|
||||||
|
out_slice.copy_from_slice(res.unpack().transpose().as_slice());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
artiq_raise!("LinAlgError", "Matrix is not positive definite");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn np_linalg_qr(mat1: *mut InputMatrix, out_q: *mut InputMatrix, out_r: *mut InputMatrix) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let out_q = out_q.as_mut().unwrap();
|
||||||
|
let out_r = out_r.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
let outq_dim = (*out_q).get_dims();
|
||||||
|
let outr_dim = (*out_r).get_dims();
|
||||||
|
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
let out_q_slice = slice::from_raw_parts_mut(out_q.data, outq_dim[0] * outq_dim[1]);
|
||||||
|
let out_r_slice = slice::from_raw_parts_mut(out_r.data, outr_dim[0] * outr_dim[1]);
|
||||||
|
|
||||||
|
// Refer to https://github.com/dimforge/nalgebra/issues/735
|
||||||
|
let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
|
||||||
|
let res = matrix1.qr();
|
||||||
|
let (q, r) = res.unpack();
|
||||||
|
|
||||||
|
// Uses different algo need to match numpy
|
||||||
|
out_q_slice.copy_from_slice(q.transpose().as_slice());
|
||||||
|
out_r_slice.copy_from_slice(r.transpose().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn np_linalg_svd(
|
||||||
|
mat1: *mut InputMatrix,
|
||||||
|
outu: *mut InputMatrix,
|
||||||
|
outs: *mut InputMatrix,
|
||||||
|
outvh: *mut InputMatrix,
|
||||||
|
) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let outu = outu.as_mut().unwrap();
|
||||||
|
let outs = outs.as_mut().unwrap();
|
||||||
|
let outvh = outvh.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
let outu_dim = (*outu).get_dims();
|
||||||
|
let outs_dim = (*outs).get_dims();
|
||||||
|
let outvh_dim = (*outvh).get_dims();
|
||||||
|
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
let out_u_slice = slice::from_raw_parts_mut(outu.data, outu_dim[0] * outu_dim[1]);
|
||||||
|
let out_s_slice = slice::from_raw_parts_mut(outs.data, outs_dim[0]);
|
||||||
|
let out_vh_slice = slice::from_raw_parts_mut(outvh.data, outvh_dim[0] * outvh_dim[1]);
|
||||||
|
|
||||||
|
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
let result = matrix.svd(true, true);
|
||||||
|
out_u_slice.copy_from_slice(result.u.unwrap().transpose().as_slice());
|
||||||
|
out_s_slice.copy_from_slice(result.singular_values.as_slice());
|
||||||
|
out_vh_slice.copy_from_slice(result.v_t.unwrap().transpose().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn np_linalg_inv(mat1: *mut InputMatrix, out: *mut InputMatrix) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let out = out.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
|
||||||
|
if dim1[0] != dim1[1] {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"last 2 dimensions of the array must be square: {1} != {2}",
|
||||||
|
0,
|
||||||
|
dim1[0] as i64,
|
||||||
|
dim1[1] as i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let outdim = out.get_dims();
|
||||||
|
let out_slice = slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]);
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
|
||||||
|
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
if !matrix.is_invertible() {
|
||||||
|
artiq_raise!("LinAlgError", "no inverse for Singular Matrix");
|
||||||
|
}
|
||||||
|
let inv = matrix.try_inverse().unwrap();
|
||||||
|
out_slice.copy_from_slice(inv.transpose().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn np_linalg_pinv(mat1: *mut InputMatrix, out: *mut InputMatrix) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let out = out.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
let outdim = out.get_dims();
|
||||||
|
let out_slice = slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]);
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
|
||||||
|
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
let svd = matrix.svd(true, true);
|
||||||
|
let inv = svd.pseudo_inverse(1e-15);
|
||||||
|
|
||||||
|
match inv {
|
||||||
|
Ok(m) => {
|
||||||
|
out_slice.copy_from_slice(m.transpose().as_slice());
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
artiq_raise!("LinAlgError", "SVD computation does not converge");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn np_linalg_matrix_power(mat1: *mut InputMatrix, mat2: *mut InputMatrix, out: *mut InputMatrix) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let mat2 = mat2.as_mut().unwrap();
|
||||||
|
let out = out.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
let power = slice::from_raw_parts_mut(mat2.data, 1);
|
||||||
|
let power = power[0];
|
||||||
|
let outdim = out.get_dims();
|
||||||
|
let out_slice = slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]);
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
let mut abs_power = power;
|
||||||
|
if abs_power < 0.0 {
|
||||||
|
abs_power = abs_power * -1.0;
|
||||||
|
}
|
||||||
|
let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
if !matrix1.is_square() {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"last 2 dimensions of the array must be square: {1} != {2}",
|
||||||
|
0,
|
||||||
|
dim1[0] as i64,
|
||||||
|
dim1[1] as i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let mut result = matrix1.pow(abs_power as u32);
|
||||||
|
|
||||||
|
if power < 0.0 {
|
||||||
|
if !matrix1.is_invertible() {
|
||||||
|
artiq_raise!("LinAlgError", "no inverse for Singular Matrix");
|
||||||
|
}
|
||||||
|
result = result.try_inverse().unwrap();
|
||||||
|
}
|
||||||
|
out_slice.copy_from_slice(result.transpose().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn np_linalg_det(mat1: *mut InputMatrix, out: *mut InputMatrix) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let out = out.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
let out_slice = slice::from_raw_parts_mut(out.data, 1);
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
|
||||||
|
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
if !matrix.is_square() {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"last 2 dimensions of the array must be square: {1} != {2}",
|
||||||
|
0,
|
||||||
|
dim1[0] as i64,
|
||||||
|
dim1[1] as i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
out_slice[0] = matrix.determinant();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_linalg_lu(mat1: *mut InputMatrix, out_l: *mut InputMatrix, out_u: *mut InputMatrix) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let out_l = out_l.as_mut().unwrap();
|
||||||
|
let out_u = out_u.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
let outl_dim = (*out_l).get_dims();
|
||||||
|
let outu_dim = (*out_u).get_dims();
|
||||||
|
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
let out_l_slice = slice::from_raw_parts_mut(out_l.data, outl_dim[0] * outl_dim[1]);
|
||||||
|
let out_u_slice = slice::from_raw_parts_mut(out_u.data, outu_dim[0] * outu_dim[1]);
|
||||||
|
|
||||||
|
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
let (_, l, u) = matrix.lu().unpack();
|
||||||
|
|
||||||
|
out_l_slice.copy_from_slice(l.transpose().as_slice());
|
||||||
|
out_u_slice.copy_from_slice(u.transpose().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_linalg_schur(mat1: *mut InputMatrix, out_t: *mut InputMatrix, out_z: *mut InputMatrix) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let out_t = out_t.as_mut().unwrap();
|
||||||
|
let out_z = out_z.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
|
||||||
|
if dim1[0] != dim1[1] {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"last 2 dimensions of the array must be square: {1} != {2}",
|
||||||
|
0,
|
||||||
|
dim1[0] as i64,
|
||||||
|
dim1[1] as i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_t_dim = (*out_t).get_dims();
|
||||||
|
let out_z_dim = (*out_z).get_dims();
|
||||||
|
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
let out_t_slice = slice::from_raw_parts_mut(out_t.data, out_t_dim[0] * out_t_dim[1]);
|
||||||
|
let out_z_slice = slice::from_raw_parts_mut(out_z.data, out_z_dim[0] * out_z_dim[1]);
|
||||||
|
|
||||||
|
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
let (z, t) = matrix.schur().unpack();
|
||||||
|
|
||||||
|
out_t_slice.copy_from_slice(t.transpose().as_slice());
|
||||||
|
out_z_slice.copy_from_slice(z.transpose().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_linalg_hessenberg(
|
||||||
|
mat1: *mut InputMatrix,
|
||||||
|
out_h: *mut InputMatrix,
|
||||||
|
out_q: *mut InputMatrix,
|
||||||
|
) {
|
||||||
|
let mat1 = mat1.as_mut().unwrap();
|
||||||
|
let out_h = out_h.as_mut().unwrap();
|
||||||
|
let out_q = out_q.as_mut().unwrap();
|
||||||
|
|
||||||
|
if mat1.ndims != 2 {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"expected 2D Vector Input, but received {1}D input)",
|
||||||
|
0,
|
||||||
|
mat1.ndims as i64,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dim1 = (*mat1).get_dims();
|
||||||
|
|
||||||
|
if dim1[0] != dim1[1] {
|
||||||
|
artiq_raise!(
|
||||||
|
"ValueError",
|
||||||
|
"last 2 dimensions of the array must be square: {1} != {2}",
|
||||||
|
0,
|
||||||
|
dim1[0] as i64,
|
||||||
|
dim1[1] as i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_h_dim = (*out_h).get_dims();
|
||||||
|
let out_q_dim = (*out_q).get_dims();
|
||||||
|
|
||||||
|
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
|
||||||
|
let out_h_slice = slice::from_raw_parts_mut(out_h.data, out_h_dim[0] * out_h_dim[1]);
|
||||||
|
let out_q_slice = slice::from_raw_parts_mut(out_q.data, out_q_dim[0] * out_q_dim[1]);
|
||||||
|
|
||||||
|
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
|
||||||
|
let (q, h) = matrix.hessenberg().unpack();
|
||||||
|
|
||||||
|
out_h_slice.copy_from_slice(h.transpose().as_slice());
|
||||||
|
out_q_slice.copy_from_slice(q.transpose().as_slice());
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use core::ptr;
|
||||||
|
|
||||||
use libcortex_a9::{mutex::Mutex, semaphore::Semaphore, sync_channel};
|
use libcortex_a9::{mutex::Mutex, semaphore::Semaphore, sync_channel};
|
||||||
|
|
||||||
use crate::eh_artiq;
|
use crate::{eh_artiq, RPCException};
|
||||||
|
|
||||||
mod control;
|
mod control;
|
||||||
pub use control::Control;
|
pub use control::Control;
|
||||||
|
@ -13,16 +13,19 @@ mod dma;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
pub use dma::DmaRecorder;
|
pub use dma::DmaRecorder;
|
||||||
mod cache;
|
mod cache;
|
||||||
|
mod linalg;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
mod subkernel;
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RPCException {
|
pub enum SubkernelStatus {
|
||||||
pub id: u32,
|
NoError,
|
||||||
pub message: u32,
|
Timeout,
|
||||||
pub param: [i64; 3],
|
IncorrectState,
|
||||||
pub file: u32,
|
CommLost,
|
||||||
pub line: i32,
|
Exception(Vec<u8>),
|
||||||
pub column: i32,
|
OtherError,
|
||||||
pub function: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -72,6 +75,45 @@ pub enum Message {
|
||||||
UpDestinationsRequest(i32),
|
UpDestinationsRequest(i32),
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
UpDestinationsReply(bool),
|
UpDestinationsReply(bool),
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelLoadRunRequest {
|
||||||
|
id: u32,
|
||||||
|
destination: u8,
|
||||||
|
run: bool,
|
||||||
|
timestamp: u64,
|
||||||
|
},
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelLoadRunReply {
|
||||||
|
succeeded: bool,
|
||||||
|
},
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelAwaitFinishRequest {
|
||||||
|
id: u32,
|
||||||
|
timeout: i64,
|
||||||
|
},
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelAwaitFinishReply,
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelMsgSend {
|
||||||
|
id: u32,
|
||||||
|
destination: Option<u8>,
|
||||||
|
data: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelMsgSent,
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelMsgRecvRequest {
|
||||||
|
id: i32,
|
||||||
|
timeout: i64,
|
||||||
|
tags: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelMsgRecvReply {
|
||||||
|
count: u8,
|
||||||
|
},
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelError(SubkernelStatus),
|
||||||
}
|
}
|
||||||
|
|
||||||
static CHANNEL_0TO1: Mutex<Option<sync_channel::Sender<'static, Message>>> = Mutex::new(None);
|
static CHANNEL_0TO1: Mutex<Option<sync_channel::Sender<'static, Message>>> = Mutex::new(None);
|
|
@ -10,7 +10,7 @@ use crate::{eh_artiq, rpc::send_args};
|
||||||
fn rpc_send_common(is_async: bool, service: u32, tag: &CSlice<u8>, data: *const *const ()) {
|
fn rpc_send_common(is_async: bool, service: u32, tag: &CSlice<u8>, data: *const *const ()) {
|
||||||
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
||||||
let mut buffer = Vec::<u8>::new();
|
let mut buffer = Vec::<u8>::new();
|
||||||
send_args(&mut buffer, service, tag.as_ref(), data).expect("RPC encoding failed");
|
send_args(&mut buffer, service, tag.as_ref(), data, true).expect("RPC encoding failed");
|
||||||
core1_tx.send(Message::RpcSend { is_async, data: buffer });
|
core1_tx.send(Message::RpcSend { is_async, data: buffer });
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
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};
|
||||||
|
|
||||||
|
pub extern "C" fn load_run(id: u32, destination: u8, run: bool) {
|
||||||
|
unsafe {
|
||||||
|
KERNEL_CHANNEL_1TO0
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.send(Message::SubkernelLoadRunRequest {
|
||||||
|
id: id,
|
||||||
|
destination: destination,
|
||||||
|
run: run,
|
||||||
|
timestamp: now_mu() as u64,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
|
||||||
|
Message::SubkernelLoadRunReply { succeeded: true } => (),
|
||||||
|
Message::SubkernelLoadRunReply { succeeded: false } => {
|
||||||
|
artiq_raise!("SubkernelError", "Error loading or running the subkernel")
|
||||||
|
}
|
||||||
|
_ => panic!("Expected SubkernelLoadRunReply after SubkernelLoadRunRequest!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn await_finish(id: u32, timeout: i64) {
|
||||||
|
unsafe {
|
||||||
|
KERNEL_CHANNEL_1TO0
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.send(Message::SubkernelAwaitFinishRequest {
|
||||||
|
id: id,
|
||||||
|
timeout: timeout,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
|
||||||
|
Message::SubkernelAwaitFinishReply => (),
|
||||||
|
Message::SubkernelError(SubkernelStatus::IncorrectState) => {
|
||||||
|
artiq_raise!("SubkernelError", "Subkernel not running")
|
||||||
|
}
|
||||||
|
Message::SubkernelError(SubkernelStatus::Timeout) => artiq_raise!("SubkernelError", "Subkernel timed out"),
|
||||||
|
Message::SubkernelError(SubkernelStatus::CommLost) => {
|
||||||
|
artiq_raise!("SubkernelError", "Lost communication with satellite")
|
||||||
|
}
|
||||||
|
Message::SubkernelError(SubkernelStatus::OtherError) => {
|
||||||
|
artiq_raise!("SubkernelError", "An error occurred during subkernel operation")
|
||||||
|
}
|
||||||
|
Message::SubkernelError(SubkernelStatus::Exception(raw_exception)) => eh_artiq::raise_raw(&raw_exception),
|
||||||
|
_ => panic!("expected SubkernelAwaitFinishReply after SubkernelAwaitFinishRequest"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn send_message(
|
||||||
|
id: u32,
|
||||||
|
is_return: bool,
|
||||||
|
destination: u8,
|
||||||
|
count: u8,
|
||||||
|
tag: &CSlice<u8>,
|
||||||
|
data: *const *const (),
|
||||||
|
) {
|
||||||
|
let mut buffer = Vec::<u8>::new();
|
||||||
|
send_args(&mut buffer, 0, tag.as_ref(), data, false).expect("RPC encoding failed");
|
||||||
|
// overwrite service tag, include how many tags are in the message
|
||||||
|
buffer[3] = count;
|
||||||
|
unsafe {
|
||||||
|
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::SubkernelMsgSend {
|
||||||
|
id: id,
|
||||||
|
destination: if is_return { None } else { Some(destination) },
|
||||||
|
data: buffer[3..].to_vec(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
|
||||||
|
Message::SubkernelMsgSent => (),
|
||||||
|
_ => panic!("expected SubkernelMsgSent after SubkernelMsgSend"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn await_message(id: i32, timeout: i64, tags: &CSlice<u8>, min: u8, max: u8) {
|
||||||
|
unsafe {
|
||||||
|
KERNEL_CHANNEL_1TO0
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.send(Message::SubkernelMsgRecvRequest {
|
||||||
|
id: id,
|
||||||
|
timeout: timeout,
|
||||||
|
tags: tags.as_ref().to_vec(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
|
||||||
|
Message::SubkernelMsgRecvReply { count } => {
|
||||||
|
if min > count || count > max {
|
||||||
|
artiq_raise!("SubkernelError", "Received more or less arguments than required")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::SubkernelError(SubkernelStatus::IncorrectState) => {
|
||||||
|
artiq_raise!("SubkernelError", "Subkernel not running")
|
||||||
|
}
|
||||||
|
Message::SubkernelError(SubkernelStatus::Timeout) => artiq_raise!("SubkernelError", "Subkernel timed out"),
|
||||||
|
Message::SubkernelError(SubkernelStatus::CommLost) => {
|
||||||
|
artiq_raise!("SubkernelError", "Lost communication with satellite")
|
||||||
|
}
|
||||||
|
Message::SubkernelError(SubkernelStatus::OtherError) => {
|
||||||
|
artiq_raise!("SubkernelError", "An error occurred during subkernel operation")
|
||||||
|
}
|
||||||
|
Message::SubkernelError(SubkernelStatus::Exception(raw_exception)) => eh_artiq::raise_raw(&raw_exception),
|
||||||
|
_ => panic!("expected SubkernelMsgRecvReply after SubkernelMsgRecvRequest"),
|
||||||
|
}
|
||||||
|
// RpcRecvRequest should be called after this to receive message data
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
#![no_std]
|
||||||
|
#![feature(c_variadic)]
|
||||||
|
#![feature(const_btree_new)]
|
||||||
|
#![feature(const_in_array_repeat_expressions)]
|
||||||
|
#![feature(naked_functions)]
|
||||||
|
#![feature(asm)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::{collections::BTreeMap, string::String};
|
||||||
|
|
||||||
|
use io::{Cursor, ProtoRead};
|
||||||
|
use libasync::block_async;
|
||||||
|
use libconfig::Config;
|
||||||
|
use log::{error, warn};
|
||||||
|
#[cfg(has_drtiosat)]
|
||||||
|
pub use pl::csr::drtiosat as rtio_core;
|
||||||
|
#[cfg(has_rtio_core)]
|
||||||
|
pub use pl::csr::rtio_core;
|
||||||
|
use void::Void;
|
||||||
|
|
||||||
|
pub mod eh_artiq;
|
||||||
|
pub mod i2c;
|
||||||
|
pub mod irq;
|
||||||
|
pub mod kernel;
|
||||||
|
pub mod rpc;
|
||||||
|
#[cfg(ki_impl = "csr")]
|
||||||
|
#[path = "rtio_csr.rs"]
|
||||||
|
pub mod rtio;
|
||||||
|
#[cfg(ki_impl = "acp")]
|
||||||
|
#[path = "rtio_acp.rs"]
|
||||||
|
pub mod rtio;
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[path = "../../../build/pl.rs"]
|
||||||
|
pub mod pl;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RPCException {
|
||||||
|
pub id: u32,
|
||||||
|
pub message: u32,
|
||||||
|
pub param: [i64; 3],
|
||||||
|
pub file: u32,
|
||||||
|
pub line: i32,
|
||||||
|
pub column: i32,
|
||||||
|
pub function: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static mut SEEN_ASYNC_ERRORS: u8 = 0;
|
||||||
|
|
||||||
|
pub const ASYNC_ERROR_COLLISION: u8 = 1 << 0;
|
||||||
|
pub const ASYNC_ERROR_BUSY: u8 = 1 << 1;
|
||||||
|
pub const ASYNC_ERROR_SEQUENCE_ERROR: u8 = 1 << 2;
|
||||||
|
|
||||||
|
pub unsafe fn get_async_errors() -> u8 {
|
||||||
|
let errors = SEEN_ASYNC_ERRORS;
|
||||||
|
SEEN_ASYNC_ERRORS = 0;
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
|
||||||
|
unsafe {
|
||||||
|
#[cfg(has_rtio_core)]
|
||||||
|
let errors = rtio_core::async_error_read();
|
||||||
|
#[cfg(has_drtiosat)]
|
||||||
|
let errors = rtio_core::protocol_error_read();
|
||||||
|
if errors != 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn report_async_rtio_errors() {
|
||||||
|
loop {
|
||||||
|
let _ = block_async!(wait_for_async_rtio_error()).await;
|
||||||
|
unsafe {
|
||||||
|
#[cfg(has_rtio_core)]
|
||||||
|
let errors = rtio_core::async_error_read();
|
||||||
|
#[cfg(has_drtiosat)]
|
||||||
|
let errors = rtio_core::protocol_error_read();
|
||||||
|
if errors & ASYNC_ERROR_COLLISION != 0 {
|
||||||
|
let channel = rtio_core::collision_channel_read();
|
||||||
|
error!(
|
||||||
|
"RTIO collision involving channel 0x{:04x}:{}",
|
||||||
|
channel,
|
||||||
|
resolve_channel_name(channel as u32)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if errors & ASYNC_ERROR_BUSY != 0 {
|
||||||
|
let channel = rtio_core::busy_channel_read();
|
||||||
|
error!(
|
||||||
|
"RTIO busy error involving channel 0x{:04x}:{}",
|
||||||
|
channel,
|
||||||
|
resolve_channel_name(channel as u32)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if errors & ASYNC_ERROR_SEQUENCE_ERROR != 0 {
|
||||||
|
let channel = rtio_core::sequence_error_channel_read();
|
||||||
|
error!(
|
||||||
|
"RTIO sequence error involving channel 0x{:04x}:{}",
|
||||||
|
channel,
|
||||||
|
resolve_channel_name(channel as u32)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SEEN_ASYNC_ERRORS = errors;
|
||||||
|
#[cfg(has_rtio_core)]
|
||||||
|
rtio_core::async_error_write(errors);
|
||||||
|
#[cfg(has_drtiosat)]
|
||||||
|
rtio_core::protocol_error_write(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut RTIO_DEVICE_MAP: BTreeMap<u32, String> = BTreeMap::new();
|
||||||
|
|
||||||
|
fn read_device_map(cfg: &Config) -> BTreeMap<u32, String> {
|
||||||
|
let mut device_map: BTreeMap<u32, String> = BTreeMap::new();
|
||||||
|
let _ = cfg
|
||||||
|
.read("device_map")
|
||||||
|
.and_then(|raw_bytes| {
|
||||||
|
let mut bytes_cr = Cursor::new(raw_bytes);
|
||||||
|
let size = bytes_cr.read_u32().unwrap();
|
||||||
|
for _ in 0..size {
|
||||||
|
let channel = bytes_cr.read_u32().unwrap();
|
||||||
|
let device_name = bytes_cr.read_string().unwrap();
|
||||||
|
if let Some(old_entry) = device_map.insert(channel, device_name.clone()) {
|
||||||
|
warn!(
|
||||||
|
"conflicting device map entries for RTIO channel {}: '{}' and '{}'",
|
||||||
|
channel, old_entry, device_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.or_else(|err| {
|
||||||
|
warn!(
|
||||||
|
"error reading device map ({}), device names will not be available in RTIO error messages",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
Err(err)
|
||||||
|
});
|
||||||
|
device_map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _resolve_channel_name(channel: u32, device_map: &BTreeMap<u32, String>) -> String {
|
||||||
|
match device_map.get(&channel) {
|
||||||
|
Some(val) => val.clone(),
|
||||||
|
None => String::from("unknown"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_channel_name(channel: u32) -> String {
|
||||||
|
_resolve_channel_name(channel, unsafe { &RTIO_DEVICE_MAP })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_device_map(cfg: &Config) {
|
||||||
|
unsafe {
|
||||||
|
RTIO_DEVICE_MAP = read_device_map(cfg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,71 +1,62 @@
|
||||||
use alloc::boxed::Box;
|
use core::str;
|
||||||
use core::{future::Future, str};
|
|
||||||
|
|
||||||
use async_recursion::async_recursion;
|
|
||||||
use byteorder::{ByteOrder, NativeEndian};
|
use byteorder::{ByteOrder, NativeEndian};
|
||||||
use core_io::{Error, Write};
|
use core_io::{Error, Read, Write};
|
||||||
use cslice::{CMutSlice, CSlice};
|
use cslice::{CMutSlice, CSlice};
|
||||||
use io::proto::ProtoWrite;
|
use io::{ProtoRead, ProtoWrite};
|
||||||
use libasync::smoltcp::TcpStream;
|
|
||||||
use libboard_zynq::smoltcp;
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
use self::tag::{split_tag, Tag, TagIterator};
|
use self::tag::{split_tag, Tag, TagIterator};
|
||||||
use crate::proto_async;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn round_up(val: usize, power_of_two: usize) -> usize {
|
pub fn round_up(val: usize, power_of_two: usize) -> usize {
|
||||||
assert!(power_of_two.is_power_of_two());
|
assert!(power_of_two.is_power_of_two());
|
||||||
let max_rem = power_of_two - 1;
|
let max_rem = power_of_two - 1;
|
||||||
(val + max_rem) & (!max_rem)
|
(val + max_rem) & (!max_rem)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn round_up_mut<T>(ptr: *mut T, power_of_two: usize) -> *mut T {
|
pub unsafe fn round_up_mut<T>(ptr: *mut T, power_of_two: usize) -> *mut T {
|
||||||
round_up(ptr as usize, power_of_two) as *mut T
|
round_up(ptr as usize, power_of_two) as *mut T
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn round_up_const<T>(ptr: *const T, power_of_two: usize) -> *const T {
|
pub unsafe fn round_up_const<T>(ptr: *const T, power_of_two: usize) -> *const T {
|
||||||
round_up(ptr as usize, power_of_two) as *const T
|
round_up(ptr as usize, power_of_two) as *const T
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
|
pub unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
|
||||||
round_up_const(ptr, core::mem::align_of::<T>()) as *const T
|
round_up_const(ptr, core::mem::align_of::<T>()) as *const T
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
|
pub unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
|
||||||
round_up_mut(ptr, core::mem::align_of::<T>()) as *mut T
|
round_up_mut(ptr, core::mem::align_of::<T>()) as *mut T
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads (deserializes) `length` array or list elements of type `tag` from `stream`,
|
// versions for reader rather than TcpStream
|
||||||
/// writing them into the buffer given by `storage`.
|
// they will be made into sync for satellite subkernels later
|
||||||
///
|
unsafe fn recv_elements<F, R>(
|
||||||
/// `alloc` is used for nested allocations (if elements themselves contain
|
reader: &mut R,
|
||||||
/// lists/arrays), see [recv_value].
|
elt_tag: Tag,
|
||||||
#[async_recursion(?Send)]
|
|
||||||
async unsafe fn recv_elements<F>(
|
|
||||||
stream: &TcpStream,
|
|
||||||
elt_tag: Tag<'async_recursion>,
|
|
||||||
length: usize,
|
length: usize,
|
||||||
storage: *mut (),
|
storage: *mut (),
|
||||||
alloc: &(impl Fn(usize) -> F + 'async_recursion),
|
alloc: &mut F,
|
||||||
) -> Result<(), smoltcp::Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: Future<Output = *mut ()>,
|
F: FnMut(usize) -> *mut (),
|
||||||
|
R: Read + ?Sized,
|
||||||
{
|
{
|
||||||
// List of simple types are special-cased in the protocol for performance.
|
|
||||||
match elt_tag {
|
match elt_tag {
|
||||||
Tag::Bool => {
|
Tag::Bool => {
|
||||||
let dest = core::slice::from_raw_parts_mut(storage as *mut u8, length);
|
let dest = core::slice::from_raw_parts_mut(storage as *mut u8, length);
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
reader.read_exact(dest)?;
|
||||||
}
|
}
|
||||||
Tag::Int32 => {
|
Tag::Int32 => {
|
||||||
let ptr = storage as *mut u32;
|
let ptr = storage as *mut u32;
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
|
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
reader.read_exact(dest)?;
|
||||||
drop(dest);
|
drop(dest);
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
||||||
NativeEndian::from_slice_u32(dest);
|
NativeEndian::from_slice_u32(dest);
|
||||||
|
@ -73,7 +64,7 @@ where
|
||||||
Tag::Int64 | Tag::Float64 => {
|
Tag::Int64 | Tag::Float64 => {
|
||||||
let ptr = storage as *mut u64;
|
let ptr = storage as *mut u64;
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
|
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
reader.read_exact(dest)?;
|
||||||
drop(dest);
|
drop(dest);
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
||||||
NativeEndian::from_slice_u64(dest);
|
NativeEndian::from_slice_u64(dest);
|
||||||
|
@ -81,27 +72,17 @@ where
|
||||||
_ => {
|
_ => {
|
||||||
let mut data = storage;
|
let mut data = storage;
|
||||||
for _ in 0..length {
|
for _ in 0..length {
|
||||||
recv_value(stream, elt_tag, &mut data, alloc).await?
|
recv_value(reader, elt_tag, &mut data, alloc)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads (deserializes) a value of type `tag` from `stream`, writing the results to
|
unsafe fn recv_value<F, R>(reader: &mut R, tag: Tag, data: &mut *mut (), alloc: &mut F) -> Result<(), Error>
|
||||||
/// the kernel-side buffer `data` (the passed pointer to which is incremented to point
|
|
||||||
/// past the just-received data). For nested allocations (lists/arrays), `alloc` is
|
|
||||||
/// invoked any number of times with the size of the required allocation as a parameter
|
|
||||||
/// (which is assumed to be correctly aligned for all payload types).
|
|
||||||
#[async_recursion(?Send)]
|
|
||||||
async unsafe fn recv_value<F>(
|
|
||||||
stream: &TcpStream,
|
|
||||||
tag: Tag<'async_recursion>,
|
|
||||||
data: &mut *mut (),
|
|
||||||
alloc: &(impl Fn(usize) -> F + 'async_recursion),
|
|
||||||
) -> Result<(), smoltcp::Error>
|
|
||||||
where
|
where
|
||||||
F: Future<Output = *mut ()>,
|
F: FnMut(usize) -> *mut (),
|
||||||
|
R: Read + ?Sized,
|
||||||
{
|
{
|
||||||
macro_rules! consume_value {
|
macro_rules! consume_value {
|
||||||
($ty:ty, | $ptr:ident | $map:expr) => {{
|
($ty:ty, | $ptr:ident | $map:expr) => {{
|
||||||
|
@ -114,22 +95,22 @@ where
|
||||||
match tag {
|
match tag {
|
||||||
Tag::None => Ok(()),
|
Tag::None => Ok(()),
|
||||||
Tag::Bool => consume_value!(i8, |ptr| {
|
Tag::Bool => consume_value!(i8, |ptr| {
|
||||||
*ptr = proto_async::read_i8(stream).await?;
|
*ptr = reader.read_u8()? as i8;
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
Tag::Int32 => consume_value!(i32, |ptr| {
|
Tag::Int32 => consume_value!(i32, |ptr| {
|
||||||
*ptr = proto_async::read_i32(stream).await?;
|
*ptr = reader.read_u32()? as i32;
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
Tag::Int64 | Tag::Float64 => consume_value!(i64, |ptr| {
|
Tag::Int64 | Tag::Float64 => consume_value!(i64, |ptr| {
|
||||||
*ptr = proto_async::read_i64(stream).await?;
|
*ptr = reader.read_u64()? as i64;
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
Tag::String | Tag::Bytes | Tag::ByteArray => {
|
Tag::String | Tag::Bytes | Tag::ByteArray => {
|
||||||
consume_value!(CMutSlice<u8>, |ptr| {
|
consume_value!(CMutSlice<u8>, |ptr| {
|
||||||
let length = proto_async::read_i32(stream).await? as usize;
|
let length = reader.read_u32()? as usize;
|
||||||
*ptr = CMutSlice::new(alloc(length).await as *mut u8, length);
|
*ptr = CMutSlice::new(alloc(length) as *mut u8, length);
|
||||||
proto_async::read_chunk(stream, (*ptr).as_mut()).await?;
|
reader.read_exact((*ptr).as_mut())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -139,10 +120,8 @@ where
|
||||||
let mut it = it.clone();
|
let mut it = it.clone();
|
||||||
for _ in 0..arity {
|
for _ in 0..arity {
|
||||||
let tag = it.next().expect("truncated tag");
|
let tag = it.next().expect("truncated tag");
|
||||||
recv_value(stream, tag, data, alloc).await?
|
recv_value(reader, tag, data, alloc)?
|
||||||
}
|
}
|
||||||
// Take into account any tail padding (if element(s) with largest alignment
|
|
||||||
// are not at the end).
|
|
||||||
*data = round_up_mut(*data, alignment);
|
*data = round_up_mut(*data, alignment);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -154,50 +133,41 @@ where
|
||||||
}
|
}
|
||||||
consume_value!(*mut List, |ptr_to_list| {
|
consume_value!(*mut List, |ptr_to_list| {
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
let length = proto_async::read_i32(stream).await? as usize;
|
let length = reader.read_u32()? as usize;
|
||||||
|
|
||||||
// To avoid multiple kernel CPU roundtrips, use a single allocation for
|
|
||||||
// both the pointer/length List (slice) and the backing storage for the
|
|
||||||
// elements. We can assume that alloc() is aligned suitably, so just
|
|
||||||
// need to take into account any extra padding required.
|
|
||||||
// (Note: At the time of writing, there will never actually be any types
|
|
||||||
// with alignment larger than 8 bytes, so storage_offset == 0 always.)
|
|
||||||
let list_size = 4 + 4;
|
let list_size = 4 + 4;
|
||||||
let storage_offset = round_up(list_size, tag.alignment());
|
let storage_offset = round_up(list_size, tag.alignment());
|
||||||
let storage_size = tag.size() * length;
|
let storage_size = tag.size() * length;
|
||||||
|
|
||||||
let allocation = alloc(storage_offset + storage_size).await as *mut u8;
|
let allocation = alloc(storage_offset + storage_size) as *mut u8;
|
||||||
*ptr_to_list = allocation as *mut List;
|
*ptr_to_list = allocation as *mut List;
|
||||||
let storage = allocation.offset(storage_offset as isize) as *mut ();
|
let storage = allocation.offset(storage_offset as isize) as *mut ();
|
||||||
|
|
||||||
(**ptr_to_list).length = length;
|
(**ptr_to_list).length = length;
|
||||||
(**ptr_to_list).elements = storage;
|
(**ptr_to_list).elements = storage;
|
||||||
recv_elements(stream, tag, length, storage, alloc).await
|
recv_elements(reader, tag, length, storage, alloc)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Tag::Array(it, num_dims) => {
|
Tag::Array(it, num_dims) => {
|
||||||
consume_value!(*mut (), |buffer| {
|
consume_value!(*mut (), |buffer| {
|
||||||
// Deserialize length along each dimension and compute total number of
|
|
||||||
// elements.
|
|
||||||
let mut total_len: usize = 1;
|
let mut total_len: usize = 1;
|
||||||
for _ in 0..num_dims {
|
for _ in 0..num_dims {
|
||||||
let len = proto_async::read_i32(stream).await? as usize;
|
let len = reader.read_u32()? as usize;
|
||||||
total_len *= len;
|
total_len *= len;
|
||||||
consume_value!(usize, |ptr| *ptr = len)
|
consume_value!(usize, |ptr| *ptr = len)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate backing storage for elements; deserialize them.
|
|
||||||
let elt_tag = it.clone().next().expect("truncated tag");
|
let elt_tag = it.clone().next().expect("truncated tag");
|
||||||
*buffer = alloc(elt_tag.size() * total_len).await;
|
*buffer = alloc(elt_tag.size() * total_len);
|
||||||
recv_elements(stream, elt_tag, total_len, *buffer, alloc).await
|
recv_elements(reader, elt_tag, total_len, *buffer, alloc)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Tag::Range(it) => {
|
Tag::Range(it) => {
|
||||||
*data = round_up_mut(*data, tag.alignment());
|
*data = round_up_mut(*data, tag.alignment());
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
recv_value(stream, tag, data, alloc).await?;
|
recv_value(reader, tag, data, alloc)?;
|
||||||
recv_value(stream, tag, data, alloc).await?;
|
recv_value(reader, tag, data, alloc)?;
|
||||||
recv_value(stream, tag, data, alloc).await?;
|
recv_value(reader, tag, data, alloc)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Tag::Keyword(_) => unreachable!(),
|
Tag::Keyword(_) => unreachable!(),
|
||||||
|
@ -205,28 +175,39 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn recv_return<F>(
|
pub fn recv_return<'a, F, R>(
|
||||||
stream: &TcpStream,
|
reader: &mut R,
|
||||||
tag_bytes: &[u8],
|
tag_bytes: &'a [u8],
|
||||||
data: *mut (),
|
data: *mut (),
|
||||||
alloc: &impl Fn(usize) -> F,
|
alloc: &mut F,
|
||||||
) -> Result<(), smoltcp::Error>
|
) -> Result<&'a [u8], Error>
|
||||||
where
|
where
|
||||||
F: Future<Output = *mut ()>,
|
F: FnMut(usize) -> *mut (),
|
||||||
|
R: Read + ?Sized,
|
||||||
{
|
{
|
||||||
let mut it = TagIterator::new(tag_bytes);
|
let mut it = TagIterator::new(tag_bytes);
|
||||||
trace!("recv ...->{}", it);
|
trace!("recv ...->{}", it);
|
||||||
|
|
||||||
let tag = it.next().expect("truncated tag");
|
let tag = it.next().expect("truncated tag");
|
||||||
let mut data = data;
|
let mut data = data;
|
||||||
unsafe { recv_value(stream, tag, &mut data, alloc).await? };
|
unsafe { recv_value(reader, tag, &mut data, alloc)? };
|
||||||
|
|
||||||
Ok(())
|
Ok(it.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn send_elements<W>(writer: &mut W, elt_tag: Tag, length: usize, data: *const ()) -> Result<(), Error>
|
unsafe fn send_elements<W>(
|
||||||
where W: Write + ?Sized {
|
writer: &mut W,
|
||||||
|
elt_tag: Tag,
|
||||||
|
length: usize,
|
||||||
|
data: *const (),
|
||||||
|
write_tags: bool,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
W: Write + ?Sized,
|
||||||
|
{
|
||||||
|
if write_tags {
|
||||||
writer.write_u8(elt_tag.as_u8())?;
|
writer.write_u8(elt_tag.as_u8())?;
|
||||||
|
}
|
||||||
match elt_tag {
|
match elt_tag {
|
||||||
// we cannot use NativeEndian::from_slice_i32 as the data is not mutable,
|
// we cannot use NativeEndian::from_slice_i32 as the data is not mutable,
|
||||||
// and that is not needed as the data is already in native endian
|
// and that is not needed as the data is already in native endian
|
||||||
|
@ -245,14 +226,14 @@ where W: Write + ?Sized {
|
||||||
_ => {
|
_ => {
|
||||||
let mut data = data;
|
let mut data = data;
|
||||||
for _ in 0..length {
|
for _ in 0..length {
|
||||||
send_value(writer, elt_tag, &mut data)?;
|
send_value(writer, elt_tag, &mut data, write_tags)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ()) -> Result<(), Error>
|
unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const (), write_tags: bool) -> Result<(), Error>
|
||||||
where W: Write + ?Sized {
|
where W: Write + ?Sized {
|
||||||
macro_rules! consume_value {
|
macro_rules! consume_value {
|
||||||
($ty:ty, | $ptr:ident | $map:expr) => {{
|
($ty:ty, | $ptr:ident | $map:expr) => {{
|
||||||
|
@ -262,7 +243,9 @@ where W: Write + ?Sized {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if write_tags {
|
||||||
writer.write_u8(tag.as_u8())?;
|
writer.write_u8(tag.as_u8())?;
|
||||||
|
}
|
||||||
match tag {
|
match tag {
|
||||||
Tag::None => Ok(()),
|
Tag::None => Ok(()),
|
||||||
Tag::Bool => consume_value!(u8, |ptr| writer.write_u8(*ptr)),
|
Tag::Bool => consume_value!(u8, |ptr| writer.write_u8(*ptr)),
|
||||||
|
@ -274,12 +257,14 @@ where W: Write + ?Sized {
|
||||||
Tag::Bytes | Tag::ByteArray => consume_value!(CSlice<u8>, |ptr| writer.write_bytes((*ptr).as_ref())),
|
Tag::Bytes | Tag::ByteArray => consume_value!(CSlice<u8>, |ptr| writer.write_bytes((*ptr).as_ref())),
|
||||||
Tag::Tuple(it, arity) => {
|
Tag::Tuple(it, arity) => {
|
||||||
let mut it = it.clone();
|
let mut it = it.clone();
|
||||||
|
if write_tags {
|
||||||
writer.write_u8(arity)?;
|
writer.write_u8(arity)?;
|
||||||
|
}
|
||||||
let mut max_alignment = 0;
|
let mut max_alignment = 0;
|
||||||
for _ in 0..arity {
|
for _ in 0..arity {
|
||||||
let tag = it.next().expect("truncated tag");
|
let tag = it.next().expect("truncated tag");
|
||||||
max_alignment = core::cmp::max(max_alignment, tag.alignment());
|
max_alignment = core::cmp::max(max_alignment, tag.alignment());
|
||||||
send_value(writer, tag, data)?
|
send_value(writer, tag, data, write_tags)?
|
||||||
}
|
}
|
||||||
*data = round_up_const(*data, max_alignment);
|
*data = round_up_const(*data, max_alignment);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -294,11 +279,13 @@ where W: Write + ?Sized {
|
||||||
let length = (**ptr).length as usize;
|
let length = (**ptr).length as usize;
|
||||||
writer.write_u32((*ptr).length)?;
|
writer.write_u32((*ptr).length)?;
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
send_elements(writer, tag, length, (**ptr).elements)
|
send_elements(writer, tag, length, (**ptr).elements, write_tags)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Tag::Array(it, num_dims) => {
|
Tag::Array(it, num_dims) => {
|
||||||
|
if write_tags {
|
||||||
writer.write_u8(num_dims)?;
|
writer.write_u8(num_dims)?;
|
||||||
|
}
|
||||||
consume_value!(*const (), |buffer| {
|
consume_value!(*const (), |buffer| {
|
||||||
let elt_tag = it.clone().next().expect("truncated tag");
|
let elt_tag = it.clone().next().expect("truncated tag");
|
||||||
|
|
||||||
|
@ -310,14 +297,14 @@ where W: Write + ?Sized {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
let length = total_len as usize;
|
let length = total_len as usize;
|
||||||
send_elements(writer, elt_tag, length, *buffer)
|
send_elements(writer, elt_tag, length, *buffer, write_tags)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Tag::Range(it) => {
|
Tag::Range(it) => {
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
send_value(writer, tag, data)?;
|
send_value(writer, tag, data, write_tags)?;
|
||||||
send_value(writer, tag, data)?;
|
send_value(writer, tag, data, write_tags)?;
|
||||||
send_value(writer, tag, data)?;
|
send_value(writer, tag, data, write_tags)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Tag::Keyword(it) => {
|
Tag::Keyword(it) => {
|
||||||
|
@ -329,7 +316,7 @@ where W: Write + ?Sized {
|
||||||
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
|
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
let mut data = ptr.offset(1) as *const ();
|
let mut data = ptr.offset(1) as *const ();
|
||||||
send_value(writer, tag, &mut data)
|
send_value(writer, tag, &mut data, write_tags)
|
||||||
})
|
})
|
||||||
// Tag::Keyword never appears in composite types, so we don't have
|
// Tag::Keyword never appears in composite types, so we don't have
|
||||||
// to accurately advance data.
|
// to accurately advance data.
|
||||||
|
@ -344,8 +331,16 @@ where W: Write + ?Sized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_args<W>(writer: &mut W, service: u32, tag_bytes: &[u8], data: *const *const ()) -> Result<(), Error>
|
pub fn send_args<W>(
|
||||||
where W: Write + ?Sized {
|
writer: &mut W,
|
||||||
|
service: u32,
|
||||||
|
tag_bytes: &[u8],
|
||||||
|
data: *const *const (),
|
||||||
|
write_tags: bool,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
W: Write + ?Sized,
|
||||||
|
{
|
||||||
let (arg_tags_bytes, return_tag_bytes) = split_tag(tag_bytes);
|
let (arg_tags_bytes, return_tag_bytes) = split_tag(tag_bytes);
|
||||||
|
|
||||||
let mut args_it = TagIterator::new(arg_tags_bytes);
|
let mut args_it = TagIterator::new(arg_tags_bytes);
|
||||||
|
@ -356,7 +351,7 @@ where W: Write + ?Sized {
|
||||||
for index in 0.. {
|
for index in 0.. {
|
||||||
if let Some(arg_tag) = args_it.next() {
|
if let Some(arg_tag) = args_it.next() {
|
||||||
let mut data = unsafe { *data.offset(index) };
|
let mut data = unsafe { *data.offset(index) };
|
||||||
unsafe { send_value(writer, arg_tag, &mut data)? };
|
unsafe { send_value(writer, arg_tag, &mut data, write_tags)? };
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -367,7 +362,7 @@ where W: Write + ?Sized {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
mod tag {
|
pub mod tag {
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
pub fn split_tag(tag_bytes: &[u8]) -> (&[u8], &[u8]) {
|
pub fn split_tag(tag_bytes: &[u8]) -> (&[u8], &[u8]) {
|
||||||
|
@ -484,7 +479,7 @@ mod tag {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct TagIterator<'a> {
|
pub struct TagIterator<'a> {
|
||||||
data: &'a [u8],
|
pub data: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TagIterator<'a> {
|
impl<'a> TagIterator<'a> {
|
|
@ -4,7 +4,7 @@ use cslice::CSlice;
|
||||||
use libcortex_a9::asm;
|
use libcortex_a9::asm;
|
||||||
use vcell::VolatileCell;
|
use vcell::VolatileCell;
|
||||||
|
|
||||||
use crate::{artiq_raise, pl::csr, rtio_mgt::resolve_channel_name};
|
use crate::{artiq_raise, pl::csr, resolve_channel_name, rtio_core};
|
||||||
|
|
||||||
pub const RTIO_O_STATUS_WAIT: i32 = 1;
|
pub const RTIO_O_STATUS_WAIT: i32 = 1;
|
||||||
pub const RTIO_O_STATUS_UNDERFLOW: i32 = 2;
|
pub const RTIO_O_STATUS_UNDERFLOW: i32 = 2;
|
||||||
|
@ -52,7 +52,7 @@ static mut TRANSACTION_BUFFER: Transaction = Transaction {
|
||||||
|
|
||||||
pub extern "C" fn init() {
|
pub extern "C" fn init() {
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::rtio_core::reset_write(1);
|
rtio_core::reset_write(1);
|
||||||
csr::rtio::engine_addr_base_write(&TRANSACTION_BUFFER as *const Transaction as u32);
|
csr::rtio::engine_addr_base_write(&TRANSACTION_BUFFER as *const Transaction as u32);
|
||||||
csr::rtio::enable_write(1);
|
csr::rtio::enable_write(1);
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ use core::ptr::{read_volatile, write_volatile};
|
||||||
|
|
||||||
use cslice::CSlice;
|
use cslice::CSlice;
|
||||||
|
|
||||||
use crate::{artiq_raise, pl::csr, rtio_mgt::resolve_channel_name};
|
use crate::{artiq_raise, pl::csr, resolve_channel_name, rtio_core};
|
||||||
|
|
||||||
pub const RTIO_O_STATUS_WAIT: u8 = 1;
|
pub const RTIO_O_STATUS_WAIT: u8 = 1;
|
||||||
pub const RTIO_O_STATUS_UNDERFLOW: u8 = 2;
|
pub const RTIO_O_STATUS_UNDERFLOW: u8 = 2;
|
||||||
|
@ -20,7 +20,7 @@ pub struct TimestampedData {
|
||||||
|
|
||||||
pub extern "C" fn init() {
|
pub extern "C" fn init() {
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::rtio_core::reset_write(1);
|
rtio_core::reset_write(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ edition = "2018"
|
||||||
[features]
|
[features]
|
||||||
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
|
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_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"]
|
default = ["target_zc706"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -18,27 +19,31 @@ num-traits = { version = "0.2", default-features = false }
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
cslice = "0.3"
|
cslice = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nb = "0.1"
|
|
||||||
embedded-hal = "0.2"
|
embedded-hal = "0.2"
|
||||||
core_io = { version = "0.1", features = ["collections"] }
|
core_io = { version = "0.1", features = ["collections"] }
|
||||||
|
crc = { version = "1.7", default-features = false }
|
||||||
byteorder = { version = "1.3", default-features = false }
|
byteorder = { version = "1.3", default-features = false }
|
||||||
void = { version = "1", default-features = false }
|
void = { version = "1", default-features = false }
|
||||||
futures = { version = "0.3", default-features = false, features = ["async-await"] }
|
futures = { version = "0.3", default-features = false, features = ["async-await"] }
|
||||||
async-recursion = "0.3"
|
async-recursion = "0.3"
|
||||||
log_buffer = { version = "1.2" }
|
log_buffer = { version = "1.2" }
|
||||||
libm = { version = "0.2", features = ["unstable"] }
|
|
||||||
vcell = "0.1"
|
vcell = "0.1"
|
||||||
|
|
||||||
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["ipv6"]}
|
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq", features = ["ipv6"]}
|
||||||
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] }
|
||||||
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" }
|
||||||
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libasync = { path = "@@ZYNQ_RS@@/libasync" }
|
||||||
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
libregister = { path = "@@ZYNQ_RS@@/libregister" }
|
||||||
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn", "ipv6"] }
|
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn", "ipv6"] }
|
||||||
|
|
||||||
dyld = { path = "../libdyld" }
|
dyld = { path = "../libdyld" }
|
||||||
dwarf = { path = "../libdwarf" }
|
dwarf = { path = "../libdwarf" }
|
||||||
unwind = { path = "../libunwind" }
|
unwind = { path = "../libunwind" }
|
||||||
libc = { path = "../libc" }
|
libc = { path = "../libc" }
|
||||||
io = { path = "../libio" }
|
io = { path = "../libio", features = ["alloc"] }
|
||||||
|
ksupport = { path = "../libksupport" }
|
||||||
libboard_artiq = { path = "../libboard_artiq" }
|
libboard_artiq = { path = "../libboard_artiq" }
|
||||||
|
|
||||||
|
[dependencies.tar-no-std]
|
||||||
|
git = "https://git.m-labs.hk/M-Labs/tar-no-std"
|
||||||
|
rev = "2ab6dc5"
|
|
@ -60,7 +60,7 @@ pub mod remote_analyzer {
|
||||||
routing_table: &drtio_routing::RoutingTable,
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
timer: GlobalTimer,
|
timer: GlobalTimer,
|
||||||
) -> Result<RemoteBuffer, &'static str> {
|
) -> Result<RemoteBuffer, drtio::Error> {
|
||||||
// gets data from satellites and returns consolidated data
|
// gets data from satellites and returns consolidated data
|
||||||
let mut remote_data: Vec<u8> = Vec::new();
|
let mut remote_data: Vec<u8> = Vec::new();
|
||||||
let mut remote_error = false;
|
let mut remote_error = false;
|
||||||
|
|
|
@ -1,13 +1,24 @@
|
||||||
use alloc::{collections::BTreeMap, rc::Rc, string::String, vec, vec::Vec};
|
use alloc::{collections::BTreeMap, rc::Rc, string::String, vec, vec::Vec};
|
||||||
use core::{cell::RefCell, fmt, slice, str};
|
use core::{cell::RefCell, fmt, slice, str};
|
||||||
|
|
||||||
|
use core_io::Error as IoError;
|
||||||
use cslice::CSlice;
|
use cslice::CSlice;
|
||||||
|
use dyld::elf;
|
||||||
use futures::{future::FutureExt, select_biased};
|
use futures::{future::FutureExt, select_biased};
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use io::Cursor;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use ksupport::rpc;
|
||||||
|
use ksupport::{kernel, resolve_channel_name};
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use libasync::delay;
|
||||||
use libasync::{smoltcp::{Sockets, TcpStream},
|
use libasync::{smoltcp::{Sockets, TcpStream},
|
||||||
task};
|
task};
|
||||||
use libboard_artiq::drtio_routing;
|
use libboard_artiq::drtio_routing;
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
use libboard_zynq::error_led::ErrorLED;
|
use libboard_zynq::error_led::ErrorLED;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use libboard_zynq::time::Milliseconds;
|
||||||
use libboard_zynq::{self as zynq,
|
use libboard_zynq::{self as zynq,
|
||||||
smoltcp::{self,
|
smoltcp::{self,
|
||||||
iface::{EthernetInterfaceBuilder, NeighborCache},
|
iface::{EthernetInterfaceBuilder, NeighborCache},
|
||||||
|
@ -21,20 +32,26 @@ use libcortex_a9::{mutex::Mutex,
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
use num_traits::{FromPrimitive, ToPrimitive};
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use tar_no_std::TarArchiveRef;
|
||||||
|
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
use crate::pl;
|
use crate::pl;
|
||||||
use crate::{analyzer, kernel, mgmt, moninj,
|
use crate::{analyzer, mgmt, moninj, proto_async::*, rpc_async, rtio_dma, rtio_mgt};
|
||||||
proto_async::*,
|
#[cfg(has_drtio)]
|
||||||
rpc, rtio_dma,
|
use crate::{subkernel, subkernel::Error as SubkernelError};
|
||||||
rtio_mgt::{self, resolve_channel_name}};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
NetworkError(smoltcp::Error),
|
NetworkError(smoltcp::Error),
|
||||||
|
IoError,
|
||||||
UnexpectedPattern,
|
UnexpectedPattern,
|
||||||
UnrecognizedPacket,
|
UnrecognizedPacket,
|
||||||
BufferExhausted,
|
BufferExhausted,
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
SubkernelError(subkernel::Error),
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
DestinationDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
@ -43,9 +60,14 @@ impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::NetworkError(error) => write!(f, "network error: {}", error),
|
Error::NetworkError(error) => write!(f, "network error: {}", error),
|
||||||
|
Error::IoError => write!(f, "io error"),
|
||||||
Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
||||||
Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
||||||
Error::BufferExhausted => write!(f, "buffer exhausted"),
|
Error::BufferExhausted => write!(f, "buffer exhausted"),
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
Error::SubkernelError(error) => write!(f, "subkernel error: {:?}", error),
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
Error::DestinationDown => write!(f, "subkernel destination down"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +78,19 @@ impl From<smoltcp::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<IoError> for Error {
|
||||||
|
fn from(_error: IoError) -> Self {
|
||||||
|
Error::IoError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
impl From<subkernel::Error> for Error {
|
||||||
|
fn from(error: subkernel::Error) -> Self {
|
||||||
|
Error::SubkernelError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromPrimitive, ToPrimitive)]
|
#[derive(Debug, FromPrimitive, ToPrimitive)]
|
||||||
enum Request {
|
enum Request {
|
||||||
SystemInfo = 3,
|
SystemInfo = 3,
|
||||||
|
@ -63,6 +98,7 @@ enum Request {
|
||||||
RunKernel = 6,
|
RunKernel = 6,
|
||||||
RPCReply = 7,
|
RPCReply = 7,
|
||||||
RPCException = 8,
|
RPCException = 8,
|
||||||
|
UploadSubkernel = 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromPrimitive, ToPrimitive)]
|
#[derive(Debug, FromPrimitive, ToPrimitive)]
|
||||||
|
@ -182,7 +218,7 @@ async fn handle_run_kernel(
|
||||||
kernel::Message::RpcRecvRequest(slot) => slot,
|
kernel::Message::RpcRecvRequest(slot) => slot,
|
||||||
other => panic!("expected root value slot from core1, not {:?}", other),
|
other => panic!("expected root value slot from core1, not {:?}", other),
|
||||||
};
|
};
|
||||||
rpc::recv_return(stream, &tag, slot, &|size| {
|
rpc_async::recv_return(stream, &tag, slot, &|size| {
|
||||||
let control = control.clone();
|
let control = control.clone();
|
||||||
async move {
|
async move {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
|
@ -227,7 +263,7 @@ async fn handle_run_kernel(
|
||||||
let function = read_i32(stream).await? as u32;
|
let function = read_i32(stream).await? as u32;
|
||||||
control
|
control
|
||||||
.tx
|
.tx
|
||||||
.async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException {
|
.async_send(kernel::Message::RpcRecvReply(Err(ksupport::RPCException {
|
||||||
id,
|
id,
|
||||||
message,
|
message,
|
||||||
param,
|
param,
|
||||||
|
@ -365,6 +401,122 @@ async fn handle_run_kernel(
|
||||||
control.borrow_mut().tx.async_send(reply).await;
|
control.borrow_mut().tx.async_send(reply).await;
|
||||||
}
|
}
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
|
kernel::Message::SubkernelLoadRunRequest {
|
||||||
|
id,
|
||||||
|
destination: _,
|
||||||
|
run,
|
||||||
|
timestamp,
|
||||||
|
} => {
|
||||||
|
let succeeded = match subkernel::load(aux_mutex, routing_table, timer, id, run, timestamp).await {
|
||||||
|
Ok(()) => true,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error loading subkernel: {:?}", e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
control
|
||||||
|
.borrow_mut()
|
||||||
|
.tx
|
||||||
|
.async_send(kernel::Message::SubkernelLoadRunReply { succeeded: succeeded })
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
kernel::Message::SubkernelAwaitFinishRequest { id, timeout } => {
|
||||||
|
let res = subkernel::await_finish(aux_mutex, routing_table, timer, id, timeout).await;
|
||||||
|
let response = match res {
|
||||||
|
Ok(res) => {
|
||||||
|
if res.status == subkernel::FinishStatus::CommLost {
|
||||||
|
kernel::Message::SubkernelError(kernel::SubkernelStatus::CommLost)
|
||||||
|
} else if let Some(exception) = res.exception {
|
||||||
|
kernel::Message::SubkernelError(kernel::SubkernelStatus::Exception(exception))
|
||||||
|
} else {
|
||||||
|
kernel::Message::SubkernelAwaitFinishReply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(SubkernelError::Timeout) => kernel::Message::SubkernelError(kernel::SubkernelStatus::Timeout),
|
||||||
|
Err(SubkernelError::IncorrectState) => {
|
||||||
|
kernel::Message::SubkernelError(kernel::SubkernelStatus::IncorrectState)
|
||||||
|
}
|
||||||
|
Err(_) => kernel::Message::SubkernelError(kernel::SubkernelStatus::OtherError),
|
||||||
|
};
|
||||||
|
control.borrow_mut().tx.async_send(response).await;
|
||||||
|
}
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
kernel::Message::SubkernelMsgSend { id, destination, data } => {
|
||||||
|
let res =
|
||||||
|
subkernel::message_send(aux_mutex, routing_table, timer, id, destination.unwrap(), data).await;
|
||||||
|
match res {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
error!("error sending subkernel message: {:?}", e)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
control
|
||||||
|
.borrow_mut()
|
||||||
|
.tx
|
||||||
|
.async_send(kernel::Message::SubkernelMsgSent)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
kernel::Message::SubkernelMsgRecvRequest { id, timeout, tags } => {
|
||||||
|
let message_received = subkernel::message_await(id as u32, timeout, timer).await;
|
||||||
|
let response = match message_received {
|
||||||
|
Ok(ref message) => kernel::Message::SubkernelMsgRecvReply { count: message.count },
|
||||||
|
Err(SubkernelError::Timeout) => kernel::Message::SubkernelError(kernel::SubkernelStatus::Timeout),
|
||||||
|
Err(SubkernelError::IncorrectState) => {
|
||||||
|
kernel::Message::SubkernelError(kernel::SubkernelStatus::IncorrectState)
|
||||||
|
}
|
||||||
|
Err(SubkernelError::CommLost) => kernel::Message::SubkernelError(kernel::SubkernelStatus::CommLost),
|
||||||
|
Err(SubkernelError::SubkernelException) => {
|
||||||
|
// just retrieve the exception
|
||||||
|
let status = subkernel::await_finish(aux_mutex, routing_table, timer, id as u32, timeout)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
kernel::Message::SubkernelError(kernel::SubkernelStatus::Exception(status.exception.unwrap()))
|
||||||
|
}
|
||||||
|
Err(_) => kernel::Message::SubkernelError(kernel::SubkernelStatus::OtherError),
|
||||||
|
};
|
||||||
|
control.borrow_mut().tx.async_send(response).await;
|
||||||
|
if let Ok(message) = message_received {
|
||||||
|
// receive code almost identical to RPC recv, except we are not reading from a stream
|
||||||
|
let mut reader = Cursor::new(message.data);
|
||||||
|
let mut current_tags: &[u8] = &tags;
|
||||||
|
let mut i = 0;
|
||||||
|
loop {
|
||||||
|
// kernel has to consume all arguments in the whole message
|
||||||
|
let slot = match fast_recv(&mut control.borrow_mut().rx).await {
|
||||||
|
kernel::Message::RpcRecvRequest(slot) => slot,
|
||||||
|
other => panic!("expected root value slot from core1, not {:?}", other),
|
||||||
|
};
|
||||||
|
let remaining_tags = rpc::recv_return(&mut reader, ¤t_tags, slot, &mut |size| {
|
||||||
|
if size == 0 {
|
||||||
|
0 as *mut ()
|
||||||
|
} else {
|
||||||
|
let mut control = control.borrow_mut();
|
||||||
|
control.tx.send(kernel::Message::RpcRecvReply(Ok(size)));
|
||||||
|
match control.rx.recv() {
|
||||||
|
kernel::Message::RpcRecvRequest(slot) => slot,
|
||||||
|
other => {
|
||||||
|
panic!("expected nested value slot from kernel CPU, not {:?}", other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
control
|
||||||
|
.borrow_mut()
|
||||||
|
.tx
|
||||||
|
.async_send(kernel::Message::RpcRecvReply(Ok(0)))
|
||||||
|
.await;
|
||||||
|
i += 1;
|
||||||
|
if i < message.count {
|
||||||
|
current_tags = remaining_tags;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(has_drtio)]
|
||||||
kernel::Message::UpDestinationsRequest(destination) => {
|
kernel::Message::UpDestinationsRequest(destination) => {
|
||||||
let result = _up_destinations.borrow()[destination as usize];
|
let result = _up_destinations.borrow()[destination as usize];
|
||||||
control
|
control
|
||||||
|
@ -381,6 +533,56 @@ async fn handle_run_kernel(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_flash_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,
|
||||||
|
) -> Result<()> {
|
||||||
|
if buffer[0] == elf::ELFMAG0 && buffer[1] == elf::ELFMAG1 && buffer[2] == elf::ELFMAG2 && buffer[3] == elf::ELFMAG3
|
||||||
|
{
|
||||||
|
// assume ELF file, proceed as before
|
||||||
|
load_kernel(buffer, control, None).await
|
||||||
|
} else {
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
{
|
||||||
|
let archive = TarArchiveRef::new(buffer.as_ref());
|
||||||
|
let entries = archive.entries();
|
||||||
|
let mut main_lib: Vec<u8> = Vec::new();
|
||||||
|
for entry in entries {
|
||||||
|
if entry.filename().as_str() == "main.elf" {
|
||||||
|
main_lib = entry.data().to_vec();
|
||||||
|
} else {
|
||||||
|
// subkernel filename must be in format:
|
||||||
|
// "<subkernel id> <destination>.elf"
|
||||||
|
let filename = entry.filename();
|
||||||
|
let mut iter = filename.as_str().split_whitespace();
|
||||||
|
let sid: u32 = iter.next().unwrap().parse().unwrap();
|
||||||
|
let dest: u8 = iter.next().unwrap().strip_suffix(".elf").unwrap().parse().unwrap();
|
||||||
|
let up = _up_destinations.borrow()[dest as usize];
|
||||||
|
if up {
|
||||||
|
let subkernel_lib = entry.data().to_vec();
|
||||||
|
subkernel::add_subkernel(sid, dest, subkernel_lib).await;
|
||||||
|
match subkernel::upload(_aux_mutex, _routing_table, _timer, sid).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => return Err(Error::UnexpectedPattern),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::DestinationDown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
load_kernel(&main_lib, control, None).await
|
||||||
|
}
|
||||||
|
#[cfg(not(has_drtio))]
|
||||||
|
{
|
||||||
|
panic!("multi-kernel libraries are not supported in standalone systems");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn load_kernel(
|
async fn load_kernel(
|
||||||
buffer: &Vec<u8>,
|
buffer: &Vec<u8>,
|
||||||
control: &Rc<RefCell<kernel::Control>>,
|
control: &Rc<RefCell<kernel::Control>>,
|
||||||
|
@ -434,9 +636,13 @@ async fn handle_connection(
|
||||||
return Err(Error::UnexpectedPattern);
|
return Err(Error::UnexpectedPattern);
|
||||||
}
|
}
|
||||||
stream.send_slice("e".as_bytes()).await?;
|
stream.send_slice("e".as_bytes()).await?;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
subkernel::clear_subkernels().await;
|
||||||
loop {
|
loop {
|
||||||
let request = read_request(stream, true).await?;
|
let request = read_request(stream, true).await?;
|
||||||
if request.is_none() {
|
if request.is_none() {
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
subkernel::clear_subkernels().await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let request = request.unwrap();
|
let request = request.unwrap();
|
||||||
|
@ -460,6 +666,29 @@ async fn handle_connection(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
Request::UploadSubkernel => {
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
{
|
||||||
|
let id = read_i32(stream).await? as u32;
|
||||||
|
let destination = read_i8(stream).await? as u8;
|
||||||
|
let buffer = read_bytes(stream, 1024 * 1024).await?;
|
||||||
|
subkernel::add_subkernel(id, destination, buffer).await;
|
||||||
|
match subkernel::upload(aux_mutex, routing_table, timer, id).await {
|
||||||
|
Ok(_) => write_header(stream, Reply::LoadCompleted).await?,
|
||||||
|
Err(_) => {
|
||||||
|
write_header(stream, Reply::LoadFailed).await?;
|
||||||
|
write_chunk(stream, b"subkernel failed to load").await?;
|
||||||
|
return Err(Error::UnexpectedPattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(has_drtio))]
|
||||||
|
{
|
||||||
|
write_header(stream, Reply::LoadFailed).await?;
|
||||||
|
write_chunk(stream, b"No DRTIO on this system, subkernels are not supported").await?;
|
||||||
|
return Err(Error::UnexpectedPattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("unexpected request from host: {:?}", request);
|
error!("unexpected request from host: {:?}", request);
|
||||||
return Err(Error::UnrecognizedPacket);
|
return Err(Error::UnrecognizedPacket);
|
||||||
|
@ -509,7 +738,6 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
|
|
||||||
Sockets::init(32);
|
Sockets::init(32);
|
||||||
|
|
||||||
// before, mutex was on io, but now that io isn't used...?
|
|
||||||
let aux_mutex: Rc<Mutex<bool>> = Rc::new(Mutex::new(false));
|
let aux_mutex: Rc<Mutex<bool>> = Rc::new(Mutex::new(false));
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
let drtio_routing_table = Rc::new(RefCell::new(drtio_routing::config_routing_table(
|
let drtio_routing_table = Rc::new(RefCell::new(drtio_routing::config_routing_table(
|
||||||
|
@ -522,18 +750,25 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
#[cfg(has_drtio_routing)]
|
#[cfg(has_drtio_routing)]
|
||||||
drtio_routing::interconnect_disable_all();
|
drtio_routing::interconnect_disable_all();
|
||||||
|
|
||||||
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer, &cfg);
|
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, &cfg, timer);
|
||||||
|
ksupport::setup_device_map(&cfg);
|
||||||
|
|
||||||
analyzer::start(&aux_mutex, &drtio_routing_table, &up_destinations, timer);
|
analyzer::start(&aux_mutex, &drtio_routing_table, &up_destinations, timer);
|
||||||
moninj::start(timer, &aux_mutex, &drtio_routing_table);
|
moninj::start(timer, &aux_mutex, &drtio_routing_table);
|
||||||
|
|
||||||
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
|
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") {
|
if let Ok(buffer) = cfg.read("startup_kernel") {
|
||||||
info!("Loading startup kernel...");
|
info!("Loading startup kernel...");
|
||||||
if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) {
|
|
||||||
info!("Starting startup kernel...");
|
|
||||||
let routing_table = drtio_routing_table.borrow();
|
let routing_table = drtio_routing_table.borrow();
|
||||||
|
if let Ok(()) = task::block_on(handle_flash_kernel(
|
||||||
|
&buffer,
|
||||||
|
&control,
|
||||||
|
&up_destinations,
|
||||||
|
&aux_mutex,
|
||||||
|
&routing_table,
|
||||||
|
timer,
|
||||||
|
)) {
|
||||||
|
info!("Starting startup kernel...");
|
||||||
let _ = task::block_on(handle_run_kernel(
|
let _ = task::block_on(handle_run_kernel(
|
||||||
None,
|
None,
|
||||||
&control,
|
&control,
|
||||||
|
@ -548,13 +783,34 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mgmt::start(cfg);
|
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,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
let connection = Rc::new(Semaphore::new(1, 1));
|
let connection = Rc::new(Semaphore::new(1, 1));
|
||||||
let terminate = 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 {
|
loop {
|
||||||
let mut stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
|
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
|
||||||
|
};
|
||||||
|
|
||||||
if connection.try_wait().is_none() {
|
if connection.try_wait().is_none() {
|
||||||
// there is an existing connection
|
// there is an existing connection
|
||||||
|
@ -562,38 +818,58 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
connection.async_wait().await;
|
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 control = control.clone();
|
||||||
let idle_kernel = idle_kernel.clone();
|
|
||||||
let connection = connection.clone();
|
let connection = connection.clone();
|
||||||
let terminate = terminate.clone();
|
let terminate = terminate.clone();
|
||||||
|
let can_restart_idle = can_restart_idle.clone();
|
||||||
let up_destinations = up_destinations.clone();
|
let up_destinations = up_destinations.clone();
|
||||||
let aux_mutex = aux_mutex.clone();
|
let aux_mutex = aux_mutex.clone();
|
||||||
let routing_table = drtio_routing_table.clone();
|
let routing_table = drtio_routing_table.clone();
|
||||||
|
|
||||||
// we make sure the value of terminate is 0 before we start
|
// we make sure the value of terminate is 0 before we start
|
||||||
let _ = terminate.try_wait();
|
let _ = terminate.try_wait();
|
||||||
|
let _ = can_restart_idle.try_wait();
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
let routing_table = routing_table.borrow();
|
let routing_table = routing_table.borrow();
|
||||||
select_biased! {
|
select_biased! {
|
||||||
_ = (async {
|
_ = (async {
|
||||||
let _ = handle_connection(&mut stream, control.clone(), &up_destinations, &aux_mutex, &routing_table, timer)
|
if let Some(stream) = &mut maybe_stream {
|
||||||
|
let _ = handle_connection(stream, control.clone(), &up_destinations, &aux_mutex, &routing_table, timer)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| warn!("connection terminated: {}", e));
|
.map_err(|e| warn!("connection terminated: {}", e));
|
||||||
if let Some(buffer) = &*idle_kernel {
|
}
|
||||||
info!("Loading idle kernel");
|
can_restart_idle.signal();
|
||||||
let _ = load_kernel(&buffer, &control, None)
|
match maybe_idle_kernel {
|
||||||
.await.map_err(|_| warn!("error loading idle kernel"));
|
Some(buffer) => {
|
||||||
info!("Running idle kernel");
|
loop {
|
||||||
let _ = handle_run_kernel(None, &control, &up_destinations, &aux_mutex, &routing_table, timer)
|
info!("loading idle kernel");
|
||||||
.await.map_err(|_| warn!("error running idle kernel"));
|
match handle_flash_kernel(&buffer, &control, &up_destinations, &aux_mutex, &routing_table, timer).await {
|
||||||
info!("Idle kernel terminated");
|
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")
|
||||||
}
|
}
|
||||||
}).fuse() => (),
|
}).fuse() => (),
|
||||||
_ = terminate.async_wait().fuse() => ()
|
_ = terminate.async_wait().fuse() => ()
|
||||||
}
|
}
|
||||||
connection.signal();
|
connection.signal();
|
||||||
|
if let Some(stream) = maybe_stream {
|
||||||
let _ = stream.flush().await;
|
let _ = stream.flush().await;
|
||||||
let _ = stream.abort().await;
|
let _ = stream.abort().await;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -642,7 +918,8 @@ pub fn soft_panic_main(timer: GlobalTimer, cfg: Config) -> ! {
|
||||||
|
|
||||||
Sockets::init(32);
|
Sockets::init(32);
|
||||||
|
|
||||||
mgmt::start(cfg);
|
let dummy = Rc::new(Semaphore::new(0, 1));
|
||||||
|
mgmt::start(Rc::new(cfg), dummy, None);
|
||||||
|
|
||||||
// getting eth settings disables the LED as it resets GPIO
|
// getting eth settings disables the LED as it resets GPIO
|
||||||
// need to re-enable it here
|
// need to re-enable it here
|
||||||
|
|
|
@ -2,110 +2,48 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![recursion_limit = "1024"] // for futures_util::select!
|
#![recursion_limit = "1024"] // for futures_util::select!
|
||||||
#![feature(alloc_error_handler)]
|
#![feature(alloc_error_handler)]
|
||||||
#![feature(panic_info_message)]
|
|
||||||
#![feature(c_variadic)]
|
|
||||||
#![feature(const_btree_new)]
|
#![feature(const_btree_new)]
|
||||||
#![feature(const_in_array_repeat_expressions)]
|
#![feature(panic_info_message)]
|
||||||
#![feature(naked_functions)]
|
|
||||||
#![feature(asm)]
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
#[cfg(all(feature = "target_kasli_soc", has_virtual_leds))]
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use libasync::{block_async, task};
|
use ksupport;
|
||||||
|
use libasync::task;
|
||||||
|
#[cfg(has_drtio_eem)]
|
||||||
|
use libboard_artiq::drtio_eem;
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
use libboard_artiq::io_expander;
|
use libboard_artiq::io_expander;
|
||||||
use libboard_artiq::{identifier_read, logger, pl};
|
use libboard_artiq::{identifier_read, logger, pl};
|
||||||
use libboard_zynq::{gic, mpcore, timer::GlobalTimer};
|
use libboard_zynq::{gic, mpcore, timer::GlobalTimer};
|
||||||
use libconfig::Config;
|
use libconfig::Config;
|
||||||
use libcortex_a9::l2c::enable_l2_cache;
|
use libcortex_a9::l2c::enable_l2_cache;
|
||||||
use libsupport_zynq::ram;
|
use libsupport_zynq::{exception_vectors, ram};
|
||||||
use log::{error, info, warn};
|
use log::{info, warn};
|
||||||
use nb;
|
|
||||||
use void::Void;
|
|
||||||
|
|
||||||
const ASYNC_ERROR_COLLISION: u8 = 1 << 0;
|
|
||||||
const ASYNC_ERROR_BUSY: u8 = 1 << 1;
|
|
||||||
const ASYNC_ERROR_SEQUENCE_ERROR: u8 = 1 << 2;
|
|
||||||
|
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
mod comms;
|
mod comms;
|
||||||
mod eh_artiq;
|
|
||||||
mod i2c;
|
|
||||||
mod irq;
|
|
||||||
mod kernel;
|
|
||||||
mod mgmt;
|
mod mgmt;
|
||||||
mod moninj;
|
mod moninj;
|
||||||
mod panic;
|
mod panic;
|
||||||
mod proto_async;
|
mod proto_async;
|
||||||
mod rpc;
|
mod rpc_async;
|
||||||
#[cfg(ki_impl = "csr")]
|
|
||||||
#[path = "rtio_csr.rs"]
|
|
||||||
mod rtio;
|
|
||||||
#[cfg(ki_impl = "acp")]
|
|
||||||
#[path = "rtio_acp.rs"]
|
|
||||||
mod rtio;
|
|
||||||
mod rtio_clocking;
|
mod rtio_clocking;
|
||||||
mod rtio_dma;
|
mod rtio_dma;
|
||||||
mod rtio_mgt;
|
mod rtio_mgt;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
mod subkernel;
|
||||||
|
|
||||||
static mut SEEN_ASYNC_ERRORS: u8 = 0;
|
// linker symbols
|
||||||
|
extern "C" {
|
||||||
pub unsafe fn get_async_errors() -> u8 {
|
static __exceptions_start: u32;
|
||||||
let errors = SEEN_ASYNC_ERRORS;
|
|
||||||
SEEN_ASYNC_ERRORS = 0;
|
|
||||||
errors
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
|
#[cfg(all(feature = "target_kasli_soc", has_virtual_leds))]
|
||||||
unsafe {
|
|
||||||
if pl::csr::rtio_core::async_error_read() != 0 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(nb::Error::WouldBlock)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn report_async_rtio_errors() {
|
|
||||||
loop {
|
|
||||||
let _ = block_async!(wait_for_async_rtio_error()).await;
|
|
||||||
unsafe {
|
|
||||||
let errors = pl::csr::rtio_core::async_error_read();
|
|
||||||
if errors & ASYNC_ERROR_COLLISION != 0 {
|
|
||||||
let channel = pl::csr::rtio_core::collision_channel_read();
|
|
||||||
error!(
|
|
||||||
"RTIO collision involving channel 0x{:04x}:{}",
|
|
||||||
channel,
|
|
||||||
rtio_mgt::resolve_channel_name(channel as u32)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if errors & ASYNC_ERROR_BUSY != 0 {
|
|
||||||
let channel = pl::csr::rtio_core::busy_channel_read();
|
|
||||||
error!(
|
|
||||||
"RTIO busy error involving channel 0x{:04x}:{}",
|
|
||||||
channel,
|
|
||||||
rtio_mgt::resolve_channel_name(channel as u32)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if errors & ASYNC_ERROR_SEQUENCE_ERROR != 0 {
|
|
||||||
let channel = pl::csr::rtio_core::sequence_error_channel_read();
|
|
||||||
error!(
|
|
||||||
"RTIO sequence error involving channel 0x{:04x}:{}",
|
|
||||||
channel,
|
|
||||||
rtio_mgt::resolve_channel_name(channel as u32)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
SEEN_ASYNC_ERRORS = errors;
|
|
||||||
pl::csr::rtio_core::async_error_write(errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
|
||||||
async fn io_expanders_service(
|
async fn io_expanders_service(
|
||||||
i2c_bus: RefCell<&mut libboard_zynq::i2c::I2c>,
|
i2c_bus: RefCell<&mut libboard_zynq::i2c::I2c>,
|
||||||
io_expander0: RefCell<io_expander::IoExpander>,
|
io_expander0: RefCell<io_expander::IoExpander>,
|
||||||
|
@ -124,10 +62,29 @@ async fn io_expanders_service(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_grabber)]
|
||||||
|
mod grabber {
|
||||||
|
use libasync::delay;
|
||||||
|
use libboard_artiq::grabber;
|
||||||
|
use libboard_zynq::time::Milliseconds;
|
||||||
|
|
||||||
|
use crate::GlobalTimer;
|
||||||
|
pub async fn grabber_thread(timer: GlobalTimer) {
|
||||||
|
let mut countdown = timer.countdown();
|
||||||
|
loop {
|
||||||
|
grabber::tick();
|
||||||
|
delay(&mut countdown, Milliseconds(200)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17];
|
static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17];
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn main_core0() {
|
pub fn main_core0() {
|
||||||
|
unsafe {
|
||||||
|
exception_vectors::set_vector_table(&__exceptions_start as *const u32 as u32);
|
||||||
|
}
|
||||||
enable_l2_cache(0x8);
|
enable_l2_cache(0x8);
|
||||||
let mut timer = GlobalTimer::start();
|
let mut timer = GlobalTimer::start();
|
||||||
|
|
||||||
|
@ -143,22 +100,23 @@ pub fn main_core0() {
|
||||||
|
|
||||||
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
|
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
|
||||||
|
|
||||||
i2c::init();
|
ksupport::i2c::init();
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
|
||||||
let i2c_bus = unsafe { (i2c::I2C_BUS).as_mut().unwrap() };
|
|
||||||
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
|
||||||
let (mut io_expander0, mut io_expander1);
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
{
|
{
|
||||||
io_expander0 = io_expander::IoExpander::new(i2c_bus, 0).unwrap();
|
let i2c_bus = unsafe { (ksupport::i2c::I2C_BUS).as_mut().unwrap() };
|
||||||
io_expander1 = io_expander::IoExpander::new(i2c_bus, 1).unwrap();
|
let mut io_expander0 = io_expander::IoExpander::new(i2c_bus, 0).unwrap();
|
||||||
|
let mut io_expander1 = io_expander::IoExpander::new(i2c_bus, 1).unwrap();
|
||||||
io_expander0
|
io_expander0
|
||||||
.init(i2c_bus)
|
.init(i2c_bus)
|
||||||
.expect("I2C I/O expander #0 initialization failed");
|
.expect("I2C I/O expander #0 initialization failed");
|
||||||
io_expander1
|
io_expander1
|
||||||
.init(i2c_bus)
|
.init(i2c_bus)
|
||||||
.expect("I2C I/O expander #1 initialization failed");
|
.expect("I2C I/O expander #1 initialization failed");
|
||||||
|
|
||||||
|
// Drive CLK_SEL to true
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
io_expander0.set(1, 7, true);
|
||||||
|
|
||||||
// Drive TX_DISABLE to false on SFP0..3
|
// Drive TX_DISABLE to false on SFP0..3
|
||||||
io_expander0.set(0, 1, false);
|
io_expander0.set(0, 1, false);
|
||||||
io_expander1.set(0, 1, false);
|
io_expander1.set(0, 1, false);
|
||||||
|
@ -166,6 +124,12 @@ pub fn main_core0() {
|
||||||
io_expander1.set(1, 1, false);
|
io_expander1.set(1, 1, false);
|
||||||
io_expander0.service(i2c_bus).unwrap();
|
io_expander0.service(i2c_bus).unwrap();
|
||||||
io_expander1.service(i2c_bus).unwrap();
|
io_expander1.service(i2c_bus).unwrap();
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
|
task::spawn(io_expanders_service(
|
||||||
|
RefCell::new(i2c_bus),
|
||||||
|
RefCell::new(io_expander0),
|
||||||
|
RefCell::new(io_expander1),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let cfg = match Config::new() {
|
let cfg = match Config::new() {
|
||||||
|
@ -178,13 +142,13 @@ pub fn main_core0() {
|
||||||
|
|
||||||
rtio_clocking::init(&mut timer, &cfg);
|
rtio_clocking::init(&mut timer, &cfg);
|
||||||
|
|
||||||
task::spawn(report_async_rtio_errors());
|
#[cfg(has_drtio_eem)]
|
||||||
|
drtio_eem::init(&mut timer, &cfg);
|
||||||
|
|
||||||
|
#[cfg(has_grabber)]
|
||||||
|
task::spawn(grabber::grabber_thread(timer));
|
||||||
|
|
||||||
|
task::spawn(ksupport::report_async_rtio_errors());
|
||||||
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
|
||||||
task::spawn(io_expanders_service(
|
|
||||||
RefCell::new(i2c_bus),
|
|
||||||
RefCell::new(io_expander0),
|
|
||||||
RefCell::new(io_expander1),
|
|
||||||
));
|
|
||||||
comms::main(timer, cfg);
|
comms::main(timer, cfg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
use alloc::{rc::Rc, string::String, vec::Vec};
|
use alloc::{rc::Rc, string::String, vec::Vec};
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use byteorder::{ByteOrder, NativeEndian};
|
||||||
|
use crc::crc32;
|
||||||
use futures::{future::poll_fn, task::Poll};
|
use futures::{future::poll_fn, task::Poll};
|
||||||
use libasync::{smoltcp::TcpStream, task};
|
use libasync::{smoltcp::TcpStream, task};
|
||||||
use libboard_artiq::logger::{BufferLogger, LogBufferRef};
|
use libboard_artiq::{drtio_routing::RoutingTable,
|
||||||
use libboard_zynq::{slcr, smoltcp};
|
logger::{BufferLogger, LogBufferRef}};
|
||||||
|
use libboard_zynq::{smoltcp, timer::GlobalTimer};
|
||||||
use libconfig::Config;
|
use libconfig::Config;
|
||||||
use log::{self, debug, error, info, warn, LevelFilter};
|
use libcortex_a9::{mutex::Mutex, semaphore::Semaphore};
|
||||||
|
use log::{self, debug, error, info, warn};
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
use crate::proto_async::*;
|
use crate::proto_async::*;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use crate::rtio_mgt::drtio;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -18,6 +24,8 @@ pub enum Error {
|
||||||
UnknownLogLevel(u8),
|
UnknownLogLevel(u8),
|
||||||
UnexpectedPattern,
|
UnexpectedPattern,
|
||||||
UnrecognizedPacket,
|
UnrecognizedPacket,
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
DrtioError(drtio::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = core::result::Result<T, Error>;
|
type Result<T> = core::result::Result<T, Error>;
|
||||||
|
@ -29,6 +37,8 @@ impl core::fmt::Display for Error {
|
||||||
&Error::UnknownLogLevel(lvl) => write!(f, "unknown log level {}", lvl),
|
&Error::UnknownLogLevel(lvl) => write!(f, "unknown log level {}", lvl),
|
||||||
&Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
&Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
||||||
&Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
&Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
&Error::DrtioError(error) => write!(f, "drtio error: {}", error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +49,13 @@ impl From<smoltcp::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
impl From<drtio::Error> for Error {
|
||||||
|
fn from(error: drtio::Error) -> Self {
|
||||||
|
Error::DrtioError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromPrimitive)]
|
#[derive(Debug, FromPrimitive)]
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
GetLog = 1,
|
GetLog = 1,
|
||||||
|
@ -51,6 +68,11 @@ pub enum Request {
|
||||||
ConfigRead = 12,
|
ConfigRead = 12,
|
||||||
ConfigWrite = 13,
|
ConfigWrite = 13,
|
||||||
ConfigRemove = 14,
|
ConfigRemove = 14,
|
||||||
|
ConfigErase = 15,
|
||||||
|
|
||||||
|
DebugAllocator = 8,
|
||||||
|
|
||||||
|
Flash = 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(i8)]
|
#[repr(i8)]
|
||||||
|
@ -111,30 +133,605 @@ async fn read_key(stream: &mut TcpStream) -> Result<String> {
|
||||||
Ok(String::from_utf8(buffer).unwrap())
|
Ok(String::from_utf8(buffer).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_connection(stream: &mut TcpStream, pull_id: Rc<RefCell<u32>>, cfg: Rc<Config>) -> Result<()> {
|
#[cfg(has_drtio)]
|
||||||
if !expect(&stream, b"ARTIQ management\n").await? {
|
mod remote_coremgmt {
|
||||||
return Err(Error::UnexpectedPattern);
|
use core_io::Read;
|
||||||
}
|
use io::ProtoWrite;
|
||||||
stream.send_slice("e".as_bytes()).await?;
|
use libboard_artiq::{drtioaux_async,
|
||||||
|
drtioaux_proto::{Packet, MASTER_PAYLOAD_MAX_SIZE}};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub async fn get_log(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let msg = read_i8(stream).await;
|
let reply = drtio::aux_transact(
|
||||||
if let Err(smoltcp::Error::Finished) = msg {
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtGetLogRequest {
|
||||||
|
destination,
|
||||||
|
clear: false,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtGetLogReply { last, length, data }) => {
|
||||||
|
buffer.extend(&data[..length as usize]);
|
||||||
|
if last {
|
||||||
|
write_i8(stream, Reply::LogContent as i8).await?;
|
||||||
|
write_chunk(stream, &buffer).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?;
|
}
|
||||||
match msg {
|
Ok(packet) => {
|
||||||
Request::GetLog => {
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
return Err(drtio::Error::UnexpectedReply.into());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn clear_log(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
) -> Result<()> {
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtClearLogRequest { destination },
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::UnexpectedReply.into())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn pull_log(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
pull_id: &Rc<RefCell<u32>>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let id = {
|
||||||
|
let mut guard = pull_id.borrow_mut();
|
||||||
|
*guard += 1;
|
||||||
|
*guard
|
||||||
|
};
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if id != *pull_id.borrow() {
|
||||||
|
// another connection attempts to pull the log...
|
||||||
|
// abort this connection...
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtGetLogRequest {
|
||||||
|
destination,
|
||||||
|
clear: true,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtGetLogReply { last, length, data }) => {
|
||||||
|
buffer.extend(&data[..length as usize]);
|
||||||
|
if last {
|
||||||
|
write_chunk(stream, &buffer).await?;
|
||||||
|
buffer.clear();
|
||||||
|
task::r#yield().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
return Err(drtio::Error::UnexpectedReply.into());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_log_filter(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
level: log::LevelFilter,
|
||||||
|
) -> Result<()> {
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtSetLogLevelRequest {
|
||||||
|
destination,
|
||||||
|
log_level: level as u8,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::UnexpectedReply.into())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_uart_log_filter(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
level: log::LevelFilter,
|
||||||
|
) -> Result<()> {
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtSetUartLogLevelRequest {
|
||||||
|
destination,
|
||||||
|
log_level: level as u8,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::UnexpectedReply.into())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_read(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
_cfg: &Rc<Config>,
|
||||||
|
key: &String,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut config_key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||||
|
let len = key.len();
|
||||||
|
config_key[..len].clone_from_slice(key.as_bytes());
|
||||||
|
|
||||||
|
let mut reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtConfigReadRequest {
|
||||||
|
destination: destination,
|
||||||
|
length: len as u16,
|
||||||
|
key: config_key,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut buffer = Vec::<u8>::new();
|
||||||
|
loop {
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtConfigReadReply { last, length, value }) => {
|
||||||
|
buffer.extend(&value[..length as usize]);
|
||||||
|
|
||||||
|
if last {
|
||||||
|
write_i8(stream, Reply::ConfigData as i8).await?;
|
||||||
|
write_chunk(stream, &buffer).await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtConfigReadContinue {
|
||||||
|
destination: destination,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
return Err(drtio::Error::UnexpectedReply.into());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_write(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
_cfg: &Rc<Config>,
|
||||||
|
key: &String,
|
||||||
|
value: Vec<u8>,
|
||||||
|
_restart_idle: &Rc<Semaphore>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut message = Vec::with_capacity(key.len() + value.len() + 4 * 2);
|
||||||
|
message.write_string(key).unwrap();
|
||||||
|
message.write_bytes(&value).unwrap();
|
||||||
|
|
||||||
|
match drtio::partition_data(
|
||||||
|
linkno,
|
||||||
|
aux_mutex,
|
||||||
|
routing_table,
|
||||||
|
timer,
|
||||||
|
&message,
|
||||||
|
|slice, status, len: usize| Packet::CoreMgmtConfigWriteRequest {
|
||||||
|
destination: destination,
|
||||||
|
last: status.is_last(),
|
||||||
|
length: len as u16,
|
||||||
|
data: *slice,
|
||||||
|
},
|
||||||
|
|reply| match reply {
|
||||||
|
Packet::CoreMgmtReply { succeeded: true } => Ok(()),
|
||||||
|
packet => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
Err(drtio::Error::UnexpectedReply)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_remove(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
_cfg: &Rc<Config>,
|
||||||
|
key: &String,
|
||||||
|
_restart_idle: &Rc<Semaphore>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut config_key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||||
|
let len = key.len();
|
||||||
|
config_key[..len].clone_from_slice(key.as_bytes());
|
||||||
|
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtConfigRemoveRequest {
|
||||||
|
destination: destination,
|
||||||
|
length: len as u16,
|
||||||
|
key: config_key,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::UnexpectedReply.into())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_erase(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
) -> Result<()> {
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtConfigEraseRequest {
|
||||||
|
destination: destination,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::UnexpectedReply.into())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reboot(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
) -> Result<()> {
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtRebootRequest {
|
||||||
|
destination: destination,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||||
|
write_i8(stream, Reply::RebootImminent as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::UnexpectedReply.into())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn debug_allocator(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
) -> Result<()> {
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtAllocatorDebugRequest {
|
||||||
|
destination: destination,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
Err(drtio::Error::UnexpectedReply.into())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn image_write(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
linkno: u8,
|
||||||
|
destination: u8,
|
||||||
|
_cfg: &Rc<Config>,
|
||||||
|
image: Vec<u8>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut image = &image[..];
|
||||||
|
|
||||||
|
let alloc_reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtFlashRequest {
|
||||||
|
destination: destination,
|
||||||
|
payload_length: image.len() as u32,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match alloc_reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) => Ok(()),
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::UnexpectedReply)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::AuxError)
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
|
while !image.is_empty() {
|
||||||
|
let mut data = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||||
|
let len = image.read(&mut data).unwrap();
|
||||||
|
let last = image.is_empty();
|
||||||
|
|
||||||
|
let reply = drtio::aux_transact(
|
||||||
|
aux_mutex,
|
||||||
|
linkno,
|
||||||
|
routing_table,
|
||||||
|
&Packet::CoreMgmtFlashAddDataRequest {
|
||||||
|
destination: destination,
|
||||||
|
last: last,
|
||||||
|
length: len as u16,
|
||||||
|
data: data,
|
||||||
|
},
|
||||||
|
timer,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::CoreMgmtReply { succeeded: true }) if !last => Ok(()),
|
||||||
|
Ok(Packet::CoreMgmtDropLink) if last => drtioaux_async::send(
|
||||||
|
linkno,
|
||||||
|
&Packet::CoreMgmtDropLinkAck {
|
||||||
|
destination: destination,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| drtio::Error::AuxError),
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::UnexpectedReply)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::AuxError)
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_i8(stream, Reply::RebootImminent as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod local_coremgmt {
|
||||||
|
use libboard_zynq::slcr;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub async fn get_log(stream: &mut TcpStream) -> Result<()> {
|
||||||
let buffer = get_logger_buffer().await.extract().as_bytes().to_vec();
|
let buffer = get_logger_buffer().await.extract().as_bytes().to_vec();
|
||||||
write_i8(stream, Reply::LogContent as i8).await?;
|
write_i8(stream, Reply::LogContent as i8).await?;
|
||||||
write_chunk(stream, &buffer).await?;
|
write_chunk(stream, &buffer).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Request::ClearLog => {
|
|
||||||
|
pub async fn clear_log(stream: &mut TcpStream) -> Result<()> {
|
||||||
let mut buffer = get_logger_buffer().await;
|
let mut buffer = get_logger_buffer().await;
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
write_i8(stream, Reply::Success as i8).await?;
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Request::PullLog => {
|
|
||||||
|
pub async fn pull_log(stream: &mut TcpStream, pull_id: &Rc<RefCell<u32>>) -> Result<()> {
|
||||||
let id = {
|
let id = {
|
||||||
let mut guard = pull_id.borrow_mut();
|
let mut guard = pull_id.borrow_mut();
|
||||||
*guard += 1;
|
*guard += 1;
|
||||||
|
@ -151,32 +748,34 @@ async fn handle_connection(stream: &mut TcpStream, pull_id: Rc<RefCell<u32>>, cf
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
core::mem::drop(buffer);
|
core::mem::drop(buffer);
|
||||||
write_chunk(stream, &bytes).await?;
|
write_chunk(stream, &bytes).await?;
|
||||||
if log::max_level() == LevelFilter::Trace {
|
if log::max_level() == log::LevelFilter::Trace {
|
||||||
// temporarily discard all trace level log
|
// temporarily discard all trace level log
|
||||||
let logger = unsafe { BufferLogger::get_logger().as_mut().unwrap() };
|
let logger = unsafe { BufferLogger::get_logger().as_mut().unwrap() };
|
||||||
logger.set_buffer_log_level(LevelFilter::Debug);
|
logger.set_buffer_log_level(log::LevelFilter::Debug);
|
||||||
stream.flush().await?;
|
stream.flush().await?;
|
||||||
logger.set_buffer_log_level(LevelFilter::Trace);
|
logger.set_buffer_log_level(log::LevelFilter::Trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Request::SetLogFilter => {
|
|
||||||
let lvl = read_log_level_filter(stream).await?;
|
pub async fn set_log_filter(stream: &mut TcpStream, lvl: log::LevelFilter) -> Result<()> {
|
||||||
info!("Changing log level to {}", lvl);
|
info!("Changing log level to {}", lvl);
|
||||||
log::set_max_level(lvl);
|
log::set_max_level(lvl);
|
||||||
write_i8(stream, Reply::Success as i8).await?;
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Request::SetUartLogFilter => {
|
|
||||||
let lvl = read_log_level_filter(stream).await?;
|
pub async fn set_uart_log_filter(stream: &mut TcpStream, lvl: log::LevelFilter) -> Result<()> {
|
||||||
info!("Changing UART log level to {}", lvl);
|
info!("Changing UART log level to {}", lvl);
|
||||||
unsafe {
|
unsafe {
|
||||||
BufferLogger::get_logger().as_ref().unwrap().set_uart_log_level(lvl);
|
BufferLogger::get_logger().as_ref().unwrap().set_uart_log_level(lvl);
|
||||||
}
|
}
|
||||||
write_i8(stream, Reply::Success as i8).await?;
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Request::ConfigRead => {
|
|
||||||
let key = read_key(stream).await?;
|
pub async fn config_read(stream: &mut TcpStream, cfg: &Rc<Config>, key: &String) -> Result<()> {
|
||||||
debug!("read key: {}", key);
|
|
||||||
let value = cfg.read(&key);
|
let value = cfg.read(&key);
|
||||||
if let Ok(value) = value {
|
if let Ok(value) = value {
|
||||||
debug!("got value");
|
debug!("got value");
|
||||||
|
@ -186,10 +785,165 @@ async fn handle_connection(stream: &mut TcpStream, pull_id: Rc<RefCell<u32>>, cf
|
||||||
warn!("read error: no such key");
|
warn!("read error: no such key");
|
||||||
write_i8(stream, Reply::Error as i8).await?;
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_write(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
cfg: &Rc<Config>,
|
||||||
|
key: &String,
|
||||||
|
value: Vec<u8>,
|
||||||
|
restart_idle: &Rc<Semaphore>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let value = cfg.write(&key, value);
|
||||||
|
if value.is_ok() {
|
||||||
|
debug!("write success");
|
||||||
|
if key == "idle_kernel" {
|
||||||
|
restart_idle.signal();
|
||||||
|
}
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
} else {
|
||||||
|
// this is an error because we do not expect write to fail
|
||||||
|
error!("failed to write: {:?}", value);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_remove(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
cfg: &Rc<Config>,
|
||||||
|
key: &String,
|
||||||
|
restart_idle: &Rc<Semaphore>,
|
||||||
|
) -> Result<()> {
|
||||||
|
debug!("erase key: {}", key);
|
||||||
|
let value = cfg.remove(&key);
|
||||||
|
if value.is_ok() {
|
||||||
|
debug!("erase success");
|
||||||
|
if key == "idle_kernel" {
|
||||||
|
restart_idle.signal();
|
||||||
|
}
|
||||||
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
|
} else {
|
||||||
|
warn!("erase failed");
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_erase(stream: &mut TcpStream) -> Result<()> {
|
||||||
|
error!("zynq device does not support config erase");
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reboot(stream: &mut TcpStream) -> Result<()> {
|
||||||
|
info!("rebooting");
|
||||||
|
write_i8(stream, Reply::RebootImminent as i8).await?;
|
||||||
|
stream.flush().await?;
|
||||||
|
slcr::reboot();
|
||||||
|
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn debug_allocator(_stream: &mut TcpStream) -> Result<()> {
|
||||||
|
error!("zynq device does not support allocator debug print");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn image_write(stream: &mut TcpStream, cfg: &Rc<Config>, image: Vec<u8>) -> Result<()> {
|
||||||
|
let mut image = image.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);
|
||||||
|
cfg.write("boot", image).expect("failed to write boot image");
|
||||||
|
reboot(stream).await?;
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"CRC failed, images have not been written to flash.\n(actual {:08x}, expected {:08x})",
|
||||||
|
actual_crc, expected_crc
|
||||||
|
);
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
macro_rules! process {
|
||||||
|
($stream: ident, $drtio_context:ident, $destination:expr, $func:ident $(, $param:expr)*) => {{
|
||||||
|
if $destination == 0 {
|
||||||
|
local_coremgmt::$func($stream, $($param, )*).await
|
||||||
|
} else if let Some(DrtioContext(ref aux_mutex, ref routing_table, timer)) = $drtio_context {
|
||||||
|
let routing_table = routing_table.borrow();
|
||||||
|
let linkno = routing_table.0[$destination as usize][0] - 1 as u8;
|
||||||
|
remote_coremgmt::$func($stream, &aux_mutex, &routing_table, timer, linkno, $destination, $($param, )*).await
|
||||||
|
} else {
|
||||||
|
error!("coremgmt-over-drtio not supported for panicked device, please reboot");
|
||||||
|
write_i8($stream, Reply::Error as i8).await?;
|
||||||
|
Err(drtio::Error::LinkDown.into())
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_drtio))]
|
||||||
|
macro_rules! process {
|
||||||
|
($stream: ident, $drtio_context:ident, $destination:expr, $func:ident $(, $param:expr)*) => {{
|
||||||
|
local_coremgmt::$func($stream, $($param, )*).await
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DrtioContext(pub Rc<Mutex<bool>>, pub Rc<RefCell<RoutingTable>>, pub GlobalTimer);
|
||||||
|
|
||||||
|
async fn handle_connection(
|
||||||
|
stream: &mut TcpStream,
|
||||||
|
pull_id: Rc<RefCell<u32>>,
|
||||||
|
cfg: Rc<Config>,
|
||||||
|
restart_idle: Rc<Semaphore>,
|
||||||
|
_drtio_context: Option<DrtioContext>,
|
||||||
|
) -> Result<()> {
|
||||||
|
if !expect(&stream, b"ARTIQ management\n").await? {
|
||||||
|
return Err(Error::UnexpectedPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _destination: u8 = read_i8(stream).await? as u8;
|
||||||
|
stream.send_slice("e".as_bytes()).await?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let msg = read_i8(stream).await;
|
||||||
|
if let Err(smoltcp::Error::Finished) = msg {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?;
|
||||||
|
match msg {
|
||||||
|
Request::GetLog => process!(stream, _drtio_context, _destination, get_log),
|
||||||
|
Request::ClearLog => process!(stream, _drtio_context, _destination, clear_log),
|
||||||
|
Request::PullLog => process!(stream, _drtio_context, _destination, pull_log, &pull_id),
|
||||||
|
Request::SetLogFilter => {
|
||||||
|
let lvl = read_log_level_filter(stream).await?;
|
||||||
|
process!(stream, _drtio_context, _destination, set_log_filter, lvl)
|
||||||
|
}
|
||||||
|
Request::SetUartLogFilter => {
|
||||||
|
let lvl = read_log_level_filter(stream).await?;
|
||||||
|
process!(stream, _drtio_context, _destination, set_uart_log_filter, lvl)
|
||||||
|
}
|
||||||
|
Request::ConfigRead => {
|
||||||
|
let key = read_key(stream).await?;
|
||||||
|
process!(stream, _drtio_context, _destination, config_read, &cfg, &key)
|
||||||
}
|
}
|
||||||
Request::ConfigWrite => {
|
Request::ConfigWrite => {
|
||||||
let key = read_key(stream).await?;
|
let key = read_key(stream).await?;
|
||||||
debug!("write key: {}", key);
|
|
||||||
let len = read_i32(stream).await?;
|
let len = read_i32(stream).await?;
|
||||||
let len = if len <= 0 { 0 } else { len as usize };
|
let len = if len <= 0 { 0 } else { len as usize };
|
||||||
let mut buffer = Vec::with_capacity(len);
|
let mut buffer = Vec::with_capacity(len);
|
||||||
|
@ -197,49 +951,67 @@ async fn handle_connection(stream: &mut TcpStream, pull_id: Rc<RefCell<u32>>, cf
|
||||||
buffer.set_len(len);
|
buffer.set_len(len);
|
||||||
}
|
}
|
||||||
read_chunk(stream, &mut buffer).await?;
|
read_chunk(stream, &mut buffer).await?;
|
||||||
let value = cfg.write(&key, buffer);
|
process!(
|
||||||
if value.is_ok() {
|
stream,
|
||||||
debug!("write success");
|
_drtio_context,
|
||||||
write_i8(stream, Reply::Success as i8).await?;
|
_destination,
|
||||||
} else {
|
config_write,
|
||||||
// this is an error because we do not expect write to fail
|
&cfg,
|
||||||
error!("failed to write: {:?}", value);
|
&key,
|
||||||
write_i8(stream, Reply::Error as i8).await?;
|
buffer,
|
||||||
}
|
&restart_idle
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Request::ConfigRemove => {
|
Request::ConfigRemove => {
|
||||||
let key = read_key(stream).await?;
|
let key = read_key(stream).await?;
|
||||||
debug!("erase key: {}", key);
|
process!(
|
||||||
let value = cfg.remove(&key);
|
stream,
|
||||||
if value.is_ok() {
|
_drtio_context,
|
||||||
debug!("erase success");
|
_destination,
|
||||||
write_i8(stream, Reply::Success as i8).await?;
|
config_remove,
|
||||||
} else {
|
&cfg,
|
||||||
warn!("erase failed");
|
&key,
|
||||||
write_i8(stream, Reply::Error as i8).await?;
|
&restart_idle
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
Request::Reboot => {
|
Request::Reboot => {
|
||||||
info!("rebooting");
|
process!(stream, _drtio_context, _destination, reboot)
|
||||||
write_i8(stream, Reply::RebootImminent as i8).await?;
|
|
||||||
stream.flush().await?;
|
|
||||||
slcr::reboot();
|
|
||||||
}
|
}
|
||||||
|
Request::ConfigErase => {
|
||||||
|
process!(stream, _drtio_context, _destination, config_erase)
|
||||||
}
|
}
|
||||||
|
Request::DebugAllocator => {
|
||||||
|
process!(stream, _drtio_context, _destination, debug_allocator)
|
||||||
|
}
|
||||||
|
Request::Flash => {
|
||||||
|
let len = read_i32(stream).await?;
|
||||||
|
if len <= 0 {
|
||||||
|
write_i8(stream, Reply::Error as i8).await?;
|
||||||
|
return Err(Error::UnexpectedPattern);
|
||||||
|
}
|
||||||
|
let mut buffer = Vec::with_capacity(len as usize);
|
||||||
|
unsafe {
|
||||||
|
buffer.set_len(len as usize);
|
||||||
|
}
|
||||||
|
read_chunk(stream, &mut buffer).await?;
|
||||||
|
process!(stream, _drtio_context, _destination, image_write, &cfg, buffer)
|
||||||
|
}
|
||||||
|
}?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(cfg: Config) {
|
pub fn start(cfg: Rc<Config>, restart_idle: Rc<Semaphore>, drtio_context: Option<DrtioContext>) {
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
let pull_id = Rc::new(RefCell::new(0u32));
|
let pull_id = Rc::new(RefCell::new(0u32));
|
||||||
let cfg = Rc::new(cfg);
|
|
||||||
loop {
|
loop {
|
||||||
let mut stream = TcpStream::accept(1380, 2048, 2048).await.unwrap();
|
let mut stream = TcpStream::accept(1380, 2048, 2048).await.unwrap();
|
||||||
let pull_id = pull_id.clone();
|
let pull_id = pull_id.clone();
|
||||||
let cfg = cfg.clone();
|
let cfg = cfg.clone();
|
||||||
|
let restart_idle = restart_idle.clone();
|
||||||
|
let drtio_context = drtio_context.clone();
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
info!("received connection");
|
info!("received connection");
|
||||||
let _ = handle_connection(&mut stream, pull_id, cfg)
|
let _ = handle_connection(&mut stream, pull_id, cfg, restart_idle, drtio_context)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| warn!("connection terminated: {:?}", e));
|
.map_err(|e| warn!("connection terminated: {:?}", e));
|
||||||
let _ = stream.flush().await;
|
let _ = stream.flush().await;
|
||||||
|
|
|
@ -58,10 +58,11 @@ mod remote_moninj {
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::rtio_mgt::drtio;
|
use crate::rtio_mgt::{drtio, drtio::Error as DrtioError};
|
||||||
|
|
||||||
pub async fn read_probe(
|
pub async fn read_probe(
|
||||||
aux_mutex: &Rc<Mutex<bool>>,
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
timer: GlobalTimer,
|
timer: GlobalTimer,
|
||||||
linkno: u8,
|
linkno: u8,
|
||||||
destination: u8,
|
destination: u8,
|
||||||
|
@ -71,6 +72,7 @@ mod remote_moninj {
|
||||||
let reply = drtio::aux_transact(
|
let reply = drtio::aux_transact(
|
||||||
aux_mutex,
|
aux_mutex,
|
||||||
linkno,
|
linkno,
|
||||||
|
routing_table,
|
||||||
&drtioaux_async::Packet::MonitorRequest {
|
&drtioaux_async::Packet::MonitorRequest {
|
||||||
destination: destination,
|
destination: destination,
|
||||||
channel: channel as _,
|
channel: channel as _,
|
||||||
|
@ -82,8 +84,8 @@ mod remote_moninj {
|
||||||
match reply {
|
match reply {
|
||||||
Ok(drtioaux_async::Packet::MonitorReply { value }) => return value as i64,
|
Ok(drtioaux_async::Packet::MonitorReply { value }) => return value as i64,
|
||||||
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||||
Err("link went down") => {
|
Err(DrtioError::LinkDown) => {
|
||||||
debug!("link is down");
|
warn!("link is down");
|
||||||
}
|
}
|
||||||
Err(e) => error!("aux packet error ({})", e),
|
Err(e) => error!("aux packet error ({})", e),
|
||||||
}
|
}
|
||||||
|
@ -92,6 +94,7 @@ mod remote_moninj {
|
||||||
|
|
||||||
pub async fn inject(
|
pub async fn inject(
|
||||||
aux_mutex: &Rc<Mutex<bool>>,
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
_routing_table: &drtio_routing::RoutingTable,
|
||||||
_timer: GlobalTimer,
|
_timer: GlobalTimer,
|
||||||
linkno: u8,
|
linkno: u8,
|
||||||
destination: u8,
|
destination: u8,
|
||||||
|
@ -99,7 +102,7 @@ mod remote_moninj {
|
||||||
overrd: i8,
|
overrd: i8,
|
||||||
value: i8,
|
value: i8,
|
||||||
) {
|
) {
|
||||||
let _lock = aux_mutex.lock();
|
let _lock = aux_mutex.async_lock().await;
|
||||||
drtioaux_async::send(
|
drtioaux_async::send(
|
||||||
linkno,
|
linkno,
|
||||||
&drtioaux_async::Packet::InjectionRequest {
|
&drtioaux_async::Packet::InjectionRequest {
|
||||||
|
@ -115,6 +118,7 @@ mod remote_moninj {
|
||||||
|
|
||||||
pub async fn read_injection_status(
|
pub async fn read_injection_status(
|
||||||
aux_mutex: &Rc<Mutex<bool>>,
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
timer: GlobalTimer,
|
timer: GlobalTimer,
|
||||||
linkno: u8,
|
linkno: u8,
|
||||||
destination: u8,
|
destination: u8,
|
||||||
|
@ -124,6 +128,7 @@ mod remote_moninj {
|
||||||
let reply = drtio::aux_transact(
|
let reply = drtio::aux_transact(
|
||||||
aux_mutex,
|
aux_mutex,
|
||||||
linkno,
|
linkno,
|
||||||
|
routing_table,
|
||||||
&drtioaux_async::Packet::InjectionStatusRequest {
|
&drtioaux_async::Packet::InjectionStatusRequest {
|
||||||
destination: destination,
|
destination: destination,
|
||||||
channel: channel as _,
|
channel: channel as _,
|
||||||
|
@ -135,8 +140,8 @@ mod remote_moninj {
|
||||||
match reply {
|
match reply {
|
||||||
Ok(drtioaux_async::Packet::InjectionStatusReply { value }) => return value as i8,
|
Ok(drtioaux_async::Packet::InjectionStatusReply { value }) => return value as i8,
|
||||||
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||||
Err("link went down") => {
|
Err(DrtioError::LinkDown) => {
|
||||||
debug!("link is down");
|
warn!("link is down");
|
||||||
}
|
}
|
||||||
Err(e) => error!("aux packet error ({})", e),
|
Err(e) => error!("aux packet error ({})", e),
|
||||||
}
|
}
|
||||||
|
@ -183,7 +188,7 @@ macro_rules! dispatch {
|
||||||
local_moninj::$func(channel.into(), $($param, )*)
|
local_moninj::$func(channel.into(), $($param, )*)
|
||||||
} else {
|
} else {
|
||||||
let linkno = hop - 1 as u8;
|
let linkno = hop - 1 as u8;
|
||||||
remote_moninj::$func($aux_mutex, $timer, linkno, destination, channel, $($param, )*).await
|
remote_moninj::$func($aux_mutex, $routing_table, $timer, linkno, destination, channel, $($param, )*).await
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use core::future::Future;
|
||||||
|
|
||||||
|
use async_recursion::async_recursion;
|
||||||
|
use byteorder::{ByteOrder, NativeEndian};
|
||||||
|
use cslice::CMutSlice;
|
||||||
|
use ksupport::rpc::{tag::{Tag, TagIterator},
|
||||||
|
*};
|
||||||
|
use libasync::smoltcp::TcpStream;
|
||||||
|
use libboard_zynq::smoltcp;
|
||||||
|
use log::trace;
|
||||||
|
|
||||||
|
use crate::proto_async;
|
||||||
|
|
||||||
|
/// Reads (deserializes) `length` array or list elements of type `tag` from `stream`,
|
||||||
|
/// writing them into the buffer given by `storage`.
|
||||||
|
///
|
||||||
|
/// `alloc` is used for nested allocations (if elements themselves contain
|
||||||
|
/// lists/arrays), see [recv_value].
|
||||||
|
#[async_recursion(?Send)]
|
||||||
|
async unsafe fn recv_elements<F>(
|
||||||
|
stream: &TcpStream,
|
||||||
|
elt_tag: Tag<'async_recursion>,
|
||||||
|
length: usize,
|
||||||
|
storage: *mut (),
|
||||||
|
alloc: &(impl Fn(usize) -> F + 'async_recursion),
|
||||||
|
) -> Result<(), smoltcp::Error>
|
||||||
|
where
|
||||||
|
F: Future<Output = *mut ()>,
|
||||||
|
{
|
||||||
|
// List of simple types are special-cased in the protocol for performance.
|
||||||
|
match elt_tag {
|
||||||
|
Tag::Bool => {
|
||||||
|
let dest = core::slice::from_raw_parts_mut(storage as *mut u8, length);
|
||||||
|
proto_async::read_chunk(stream, dest).await?;
|
||||||
|
}
|
||||||
|
Tag::Int32 => {
|
||||||
|
let ptr = storage as *mut u32;
|
||||||
|
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
|
||||||
|
proto_async::read_chunk(stream, dest).await?;
|
||||||
|
drop(dest);
|
||||||
|
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
||||||
|
NativeEndian::from_slice_u32(dest);
|
||||||
|
}
|
||||||
|
Tag::Int64 | Tag::Float64 => {
|
||||||
|
let ptr = storage as *mut u64;
|
||||||
|
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
|
||||||
|
proto_async::read_chunk(stream, dest).await?;
|
||||||
|
drop(dest);
|
||||||
|
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
||||||
|
NativeEndian::from_slice_u64(dest);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mut data = storage;
|
||||||
|
for _ in 0..length {
|
||||||
|
recv_value(stream, elt_tag, &mut data, alloc).await?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads (deserializes) a value of type `tag` from `stream`, writing the results to
|
||||||
|
/// the kernel-side buffer `data` (the passed pointer to which is incremented to point
|
||||||
|
/// past the just-received data). For nested allocations (lists/arrays), `alloc` is
|
||||||
|
/// invoked any number of times with the size of the required allocation as a parameter
|
||||||
|
/// (which is assumed to be correctly aligned for all payload types).
|
||||||
|
#[async_recursion(?Send)]
|
||||||
|
async unsafe fn recv_value<F>(
|
||||||
|
stream: &TcpStream,
|
||||||
|
tag: Tag<'async_recursion>,
|
||||||
|
data: &mut *mut (),
|
||||||
|
alloc: &(impl Fn(usize) -> F + 'async_recursion),
|
||||||
|
) -> Result<(), smoltcp::Error>
|
||||||
|
where
|
||||||
|
F: Future<Output = *mut ()>,
|
||||||
|
{
|
||||||
|
macro_rules! consume_value {
|
||||||
|
($ty:ty, | $ptr:ident | $map:expr) => {{
|
||||||
|
let $ptr = align_ptr_mut::<$ty>(*data);
|
||||||
|
*data = $ptr.offset(1) as *mut ();
|
||||||
|
$map
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
match tag {
|
||||||
|
Tag::None => Ok(()),
|
||||||
|
Tag::Bool => consume_value!(i8, |ptr| {
|
||||||
|
*ptr = proto_async::read_i8(stream).await?;
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
Tag::Int32 => consume_value!(i32, |ptr| {
|
||||||
|
*ptr = proto_async::read_i32(stream).await?;
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
Tag::Int64 | Tag::Float64 => consume_value!(i64, |ptr| {
|
||||||
|
*ptr = proto_async::read_i64(stream).await?;
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
Tag::String | Tag::Bytes | Tag::ByteArray => {
|
||||||
|
consume_value!(CMutSlice<u8>, |ptr| {
|
||||||
|
let length = proto_async::read_i32(stream).await? as usize;
|
||||||
|
*ptr = CMutSlice::new(alloc(length).await as *mut u8, length);
|
||||||
|
proto_async::read_chunk(stream, (*ptr).as_mut()).await?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Tag::Tuple(it, arity) => {
|
||||||
|
let alignment = tag.alignment();
|
||||||
|
*data = round_up_mut(*data, alignment);
|
||||||
|
let mut it = it.clone();
|
||||||
|
for _ in 0..arity {
|
||||||
|
let tag = it.next().expect("truncated tag");
|
||||||
|
recv_value(stream, tag, data, alloc).await?
|
||||||
|
}
|
||||||
|
// Take into account any tail padding (if element(s) with largest alignment
|
||||||
|
// are not at the end).
|
||||||
|
*data = round_up_mut(*data, alignment);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Tag::List(it) => {
|
||||||
|
#[repr(C)]
|
||||||
|
struct List {
|
||||||
|
elements: *mut (),
|
||||||
|
length: usize,
|
||||||
|
}
|
||||||
|
consume_value!(*mut List, |ptr_to_list| {
|
||||||
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
|
let length = proto_async::read_i32(stream).await? as usize;
|
||||||
|
|
||||||
|
// To avoid multiple kernel CPU roundtrips, use a single allocation for
|
||||||
|
// both the pointer/length List (slice) and the backing storage for the
|
||||||
|
// elements. We can assume that alloc() is aligned suitably, so just
|
||||||
|
// need to take into account any extra padding required.
|
||||||
|
// (Note: At the time of writing, there will never actually be any types
|
||||||
|
// with alignment larger than 8 bytes, so storage_offset == 0 always.)
|
||||||
|
let list_size = 4 + 4;
|
||||||
|
let storage_offset = round_up(list_size, tag.alignment());
|
||||||
|
let storage_size = tag.size() * length;
|
||||||
|
|
||||||
|
let allocation = alloc(storage_offset + storage_size).await as *mut u8;
|
||||||
|
*ptr_to_list = allocation as *mut List;
|
||||||
|
let storage = allocation.offset(storage_offset as isize) as *mut ();
|
||||||
|
|
||||||
|
(**ptr_to_list).length = length;
|
||||||
|
(**ptr_to_list).elements = storage;
|
||||||
|
recv_elements(stream, tag, length, storage, alloc).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Tag::Array(it, num_dims) => {
|
||||||
|
consume_value!(*mut (), |buffer| {
|
||||||
|
// Deserialize length along each dimension and compute total number of
|
||||||
|
// elements.
|
||||||
|
let mut total_len: usize = 1;
|
||||||
|
for _ in 0..num_dims {
|
||||||
|
let len = proto_async::read_i32(stream).await? as usize;
|
||||||
|
total_len *= len;
|
||||||
|
consume_value!(usize, |ptr| *ptr = len)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate backing storage for elements; deserialize them.
|
||||||
|
let elt_tag = it.clone().next().expect("truncated tag");
|
||||||
|
*buffer = alloc(elt_tag.size() * total_len).await;
|
||||||
|
recv_elements(stream, elt_tag, total_len, *buffer, alloc).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Tag::Range(it) => {
|
||||||
|
*data = round_up_mut(*data, tag.alignment());
|
||||||
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
|
recv_value(stream, tag, data, alloc).await?;
|
||||||
|
recv_value(stream, tag, data, alloc).await?;
|
||||||
|
recv_value(stream, tag, data, alloc).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Tag::Keyword(_) => unreachable!(),
|
||||||
|
Tag::Object => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recv_return<F>(
|
||||||
|
stream: &TcpStream,
|
||||||
|
tag_bytes: &[u8],
|
||||||
|
data: *mut (),
|
||||||
|
alloc: &impl Fn(usize) -> F,
|
||||||
|
) -> Result<(), smoltcp::Error>
|
||||||
|
where
|
||||||
|
F: Future<Output = *mut ()>,
|
||||||
|
{
|
||||||
|
let mut it = TagIterator::new(tag_bytes);
|
||||||
|
trace!("recv ...->{}", it);
|
||||||
|
|
||||||
|
let tag = it.next().expect("truncated tag");
|
||||||
|
let mut data = data;
|
||||||
|
unsafe { recv_value(stream, tag, &mut data, alloc).await? };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,15 +1,20 @@
|
||||||
|
#[cfg(not(feature = "target_ebaz4205"))]
|
||||||
use embedded_hal::blocking::delay::DelayMs;
|
use embedded_hal::blocking::delay::DelayMs;
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
use ksupport::i2c;
|
||||||
|
#[cfg(not(feature = "target_ebaz4205"))]
|
||||||
use libboard_artiq::pl;
|
use libboard_artiq::pl;
|
||||||
#[cfg(has_si5324)]
|
#[cfg(has_si5324)]
|
||||||
use libboard_artiq::si5324;
|
use libboard_artiq::si5324;
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
use libboard_artiq::si549;
|
||||||
#[cfg(has_si5324)]
|
#[cfg(has_si5324)]
|
||||||
use libboard_zynq::i2c::I2c;
|
use libboard_zynq::i2c::I2c;
|
||||||
use libboard_zynq::timer::GlobalTimer;
|
use libboard_zynq::timer::GlobalTimer;
|
||||||
use libconfig::Config;
|
use libconfig::Config;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
#[cfg(feature = "target_ebaz4205")]
|
||||||
#[cfg(has_si5324)]
|
use {libboard_zynq::slcr, libregister::RegisterRW};
|
||||||
use crate::i2c;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
@ -68,7 +73,7 @@ fn get_rtio_clock_cfg(cfg: &Config) -> RtioClock {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(has_drtio))]
|
#[cfg(not(any(has_drtio, feature = "target_ebaz4205")))]
|
||||||
fn init_rtio(timer: &mut GlobalTimer) {
|
fn init_rtio(timer: &mut GlobalTimer) {
|
||||||
info!("Switching SYS clocks...");
|
info!("Switching SYS clocks...");
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -105,6 +110,9 @@ fn init_drtio(timer: &mut GlobalTimer) {
|
||||||
unsafe {
|
unsafe {
|
||||||
pl::csr::rtio_core::reset_phy_write(1);
|
pl::csr::rtio_core::reset_phy_write(1);
|
||||||
pl::csr::gt_drtio::txenable_write(0xffffffffu32 as _);
|
pl::csr::gt_drtio::txenable_write(0xffffffffu32 as _);
|
||||||
|
|
||||||
|
#[cfg(has_drtio_eem)]
|
||||||
|
pl::csr::eem_transceiver::txenable_write(0xffffffffu32 as _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +266,182 @@ fn setup_si5324(i2c: &mut I2c, timer: &mut GlobalTimer, clk: RtioClock) {
|
||||||
si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324");
|
si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(has_si549, has_wrpll))]
|
||||||
|
fn wrpll_setup(timer: &mut GlobalTimer, clk: RtioClock, si549_settings: &si549::FrequencySetting) {
|
||||||
|
// register values are directly copied from preconfigured mmcm
|
||||||
|
let (mmcm_setting, mmcm_bypass) = match clk {
|
||||||
|
RtioClock::Ext0_Synth0_10to125 => (
|
||||||
|
si549::wrpll_refclk::MmcmSetting {
|
||||||
|
// CLKFBOUT_MULT = 62.5, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 5
|
||||||
|
clkout0_reg1: 0x1083,
|
||||||
|
clkout0_reg2: 0x0080,
|
||||||
|
clkfbout_reg1: 0x179e,
|
||||||
|
clkfbout_reg2: 0x4c00,
|
||||||
|
div_reg: 0x1041,
|
||||||
|
lock_reg1: 0x00fa,
|
||||||
|
lock_reg2: 0x7c01,
|
||||||
|
lock_reg3: 0xffe9,
|
||||||
|
power_reg: 0x9900,
|
||||||
|
filt_reg1: 0x1008,
|
||||||
|
filt_reg2: 0x8800,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
RtioClock::Ext0_Synth0_80to125 => (
|
||||||
|
si549::wrpll_refclk::MmcmSetting {
|
||||||
|
// CLKFBOUT_MULT = 15.625, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 10
|
||||||
|
clkout0_reg1: 0x1145,
|
||||||
|
clkout0_reg2: 0x0000,
|
||||||
|
clkfbout_reg1: 0x11c7,
|
||||||
|
clkfbout_reg2: 0x5880,
|
||||||
|
div_reg: 0x1041,
|
||||||
|
lock_reg1: 0x028a,
|
||||||
|
lock_reg2: 0x7c01,
|
||||||
|
lock_reg3: 0xffe9,
|
||||||
|
power_reg: 0x9900,
|
||||||
|
filt_reg1: 0x9908,
|
||||||
|
filt_reg2: 0x8100,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
RtioClock::Ext0_Synth0_100to125 => (
|
||||||
|
si549::wrpll_refclk::MmcmSetting {
|
||||||
|
// CLKFBOUT_MULT = 12.5, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 10
|
||||||
|
clkout0_reg1: 0x1145,
|
||||||
|
clkout0_reg2: 0x0000,
|
||||||
|
clkfbout_reg1: 0x1145,
|
||||||
|
clkfbout_reg2: 0x4c00,
|
||||||
|
div_reg: 0x1041,
|
||||||
|
lock_reg1: 0x0339,
|
||||||
|
lock_reg2: 0x7c01,
|
||||||
|
lock_reg3: 0xffe9,
|
||||||
|
power_reg: 0x9900,
|
||||||
|
filt_reg1: 0x9108,
|
||||||
|
filt_reg2: 0x0100,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
RtioClock::Ext0_Synth0_125to125 => (
|
||||||
|
si549::wrpll_refclk::MmcmSetting {
|
||||||
|
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 10
|
||||||
|
clkout0_reg1: 0x1145,
|
||||||
|
clkout0_reg2: 0x0000,
|
||||||
|
clkfbout_reg1: 0x1145,
|
||||||
|
clkfbout_reg2: 0x0000,
|
||||||
|
div_reg: 0x1041,
|
||||||
|
lock_reg1: 0x03e8,
|
||||||
|
lock_reg2: 0x7001,
|
||||||
|
lock_reg3: 0xf3e9,
|
||||||
|
power_reg: 0x0100,
|
||||||
|
filt_reg1: 0x9908,
|
||||||
|
filt_reg2: 0x1100,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
si549::helper_setup(timer, &si549_settings).expect("cannot initialize helper Si549");
|
||||||
|
si549::wrpll_refclk::setup(timer, mmcm_setting, mmcm_bypass).expect("cannot initialize ref clk for wrpll");
|
||||||
|
si549::wrpll::select_recovered_clock(true, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
fn get_si549_setting(clk: RtioClock) -> si549::FrequencySetting {
|
||||||
|
match clk {
|
||||||
|
RtioClock::Ext0_Synth0_10to125 => {
|
||||||
|
info!("using 10MHz reference to make 125MHz RTIO clock with WRPLL");
|
||||||
|
}
|
||||||
|
RtioClock::Ext0_Synth0_80to125 => {
|
||||||
|
info!("using 80MHz reference to make 125MHz RTIO clock with WRPLL");
|
||||||
|
}
|
||||||
|
RtioClock::Ext0_Synth0_100to125 => {
|
||||||
|
info!("using 100MHz reference to make 125MHz RTIO clock with WRPLL");
|
||||||
|
}
|
||||||
|
RtioClock::Ext0_Synth0_125to125 => {
|
||||||
|
info!("using 125MHz reference to make 125MHz RTIO clock with WRPLL");
|
||||||
|
}
|
||||||
|
RtioClock::Int_100 => {
|
||||||
|
info!("using internal 100MHz RTIO clock");
|
||||||
|
}
|
||||||
|
RtioClock::Int_125 => {
|
||||||
|
info!("using internal 125MHz RTIO clock");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!(
|
||||||
|
"rtio_clock setting '{:?}' is unsupported. Falling back to default internal 125MHz RTIO clock.",
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match clk {
|
||||||
|
RtioClock::Int_100 => {
|
||||||
|
si549::FrequencySetting {
|
||||||
|
main: si549::DividerConfig {
|
||||||
|
hsdiv: 0x06C,
|
||||||
|
lsdiv: 0,
|
||||||
|
fbdiv: 0x046C5F49797,
|
||||||
|
},
|
||||||
|
helper: si549::DividerConfig {
|
||||||
|
// 100MHz*32767/32768
|
||||||
|
hsdiv: 0x06C,
|
||||||
|
lsdiv: 0,
|
||||||
|
fbdiv: 0x046C5670BBD,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Everything else use 125MHz
|
||||||
|
si549::FrequencySetting {
|
||||||
|
main: si549::DividerConfig {
|
||||||
|
hsdiv: 0x058,
|
||||||
|
lsdiv: 0,
|
||||||
|
fbdiv: 0x04815791F25,
|
||||||
|
},
|
||||||
|
helper: si549::DividerConfig {
|
||||||
|
// 125MHz*32767/32768
|
||||||
|
hsdiv: 0x058,
|
||||||
|
lsdiv: 0,
|
||||||
|
fbdiv: 0x04814E8F442,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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) {
|
pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
||||||
let clk = get_rtio_clock_cfg(cfg);
|
let clk = get_rtio_clock_cfg(cfg);
|
||||||
#[cfg(has_si5324)]
|
#[cfg(has_si5324)]
|
||||||
|
@ -272,9 +456,39 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
let si549_settings = get_si549_setting(clk);
|
||||||
|
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
si549::main_setup(timer, &si549_settings).expect("cannot initialize main Si549");
|
||||||
|
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
init_drtio(timer);
|
init_drtio(timer);
|
||||||
|
|
||||||
#[cfg(not(has_drtio))]
|
#[cfg(not(any(has_drtio, feature = "target_ebaz4205")))]
|
||||||
init_rtio(timer);
|
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
|
||||||
|
match clk {
|
||||||
|
RtioClock::Ext0_Synth0_10to125
|
||||||
|
| RtioClock::Ext0_Synth0_80to125
|
||||||
|
| RtioClock::Ext0_Synth0_100to125
|
||||||
|
| RtioClock::Ext0_Synth0_125to125 => {
|
||||||
|
wrpll_setup(timer, clk, &si549_settings);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,13 @@ use alloc::{collections::BTreeMap, rc::Rc, string::String, vec::Vec};
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
|
use ksupport::kernel::DmaRecorder;
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
use libasync::task;
|
use libasync::task;
|
||||||
use libboard_artiq::drtio_routing::RoutingTable;
|
use libboard_artiq::drtio_routing::RoutingTable;
|
||||||
use libboard_zynq::timer::GlobalTimer;
|
use libboard_zynq::timer::GlobalTimer;
|
||||||
use libcortex_a9::{cache::dcci_slice, mutex::Mutex};
|
use libcortex_a9::{cache::dcci_slice, mutex::Mutex};
|
||||||
|
|
||||||
use crate::kernel::DmaRecorder;
|
|
||||||
|
|
||||||
const ALIGNMENT: usize = 16 * 8;
|
const ALIGNMENT: usize = 16 * 8;
|
||||||
|
|
||||||
static DMA_RECORD_STORE: Mutex<BTreeMap<String, (u32, Vec<u8>, i64)>> = Mutex::new(BTreeMap::new());
|
static DMA_RECORD_STORE: Mutex<BTreeMap<String, (u32, Vec<u8>, i64)>> = Mutex::new(BTreeMap::new());
|
||||||
|
@ -143,9 +142,9 @@ pub mod remote_dma {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn playback_done(&mut self, destination: u8, error: u8, channel: u32, timestamp: u64) {
|
pub async fn playback_done(&mut self, source: u8, error: u8, channel: u32, timestamp: u64) {
|
||||||
let mut traces_locked = self.traces.async_lock().await;
|
let mut traces_locked = self.traces.async_lock().await;
|
||||||
let mut trace = traces_locked.get_mut(&destination).unwrap();
|
let mut trace = traces_locked.get_mut(&source).unwrap();
|
||||||
trace.state = RemoteState::PlaybackEnded {
|
trace.state = RemoteState::PlaybackEnded {
|
||||||
error: error,
|
error: error,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,335 @@
|
||||||
|
use alloc::{collections::BTreeMap, rc::Rc, vec::Vec};
|
||||||
|
|
||||||
|
use libasync::task;
|
||||||
|
use libboard_artiq::{drtio_routing::RoutingTable,
|
||||||
|
drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE}};
|
||||||
|
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
|
||||||
|
use libcortex_a9::mutex::Mutex;
|
||||||
|
use log::{error, warn};
|
||||||
|
|
||||||
|
use crate::rtio_mgt::{drtio, drtio::Error as DrtioError};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
pub enum FinishStatus {
|
||||||
|
Ok,
|
||||||
|
CommLost,
|
||||||
|
Exception(u8), // exception source
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
pub enum SubkernelState {
|
||||||
|
NotLoaded,
|
||||||
|
Uploaded,
|
||||||
|
Running,
|
||||||
|
Finished { status: FinishStatus },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum Error {
|
||||||
|
Timeout,
|
||||||
|
IncorrectState,
|
||||||
|
SubkernelNotFound,
|
||||||
|
SubkernelException,
|
||||||
|
CommLost,
|
||||||
|
DrtioError(DrtioError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DrtioError> for Error {
|
||||||
|
fn from(value: DrtioError) -> Error {
|
||||||
|
Error::DrtioError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SubkernelFinished {
|
||||||
|
pub id: u32,
|
||||||
|
pub status: FinishStatus,
|
||||||
|
pub exception: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Subkernel {
|
||||||
|
pub destination: u8,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
pub state: SubkernelState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Subkernel {
|
||||||
|
pub fn new(destination: u8, data: Vec<u8>) -> Self {
|
||||||
|
Subkernel {
|
||||||
|
destination: destination,
|
||||||
|
data: data,
|
||||||
|
state: SubkernelState::NotLoaded,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SUBKERNELS: Mutex<BTreeMap<u32, Subkernel>> = Mutex::new(BTreeMap::new());
|
||||||
|
|
||||||
|
pub async fn add_subkernel(id: u32, destination: u8, kernel: Vec<u8>) {
|
||||||
|
SUBKERNELS
|
||||||
|
.async_lock()
|
||||||
|
.await
|
||||||
|
.insert(id, Subkernel::new(destination, kernel));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn upload(
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
id: u32,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if let Some(subkernel) = SUBKERNELS.async_lock().await.get_mut(&id) {
|
||||||
|
drtio::subkernel_upload(
|
||||||
|
aux_mutex,
|
||||||
|
routing_table,
|
||||||
|
timer,
|
||||||
|
id,
|
||||||
|
subkernel.destination,
|
||||||
|
&subkernel.data,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
subkernel.state = SubkernelState::Uploaded;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::SubkernelNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load(
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
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?;
|
||||||
|
if run {
|
||||||
|
subkernel.state = SubkernelState::Running;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::SubkernelNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn clear_subkernels() {
|
||||||
|
SUBKERNELS.async_lock().await.clear();
|
||||||
|
MESSAGE_QUEUE.async_lock().await.clear();
|
||||||
|
CURRENT_MESSAGES.async_lock().await.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subkernel_finished(id: u32, with_exception: bool, exception_src: u8) {
|
||||||
|
// called upon receiving DRTIO SubkernelRunDone
|
||||||
|
// may be None if session ends and is cleared
|
||||||
|
if let Some(subkernel) = SUBKERNELS.async_lock().await.get_mut(&id) {
|
||||||
|
if subkernel.state == SubkernelState::Running {
|
||||||
|
subkernel.state = SubkernelState::Finished {
|
||||||
|
status: match with_exception {
|
||||||
|
true => FinishStatus::Exception(exception_src),
|
||||||
|
false => FinishStatus::Ok,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn destination_changed(
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
destination: u8,
|
||||||
|
up: bool,
|
||||||
|
) {
|
||||||
|
let mut locked_subkernels = SUBKERNELS.async_lock().await;
|
||||||
|
for (id, subkernel) in locked_subkernels.iter_mut() {
|
||||||
|
if subkernel.destination == destination {
|
||||||
|
if up {
|
||||||
|
match drtio::subkernel_upload(aux_mutex, routing_table, timer, *id, destination, &subkernel.data).await
|
||||||
|
{
|
||||||
|
Ok(_) => subkernel.state = SubkernelState::Uploaded,
|
||||||
|
Err(e) => error!("Error adding subkernel on destination {}: {}", destination, e),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subkernel.state = match subkernel.state {
|
||||||
|
SubkernelState::Running => SubkernelState::Finished {
|
||||||
|
status: FinishStatus::CommLost,
|
||||||
|
},
|
||||||
|
_ => SubkernelState::NotLoaded,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn await_finish(
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
id: u32,
|
||||||
|
timeout: i64,
|
||||||
|
) -> Result<SubkernelFinished, Error> {
|
||||||
|
match SUBKERNELS.async_lock().await.get(&id).unwrap().state {
|
||||||
|
SubkernelState::Running | SubkernelState::Finished { .. } => (),
|
||||||
|
_ => return Err(Error::IncorrectState),
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
let max_time = timer.get_time() + Milliseconds(timeout as u64);
|
||||||
|
while timer.get_time() < max_time {
|
||||||
|
match SUBKERNELS.async_lock().await.get(&id).unwrap().state {
|
||||||
|
SubkernelState::Finished { .. } => break,
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
task::r#yield().await;
|
||||||
|
}
|
||||||
|
if timer.get_time() >= max_time {
|
||||||
|
error!("Remote subkernel finish await timed out");
|
||||||
|
return Err(Error::Timeout);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no timeout, wait forever
|
||||||
|
loop {
|
||||||
|
match SUBKERNELS.async_lock().await.get(&id).unwrap().state {
|
||||||
|
SubkernelState::Finished { .. } => break,
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
task::r#yield().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(subkernel) = SUBKERNELS.async_lock().await.get_mut(&id) {
|
||||||
|
match subkernel.state {
|
||||||
|
SubkernelState::Finished { status } => {
|
||||||
|
subkernel.state = SubkernelState::Uploaded;
|
||||||
|
Ok(SubkernelFinished {
|
||||||
|
id: id,
|
||||||
|
status: status,
|
||||||
|
exception: if let FinishStatus::Exception(dest) = status {
|
||||||
|
Some(drtio::subkernel_retrieve_exception(aux_mutex, routing_table, timer, dest).await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(Error::IncorrectState),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::SubkernelNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Message {
|
||||||
|
from_id: u32,
|
||||||
|
pub count: u8,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIFO queue of messages
|
||||||
|
static MESSAGE_QUEUE: Mutex<Vec<Message>> = Mutex::new(Vec::new());
|
||||||
|
// currently under construction message(s) (can be from multiple sources)
|
||||||
|
static CURRENT_MESSAGES: Mutex<BTreeMap<u32, Message>> = Mutex::new(BTreeMap::new());
|
||||||
|
|
||||||
|
pub async fn message_handle_incoming(
|
||||||
|
id: u32,
|
||||||
|
status: PayloadStatus,
|
||||||
|
length: usize,
|
||||||
|
data: &[u8; MASTER_PAYLOAD_MAX_SIZE],
|
||||||
|
) {
|
||||||
|
// called when receiving a message from satellite
|
||||||
|
{
|
||||||
|
let subkernel_lock = SUBKERNELS.async_lock().await;
|
||||||
|
let subkernel = subkernel_lock.get(&id);
|
||||||
|
if subkernel.is_some() && subkernel.unwrap().state != SubkernelState::Running {
|
||||||
|
// do not add messages for non-running or deleted subkernels
|
||||||
|
warn!("received a message for a non-running subkernel #{}", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut current_messages = CURRENT_MESSAGES.async_lock().await;
|
||||||
|
|
||||||
|
if status.is_first() {
|
||||||
|
current_messages.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
match current_messages.get_mut(&id) {
|
||||||
|
Some(message) => message.data.extend(&data[..length]),
|
||||||
|
None => {
|
||||||
|
current_messages.insert(
|
||||||
|
id,
|
||||||
|
Message {
|
||||||
|
from_id: id,
|
||||||
|
count: data[0],
|
||||||
|
data: data[1..length].to_vec(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if status.is_last() {
|
||||||
|
// when done, remove from working queue
|
||||||
|
MESSAGE_QUEUE
|
||||||
|
.async_lock()
|
||||||
|
.await
|
||||||
|
.push(current_messages.remove(&id).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn message_await(id: u32, timeout: i64, timer: GlobalTimer) -> Result<Message, Error> {
|
||||||
|
let is_subkernel = SUBKERNELS.async_lock().await.get(&id).is_some();
|
||||||
|
if is_subkernel {
|
||||||
|
match SUBKERNELS.async_lock().await.get(&id).unwrap().state {
|
||||||
|
SubkernelState::Finished {
|
||||||
|
status: FinishStatus::CommLost,
|
||||||
|
} => return Err(Error::CommLost),
|
||||||
|
SubkernelState::Running | SubkernelState::Finished { .. } => (),
|
||||||
|
_ => return Err(Error::IncorrectState),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let max_time = timer.get_time() + Milliseconds(timeout as u64);
|
||||||
|
while timeout < 0 || (timeout > 0 && timer.get_time() < max_time) {
|
||||||
|
{
|
||||||
|
let mut message_queue = MESSAGE_QUEUE.async_lock().await;
|
||||||
|
for i in 0..message_queue.len() {
|
||||||
|
let msg = &message_queue[i];
|
||||||
|
if msg.from_id == id {
|
||||||
|
let message = message_queue.remove(i);
|
||||||
|
return Ok(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_subkernel {
|
||||||
|
match SUBKERNELS.async_lock().await.get(&id).unwrap().state {
|
||||||
|
SubkernelState::Finished {
|
||||||
|
status: FinishStatus::CommLost,
|
||||||
|
} => return Err(Error::CommLost),
|
||||||
|
SubkernelState::Finished {
|
||||||
|
status: FinishStatus::Exception(_),
|
||||||
|
} => return Err(Error::SubkernelException),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
task::r#yield().await;
|
||||||
|
}
|
||||||
|
Err(Error::Timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn message_send<'a>(
|
||||||
|
aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
timer: GlobalTimer,
|
||||||
|
id: u32,
|
||||||
|
destination: u8,
|
||||||
|
message: Vec<u8>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Ok(drtio::subkernel_send_message(aux_mutex, routing_table, timer, id, destination, &message).await?)
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
[package]
|
|
||||||
authors = ["M-Labs"]
|
|
||||||
name = "satman"
|
|
||||||
version = "0.0.0"
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[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"]
|
|
||||||
default = ["target_zc706", ]
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build_zynq = { path = "../libbuild_zynq" }
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
log = { version = "0.4", default-features = false }
|
|
||||||
embedded-hal = "0.2"
|
|
||||||
|
|
||||||
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["ipv6"]}
|
|
||||||
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
|
||||||
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
|
||||||
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
|
||||||
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
|
||||||
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn", "ipv6"] }
|
|
||||||
|
|
||||||
libboard_artiq = { path = "../libboard_artiq" }
|
|
||||||
unwind = { path = "../libunwind" }
|
|
||||||
libc = { path = "../libc" }
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "satman"
|
||||||
|
version = "0.0.0"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[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"]
|
||||||
|
calibrate_wrpll_skew = ["libboard_artiq/calibrate_wrpll_skew"]
|
||||||
|
default = ["target_zc706", ]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
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"
|
||||||
|
|
||||||
|
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq", features = ["ipv6"]}
|
||||||
|
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] }
|
||||||
|
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" }
|
||||||
|
libasync = { path = "@@ZYNQ_RS@@/libasync" }
|
||||||
|
libregister = { path = "@@ZYNQ_RS@@/libregister" }
|
||||||
|
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn", "ipv6"] }
|
||||||
|
|
||||||
|
libboard_artiq = { path = "../libboard_artiq" }
|
||||||
|
unwind = { path = "../libunwind" }
|
||||||
|
libc = { path = "../libc" }
|
||||||
|
io = { path = "../libio", features = ["alloc"] }
|
||||||
|
ksupport = { path = "../libksupport" }
|
|
@ -1,6 +1,6 @@
|
||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
|
|
||||||
use libboard_artiq::{drtioaux_proto::ANALYZER_MAX_SIZE, pl::csr};
|
use libboard_artiq::{drtioaux_proto::SAT_PAYLOAD_MAX_SIZE, pl::csr};
|
||||||
use libcortex_a9::cache;
|
use libcortex_a9::cache;
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 512 * 1024;
|
const BUFFER_SIZE: usize = 512 * 1024;
|
||||||
|
@ -100,10 +100,10 @@ impl Analyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_data(&mut self, data_slice: &mut [u8; ANALYZER_MAX_SIZE]) -> AnalyzerSliceMeta {
|
pub fn get_data(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> AnalyzerSliceMeta {
|
||||||
let data = unsafe { &BUFFER.data[..] };
|
let data = unsafe { &BUFFER.data[..] };
|
||||||
let i = (self.data_pointer + self.sent_bytes) % BUFFER_SIZE;
|
let i = (self.data_pointer + self.sent_bytes) % BUFFER_SIZE;
|
||||||
let len = min(ANALYZER_MAX_SIZE, self.data_len - self.sent_bytes);
|
let len = min(SAT_PAYLOAD_MAX_SIZE, self.data_len - self.sent_bytes);
|
||||||
let last = self.sent_bytes + len == self.data_len;
|
let last = self.sent_bytes + len == self.data_len;
|
||||||
|
|
||||||
if i + len >= BUFFER_SIZE {
|
if i + len >= BUFFER_SIZE {
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
use alloc::{collections::btree_map::BTreeMap, vec::Vec};
|
use alloc::{collections::btree_map::BTreeMap, string::String, vec::Vec};
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
use libboard_artiq::pl::csr;
|
use ksupport::kernel::DmaRecorder;
|
||||||
|
use libboard_artiq::{drtio_routing::RoutingTable,
|
||||||
|
drtioaux_proto::{Packet, PayloadStatus, MASTER_PAYLOAD_MAX_SIZE},
|
||||||
|
pl::csr};
|
||||||
use libcortex_a9::cache::dcci_slice;
|
use libcortex_a9::cache::dcci_slice;
|
||||||
|
use routing::{Router, Sliceable};
|
||||||
|
use subkernel::Manager as KernelManager;
|
||||||
|
|
||||||
const ALIGNMENT: usize = 64;
|
const ALIGNMENT: usize = 64;
|
||||||
|
|
||||||
|
@ -12,16 +18,20 @@ enum ManagerState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RtioStatus {
|
pub struct RtioStatus {
|
||||||
|
pub source: u8,
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub error: u8,
|
pub error: u8,
|
||||||
pub channel: u32,
|
pub channel: u32,
|
||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
IdNotFound,
|
IdNotFound,
|
||||||
PlaybackInProgress,
|
PlaybackInProgress,
|
||||||
EntryNotComplete,
|
EntryNotComplete,
|
||||||
|
MasterDmaFound,
|
||||||
|
UploadFail,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -29,13 +39,228 @@ struct Entry {
|
||||||
trace: Vec<u8>,
|
trace: Vec<u8>,
|
||||||
padding_len: usize,
|
padding_len: usize,
|
||||||
complete: bool,
|
complete: bool,
|
||||||
|
duration: i64, // relevant for local DMA
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entry {
|
||||||
|
pub fn from_vec(data: Vec<u8>, duration: i64) -> Entry {
|
||||||
|
let mut entry = Entry {
|
||||||
|
trace: data,
|
||||||
|
padding_len: 0,
|
||||||
|
complete: true,
|
||||||
|
duration: duration,
|
||||||
|
};
|
||||||
|
entry.realign();
|
||||||
|
entry
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> u32 {
|
||||||
|
self.trace[self.padding_len..].as_ptr() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn realign(&mut self) {
|
||||||
|
self.trace.push(0);
|
||||||
|
let data_len = self.trace.len();
|
||||||
|
|
||||||
|
self.trace.reserve(ALIGNMENT - 1);
|
||||||
|
let padding = ALIGNMENT - self.trace.as_ptr() as usize % ALIGNMENT;
|
||||||
|
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
||||||
|
for _ in 0..padding {
|
||||||
|
// Vec guarantees that this will not reallocate
|
||||||
|
self.trace.push(0)
|
||||||
|
}
|
||||||
|
for i in 1..data_len + 1 {
|
||||||
|
self.trace[data_len + padding - i] = self.trace[data_len - i]
|
||||||
|
}
|
||||||
|
self.complete = true;
|
||||||
|
self.padding_len = padding;
|
||||||
|
|
||||||
|
dcci_slice(&self.trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum RemoteTraceState {
|
||||||
|
Unsent,
|
||||||
|
Sending(usize),
|
||||||
|
Ready,
|
||||||
|
Running(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RemoteTraces {
|
||||||
|
remote_traces: BTreeMap<u8, Sliceable>,
|
||||||
|
state: RemoteTraceState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RemoteTraces {
|
||||||
|
pub fn new(traces: BTreeMap<u8, Sliceable>) -> RemoteTraces {
|
||||||
|
RemoteTraces {
|
||||||
|
remote_traces: traces,
|
||||||
|
state: RemoteTraceState::Unsent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// on subkernel request
|
||||||
|
pub fn upload_traces(
|
||||||
|
&mut self,
|
||||||
|
id: u32,
|
||||||
|
router: &mut Router,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
) -> usize {
|
||||||
|
let len = self.remote_traces.len();
|
||||||
|
if len > 0 {
|
||||||
|
self.state = RemoteTraceState::Sending(self.remote_traces.len());
|
||||||
|
for (dest, trace) in self.remote_traces.iter_mut() {
|
||||||
|
// queue up the first packet for all destinations, rest will be sent after first ACK
|
||||||
|
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||||
|
let meta = trace.get_slice_master(&mut data_slice);
|
||||||
|
router.route(
|
||||||
|
Packet::DmaAddTraceRequest {
|
||||||
|
source: self_destination,
|
||||||
|
destination: *dest,
|
||||||
|
id: id,
|
||||||
|
status: meta.status,
|
||||||
|
length: meta.len,
|
||||||
|
trace: data_slice,
|
||||||
|
},
|
||||||
|
routing_table,
|
||||||
|
rank,
|
||||||
|
self_destination,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
// on incoming Packet::DmaAddTraceReply
|
||||||
|
pub fn ack_upload(
|
||||||
|
&mut self,
|
||||||
|
kernel_manager: &mut KernelManager,
|
||||||
|
source: u8,
|
||||||
|
id: u32,
|
||||||
|
succeeded: bool,
|
||||||
|
router: &mut Router,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
) {
|
||||||
|
if let RemoteTraceState::Sending(count) = self.state {
|
||||||
|
if let Some(trace) = self.remote_traces.get_mut(&source) {
|
||||||
|
if trace.at_end() {
|
||||||
|
if count - 1 == 0 {
|
||||||
|
self.state = RemoteTraceState::Ready;
|
||||||
|
if let Some((id, timestamp)) = kernel_manager.ddma_remote_uploaded(succeeded) {
|
||||||
|
self.playback(id, timestamp, router, rank, self_destination, routing_table);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.state = RemoteTraceState::Sending(count - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// send next slice
|
||||||
|
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||||
|
let meta = trace.get_slice_master(&mut data_slice);
|
||||||
|
router.route(
|
||||||
|
Packet::DmaAddTraceRequest {
|
||||||
|
source: self_destination,
|
||||||
|
destination: meta.destination,
|
||||||
|
id: id,
|
||||||
|
status: meta.status,
|
||||||
|
length: meta.len,
|
||||||
|
trace: data_slice,
|
||||||
|
},
|
||||||
|
routing_table,
|
||||||
|
rank,
|
||||||
|
self_destination,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// on subkernel request
|
||||||
|
pub fn playback(
|
||||||
|
&mut self,
|
||||||
|
id: u32,
|
||||||
|
timestamp: u64,
|
||||||
|
router: &mut Router,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
) {
|
||||||
|
// route all the playback requests
|
||||||
|
// remote traces (local trace runs on core1 unlike mainline firmware)
|
||||||
|
self.state = RemoteTraceState::Running(self.remote_traces.len());
|
||||||
|
for (dest, _) in self.remote_traces.iter() {
|
||||||
|
router.route(
|
||||||
|
Packet::DmaPlaybackRequest {
|
||||||
|
source: self_destination,
|
||||||
|
destination: *dest,
|
||||||
|
id: id,
|
||||||
|
timestamp: timestamp,
|
||||||
|
},
|
||||||
|
routing_table,
|
||||||
|
rank,
|
||||||
|
self_destination,
|
||||||
|
);
|
||||||
|
// response will be ignored (succeeded = false handled by the main thread)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// on incoming Packet::DmaPlaybackDone
|
||||||
|
pub fn remote_finished(&mut self, kernel_manager: &mut KernelManager, error: u8, channel: u32, timestamp: u64) {
|
||||||
|
if let RemoteTraceState::Running(count) = self.state {
|
||||||
|
if error != 0 || count - 1 == 0 {
|
||||||
|
// notify the kernel about a DDMA error or finish
|
||||||
|
kernel_manager.ddma_finished(error, channel, timestamp);
|
||||||
|
self.state = RemoteTraceState::Ready;
|
||||||
|
// further messages will be ignored (if there was an error)
|
||||||
|
} else {
|
||||||
|
// no error and not the last one awaited
|
||||||
|
self.state = RemoteTraceState::Running(count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn erase(
|
||||||
|
&mut self,
|
||||||
|
id: u32,
|
||||||
|
router: &mut Router,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
) {
|
||||||
|
for (dest, _) in self.remote_traces.iter() {
|
||||||
|
router.route(
|
||||||
|
Packet::DmaRemoveTraceRequest {
|
||||||
|
source: self_destination,
|
||||||
|
destination: *dest,
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
routing_table,
|
||||||
|
rank,
|
||||||
|
self_destination,
|
||||||
|
);
|
||||||
|
// response will be ignored as this object will stop existing too
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_remote_traces(&self) -> bool {
|
||||||
|
self.remote_traces.len() > 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
entries: BTreeMap<u32, Entry>,
|
entries: BTreeMap<(u8, u32), Entry>,
|
||||||
state: ManagerState,
|
state: ManagerState,
|
||||||
currentid: u32,
|
current_id: u32,
|
||||||
|
current_source: u8,
|
||||||
|
|
||||||
|
remote_entries: BTreeMap<u32, RemoteTraces>,
|
||||||
|
name_map: BTreeMap<String, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
|
@ -45,79 +270,238 @@ impl Manager {
|
||||||
unsafe { while csr::rtio_dma::enable_read() != 0 {} }
|
unsafe { while csr::rtio_dma::enable_read() != 0 {} }
|
||||||
Manager {
|
Manager {
|
||||||
entries: BTreeMap::new(),
|
entries: BTreeMap::new(),
|
||||||
currentid: 0,
|
current_id: 0,
|
||||||
|
current_source: 0,
|
||||||
state: ManagerState::Idle,
|
state: ManagerState::Idle,
|
||||||
|
remote_entries: BTreeMap::new(),
|
||||||
|
name_map: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, id: u32, last: bool, trace: &[u8], trace_len: usize) -> Result<(), Error> {
|
pub fn add(
|
||||||
let entry = match self.entries.get_mut(&id) {
|
&mut self,
|
||||||
|
source: u8,
|
||||||
|
id: u32,
|
||||||
|
status: PayloadStatus,
|
||||||
|
trace: &[u8],
|
||||||
|
trace_len: usize,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let entry = match self.entries.get_mut(&(source, id)) {
|
||||||
Some(entry) => {
|
Some(entry) => {
|
||||||
if entry.complete {
|
if entry.complete || status.is_first() {
|
||||||
// replace entry
|
// replace entry
|
||||||
self.entries.remove(&id);
|
self.entries.remove(&(source, id));
|
||||||
self.entries.insert(
|
self.entries.insert(
|
||||||
id,
|
(source, id),
|
||||||
Entry {
|
Entry {
|
||||||
trace: Vec::new(),
|
trace: Vec::new(),
|
||||||
padding_len: 0,
|
padding_len: 0,
|
||||||
complete: false,
|
complete: false,
|
||||||
|
duration: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.entries.get_mut(&id).unwrap()
|
self.entries.get_mut(&(source, id)).unwrap()
|
||||||
} else {
|
} else {
|
||||||
entry
|
entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.entries.insert(
|
self.entries.insert(
|
||||||
id,
|
(source, id),
|
||||||
Entry {
|
Entry {
|
||||||
trace: Vec::new(),
|
trace: Vec::new(),
|
||||||
padding_len: 0,
|
padding_len: 0,
|
||||||
complete: false,
|
complete: false,
|
||||||
|
duration: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.entries.get_mut(&id).unwrap()
|
self.entries.get_mut(&(source, id)).unwrap()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
entry.trace.extend(&trace[0..trace_len]);
|
entry.trace.extend(&trace[0..trace_len]);
|
||||||
|
|
||||||
if last {
|
if status.is_last() {
|
||||||
entry.trace.push(0);
|
entry.realign();
|
||||||
let data_len = entry.trace.len();
|
|
||||||
|
|
||||||
// Realign.
|
|
||||||
entry.trace.reserve(ALIGNMENT - 1);
|
|
||||||
let padding = ALIGNMENT - entry.trace.as_ptr() as usize % ALIGNMENT;
|
|
||||||
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
|
||||||
for _ in 0..padding {
|
|
||||||
// Vec guarantees that this will not reallocate
|
|
||||||
entry.trace.push(0)
|
|
||||||
}
|
|
||||||
for i in 1..data_len + 1 {
|
|
||||||
entry.trace[data_len + padding - i] = entry.trace[data_len - i]
|
|
||||||
}
|
|
||||||
entry.complete = true;
|
|
||||||
entry.padding_len = padding;
|
|
||||||
dcci_slice(&entry.trace);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn erase(&mut self, id: u32) -> Result<(), Error> {
|
// api for DRTIO
|
||||||
match self.entries.remove(&id) {
|
pub fn erase(&mut self, source: u8, id: u32) -> Result<(), Error> {
|
||||||
|
match self.entries.remove(&(source, id)) {
|
||||||
Some(_) => Ok(()),
|
Some(_) => Ok(()),
|
||||||
None => Err(Error::IdNotFound),
|
None => Err(Error::IdNotFound),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn playback(&mut self, id: u32, timestamp: u64) -> Result<(), Error> {
|
// API for subkernel
|
||||||
|
pub fn erase_name(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
router: &mut Router,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
) {
|
||||||
|
if let Some(id) = self.name_map.get(name) {
|
||||||
|
if let Some(traces) = self.remote_entries.get_mut(&id) {
|
||||||
|
traces.erase(*id, router, rank, self_destination, routing_table);
|
||||||
|
self.remote_entries.remove(&id);
|
||||||
|
}
|
||||||
|
self.entries.remove(&(self_destination, *id));
|
||||||
|
self.name_map.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remote_finished(
|
||||||
|
&mut self,
|
||||||
|
kernel_manager: &mut KernelManager,
|
||||||
|
id: u32,
|
||||||
|
error: u8,
|
||||||
|
channel: u32,
|
||||||
|
timestamp: u64,
|
||||||
|
) {
|
||||||
|
if let Some(entry) = self.remote_entries.get_mut(&id) {
|
||||||
|
entry.remote_finished(kernel_manager, error, channel, timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ack_upload(
|
||||||
|
&mut self,
|
||||||
|
kernel_manager: &mut KernelManager,
|
||||||
|
source: u8,
|
||||||
|
id: u32,
|
||||||
|
succeeded: bool,
|
||||||
|
router: &mut Router,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
) {
|
||||||
|
if let Some(entry) = self.remote_entries.get_mut(&id) {
|
||||||
|
entry.ack_upload(
|
||||||
|
kernel_manager,
|
||||||
|
source,
|
||||||
|
id,
|
||||||
|
succeeded,
|
||||||
|
router,
|
||||||
|
rank,
|
||||||
|
self_destination,
|
||||||
|
routing_table,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API for subkernel
|
||||||
|
pub fn upload_traces(
|
||||||
|
&mut self,
|
||||||
|
id: u32,
|
||||||
|
router: &mut Router,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
let remote_traces = self.remote_entries.get_mut(&id);
|
||||||
|
let mut len = 0;
|
||||||
|
if let Some(traces) = remote_traces {
|
||||||
|
len = traces.upload_traces(id, router, rank, self_destination, routing_table);
|
||||||
|
}
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
// API for subkernel
|
||||||
|
pub fn playback_remote(
|
||||||
|
&mut self,
|
||||||
|
id: u32,
|
||||||
|
timestamp: u64,
|
||||||
|
router: &mut Router,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
routing_table: &RoutingTable,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if let Some(traces) = self.remote_entries.get_mut(&id) {
|
||||||
|
traces.playback(id, timestamp, router, rank, self_destination, routing_table);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::IdNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API for subkernel
|
||||||
|
pub fn cleanup(&mut self, router: &mut Router, rank: u8, self_destination: u8, routing_table: &RoutingTable) {
|
||||||
|
// after subkernel ends, remove all self-generated traces
|
||||||
|
for (_, id) in self.name_map.iter_mut() {
|
||||||
|
if let Some(traces) = self.remote_entries.get_mut(&id) {
|
||||||
|
traces.erase(*id, router, rank, self_destination, routing_table);
|
||||||
|
self.remote_entries.remove(&id);
|
||||||
|
}
|
||||||
|
self.entries.remove(&(self_destination, *id));
|
||||||
|
}
|
||||||
|
self.name_map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// API for subkernel
|
||||||
|
pub fn retrieve(&self, self_destination: u8, name: &String) -> Option<(i32, i64, bool)> {
|
||||||
|
let id = self.name_map.get(name)?;
|
||||||
|
let duration = self.entries.get(&(self_destination, *id))?.duration;
|
||||||
|
let uses_ddma = self.has_remote_traces(*id);
|
||||||
|
Some((*id as i32, duration, uses_ddma))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_remote_traces(&self, id: u32) -> bool {
|
||||||
|
match self.remote_entries.get(&id) {
|
||||||
|
Some(traces) => traces.has_remote_traces(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_record(&mut self, mut recorder: DmaRecorder, self_destination: u8) -> Result<u32, Error> {
|
||||||
|
let mut remote_traces: BTreeMap<u8, Sliceable> = BTreeMap::new();
|
||||||
|
|
||||||
|
let mut local_trace: Vec<u8> = Vec::new();
|
||||||
|
// analyze each entry and put in proper buckets, as the kernel core
|
||||||
|
// sends whole chunks, to limit comms/kernel CPU communication,
|
||||||
|
// and as only comms core has access to varios DMA buffers.
|
||||||
|
let mut ptr = 0;
|
||||||
|
recorder.buffer.push(0);
|
||||||
|
while recorder.buffer[ptr] != 0 {
|
||||||
|
// ptr + 3 = tgt >> 24 (destination)
|
||||||
|
let len = recorder.buffer[ptr] as usize;
|
||||||
|
let destination = recorder.buffer[ptr + 3];
|
||||||
|
if destination == 0 {
|
||||||
|
return Err(Error::MasterDmaFound);
|
||||||
|
} else if destination == self_destination {
|
||||||
|
local_trace.extend(&recorder.buffer[ptr..ptr + len]);
|
||||||
|
} else {
|
||||||
|
if let Some(remote_trace) = remote_traces.get_mut(&destination) {
|
||||||
|
remote_trace.extend(&recorder.buffer[ptr..ptr + len]);
|
||||||
|
} else {
|
||||||
|
remote_traces.insert(
|
||||||
|
destination,
|
||||||
|
Sliceable::new(destination, recorder.buffer[ptr..ptr + len].to_vec()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// and jump to the next event
|
||||||
|
ptr += len;
|
||||||
|
}
|
||||||
|
let local_entry = Entry::from_vec(local_trace, recorder.duration);
|
||||||
|
|
||||||
|
let id = local_entry.id();
|
||||||
|
self.entries.insert((self_destination, id), local_entry);
|
||||||
|
self.remote_entries.insert(id, RemoteTraces::new(remote_traces));
|
||||||
|
let mut name = String::new();
|
||||||
|
mem::swap(&mut recorder.name, &mut name);
|
||||||
|
self.name_map.insert(name, id);
|
||||||
|
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn playback(&mut self, source: u8, id: u32, timestamp: u64) -> Result<(), Error> {
|
||||||
if self.state != ManagerState::Idle {
|
if self.state != ManagerState::Idle {
|
||||||
return Err(Error::PlaybackInProgress);
|
return Err(Error::PlaybackInProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = match self.entries.get(&id) {
|
let entry = match self.entries.get(&(source, id)) {
|
||||||
Some(entry) => entry,
|
Some(entry) => entry,
|
||||||
None => {
|
None => {
|
||||||
return Err(Error::IdNotFound);
|
return Err(Error::IdNotFound);
|
||||||
|
@ -130,7 +514,8 @@ impl Manager {
|
||||||
assert!(ptr as u32 % 64 == 0);
|
assert!(ptr as u32 % 64 == 0);
|
||||||
|
|
||||||
self.state = ManagerState::Playback;
|
self.state = ManagerState::Playback;
|
||||||
self.currentid = id;
|
self.current_id = id;
|
||||||
|
self.current_source = source;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::rtio_dma::base_address_write(ptr as u32);
|
csr::rtio_dma::base_address_write(ptr as u32);
|
||||||
|
@ -162,7 +547,8 @@ impl Manager {
|
||||||
csr::rtio_dma::error_write(1);
|
csr::rtio_dma::error_write(1);
|
||||||
}
|
}
|
||||||
return Some(RtioStatus {
|
return Some(RtioStatus {
|
||||||
id: self.currentid,
|
source: self.current_source,
|
||||||
|
id: self.current_id,
|
||||||
error: error,
|
error: error,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
|
@ -170,4 +556,8 @@ impl Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn running(&self) -> bool {
|
||||||
|
self.state == ManagerState::Playback
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,149 @@
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ use libboard_artiq::{drtio_routing, drtioaux};
|
||||||
#[cfg(has_drtio_routing)]
|
#[cfg(has_drtio_routing)]
|
||||||
use libboard_zynq::time::Milliseconds;
|
use libboard_zynq::time::Milliseconds;
|
||||||
use libboard_zynq::timer::GlobalTimer;
|
use libboard_zynq::timer::GlobalTimer;
|
||||||
|
use routing::Router;
|
||||||
|
|
||||||
#[cfg(has_drtio_routing)]
|
#[cfg(has_drtio_routing)]
|
||||||
fn rep_link_rx_up(repno: u8) -> bool {
|
fn rep_link_rx_up(repno: u8) -> bool {
|
||||||
|
@ -53,7 +54,14 @@ impl Repeater {
|
||||||
self.state == RepeaterState::Up
|
self.state == RepeaterState::Up
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn service(&mut self, routing_table: &drtio_routing::RoutingTable, rank: u8, timer: &mut GlobalTimer) {
|
pub fn service(
|
||||||
|
&mut self,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
|
rank: u8,
|
||||||
|
destination: u8,
|
||||||
|
router: &mut Router,
|
||||||
|
timer: &mut GlobalTimer,
|
||||||
|
) {
|
||||||
self.process_local_errors();
|
self.process_local_errors();
|
||||||
|
|
||||||
match self.state {
|
match self.state {
|
||||||
|
@ -79,6 +87,10 @@ impl Repeater {
|
||||||
if rep_link_rx_up(self.repno) {
|
if rep_link_rx_up(self.repno) {
|
||||||
if let Ok(Some(drtioaux::Packet::EchoReply)) = drtioaux::recv(self.auxno) {
|
if let Ok(Some(drtioaux::Packet::EchoReply)) = drtioaux::recv(self.auxno) {
|
||||||
info!("[REP#{}] remote replied after {} packets", self.repno, ping_count);
|
info!("[REP#{}] remote replied after {} packets", self.repno, ping_count);
|
||||||
|
let max_time = timer.get_time() + Milliseconds(200);
|
||||||
|
while timer.get_time() < max_time {
|
||||||
|
let _ = drtioaux::recv(self.auxno);
|
||||||
|
}
|
||||||
self.state = RepeaterState::Up;
|
self.state = RepeaterState::Up;
|
||||||
if let Err(e) = self.sync_tsc(timer) {
|
if let Err(e) = self.sync_tsc(timer) {
|
||||||
error!("[REP#{}] failed to sync TSC ({:?})", self.repno, e);
|
error!("[REP#{}] failed to sync TSC ({:?})", self.repno, e);
|
||||||
|
@ -111,7 +123,7 @@ impl Repeater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RepeaterState::Up => {
|
RepeaterState::Up => {
|
||||||
self.process_unsolicited_aux();
|
self.process_unsolicited_aux(routing_table, rank, destination, router);
|
||||||
if !rep_link_rx_up(self.repno) {
|
if !rep_link_rx_up(self.repno) {
|
||||||
info!("[REP#{}] link is down", self.repno);
|
info!("[REP#{}] link is down", self.repno);
|
||||||
self.state = RepeaterState::Down;
|
self.state = RepeaterState::Down;
|
||||||
|
@ -126,9 +138,15 @@ impl Repeater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_unsolicited_aux(&self) {
|
fn process_unsolicited_aux(
|
||||||
|
&self,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
|
rank: u8,
|
||||||
|
destination: u8,
|
||||||
|
router: &mut Router,
|
||||||
|
) {
|
||||||
match drtioaux::recv(self.auxno) {
|
match drtioaux::recv(self.auxno) {
|
||||||
Ok(Some(packet)) => warn!("[REP#{}] unsolicited aux packet: {:?}", self.repno, packet),
|
Ok(Some(packet)) => router.route(packet, routing_table, rank, destination),
|
||||||
Ok(None) => (),
|
Ok(None) => (),
|
||||||
Err(_) => warn!("[REP#{}] aux packet error", self.repno),
|
Err(_) => warn!("[REP#{}] aux packet error", self.repno),
|
||||||
}
|
}
|
||||||
|
@ -190,14 +208,45 @@ impl Repeater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aux_forward(&self, request: &drtioaux::Packet, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
|
pub fn aux_forward(
|
||||||
|
&self,
|
||||||
|
request: &drtioaux::Packet,
|
||||||
|
router: &mut Router,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
|
rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
timer: &mut GlobalTimer,
|
||||||
|
) -> Result<(), drtioaux::Error> {
|
||||||
|
self.aux_send(request)?;
|
||||||
|
loop {
|
||||||
|
let reply = self.recv_aux_timeout(200, timer)?;
|
||||||
|
match reply {
|
||||||
|
// async/locally requested packets to be consumed or routed
|
||||||
|
// these may come while a packet would be forwarded
|
||||||
|
drtioaux::Packet::DmaPlaybackStatus { .. }
|
||||||
|
| drtioaux::Packet::SubkernelFinished { .. }
|
||||||
|
| drtioaux::Packet::SubkernelMessage { .. }
|
||||||
|
| drtioaux::Packet::SubkernelMessageAck { .. }
|
||||||
|
| drtioaux::Packet::SubkernelLoadRunReply { .. }
|
||||||
|
| drtioaux::Packet::SubkernelException { .. }
|
||||||
|
| drtioaux::Packet::DmaAddTraceReply { .. }
|
||||||
|
| drtioaux::Packet::DmaPlaybackReply { .. } => {
|
||||||
|
router.route(reply, routing_table, rank, self_destination);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
drtioaux::send(0, &reply).unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn aux_send(&self, request: &drtioaux::Packet) -> Result<(), drtioaux::Error> {
|
||||||
if self.state != RepeaterState::Up {
|
if self.state != RepeaterState::Up {
|
||||||
return Err(drtioaux::Error::LinkDown);
|
return Err(drtioaux::Error::LinkDown);
|
||||||
}
|
}
|
||||||
drtioaux::send(self.auxno, request).unwrap();
|
drtioaux::send(self.auxno, request)
|
||||||
let reply = self.recv_aux_timeout(200, timer)?;
|
|
||||||
drtioaux::send(0, &reply).unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync_tsc(&self, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
|
pub fn sync_tsc(&self, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
|
||||||
|
@ -302,7 +351,15 @@ impl Repeater {
|
||||||
Repeater::default()
|
Repeater::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn service(&self, _routing_table: &drtio_routing::RoutingTable, _rank: u8, _timer: &mut GlobalTimer) {}
|
pub fn service(
|
||||||
|
&self,
|
||||||
|
_routing_table: &drtio_routing::RoutingTable,
|
||||||
|
_rank: u8,
|
||||||
|
_destination: u8,
|
||||||
|
_router: &mut Router,
|
||||||
|
_timer: &mut GlobalTimer,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sync_tsc(&self, _timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
|
pub fn sync_tsc(&self, _timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
use alloc::{collections::vec_deque::VecDeque, vec::Vec};
|
||||||
|
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}};
|
||||||
|
|
||||||
|
pub struct SliceMeta {
|
||||||
|
pub destination: u8,
|
||||||
|
pub len: u16,
|
||||||
|
pub status: PayloadStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* represents data that has to be sent to Master */
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Sliceable {
|
||||||
|
it: usize,
|
||||||
|
data: Vec<u8>,
|
||||||
|
destination: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_slice_fn {
|
||||||
|
($name:tt, $size:expr) => {
|
||||||
|
pub fn $name(&mut self, data_slice: &mut [u8; $size]) -> SliceMeta {
|
||||||
|
let first = self.it == 0;
|
||||||
|
let len = min($size, self.data.len() - self.it);
|
||||||
|
let last = self.it + len == self.data.len();
|
||||||
|
let status = PayloadStatus::from_status(first, last);
|
||||||
|
|
||||||
|
data_slice[..len].clone_from_slice(&self.data[self.it..self.it + len]);
|
||||||
|
self.it += len;
|
||||||
|
|
||||||
|
SliceMeta {
|
||||||
|
destination: self.destination,
|
||||||
|
len: len as u16,
|
||||||
|
status: status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sliceable {
|
||||||
|
pub fn new(destination: u8, data: Vec<u8>) -> Sliceable {
|
||||||
|
Sliceable {
|
||||||
|
it: 0,
|
||||||
|
data: data,
|
||||||
|
destination: destination,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn at_end(&self) -> bool {
|
||||||
|
self.it == self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(&mut self, data: &[u8]) {
|
||||||
|
self.data.extend(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
// they're passed as soon as possible downstream (within the subtree), or sent upstream,
|
||||||
|
// which is notified about pending packets.
|
||||||
|
// for rank 1 (connected to master) satellites, these packets are passed as an answer to DestinationStatusRequest;
|
||||||
|
// for higher ranks, after getting a notification, it will transact with downstream to get the pending packets.
|
||||||
|
|
||||||
|
// forward! macro is not deprecated, as routable packets are only these that can originate
|
||||||
|
// from both master and satellite, e.g. DDMA and Subkernel.
|
||||||
|
|
||||||
|
pub struct Router {
|
||||||
|
upstream_queue: VecDeque<drtioaux::Packet>,
|
||||||
|
local_queue: VecDeque<drtioaux::Packet>,
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
downstream_queue: VecDeque<(usize, drtioaux::Packet)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Router {
|
||||||
|
pub fn new() -> Router {
|
||||||
|
Router {
|
||||||
|
upstream_queue: VecDeque::new(),
|
||||||
|
local_queue: VecDeque::new(),
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
downstream_queue: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by local sources (DDMA, kernel) and by repeaters on receiving async data;
|
||||||
|
// messages are always buffered for both upstream and downstream
|
||||||
|
pub fn route(
|
||||||
|
&mut self,
|
||||||
|
packet: drtioaux::Packet,
|
||||||
|
_routing_table: &drtio_routing::RoutingTable,
|
||||||
|
_rank: u8,
|
||||||
|
self_destination: u8,
|
||||||
|
) {
|
||||||
|
let destination = packet.routable_destination();
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
{
|
||||||
|
if let Some(destination) = destination {
|
||||||
|
let hop = _routing_table.0[destination as usize][_rank as usize] as usize;
|
||||||
|
if destination == self_destination {
|
||||||
|
self.local_queue.push_back(packet);
|
||||||
|
} else if hop > 0 && hop < csr::DRTIOREP.len() {
|
||||||
|
let repno = (hop - 1) as usize;
|
||||||
|
self.downstream_queue.push_back((repno, packet));
|
||||||
|
} else {
|
||||||
|
self.upstream_queue.push_back(packet);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Received an unroutable packet: {:?}", packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
{
|
||||||
|
if destination == Some(self_destination) {
|
||||||
|
self.local_queue.push_back(packet);
|
||||||
|
} else {
|
||||||
|
self.upstream_queue.push_back(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends a packet to a required destination, routing if necessary
|
||||||
|
pub fn send(
|
||||||
|
&mut self,
|
||||||
|
packet: drtioaux::Packet,
|
||||||
|
_routing_table: &drtio_routing::RoutingTable,
|
||||||
|
_rank: u8,
|
||||||
|
_destination: u8,
|
||||||
|
) -> Result<(), drtioaux::Error> {
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
{
|
||||||
|
let destination = packet.routable_destination();
|
||||||
|
if let Some(destination) = destination {
|
||||||
|
let hop = _routing_table.0[destination as usize][_rank as usize] as usize;
|
||||||
|
if destination == 0 {
|
||||||
|
// response is needed immediately if master required it
|
||||||
|
drtioaux::send(0, &packet)?;
|
||||||
|
} else if !(hop > 0 && hop < csr::DRTIOREP.len()) {
|
||||||
|
// higher rank can wait
|
||||||
|
self.upstream_queue.push_back(packet);
|
||||||
|
} else {
|
||||||
|
let repno = (hop - 1) as usize;
|
||||||
|
// transaction will occur at closest possible opportunity
|
||||||
|
self.downstream_queue.push_back((repno, packet));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// packet not supported in routing, fallback - sent directly
|
||||||
|
drtioaux::send(0, &packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
{
|
||||||
|
drtioaux::send(0, &packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_upstream_packet(&mut self) -> Option<drtioaux::Packet> {
|
||||||
|
self.upstream_queue.pop_front()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
pub fn get_downstream_packet(&mut self) -> Option<(usize, drtioaux::Packet)> {
|
||||||
|
self.downstream_queue.pop_front()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_local_packet(&mut self) -> Option<drtioaux::Packet> {
|
||||||
|
self.local_queue.pop_front()
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue