forked from M-Labs/artiq-zynq
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
957a72d8af | |||
79a841fd1e | |||
0005508f83 | |||
a7dbfe506e | |||
c0ab42d52d | |||
6e6c1f850b | |||
bf26f7d807 | |||
7d8268adf2 | |||
3da71dedd7 | |||
4ec1ef125b | |||
6573ecd487 | |||
b8ff602d93 | |||
0e75694c6b | |||
7cdfb1b830 | |||
9b76896edd | |||
00c5ee01b0 | |||
b47152f2c2 | |||
5597927df9 | |||
d019021ff5 | |||
3d43bc8b15 | |||
c4d5ab96cc | |||
a89a7dc495 | |||
832a861477 | |||
201953f96b | |||
7aa6ddeaf2 | |||
b0fde30e8f | |||
66d80bf51a | |||
6f867098b0 | |||
d51f86672a | |||
6b9212525a | |||
201470485c | |||
b7c49be238 | |||
da0eedaa76 | |||
a44a21436c | |||
e946bcc3ed | |||
4f49c58d3b | |||
2a71b2fc62 | |||
|
2ad1970004 | ||
d6bcc516cd | |||
99ebc2fcdb | |||
709aa8195b | |||
733f270819 | |||
d30fb96674 | |||
facc5808ef | |||
6f74cff4c5 | |||
3ea2690f15 | |||
e02b8e25fe | |||
561efb0466 |
19
README.md
19
README.md
@ -4,14 +4,13 @@ ARTIQ on Zynq
|
||||
How to use
|
||||
----------
|
||||
|
||||
1. Install ARTIQ-6 or newer.
|
||||
2. Select the latest successful build on Hydra: https://nixbld.m-labs.hk/jobset/artiq/zynq
|
||||
3. Search for the job named ``<board>-<variant>-sd`` (for example: ``zc706-nist_clock-sd`` or ``zc706-nist_qc2-sd``).
|
||||
4. Download the ``boot.bin`` "binary distribution" and place it at the root of a FAT-formatted SD card.
|
||||
5. 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.
|
||||
6. 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.
|
||||
7. 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.
|
||||
8. Create and use an ARTIQ device database as usual, but set ``"target": "cortexa9"`` in the arguments of the core device.
|
||||
1. Install the ARTIQ version that corresponds to the artiq-zynq version you are targeting.
|
||||
2. To obtain firmware binaries, select the latest successful build on [Hydra](https://nixbld.m-labs.hk/) for the targeted artiq-zynq version, or use AFWS. If using Hydra, search for the job named ``<board>-<variant>-sd`` (for example: ``zc706-nist_clock-sd`` or ``zc706-nist_qc2-sd``).
|
||||
3. Place the ``boot.bin`` file, obtained from Hydra's "binary distribution" download link or from AFWS, at the root of a FAT-formatted SD card.
|
||||
4. Optionally, create a ``config.txt`` configuration file at the root of the SD card containing ``key=value`` pairs on each line. Use the ``ip``, ``ip6`` and ``mac`` keys to respectively set the IPv4, IPv6 and MAC address of the board. Configuring an IPv6 address is entirely optional. If these keys are not found, the firmware will use default values that may or may not be compatible with your network.
|
||||
5. Insert the SD card into the board and set up the board to boot from the SD card. For the ZC706, this is achieved by placing the large DIP switch SW11 in the 00110 position.
|
||||
6. Power up the board. After the firmware starts successfully, it should respond to ping at its IP addresses, and boot messages can be observed from its UART at 115200bps.
|
||||
7. Create and use an ARTIQ device database as usual, but set ``"target": "cortexa9"`` in the arguments of the core device.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
@ -33,7 +32,7 @@ not implemented as it seems not very useful.
|
||||
Development instructions
|
||||
------------------------
|
||||
|
||||
ARTIQ on Zynq is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
|
||||
ARTIQ on Zynq is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.8+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
|
||||
|
||||
Pure build with Nix and execution on a remote JTAG server:
|
||||
|
||||
@ -64,7 +63,7 @@ Notes:
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright (C) 2019-2022 M-Labs Limited.
|
||||
Copyright (C) 2019-2023 M-Labs Limited.
|
||||
|
||||
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
|
||||
|
@ -8,7 +8,7 @@ device_db = {
|
||||
"arguments": {
|
||||
"host": "192.168.1.52",
|
||||
"ref_period": 1e-9,
|
||||
"ref_multiplier": 1,
|
||||
"ref_multiplier": 8,
|
||||
"target": "cortexa9"
|
||||
}
|
||||
},
|
||||
@ -59,6 +59,14 @@ device_db["ad9914dds1"] = {
|
||||
"arguments": {"sysclk": 3e9, "bus_channel": 50, "channel": 1},
|
||||
}
|
||||
|
||||
for i in range(4):
|
||||
device_db["ttl"+str(i)+"_counter"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.edge_counter",
|
||||
"class": "EdgeCounter",
|
||||
"arguments": {"channel": 52+i}
|
||||
}
|
||||
|
||||
# for ARTIQ test suite
|
||||
device_db.update(
|
||||
loop_out="ttl0",
|
||||
|
107
flake.lock
generated
107
flake.lock
generated
@ -11,21 +11,23 @@
|
||||
"src-pythonparser": "src-pythonparser"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1654087576,
|
||||
"narHash": "sha256-d+LCvCXikhhEjsf4qBZakx3Sda85p+4vVZVwE1YuS90=",
|
||||
"ref": "master",
|
||||
"rev": "68ef0073ea66fe9c7f7c178979ecb8c681c47caa",
|
||||
"revCount": 8068,
|
||||
"lastModified": 1724401549,
|
||||
"narHash": "sha256-7XM9kitfLffUvBNbFnpMWy6WcO5I6KGfR21O5LzxoWs=",
|
||||
"ref": "release-7",
|
||||
"rev": "38c72fdab41679c300c235d960e87b9e06bea5b4",
|
||||
"revCount": 8208,
|
||||
"type": "git",
|
||||
"url": "https://github.com/m-labs/artiq.git"
|
||||
},
|
||||
"original": {
|
||||
"ref": "release-7",
|
||||
"type": "git",
|
||||
"url": "https://github.com/m-labs/artiq.git"
|
||||
}
|
||||
},
|
||||
"artiq-comtools": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"artiq",
|
||||
"nixpkgs"
|
||||
@ -36,11 +38,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1654007592,
|
||||
"narHash": "sha256-vaDFhE1ItjqtIcinC/6RAJGbj44pxxMUEeQUa3FtgEE=",
|
||||
"lastModified": 1717637438,
|
||||
"narHash": "sha256-BXFidNm3Em8iChPGu1L0s2bY+f2yQ0VVid4MuOoTehw=",
|
||||
"owner": "m-labs",
|
||||
"repo": "artiq-comtools",
|
||||
"rev": "cb73281154656ee8f74db1866859e31bf42755cd",
|
||||
"rev": "78d27026efe76a13f7b4698a554f55811369ec4d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -49,14 +51,32 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mozilla-overlay": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1650459918,
|
||||
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
|
||||
"lastModified": 1704373101,
|
||||
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
|
||||
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -68,11 +88,11 @@
|
||||
"mozilla-overlay_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1650459918,
|
||||
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
|
||||
"lastModified": 1704373101,
|
||||
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
|
||||
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -84,11 +104,11 @@
|
||||
"mozilla-overlay_3": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1650459918,
|
||||
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
|
||||
"lastModified": 1704373101,
|
||||
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
|
||||
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -99,11 +119,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1653920503,
|
||||
"narHash": "sha256-BBeCZwZImtjP3oYy4WogkQYy5OxNyfNciVSc1AfZgLQ=",
|
||||
"lastModified": 1685573264,
|
||||
"narHash": "sha256-Zffu01pONhs/pqH07cjlF10NnMDLok8ix5Uk4rhOnZQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a634c8f6c1fbf9b9730e01764999666f3436f10a",
|
||||
"rev": "380be19fbd2d9079f677978361792cb25e8a3635",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -128,11 +148,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1654006751,
|
||||
"narHash": "sha256-OWAnoTCutvTQcYdtdtLQuL6uRtG+7Jz7sbRhcScv8bo=",
|
||||
"lastModified": 1717637367,
|
||||
"narHash": "sha256-4mSm9wl5EMgzzrW6w86IDUevkEOT99FESHGcxcyQbD0=",
|
||||
"owner": "m-labs",
|
||||
"repo": "sipyco",
|
||||
"rev": "b3d03a94c751a24769c54a61a0dbe9d6af52dade",
|
||||
"rev": "02b96ec2473a3c3d3c980899de2564ddce949dab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -144,11 +164,11 @@
|
||||
"src-migen": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1650337393,
|
||||
"narHash": "sha256-rm1SlFmF2ASz0vIy2nDEzGlyRw2oYNeJRr8Kh8Mg2Qc=",
|
||||
"lastModified": 1715484909,
|
||||
"narHash": "sha256-4DCHBUBfc/VA+7NW2Hr0+JP4NnKPru2uVJyZjCCk0Ws=",
|
||||
"owner": "m-labs",
|
||||
"repo": "migen",
|
||||
"rev": "d4e3f34177c32f09904397179e6ed9c83175e528",
|
||||
"rev": "4790bb577681a8c3a8d226bc196a4e5deb39e4df",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -160,11 +180,11 @@
|
||||
"src-misoc": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1649324486,
|
||||
"narHash": "sha256-Mw/fQS3lHFvCm7L1k63joRkz5uyijQfywcOq+X2+o2s=",
|
||||
"ref": "master",
|
||||
"rev": "f1dc58d2b8c222ba41c25cee4301626625f46e43",
|
||||
"revCount": 2420,
|
||||
"lastModified": 1715647536,
|
||||
"narHash": "sha256-q+USDcaKHABwW56Jzq8u94iGPWlyLXMyVt0j/Gyg+IE=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "fea9de558c730bc394a5936094ae95bb9d6fa726",
|
||||
"revCount": 2455,
|
||||
"submodules": true,
|
||||
"type": "git",
|
||||
"url": "https://github.com/m-labs/misoc.git"
|
||||
@ -191,6 +211,21 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zynq-rs": {
|
||||
"inputs": {
|
||||
"mozilla-overlay": "mozilla-overlay_3",
|
||||
@ -200,11 +235,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1654002148,
|
||||
"narHash": "sha256-Ztzx7ze8o0gdu+sDpc+PS2M88h/b22rwFq0b12wMqZQ=",
|
||||
"ref": "master",
|
||||
"rev": "6cd32f6ee0e7bb7ce5a279f8be943903a393a476",
|
||||
"revCount": 617,
|
||||
"lastModified": 1720537402,
|
||||
"narHash": "sha256-ybvaQ48SVBqYVqgYmGUdefGZkni7PJ90qYQPHnFOwDs=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "b2b3e5c933cbc4b7cb14adde480d7561a3ae71ee",
|
||||
"revCount": 648,
|
||||
"type": "git",
|
||||
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
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?ref=release-7;
|
||||
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
|
||||
inputs.zynq-rs.url = git+https://git.m-labs.hk/m-labs/zynq-rs;
|
||||
inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs";
|
||||
@ -134,7 +134,7 @@
|
||||
cargoLock = {
|
||||
lockFile = src/Cargo.lock;
|
||||
outputHashes = {
|
||||
"libasync-0.0.0" = "sha256-xuwesRrQiccopPTCkwGqQxld74X74q7EVsKIrE0zirc=";
|
||||
"libasync-0.0.0" = "sha256-f8IPIYenff7B8S3p/pjVxT/0HVZuJXtdg2jtMpaZEbM=";
|
||||
};
|
||||
};
|
||||
|
||||
@ -378,4 +378,4 @@
|
||||
makeArtiqZynqPackage = build;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
12
src/Cargo.lock
generated
12
src/Cargo.lock
generated
@ -219,7 +219,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libasync"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#91bae572f913abc2f95acb899ca5afa33eeaa036"
|
||||
dependencies = [
|
||||
"embedded-hal",
|
||||
"libcortex_a9",
|
||||
@ -251,7 +251,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libboard_zynq"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#91bae572f913abc2f95acb899ca5afa33eeaa036"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"embedded-hal",
|
||||
@ -276,7 +276,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libconfig"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#91bae572f913abc2f95acb899ca5afa33eeaa036"
|
||||
dependencies = [
|
||||
"core_io",
|
||||
"fatfs",
|
||||
@ -287,7 +287,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcortex_a9"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#91bae572f913abc2f95acb899ca5afa33eeaa036"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"libregister",
|
||||
@ -303,7 +303,7 @@ checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"
|
||||
[[package]]
|
||||
name = "libregister"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#91bae572f913abc2f95acb899ca5afa33eeaa036"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"vcell",
|
||||
@ -313,7 +313,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libsupport_zynq"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#91bae572f913abc2f95acb899ca5afa33eeaa036"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"compiler_builtins",
|
||||
|
@ -10,11 +10,13 @@ from migen.genlib.cdc import MultiReg
|
||||
from migen_axi.integration.soc_core import SoCCore
|
||||
from migen_axi.platforms import kasli_soc
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.cores import virtual_leds
|
||||
from misoc.integration import cpu_interface
|
||||
|
||||
from artiq.coredevice import jsondesc
|
||||
from artiq.gateware import rtio, eem_7series
|
||||
from artiq.gateware.rtio.phy import ttl_simple
|
||||
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
||||
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier
|
||||
from artiq.gateware.drtio.transceiver import gtx_7series
|
||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
||||
@ -75,7 +77,7 @@ class RTIOCRG(Module, AutoCSR):
|
||||
MultiReg(pll_locked, self.pll_locked.status)
|
||||
]
|
||||
|
||||
|
||||
|
||||
eem_iostandard_dict = {
|
||||
0: "LVDS_25",
|
||||
1: "LVDS_25",
|
||||
@ -112,24 +114,26 @@ class SMAClkinForward(Module):
|
||||
class GenericStandalone(SoCCore):
|
||||
def __init__(self, description, acpki=False):
|
||||
self.acpki = acpki
|
||||
self.rustc_cfg = dict()
|
||||
sys_clk_freq = 125e6
|
||||
|
||||
platform = kasli_soc.Platform()
|
||||
platform.toolchain.bitstream_commands.extend([
|
||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||
])
|
||||
ident = self.__class__.__name__
|
||||
ident = description["variant"]
|
||||
if self.acpki:
|
||||
ident = "acpki_" + ident
|
||||
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
||||
|
||||
self.config["hw_rev"] = description["hw_rev"]
|
||||
|
||||
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
||||
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
||||
|
||||
self.submodules += SMAClkinForward(self.platform)
|
||||
|
||||
self.rustc_cfg["has_si5324"] = None
|
||||
self.rustc_cfg["si5324_soft_reset"] = None
|
||||
self.config["HAS_SI5324"] = None
|
||||
self.config["SI5324_SOFT_RESET"] = None
|
||||
|
||||
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
||||
self.submodules.rtio_crg = RTIOCRG(self.platform)
|
||||
@ -138,6 +142,8 @@ class GenericStandalone(SoCCore):
|
||||
self.platform.add_false_path_constraints(
|
||||
self.ps7.cd_sys.clk,
|
||||
self.rtio_crg.cd_rtio.clk)
|
||||
fix_serdes_timing_path(platform)
|
||||
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)
|
||||
|
||||
self.rtio_channels = []
|
||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||
@ -154,18 +160,20 @@ class GenericStandalone(SoCCore):
|
||||
self.rtio_channels.append(rtio.LogChannel())
|
||||
|
||||
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
|
||||
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
|
||||
self.submodules.rtio_core = rtio.Core(
|
||||
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
|
||||
)
|
||||
self.csr_devices.append("rtio_core")
|
||||
|
||||
if self.acpki:
|
||||
self.rustc_cfg["ki_impl"] = "acp"
|
||||
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.rustc_cfg["ki_impl"] = "csr"
|
||||
self.config["KI_IMPL"] = "csr"
|
||||
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
|
||||
self.csr_devices.append("rtio")
|
||||
|
||||
@ -185,7 +193,7 @@ class GenericStandalone(SoCCore):
|
||||
self.csr_devices.append("rtio_analyzer")
|
||||
|
||||
if has_grabber:
|
||||
self.rustc_cfg["has_grabber"] = None
|
||||
self.config["HAS_GRABBER"] = None
|
||||
self.add_csr_group("grabber", self.grabber_csr_group)
|
||||
for grabber in self.grabber_csr_group:
|
||||
self.platform.add_false_path_constraints(
|
||||
@ -198,17 +206,18 @@ class GenericMaster(SoCCore):
|
||||
rtio_clk_freq = description["rtio_frequency"]
|
||||
|
||||
self.acpki = acpki
|
||||
self.rustc_cfg = dict()
|
||||
|
||||
platform = kasli_soc.Platform()
|
||||
platform.toolchain.bitstream_commands.extend([
|
||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||
])
|
||||
ident = self.__class__.__name__
|
||||
ident = description["variant"]
|
||||
if self.acpki:
|
||||
ident = "acpki_" + ident
|
||||
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
||||
|
||||
self.config["hw_rev"] = description["hw_rev"]
|
||||
|
||||
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
||||
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
||||
|
||||
@ -221,13 +230,15 @@ class GenericMaster(SoCCore):
|
||||
pads=data_pads,
|
||||
sys_clk_freq=sys_clk_freq)
|
||||
self.csr_devices.append("drtio_transceiver")
|
||||
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)
|
||||
|
||||
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
||||
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
|
||||
self.csr_devices.append("rtio_crg")
|
||||
fix_serdes_timing_path(platform)
|
||||
|
||||
self.rustc_cfg["has_si5324"] = None
|
||||
self.rustc_cfg["si5324_soft_reset"] = None
|
||||
self.config["HAS_SI5324"] = None
|
||||
self.config["SI5324_SOFT_RESET"] = None
|
||||
|
||||
self.rtio_channels = []
|
||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||
@ -272,24 +283,26 @@ class GenericMaster(SoCCore):
|
||||
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)
|
||||
self.rustc_cfg["has_drtio"] = None
|
||||
self.rustc_cfg["has_drtio_routing"] = None
|
||||
self.config["HAS_DRTIO"] = None
|
||||
self.config["HAS_DRTIO_ROUTING"] = None
|
||||
self.add_csr_group("drtio", drtio_csr_group)
|
||||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
||||
|
||||
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
|
||||
self.submodules.rtio_core = rtio.Core(
|
||||
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
|
||||
)
|
||||
self.csr_devices.append("rtio_core")
|
||||
|
||||
if self.acpki:
|
||||
self.rustc_cfg["ki_impl"] = "acp"
|
||||
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.rustc_cfg["ki_impl"] = "csr"
|
||||
self.config["KI_IMPL"] = "csr"
|
||||
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
|
||||
self.csr_devices.append("rtio")
|
||||
|
||||
@ -308,40 +321,46 @@ class GenericMaster(SoCCore):
|
||||
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
|
||||
self.csr_devices.append("routing_table")
|
||||
|
||||
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
|
||||
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.cri_con.switch.slave,
|
||||
self.ps7.s_axi_hp1)
|
||||
self.csr_devices.append("rtio_analyzer")
|
||||
|
||||
if has_grabber:
|
||||
self.rustc_cfg["has_grabber"] = None
|
||||
self.config["HAS_GRABBER"] = None
|
||||
self.add_csr_group("grabber", self.grabber_csr_group)
|
||||
|
||||
self.submodules.virtual_leds = virtual_leds.VirtualLeds()
|
||||
self.csr_devices.append("virtual_leds")
|
||||
|
||||
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
|
||||
for i, channel in enumerate(self.drtio_transceiver.channels)]
|
||||
|
||||
class GenericSatellite(SoCCore):
|
||||
def __init__(self, description, acpki=False):
|
||||
sys_clk_freq = 125e6
|
||||
sys_clk_freq = 125e6
|
||||
rtio_clk_freq = description["rtio_frequency"]
|
||||
|
||||
self.acpki = acpki
|
||||
self.rustc_cfg = dict()
|
||||
|
||||
platform = kasli_soc.Platform()
|
||||
platform.toolchain.bitstream_commands.extend([
|
||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||
])
|
||||
ident = self.__class__.__name__
|
||||
ident = description["variant"]
|
||||
if self.acpki:
|
||||
ident = "acpki_" + ident
|
||||
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
||||
|
||||
self.config["hw_rev"] = description["hw_rev"]
|
||||
|
||||
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
||||
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
||||
|
||||
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
||||
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
|
||||
self.csr_devices.append("rtio_crg")
|
||||
self.rustc_cfg["has_rtio_crg"] = None
|
||||
|
||||
fix_serdes_timing_path(platform)
|
||||
|
||||
data_pads = [platform.request("sfp", i) for i in range(4)]
|
||||
|
||||
self.submodules.drtio_transceiver = gtx_7series.GTX(
|
||||
@ -408,28 +427,30 @@ class GenericSatellite(SoCCore):
|
||||
# and registered in PS interface
|
||||
# manually, because software refers to rx/tx by halves of entire memory block, not names
|
||||
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
|
||||
self.rustc_cfg["has_drtio"] = None
|
||||
self.rustc_cfg["has_drtio_routing"] = None
|
||||
self.config["HAS_DRTIO"] = None
|
||||
self.config["HAS_DRTIO_ROUTING"] = None
|
||||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
||||
self.add_csr_group("drtiorep", drtiorep_csr_group)
|
||||
|
||||
if self.acpki:
|
||||
self.rustc_cfg["ki_impl"] = "acp"
|
||||
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.rustc_cfg["ki_impl"] = "csr"
|
||||
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.local_io = SyncRTIO(self.rtio_tsc, self.rtio_channels)
|
||||
self.submodules.local_io = SyncRTIO(
|
||||
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
|
||||
)
|
||||
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
||||
|
||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||
@ -445,7 +466,8 @@ class GenericSatellite(SoCCore):
|
||||
self.csr_devices.append("rtio_moninj")
|
||||
|
||||
rtio_clk_period = 1e9/rtio_clk_freq
|
||||
self.rustc_cfg["rtio_frequency"] = str(rtio_clk_freq/1e6)
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
||||
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)
|
||||
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("cdr_clk"),
|
||||
@ -455,9 +477,8 @@ class GenericSatellite(SoCCore):
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||
self.csr_devices.append("siphaser")
|
||||
self.rustc_cfg["has_si5324"] = None
|
||||
self.rustc_cfg["has_siphaser"] = None
|
||||
self.rustc_cfg["si5324_soft_reset"] = None
|
||||
self.config["HAS_SI5324"] = None
|
||||
self.config["SI5324_SOFT_RESET"] = None
|
||||
|
||||
gtx0 = self.drtio_transceiver.gtxs[0]
|
||||
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
|
||||
@ -471,10 +492,15 @@ class GenericSatellite(SoCCore):
|
||||
self.crg.cd_sys.clk, gtx.rxoutclk)
|
||||
|
||||
if has_grabber:
|
||||
self.rustc_cfg["has_grabber"] = None
|
||||
self.config["HAS_GRABBER"] = None
|
||||
self.add_csr_group("grabber", self.grabber_csr_group)
|
||||
# no RTIO CRG here
|
||||
|
||||
|
||||
self.submodules.virtual_leds = virtual_leds.VirtualLeds()
|
||||
self.csr_devices.append("virtual_leds")
|
||||
|
||||
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
|
||||
for i, channel in enumerate(self.drtio_transceiver.channels)]
|
||||
|
||||
def write_mem_file(soc, filename):
|
||||
with open(filename, "w") as f:
|
||||
@ -490,11 +516,14 @@ def write_csr_file(soc, filename):
|
||||
|
||||
def write_rustc_cfg_file(soc, filename):
|
||||
with open(filename, "w") as f:
|
||||
for k, v in sorted(soc.rustc_cfg.items(), key=itemgetter(0)):
|
||||
if v is None:
|
||||
f.write("{}\n".format(k))
|
||||
else:
|
||||
f.write("{}=\"{}\"\n".format(k, v))
|
||||
for name, origin, busword, obj in soc.get_csr_regions():
|
||||
f.write("has_{}\n".format(name.lower()))
|
||||
for name, value in soc.get_constants():
|
||||
if name.upper().startswith("CONFIG_"):
|
||||
if value is None:
|
||||
f.write("{}\n".format(name.lower()[7:]))
|
||||
else:
|
||||
f.write("{}=\"{}\"\n".format(name.lower()[7:], str(value)))
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -14,7 +14,7 @@ from misoc.integration import cpu_interface
|
||||
from misoc.cores import gpio
|
||||
|
||||
from artiq.gateware import rtio, nist_clock, nist_qc2
|
||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
|
||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2, edge_counter
|
||||
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
|
||||
from artiq.gateware.drtio.transceiver import gtx_7series
|
||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
||||
@ -320,7 +320,7 @@ class _MasterBase(SoCCore):
|
||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
||||
self.csr_devices.append("rtio_moninj")
|
||||
|
||||
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
|
||||
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.cri_con.switch.slave,
|
||||
self.ps7.s_axi_hp1)
|
||||
self.csr_devices.append("rtio_analyzer")
|
||||
|
||||
@ -451,6 +451,7 @@ class _SatelliteBase(SoCCore):
|
||||
def add_rtio(self, rtio_channels):
|
||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
||||
self.csr_devices.append("rtio_moninj")
|
||||
self.rustc_cfg["has_rtio_moninj"] = None
|
||||
|
||||
if self.acpki:
|
||||
self.rustc_cfg["ki_impl"] = "acp"
|
||||
@ -562,12 +563,16 @@ class _NIST_QC2_RTIO:
|
||||
platform.add_extension(pmod1_33)
|
||||
|
||||
rtio_channels = []
|
||||
edge_counter_phy = []
|
||||
|
||||
# All TTL channels are In+Out capable
|
||||
for i in range(40):
|
||||
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
|
||||
# first four TTLs will also have edge counters
|
||||
if i < 4:
|
||||
edge_counter_phy.append(phy)
|
||||
|
||||
# no SMA GPIO, replaced with PMOD1_0
|
||||
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
|
||||
@ -606,6 +611,11 @@ class _NIST_QC2_RTIO:
|
||||
platform.request("dds", backplane_offset), 12, onehot=True)
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
|
||||
for phy in edge_counter_phy:
|
||||
counter = edge_counter.SimpleEdgeCounter(phy.input_state)
|
||||
self.submodules += counter
|
||||
rtio_channels.append(rtio.Channel.from_phy(counter))
|
||||
|
||||
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
|
||||
rtio_channels.append(rtio.LogChannel())
|
||||
|
163
src/libboard_artiq/src/grabber.rs
Normal file
163
src/libboard_artiq/src/grabber.rs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
177
src/libboard_artiq/src/io_expander.rs
Normal file
177
src/libboard_artiq/src/io_expander.rs
Normal file
@ -0,0 +1,177 @@
|
||||
use libboard_zynq::i2c;
|
||||
use log::info;
|
||||
|
||||
use crate::pl::csr;
|
||||
|
||||
// Only the bare minimum registers. Bits/IO connections equivalent between IC types.
|
||||
struct Registers {
|
||||
// PCA9539 equivalent register names in comments
|
||||
iodira: u8, // Configuration Port 0
|
||||
iodirb: u8, // Configuration Port 1
|
||||
gpioa: u8, // Output Port 0
|
||||
gpiob: u8, // Output Port 1
|
||||
}
|
||||
|
||||
//IO expanders pins
|
||||
const IODIR_OUT_SFP_TX_DISABLE: u8 = 0x02;
|
||||
const IODIR_OUT_SFP_LED: u8 = 0x40;
|
||||
#[cfg(hw_rev = "v1.0")]
|
||||
const IODIR_OUT_SFP0_LED: u8 = 0x40;
|
||||
#[cfg(hw_rev = "v1.1")]
|
||||
const IODIR_OUT_SFP0_LED: u8 = 0x80;
|
||||
|
||||
//IO expander port direction
|
||||
const IODIR0: [u8; 2] = [
|
||||
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP0_LED,
|
||||
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED,
|
||||
];
|
||||
|
||||
const IODIR1: [u8; 2] = [
|
||||
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED,
|
||||
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED,
|
||||
];
|
||||
|
||||
pub struct IoExpander {
|
||||
address: u8,
|
||||
virtual_led_mapping: &'static [(u8, u8, u8)],
|
||||
iodir: [u8; 2],
|
||||
out_current: [u8; 2],
|
||||
out_target: [u8; 2],
|
||||
registers: Registers,
|
||||
}
|
||||
|
||||
impl IoExpander {
|
||||
pub fn new(i2c: &mut i2c::I2c, index: u8) -> Result<Self, &'static str> {
|
||||
#[cfg(hw_rev = "v1.0")]
|
||||
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)];
|
||||
#[cfg(hw_rev = "v1.1")]
|
||||
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 7), (1, 1, 6)];
|
||||
|
||||
const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)];
|
||||
|
||||
// Both expanders on SHARED I2C bus
|
||||
let mut io_expander = match index {
|
||||
0 => IoExpander {
|
||||
address: 0x40,
|
||||
virtual_led_mapping: &VIRTUAL_LED_MAPPING0,
|
||||
iodir: IODIR0,
|
||||
out_current: [0; 2],
|
||||
out_target: [0; 2],
|
||||
registers: Registers {
|
||||
iodira: 0x00,
|
||||
iodirb: 0x01,
|
||||
gpioa: 0x12,
|
||||
gpiob: 0x13,
|
||||
},
|
||||
},
|
||||
1 => IoExpander {
|
||||
address: 0x42,
|
||||
virtual_led_mapping: &VIRTUAL_LED_MAPPING1,
|
||||
iodir: IODIR1,
|
||||
out_current: [0; 2],
|
||||
out_target: [0; 2],
|
||||
registers: Registers {
|
||||
iodira: 0x00,
|
||||
iodirb: 0x01,
|
||||
gpioa: 0x12,
|
||||
gpiob: 0x13,
|
||||
},
|
||||
},
|
||||
_ => return Err("incorrect I/O expander index"),
|
||||
};
|
||||
if !io_expander.check_ack(i2c)? {
|
||||
info!(
|
||||
"MCP23017 io expander {} not found. Checking for PCA9539.",
|
||||
index
|
||||
);
|
||||
io_expander.address += 0xa8; // translate to PCA9539 addresses (see schematic)
|
||||
io_expander.registers = Registers {
|
||||
iodira: 0x06,
|
||||
iodirb: 0x07,
|
||||
gpioa: 0x02,
|
||||
gpiob: 0x03,
|
||||
};
|
||||
if !io_expander.check_ack(i2c)? {
|
||||
return Err("Neither MCP23017 nor PCA9539 io expander found.");
|
||||
};
|
||||
}
|
||||
Ok(io_expander)
|
||||
}
|
||||
|
||||
fn select(&self, i2c: &mut i2c::I2c) -> Result<(), &'static str> {
|
||||
i2c.pca954x_select(0x70, None)?;
|
||||
i2c.pca954x_select(0x71, Some(3))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, i2c: &mut i2c::I2c, addr: u8, value: u8) -> Result<(), &'static str> {
|
||||
i2c.start()?;
|
||||
i2c.write(self.address)?;
|
||||
i2c.write(addr)?;
|
||||
i2c.write(value)?;
|
||||
i2c.stop()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_ack(&self, i2c: &mut i2c::I2c) -> Result<bool, &'static str> {
|
||||
// Check for ack from io expander
|
||||
self.select(i2c)?;
|
||||
i2c.start()?;
|
||||
let ack = i2c.write(self.address)?;
|
||||
i2c.stop()?;
|
||||
Ok(ack)
|
||||
}
|
||||
|
||||
fn update_iodir(&self, i2c: &mut i2c::I2c) -> Result<(), &'static str> {
|
||||
self.write(i2c, self.registers.iodira, self.iodir[0])?;
|
||||
self.write(i2c, self.registers.iodirb, self.iodir[1])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init(&mut self, i2c: &mut i2c::I2c) -> Result<(), &'static str> {
|
||||
self.select(i2c)?;
|
||||
self.update_iodir(i2c)?;
|
||||
|
||||
self.out_current[0] = 0x00;
|
||||
self.write(i2c, self.registers.gpioa, 0x00)?;
|
||||
self.out_current[1] = 0x00;
|
||||
self.write(i2c, self.registers.gpiob, 0x00)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_oe(&mut self, i2c: &mut i2c::I2c, port: u8, outputs: u8) -> Result<(), &'static str> {
|
||||
self.iodir[port as usize] &= !outputs;
|
||||
self.update_iodir(i2c)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set(&mut self, port: u8, bit: u8, high: bool) {
|
||||
if high {
|
||||
self.out_target[port as usize] |= 1 << bit;
|
||||
} else {
|
||||
self.out_target[port as usize] &= !(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn service(&mut self, i2c: &mut i2c::I2c) -> Result<(), &'static str> {
|
||||
#[cfg(has_virtual_leds)]
|
||||
for (led, port, bit) in self.virtual_led_mapping.iter() {
|
||||
let level = unsafe { csr::virtual_leds::status_read() >> led & 1 };
|
||||
self.set(*port, *bit, level != 0);
|
||||
}
|
||||
|
||||
if self.out_target != self.out_current {
|
||||
self.select(i2c)?;
|
||||
if self.out_target[0] != self.out_current[0] {
|
||||
self.write(i2c, self.registers.gpioa, self.out_target[0])?;
|
||||
self.out_current[0] = self.out_target[0];
|
||||
}
|
||||
if self.out_target[1] != self.out_current[1] {
|
||||
self.write(i2c, self.registers.gpiob, self.out_target[1])?;
|
||||
self.out_current[1] = self.out_target[1];
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -27,6 +27,10 @@ pub mod drtioaux_async;
|
||||
#[cfg(has_drtio)]
|
||||
#[path = "../../../build/mem.rs"]
|
||||
pub mod mem;
|
||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
||||
pub mod io_expander;
|
||||
#[cfg(has_grabber)]
|
||||
pub mod grabber;
|
||||
|
||||
use core::{cmp, str};
|
||||
use libboard_zynq::slcr;
|
||||
|
@ -427,8 +427,8 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||
moninj::start(timer, aux_mutex, drtio_routing_table);
|
||||
|
||||
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
|
||||
let idle_kernel = Rc::new(cfg.read("idle").ok());
|
||||
if let Ok(buffer) = cfg.read("startup") {
|
||||
let idle_kernel = Rc::new(cfg.read("idle_kernel").ok());
|
||||
if let Ok(buffer) = cfg.read("startup_kernel") {
|
||||
info!("Loading startup kernel...");
|
||||
if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) {
|
||||
info!("Starting startup kernel...");
|
||||
|
@ -13,7 +13,7 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use core::mem;
|
||||
use cslice::CSlice;
|
||||
use cslice::{AsCSlice, CSlice};
|
||||
use unwind as uw;
|
||||
use libc::{c_int, c_void, uintptr_t};
|
||||
use log::{trace, error};
|
||||
@ -218,8 +218,6 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
|
||||
}
|
||||
|
||||
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
||||
use cslice::AsCSlice;
|
||||
|
||||
let count = EXCEPTION_BUFFER.exception_count;
|
||||
let stack = &mut EXCEPTION_BUFFER.exception_stack;
|
||||
let diff = exception as isize - EXCEPTION_BUFFER.exceptions.as_ptr() as isize;
|
||||
@ -405,7 +403,7 @@ extern fn stop_fn(_version: c_int,
|
||||
}
|
||||
}
|
||||
|
||||
// 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] = [
|
||||
("RuntimeError", 0),
|
||||
("RTIOUnderflow", 1),
|
||||
@ -453,3 +451,29 @@ macro_rules! artiq_raise {
|
||||
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) };
|
||||
}
|
@ -280,6 +280,13 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||
}
|
||||
api!(yn = yn)
|
||||
},
|
||||
/*
|
||||
* 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()
|
||||
.find(|&&(exported, _)| exported.as_bytes() == required)
|
||||
|
@ -116,7 +116,7 @@ pub extern fn dma_record_output(target: i32, word: i32) {
|
||||
}
|
||||
}
|
||||
|
||||
pub extern fn dma_record_output_wide(target: i32, words: CSlice<i32>) {
|
||||
pub extern fn dma_record_output_wide(target: i32, words: &CSlice<i32>) {
|
||||
assert!(words.len() <= 16); // enforce the hardware limit
|
||||
|
||||
unsafe {
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
||||
use core::cell::RefCell;
|
||||
use log::{info, warn, error};
|
||||
|
||||
use libboard_zynq::{timer::GlobalTimer, mpcore, gic};
|
||||
@ -21,6 +23,8 @@ use void::Void;
|
||||
use libconfig::Config;
|
||||
use libcortex_a9::l2c::enable_l2_cache;
|
||||
use libboard_artiq::{logger, identifier_read, init_gateware, pl};
|
||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
||||
use libboard_artiq::io_expander;
|
||||
|
||||
const ASYNC_ERROR_COLLISION: u8 = 1 << 0;
|
||||
const ASYNC_ERROR_BUSY: u8 = 1 << 1;
|
||||
@ -89,6 +93,39 @@ async fn report_async_rtio_errors() {
|
||||
|
||||
|
||||
|
||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
||||
async fn io_expanders_service(
|
||||
i2c_bus: RefCell<&mut libboard_zynq::i2c::I2c>,
|
||||
io_expander0: RefCell<io_expander::IoExpander>,
|
||||
io_expander1: RefCell<io_expander::IoExpander>,
|
||||
) {
|
||||
loop {
|
||||
task::r#yield().await;
|
||||
io_expander0
|
||||
.borrow_mut()
|
||||
.service(&mut i2c_bus.borrow_mut())
|
||||
.expect("I2C I/O expander #0 service failed");
|
||||
io_expander1
|
||||
.borrow_mut()
|
||||
.service(&mut i2c_bus.borrow_mut())
|
||||
.expect("I2C I/O expander #1 service failed");
|
||||
}
|
||||
}
|
||||
#[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];
|
||||
|
||||
#[no_mangle]
|
||||
@ -109,9 +146,33 @@ pub fn main_core0() {
|
||||
gic::InterruptController::gic(mpcore::RegisterBlock::mpcore()).enable_interrupts();
|
||||
|
||||
init_gateware();
|
||||
info!("detected gateware: {}", identifier_read(&mut [0; 64]));
|
||||
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
|
||||
|
||||
i2c::init();
|
||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
||||
{
|
||||
let i2c_bus = unsafe { (i2c::I2C_BUS).as_mut().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
|
||||
.init(i2c_bus)
|
||||
.expect("I2C I/O expander #0 initialization failed");
|
||||
io_expander1
|
||||
.init(i2c_bus)
|
||||
.expect("I2C I/O expander #1 initialization failed");
|
||||
// Drive TX_DISABLE to false on SFP0..3
|
||||
io_expander0.set(0, 1, false);
|
||||
io_expander1.set(0, 1, false);
|
||||
io_expander0.set(1, 1, false);
|
||||
io_expander1.set(1, 1, false);
|
||||
io_expander0.service(i2c_bus).unwrap();
|
||||
io_expander1.service(i2c_bus).unwrap();
|
||||
task::spawn(io_expanders_service(
|
||||
RefCell::new(i2c_bus),
|
||||
RefCell::new(io_expander0),
|
||||
RefCell::new(io_expander1),
|
||||
));
|
||||
}
|
||||
|
||||
let cfg = match Config::new() {
|
||||
Ok(cfg) => cfg,
|
||||
@ -125,5 +186,8 @@ pub fn main_core0() {
|
||||
|
||||
task::spawn(report_async_rtio_errors());
|
||||
|
||||
#[cfg(has_grabber)]
|
||||
task::spawn(grabber::grabber_thread(timer));
|
||||
|
||||
comms::main(timer, cfg);
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ mod remote_moninj {
|
||||
use libboard_artiq::drtioaux_async;
|
||||
use crate::rtio_mgt::drtio;
|
||||
use log::error;
|
||||
use libasync::delay;
|
||||
|
||||
pub async fn read_probe(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, probe: i8) -> i64 {
|
||||
let reply = drtio::aux_transact(aux_mutex, linkno, &drtioaux_async::Packet::MonitorRequest {
|
||||
@ -77,7 +78,7 @@ mod remote_moninj {
|
||||
0
|
||||
}
|
||||
|
||||
pub async fn inject(aux_mutex: &Rc<Mutex<bool>>, _timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8, value: i8) {
|
||||
pub async fn inject(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8, value: i8) {
|
||||
let _lock = aux_mutex.lock();
|
||||
drtioaux_async::send(linkno, &drtioaux_async::Packet::InjectionRequest {
|
||||
destination: destination,
|
||||
@ -85,6 +86,9 @@ mod remote_moninj {
|
||||
overrd: overrd as _,
|
||||
value: value as _
|
||||
}).await.unwrap();
|
||||
// give time for the satellite to process the request before releasing mutex
|
||||
let mut countdown = timer.countdown();
|
||||
delay(&mut countdown, Milliseconds(10)).await;
|
||||
}
|
||||
|
||||
pub async fn read_injection_status(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8) -> i8 {
|
||||
|
@ -15,22 +15,85 @@ use crate::proto_async;
|
||||
use self::tag::{Tag, TagIterator, split_tag};
|
||||
|
||||
#[inline]
|
||||
fn alignment_offset(alignment: isize, ptr: isize) -> isize {
|
||||
(alignment - ptr % alignment) % alignment
|
||||
fn round_up(val: usize, power_of_two: usize) -> usize {
|
||||
assert!(power_of_two.is_power_of_two());
|
||||
let max_rem = power_of_two - 1;
|
||||
(val + max_rem) & (!max_rem)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
|
||||
let alignment = core::mem::align_of::<T>() as isize;
|
||||
let fix = alignment_offset(alignment, ptr as isize);
|
||||
((ptr as isize) + fix) as *const T
|
||||
round_up_const(ptr, core::mem::align_of::<T>()) as *const T
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
|
||||
let alignment = core::mem::align_of::<T>() as isize;
|
||||
let fix = alignment_offset(alignment, ptr as isize);
|
||||
((ptr as isize) + fix) 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`,
|
||||
/// 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))
|
||||
@ -71,120 +134,63 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
|
||||
})
|
||||
}
|
||||
Tag::Tuple(it, arity) => {
|
||||
*data = (*data).offset(alignment_offset(tag.alignment() as isize, *data as isize));
|
||||
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?;
|
||||
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: u32 }
|
||||
consume_value!(*mut List, |ptr| {
|
||||
let length = proto_async::read_i32(stream).await? as usize;
|
||||
struct List { elements: *mut (), length: usize }
|
||||
consume_value!(*mut List, |ptr_to_list| {
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
let data_size = tag.size() * length as usize +
|
||||
match tag {
|
||||
Tag::Int64 | Tag::Float64 => 4,
|
||||
_ => 0
|
||||
};
|
||||
let data = alloc(data_size + 8).await as *mut u8;
|
||||
*ptr = data as *mut List;
|
||||
let ptr = data as *mut List;
|
||||
let data = data.offset(8);
|
||||
let length = proto_async::read_i32(stream).await? as usize;
|
||||
|
||||
let alignment = tag.alignment();
|
||||
let mut data = data.offset(alignment_offset(alignment as isize, data as isize)) as *mut ();
|
||||
(*ptr).length = length as u32;
|
||||
(*ptr).elements = data;
|
||||
match tag {
|
||||
Tag::Bool => {
|
||||
let ptr = data as *mut u8;
|
||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
||||
proto_async::read_chunk(stream, dest).await?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let ptr = data as *mut u32;
|
||||
// reading as raw bytes and do endianness conversion later
|
||||
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 = data 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);
|
||||
},
|
||||
_ => {
|
||||
for _ in 0..(*ptr).length as usize {
|
||||
recv_value(stream, tag, &mut data, alloc).await?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
// 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| {
|
||||
let mut total_len: u32 = 1;
|
||||
// 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 u32;
|
||||
let len = proto_async::read_i32(stream).await? as usize;
|
||||
total_len *= len;
|
||||
consume_value!(u32, |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 data_size = elt_tag.size() * total_len as usize +
|
||||
match elt_tag {
|
||||
Tag::Int64 | Tag::Float64 => 4,
|
||||
_ => 0
|
||||
};
|
||||
let mut data = alloc(data_size).await;
|
||||
|
||||
let alignment = tag.alignment();
|
||||
data = data.offset(alignment_offset(alignment as isize, data as isize));
|
||||
*buffer = data;
|
||||
let length = total_len as usize;
|
||||
match elt_tag {
|
||||
Tag::Bool => {
|
||||
let ptr = data as *mut u8;
|
||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
||||
proto_async::read_chunk(stream, dest).await?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let ptr = data 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 = data 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);
|
||||
},
|
||||
_ => {
|
||||
for _ in 0..length {
|
||||
recv_value(stream, elt_tag, &mut data, alloc).await?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
*buffer = alloc(elt_tag.size() * total_len).await;
|
||||
recv_elements(stream, elt_tag, total_len, *buffer, alloc).await
|
||||
})
|
||||
}
|
||||
Tag::Range(it) => {
|
||||
*data = (*data).offset(alignment_offset(tag.alignment() as isize, *data as isize));
|
||||
*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?;
|
||||
@ -211,6 +217,36 @@ pub async fn recv_return<F>(stream: &TcpStream, tag_bytes: &[u8], data: *mut (),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn send_elements<W>(writer: &mut W, elt_tag: Tag, length: usize, data: *const ())
|
||||
-> Result<(), Error>
|
||||
where W: Write + ?Sized
|
||||
{
|
||||
writer.write_u8(elt_tag.as_u8())?;
|
||||
match elt_tag {
|
||||
// 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
|
||||
Tag::Bool => {
|
||||
let slice = core::slice::from_raw_parts(data as *const u8, length);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let slice = core::slice::from_raw_parts(data as *const u8, length * 4);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let slice = core::slice::from_raw_parts(data as *const u8, length * 8);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
_ => {
|
||||
let mut data = data;
|
||||
for _ in 0..length {
|
||||
send_value(writer, elt_tag, &mut data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
-> Result<(), Error>
|
||||
where W: Write + ?Sized
|
||||
@ -244,46 +280,23 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
Tag::Tuple(it, arity) => {
|
||||
let mut it = it.clone();
|
||||
writer.write_u8(arity)?;
|
||||
let mut max_alignment = 0;
|
||||
for _ in 0..arity {
|
||||
let tag = it.next().expect("truncated tag");
|
||||
max_alignment = core::cmp::max(max_alignment, tag.alignment());
|
||||
send_value(writer, tag, data)?
|
||||
}
|
||||
*data = round_up_const(*data, max_alignment);
|
||||
Ok(())
|
||||
}
|
||||
Tag::List(it) => {
|
||||
#[repr(C)]
|
||||
struct List { elements: *const (), length: u32 }
|
||||
consume_value!(&List, |ptr| {
|
||||
let length = (**ptr).length as isize;
|
||||
let length = (**ptr).length as usize;
|
||||
writer.write_u32((*ptr).length)?;
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
let mut data = (**ptr).elements;
|
||||
writer.write_u8(tag.as_u8())?;
|
||||
match tag {
|
||||
Tag::Bool => {
|
||||
// we can pretend this is u8...
|
||||
let ptr1 = align_ptr::<u8>(data);
|
||||
let slice = core::slice::from_raw_parts(ptr1, length as usize);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let ptr1 = align_ptr::<i32>(data);
|
||||
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 4);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let ptr1 = align_ptr::<i64>(data);
|
||||
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 8);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
// non-primitive types, not sure if this would happen but we can handle it...
|
||||
_ => {
|
||||
for _ in 0..length {
|
||||
send_value(writer, tag, &mut data)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
send_elements(writer, tag, length, (**ptr).elements)
|
||||
})
|
||||
}
|
||||
Tag::Array(it, num_dims) => {
|
||||
@ -298,33 +311,8 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
total_len *= *len;
|
||||
})
|
||||
}
|
||||
let mut data = *buffer;
|
||||
let length = total_len as isize;
|
||||
writer.write_u8(elt_tag.as_u8())?;
|
||||
match elt_tag {
|
||||
Tag::Bool => {
|
||||
let ptr1 = align_ptr::<u8>(data);
|
||||
let slice = core::slice::from_raw_parts(ptr1, length as usize);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let ptr1 = align_ptr::<i32>(data);
|
||||
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 4);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let ptr1 = align_ptr::<i64>(data);
|
||||
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 8);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
// non-primitive types, not sure if this would happen but we can handle it...
|
||||
_ => {
|
||||
for _ in 0..length {
|
||||
send_value(writer, elt_tag, &mut data)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
let length = total_len as usize;
|
||||
send_elements(writer, elt_tag, length, *buffer)
|
||||
})
|
||||
}
|
||||
Tag::Range(it) => {
|
||||
@ -448,18 +436,15 @@ mod tag {
|
||||
let it = it.clone();
|
||||
it.take(3).map(|t| t.alignment()).max().unwrap()
|
||||
}
|
||||
// CSlice basically
|
||||
Tag::Bytes | Tag::String | Tag::ByteArray =>
|
||||
// the ptr/length(s) pair is basically CSlice
|
||||
Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) | Tag::Array(_, _) =>
|
||||
core::mem::align_of::<CSlice<()>>(),
|
||||
// array buffer is allocated, so no need for alignment first
|
||||
Tag::List(_) | Tag::Array(_, _) => 1,
|
||||
// will not be sent from the host
|
||||
_ => unreachable!("unexpected tag from host")
|
||||
Tag::Keyword(_) => unreachable!("Tag::Keyword should not appear in composite types"),
|
||||
Tag::Object => core::mem::align_of::<u32>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self) -> usize {
|
||||
use super::alignment_offset;
|
||||
match self {
|
||||
Tag::None => 0,
|
||||
Tag::Bool => 1,
|
||||
@ -471,13 +456,18 @@ mod tag {
|
||||
Tag::ByteArray => 8,
|
||||
Tag::Tuple(it, arity) => {
|
||||
let mut size = 0;
|
||||
let mut max_alignment = 0;
|
||||
let mut it = it.clone();
|
||||
for _ in 0..arity {
|
||||
let tag = it.next().expect("truncated tag");
|
||||
let alignment = tag.alignment();
|
||||
max_alignment = core::cmp::max(max_alignment, alignment);
|
||||
size = super::round_up(size, alignment);
|
||||
size += tag.size();
|
||||
// includes padding
|
||||
size += alignment_offset(tag.alignment() as isize, size as isize) as usize;
|
||||
}
|
||||
// Take into account any tail padding (if element(s) with largest
|
||||
// alignment are not at the end).
|
||||
size = super::round_up(size, max_alignment);
|
||||
size
|
||||
}
|
||||
Tag::List(_) => 4,
|
||||
|
@ -11,6 +11,7 @@ pub const RTIO_O_STATUS_UNDERFLOW: i32 = 2;
|
||||
pub const RTIO_O_STATUS_DESTINATION_UNREACHABLE: i32 = 4;
|
||||
pub const RTIO_I_STATUS_WAIT_EVENT: i32 = 1;
|
||||
pub const RTIO_I_STATUS_OVERFLOW: i32 = 2;
|
||||
#[allow(unused)]
|
||||
pub const RTIO_I_STATUS_WAIT_STATUS: i32 = 4; // TODO
|
||||
pub const RTIO_I_STATUS_DESTINATION_UNREACHABLE: i32 = 8;
|
||||
|
||||
|
@ -94,7 +94,7 @@ pub extern fn output(target: i32, data: i32) {
|
||||
}
|
||||
}
|
||||
|
||||
pub extern fn output_wide(target: i32, data: CSlice<i32>) {
|
||||
pub extern fn output_wide(target: i32, data: &CSlice<i32>) {
|
||||
unsafe {
|
||||
csr::rtio::target_write(target as u32);
|
||||
// writing target clears o_data
|
||||
|
@ -20,8 +20,12 @@ extern crate alloc;
|
||||
|
||||
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds, print, println, mpcore, gic, stdio};
|
||||
use libsupport_zynq::ram;
|
||||
#[cfg(has_grabber)]
|
||||
use libboard_artiq::grabber;
|
||||
#[cfg(has_si5324)]
|
||||
use libboard_artiq::si5324;
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
use libboard_artiq::io_expander;
|
||||
use libboard_artiq::{pl::csr, drtio_routing, drtioaux, logger, identifier_read, init_gateware};
|
||||
use libcortex_a9::{spin_lock_yield, interrupt_handler, regs::{MPIDR, SP}, notify_spin_lock, asm, l2c::enable_l2_cache};
|
||||
use libregister::{RegisterW, RegisterR};
|
||||
@ -204,7 +208,7 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
|
||||
}
|
||||
|
||||
drtioaux::Packet::MonitorRequest { destination: _destination, channel: _channel, probe: _probe } => {
|
||||
drtioaux::Packet::MonitorRequest { destination: _destination, channel, probe } => {
|
||||
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
|
||||
let value;
|
||||
#[cfg(has_rtio_moninj)]
|
||||
@ -221,8 +225,8 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||
let reply = drtioaux::Packet::MonitorReply { value: value };
|
||||
drtioaux::send(0, &reply)
|
||||
},
|
||||
drtioaux::Packet::InjectionRequest { destination: _destination, channel: _channel,
|
||||
overrd: _overrd, value: _value } => {
|
||||
drtioaux::Packet::InjectionRequest { destination: _destination, channel,
|
||||
overrd, value } => {
|
||||
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
|
||||
#[cfg(has_rtio_moninj)]
|
||||
unsafe {
|
||||
@ -233,7 +237,7 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||
Ok(())
|
||||
},
|
||||
drtioaux::Packet::InjectionStatusRequest { destination: _destination,
|
||||
channel: _channel, overrd: _overrd } => {
|
||||
channel, overrd } => {
|
||||
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
|
||||
let value;
|
||||
#[cfg(has_rtio_moninj)]
|
||||
@ -415,6 +419,8 @@ fn hardware_tick(ts: &mut u64, timer: &mut GlobalTimer) {
|
||||
if now > ts_ms {
|
||||
ts_ms = now + Milliseconds(200);
|
||||
*ts = ts_ms.0;
|
||||
#[cfg(has_grabber)]
|
||||
grabber::tick();
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,6 +475,27 @@ pub extern fn main_core0() -> i32 {
|
||||
let mut i2c = I2c::i2c0();
|
||||
i2c.init().expect("I2C initialization failed");
|
||||
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
let (mut io_expander0, mut io_expander1);
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
{
|
||||
io_expander0 = io_expander::IoExpander::new(&mut i2c, 0).unwrap();
|
||||
io_expander1 = io_expander::IoExpander::new(&mut i2c, 1).unwrap();
|
||||
io_expander0
|
||||
.init(&mut i2c)
|
||||
.expect("I2C I/O expander #0 initialization failed");
|
||||
io_expander1
|
||||
.init(&mut i2c)
|
||||
.expect("I2C I/O expander #1 initialization failed");
|
||||
// Drive TX_DISABLE to false on SFP0..3
|
||||
io_expander0.set(0, 1, false);
|
||||
io_expander1.set(0, 1, false);
|
||||
io_expander0.set(1, 1, false);
|
||||
io_expander1.set(1, 1, false);
|
||||
io_expander0.service(&mut i2c).unwrap();
|
||||
io_expander1.service(&mut i2c).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(has_si5324)]
|
||||
si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324");
|
||||
|
||||
@ -501,6 +528,16 @@ pub extern fn main_core0() -> i32 {
|
||||
for mut rep in repeaters.iter_mut() {
|
||||
rep.service(&routing_table, rank, &mut timer);
|
||||
}
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
{
|
||||
io_expander0
|
||||
.service(&mut i2c)
|
||||
.expect("I2C I/O expander #0 service failed");
|
||||
io_expander1
|
||||
.service(&mut i2c)
|
||||
.expect("I2C I/O expander #1 service failed");
|
||||
}
|
||||
|
||||
hardware_tick(&mut hardware_tick_ts, &mut timer);
|
||||
}
|
||||
|
||||
@ -522,6 +559,15 @@ pub extern fn main_core0() -> i32 {
|
||||
for mut rep in repeaters.iter_mut() {
|
||||
rep.service(&routing_table, rank, &mut timer);
|
||||
}
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
{
|
||||
io_expander0
|
||||
.service(&mut i2c)
|
||||
.expect("I2C I/O expander #0 service failed");
|
||||
io_expander1
|
||||
.service(&mut i2c)
|
||||
.expect("I2C I/O expander #1 service failed");
|
||||
}
|
||||
hardware_tick(&mut hardware_tick_ts, &mut timer);
|
||||
if drtiosat_tsc_loaded() {
|
||||
info!("TSC loaded from uplink");
|
||||
|
Loading…
Reference in New Issue
Block a user