Compare commits

..

No commits in common. "a4d1be00c050b6d03cd7e7e726e7c076d916fbb5" and "dc08c382a23329e18f91fc61056d15d5174ac5b6" have entirely different histories.

52 changed files with 792 additions and 3258 deletions

8
.gitignore vendored
View File

@ -3,11 +3,3 @@ 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

View File

@ -58,6 +58,7 @@ Notes:
- When calling make, you need to specify both the variant and firmware type. - 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, 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.
License License
------- -------

84
flake.lock generated
View File

@ -11,11 +11,11 @@
"src-pythonparser": "src-pythonparser" "src-pythonparser": "src-pythonparser"
}, },
"locked": { "locked": {
"lastModified": 1696817638, "lastModified": 1691420543,
"narHash": "sha256-rbIN4Ll1VX4RXxDBlLGgTiQC6L6jZE9q8HYBxPhwCpY=", "narHash": "sha256-HG5eDcbY4jc6vMXPyiq0t82OMJIVXtNymqgchcuFZzU=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "d070826911e86b5ec9de958c4bcd93ef0b7fddb2", "rev": "df662c4262ea3d0b4cdf4d6ab3184ecbb1f3aafa",
"revCount": 8564, "revCount": 8462,
"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": 1664405593,
"narHash": "sha256-BdLddCWbvoEyakcGwhph9b5dIU1iA0hCQV7KYgU8nos=", "narHash": "sha256-yP441NerlLGig7n+9xHsx8yCtZ+Ggd0VqfBSzc20E04=",
"owner": "m-labs", "owner": "m-labs",
"repo": "artiq-comtools", "repo": "artiq-comtools",
"rev": "f522ef3dbc65961f17b2d3d41e927409d970fd79", "rev": "15ddac62813ef623a076ccf982b3bc63d314e651",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -51,15 +51,12 @@
} }
}, },
"flake-utils": { "flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1692799911, "lastModified": 1659877975,
"narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -71,11 +68,11 @@
"mozilla-overlay": { "mozilla-overlay": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1695805681, "lastModified": 1690536331,
"narHash": "sha256-1ElPLD8eFfnuIk0G52HGGpRtQZ4QPCjChRlEOfkZ5ro=", "narHash": "sha256-aRIf2FB2GTdfF7gl13WyETmiV/J7EhBGkSWXfZvlxcA=",
"owner": "mozilla", "owner": "mozilla",
"repo": "nixpkgs-mozilla", "repo": "nixpkgs-mozilla",
"rev": "6eabade97bc28d707a8b9d82ad13ef143836736e", "rev": "db89c8707edcffefcd8e738459d511543a339ff5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -87,11 +84,11 @@
"mozilla-overlay_2": { "mozilla-overlay_2": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1695805681, "lastModified": 1690536331,
"narHash": "sha256-1ElPLD8eFfnuIk0G52HGGpRtQZ4QPCjChRlEOfkZ5ro=", "narHash": "sha256-aRIf2FB2GTdfF7gl13WyETmiV/J7EhBGkSWXfZvlxcA=",
"owner": "mozilla", "owner": "mozilla",
"repo": "nixpkgs-mozilla", "repo": "nixpkgs-mozilla",
"rev": "6eabade97bc28d707a8b9d82ad13ef143836736e", "rev": "db89c8707edcffefcd8e738459d511543a339ff5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -118,11 +115,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1696697597, "lastModified": 1691328192,
"narHash": "sha256-q26Qv4DQ+h6IeozF2o1secyQG0jt2VUT3V0K58jr3pg=", "narHash": "sha256-w59N1zyDQ7SupfMJLFvtms/SIVbdryqlw5AS4+DiH+Y=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5a237aecb57296f67276ac9ab296a41c23981f56", "rev": "61676e4dcfeeb058f255294bcb08ea7f3bc3ce56",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -147,11 +144,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1693473454, "lastModified": 1691095538,
"narHash": "sha256-kr8Ur6JNW/xVRHdPn3ou980IAxg/n+f3ZQBHuJ1uaC4=", "narHash": "sha256-JX1Re8wzqg4odcv/QxPtEvO8Z4RO3ZbRKGQ+ZpDdtWc=",
"owner": "m-labs", "owner": "m-labs",
"repo": "sipyco", "repo": "sipyco",
"rev": "5467dcf9738673ab9a49e6f2377bda7c551b5f90", "rev": "c4f18adb658e3a546319425750cebeb8c88a016a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -163,11 +160,11 @@
"src-migen": { "src-migen": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1693990700, "lastModified": 1674045327,
"narHash": "sha256-qJLA03QcZ5S9DrqrseuzIQBTWS7rjAbYJxLYZEQ8rxA=", "narHash": "sha256-oYdeY0MbTReKbAwmSznnqw0wNawdInJoFJVWW3tesFA=",
"owner": "m-labs", "owner": "m-labs",
"repo": "migen", "repo": "migen",
"rev": "2cfee3e0db6fdca9b5918686ea77c93252e7cebd", "rev": "ccaee68e14d3636e1d8fb2e0864dd89b1b1f7384",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -179,11 +176,11 @@
"src-misoc": { "src-misoc": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1693709836, "lastModified": 1691370054,
"narHash": "sha256-YiCk05RYLzZu1CYkQ2r7XtjwVEqkUGTQn388uOls9tI=", "narHash": "sha256-ZI+H7gcQzx//TF5Jaf5iejd8PtPwiDAQ4redmb2WsDA=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "58dc4ee60d165ce9145cf3d904241fc154b6407f", "rev": "1b871635d768d6c5efbab846a1e07da12877aed9",
"revCount": 2448, "revCount": 2443,
"submodules": true, "submodules": true,
"type": "git", "type": "git",
"url": "https://github.com/m-labs/misoc.git" "url": "https://github.com/m-labs/misoc.git"
@ -210,21 +207,6 @@
"type": "github" "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": { "zynq-rs": {
"inputs": { "inputs": {
"mozilla-overlay": "mozilla-overlay_3", "mozilla-overlay": "mozilla-overlay_3",
@ -234,11 +216,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1693479539, "lastModified": 1691422018,
"narHash": "sha256-MppR7yxs3cjG7tQc82vX0MhyN71CJL2QWkM65F5hrFU=", "narHash": "sha256-a/EjMYrKgl3+iENlZdBsl3rMmF5Ey6eusT/mVuoJXkE=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "c15b54f92b3d4e125ae47a0dce7abe4b2bc9e054", "rev": "de42a5d1b28bec19849a14e999ee500550e50610",
"revCount": 628, "revCount": 627,
"type": "git", "type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs" "url": "https://git.m-labs.hk/m-labs/zynq-rs"
}, },

View File

@ -122,6 +122,9 @@
src = ./src; src = ./src;
cargoLock = { cargoLock = {
lockFile = src/Cargo.lock; lockFile = src/Cargo.lock;
outputHashes = {
"libasync-0.0.0" = "sha256-WvNMUekL4Elc55RdqX8XP43QPnBrK8Rbd0bsoI61E5U=";
};
}; };
nativeBuildInputs = [ nativeBuildInputs = [
@ -135,7 +138,6 @@
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="${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/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}
''; '';
@ -249,19 +251,18 @@
''; '';
}; };
fmt-check = pkgs.stdenvNoCC.mkDerivation { fmt-check = pkgs.stdenv.mkDerivation {
name = "fmt-check"; name = "fmt-check";
src = ./src; nativeBuildInputs = [
rust
];
nativeBuildInputs = [ rust pkgs.gnumake ]; phases = [ "buildPhase" ];
phases = [ "unpackPhase" "buildPhase" ];
buildPhase = buildPhase =
'' ''
export ZYNQ_RS=${zynq-rs} cd ${self}/src
make manifests
cargo fmt -- --check cargo fmt -- --check
touch $out touch $out
''; '';
@ -337,22 +338,18 @@
} // } //
(build { target = "zc706"; variant = "nist_clock"; }) // (build { target = "zc706"; variant = "nist_clock"; }) //
(build { target = "zc706"; variant = "nist_clock_master"; }) // (build { target = "zc706"; variant = "nist_clock_master"; }) //
(build { target = "zc706"; variant = "nist_clock_master_100mhz"; }) //
(build { target = "zc706"; variant = "nist_clock_satellite"; }) // (build { target = "zc706"; variant = "nist_clock_satellite"; }) //
(build { target = "zc706"; variant = "nist_clock_satellite_100mhz"; }) // (build { target = "zc706"; variant = "nist_clock_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "nist_qc2"; }) // (build { target = "zc706"; variant = "nist_qc2"; }) //
(build { target = "zc706"; variant = "nist_qc2_master"; }) // (build { target = "zc706"; variant = "nist_qc2_master"; }) //
(build { target = "zc706"; variant = "nist_qc2_master_100mhz"; }) //
(build { target = "zc706"; variant = "nist_qc2_satellite"; }) // (build { target = "zc706"; variant = "nist_qc2_satellite"; }) //
(build { target = "zc706"; variant = "nist_qc2_satellite_100mhz"; }) // (build { target = "zc706"; variant = "nist_qc2_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock"; }) // (build { target = "zc706"; variant = "acpki_nist_clock"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_master"; }) // (build { target = "zc706"; variant = "acpki_nist_clock_master"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_master_100mhz"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_satellite"; }) // (build { target = "zc706"; variant = "acpki_nist_clock_satellite"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_satellite_100mhz"; }) // (build { target = "zc706"; variant = "acpki_nist_clock_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2"; }) // (build { target = "zc706"; variant = "acpki_nist_qc2"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_master"; }) // (build { target = "zc706"; variant = "acpki_nist_qc2_master"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_master_100mhz"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite"; }) // (build { target = "zc706"; variant = "acpki_nist_qc2_satellite"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) // (build { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) //
(build { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) // (build { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) //
@ -380,7 +377,6 @@
]; ];
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 = "${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include";
ZYNQ_RS = "${zynq-rs}";
OPENOCD_ZYNQ = "${zynq-rs}/openocd"; OPENOCD_ZYNQ = "${zynq-rs}/openocd";
SZL = "${zynqpkgs.szl}"; SZL = "${zynqpkgs.szl}";
}; };

42
src/Cargo.lock generated
View File

@ -218,37 +218,10 @@ 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",
"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#67dbb5932fa8ff5f143983476f741f945871d286"
dependencies = [ dependencies = [
"embedded-hal", "embedded-hal",
"libcortex_a9", "libcortex_a9",
@ -271,7 +244,6 @@ 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",
@ -281,6 +253,7 @@ 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#67dbb5932fa8ff5f143983476f741f945871d286"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"embedded-hal", "embedded-hal",
@ -305,6 +278,7 @@ 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#67dbb5932fa8ff5f143983476f741f945871d286"
dependencies = [ dependencies = [
"core_io", "core_io",
"fatfs", "fatfs",
@ -315,6 +289,7 @@ 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#67dbb5932fa8ff5f143983476f741f945871d286"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"libregister", "libregister",
@ -330,6 +305,7 @@ 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#67dbb5932fa8ff5f143983476f741f945871d286"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"vcell", "vcell",
@ -339,6 +315,7 @@ 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#67dbb5932fa8ff5f143983476f741f945871d286"
dependencies = [ dependencies = [
"cc", "cc",
"compiler_builtins", "compiler_builtins",
@ -461,17 +438,18 @@ 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",
"unwind", "unwind",
@ -493,11 +471,7 @@ name = "satman"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"build_zynq", "build_zynq",
"core_io",
"cslice",
"embedded-hal", "embedded-hal",
"io",
"ksupport",
"libasync", "libasync",
"libboard_artiq", "libboard_artiq",
"libboard_zynq", "libboard_zynq",

View File

@ -5,7 +5,6 @@ members = [
"libdwarf", "libdwarf",
"libio", "libio",
"libunwind", "libunwind",
"libksupport",
"runtime", "runtime",
"satman" "satman"
] ]

View File

@ -7,20 +7,13 @@ runtime: ../build/runtime.bin
satman: ../build/satman.bin satman: ../build/satman.bin
.PHONY: all manifests .PHONY: all runtime_target satman_target
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 $(manifests) $(shell find . -type f -not -name Cargo.toml -print) ../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
cd runtime && \ cd runtime && \
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \ XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
cargo xbuild --release \ cargo xbuild --release \
@ -30,7 +23,7 @@ manifests: $(manifests)
../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 $(manifests) $(shell find . -type f -not -name Cargo.toml -print) ../build/firmware/armv7-none-eabihf/release/satman: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
cd satman && \ cd satman && \
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \ XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
cargo xbuild --release \ cargo xbuild --release \
@ -38,4 +31,4 @@ manifests: $(manifests)
--no-default-features --features=target_$(TARGET) --no-default-features --features=target_$(TARGET)
../build/satman.bin: ../build/firmware/armv7-none-eabihf/release/satman ../build/satman.bin: ../build/firmware/armv7-none-eabihf/release/satman
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/satman ../build/satman.bin llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/satman ../build/satman.bin

View File

@ -1,16 +0,0 @@
from misoc.integration import cpu_interface
def write_csr_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_csr_rust(
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
def write_mem_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_mem_rust(
soc.get_memory_regions(), soc.get_memory_groups(), None))
def write_rustc_cfg_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_rust_cfg(
soc.get_csr_regions(), soc.get_constants()))

View File

@ -10,13 +10,13 @@ from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import kasli_soc from migen_axi.platforms import kasli_soc
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
from misoc.cores import virtual_leds from misoc.integration import cpu_interface
from artiq.coredevice import jsondesc 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, eem_serdes from artiq.gateware.drtio.transceiver import gtx_7series
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 *
@ -26,7 +26,7 @@ import analyzer
import acpki import acpki
import drtio_aux_controller import drtio_aux_controller
import zynq_clocking import zynq_clocking
from config import write_csr_file, write_mem_file, write_rustc_cfg_file
eem_iostandard_dict = { eem_iostandard_dict = {
0: "LVDS_25", 0: "LVDS_25",
@ -61,14 +61,13 @@ class SMAClkinForward(Module):
] ]
class GTPBootstrapClock(Module): class GTP125BootstrapClock(Module):
def __init__(self, platform, freq=125e6): def __init__(self, platform):
self.clock_domains.cd_bootstrap = ClockDomain(reset_less=True) self.clock_domains.cd_bootstrap = ClockDomain(reset_less=True)
self.cd_bootstrap.clk.attr.add("keep") self.cd_bootstrap.clk.attr.add("keep")
bootstrap_125 = platform.request("clk125_gtp") bootstrap_125 = platform.request("clk125_gtp")
bootstrap_se = Signal() bootstrap_se = Signal()
clk_out = Signal()
platform.add_period_constraint(bootstrap_125.p, 8.0) platform.add_period_constraint(bootstrap_125.p, 8.0)
self.specials += [ self.specials += [
Instance("IBUFDS_GTE2", Instance("IBUFDS_GTE2",
@ -78,35 +77,14 @@ class GTPBootstrapClock(Module):
p_CLKCM_CFG="TRUE", p_CLKCM_CFG="TRUE",
p_CLKRCV_TRST="TRUE", p_CLKRCV_TRST="TRUE",
p_CLKSWING_CFG=3), p_CLKSWING_CFG=3),
Instance("BUFG", i_I=bootstrap_se, o_O=clk_out) Instance("BUFG", i_I=bootstrap_se, o_O=self.cd_bootstrap.clk)
] ]
if freq == 125e6:
self.comb += self.cd_bootstrap.clk.eq(clk_out)
elif freq == 100e6:
pll_fb = Signal()
pll_out = Signal()
self.specials += [
Instance("PLLE2_BASE",
p_CLKIN1_PERIOD=8.0,
i_CLKIN1=clk_out,
i_CLKFBIN=pll_fb,
o_CLKFBOUT=pll_fb,
# VCO @ 1GHz
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
# 100MHz for bootstrap
p_CLKOUT1_DIVIDE=10, p_CLKOUT1_PHASE=0.0, o_CLKOUT1=pll_out,
),
Instance("BUFG", i_I=pll_out, o_O=self.cd_bootstrap.clk)
]
else:
raise ValueError("Bootstrap frequency must be 100 or 125MHz")
class GenericStandalone(SoCCore): class GenericStandalone(SoCCore):
def __init__(self, description, acpki=False): def __init__(self, description, acpki=False):
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform() platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([ platform.toolchain.bitstream_commands.extend([
@ -117,13 +95,10 @@ class GenericStandalone(SoCCore):
ident = "acpki_" + ident ident = "acpki_" + ident
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.submodules += SMAClkinForward(self.platform) self.submodules += SMAClkinForward(self.platform)
self.config["HAS_SI5324"] = None self.rustc_cfg["has_si5324"] = None
self.config["SI5324_SOFT_RESET"] = None self.rustc_cfg["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()
@ -133,7 +108,8 @@ class GenericStandalone(SoCCore):
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)
fix_serdes_timing_path(platform) fix_serdes_timing_path(platform)
self.submodules.bootstrap = GTPBootstrapClock(self.platform, description["rtio_frequency"]) self.submodules.bootstrap = GTP125BootstrapClock(self.platform)
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)
platform.add_false_path_constraints( platform.add_false_path_constraints(
@ -157,20 +133,18 @@ class GenericStandalone(SoCCore):
self.rtio_channels.append(rtio.LogChannel()) self.rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core( self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
)
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
if self.acpki: if self.acpki:
self.config["KI_IMPL"] = "acp" self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.config["KI_IMPL"] = "csr" self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
@ -190,7 +164,7 @@ class GenericStandalone(SoCCore):
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
if has_grabber: if has_grabber:
self.config["HAS_GRABBER"] = None self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group) self.add_csr_group("grabber", self.grabber_csr_group)
for grabber in self.grabber_csr_group: for grabber in self.grabber_csr_group:
self.platform.add_false_path_constraints( self.platform.add_false_path_constraints(
@ -201,8 +175,8 @@ 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"]
has_drtio_over_eem = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"])
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform() platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([ platform.toolchain.bitstream_commands.extend([
@ -213,23 +187,21 @@ class GenericMaster(SoCCore):
ident = "acpki_" + ident ident = "acpki_" + ident
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.submodules += SMAClkinForward(self.platform) 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.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("clk_gtp"), clock_pads=platform.request("clk_gtp"),
pads=data_pads, pads=data_pads,
clk_freq=clk_freq) clk_freq=clk_freq)
self.csr_devices.append("gt_drtio") self.csr_devices.append("drtio_transceiver")
txout_buf = Signal() txout_buf = Signal()
gtx0 = self.gt_drtio.gtxs[0] gtx0 = self.drtio_transceiver.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)
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq) self.submodules.bootstrap = GTP125BootstrapClock(self.platform)
self.submodules.sys_crg = zynq_clocking.SYSCRG( self.submodules.sys_crg = zynq_clocking.SYSCRG(
self.platform, self.platform,
self.ps7, self.ps7,
@ -242,13 +214,11 @@ 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.config["HAS_SI5324"] = None self.rustc_cfg["has_si5324"] = None
self.config["SI5324_SOFT_RESET"] = None self.rustc_cfg["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)
@ -263,21 +233,21 @@ 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)
self.drtio_csr_group = [] drtio_csr_group = []
self.drtioaux_csr_group = [] drtioaux_csr_group = []
self.drtioaux_memory_group = [] drtioaux_memory_group = []
self.drtio_cri = [] self.drtio_cri = []
for i in range(len(self.gt_drtio.channels)): for i in range(len(self.drtio_transceiver.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"
self.drtio_csr_group.append(core_name) drtio_csr_group.append(core_name)
self.drtioaux_csr_group.append(coreaux_name) drtioaux_csr_group.append(coreaux_name)
self.drtioaux_memory_group.append(memory_name) drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)}) cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
core = cdr(DRTIOMaster(self.rtio_tsc, self.gt_drtio.channels[i])) core = cdr(DRTIOMaster(self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, core_name, core) setattr(self.submodules, core_name, core)
self.drtio_cri.append(core.cri) self.drtio_cri.append(core.cri)
self.csr_devices.append(core_name) self.csr_devices.append(core_name)
@ -290,27 +260,24 @@ class GenericMaster(SoCCore):
memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), size) memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), size)
self.axi2csr.register_port(coreaux.get_rx_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.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, size * 2)
self.config["HAS_DRTIO"] = None self.rustc_cfg["has_drtio"] = None
self.config["HAS_DRTIO_ROUTING"] = None self.rustc_cfg["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)
if has_drtio_over_eem: self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
self.add_eem_drtio(self.eem_drtio_channels)
self.add_drtio_cpuif_groups()
self.submodules.rtio_core = rtio.Core(
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
)
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
if self.acpki: if self.acpki:
self.config["KI_IMPL"] = "acp" self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.config["KI_IMPL"] = "csr" self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
@ -334,52 +301,8 @@ class GenericMaster(SoCCore):
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
if has_grabber: if has_grabber:
self.config["HAS_GRABBER"] = None self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group) 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.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):
@ -387,6 +310,7 @@ class GenericSatellite(SoCCore):
clk_freq = description["rtio_frequency"] clk_freq = description["rtio_frequency"]
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform() platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([ platform.toolchain.bitstream_commands.extend([
@ -397,21 +321,19 @@ class GenericSatellite(SoCCore):
ident = "acpki_" + ident ident = "acpki_" + ident
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"]
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.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("clk_gtp"), clock_pads=platform.request("clk_gtp"),
pads=data_pads, pads=data_pads,
clk_freq=clk_freq) clk_freq=clk_freq)
self.csr_devices.append("gt_drtio") self.csr_devices.append("drtio_transceiver")
txout_buf = Signal() txout_buf = Signal()
gtx0 = self.gt_drtio.gtxs[0] gtx0 = self.drtio_transceiver.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)
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq) self.submodules.bootstrap = GTP125BootstrapClock(self.platform)
self.submodules.sys_crg = zynq_clocking.SYSCRG( self.submodules.sys_crg = zynq_clocking.SYSCRG(
self.platform, self.platform,
self.ps7, self.ps7,
@ -445,7 +367,7 @@ class GenericSatellite(SoCCore):
drtioaux_memory_group = [] drtioaux_memory_group = []
drtiorep_csr_group = [] drtiorep_csr_group = []
self.drtio_cri = [] self.drtio_cri = []
for i in range(len(self.gt_drtio.channels)): for i in range(len(self.drtio_transceiver.channels)):
coreaux_name = "drtioaux" + str(i) coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem" memory_name = "drtioaux" + str(i) + "_mem"
drtioaux_csr_group.append(coreaux_name) drtioaux_csr_group.append(coreaux_name)
@ -456,7 +378,7 @@ class GenericSatellite(SoCCore):
if i == 0: if i == 0:
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer()) self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
core = cdr(DRTIOSatellite( core = cdr(DRTIOSatellite(
self.rtio_tsc, self.gt_drtio.channels[i], self.rtio_tsc, self.drtio_transceiver.channels[i],
self.rx_synchronizer)) self.rx_synchronizer))
self.submodules.drtiosat = core self.submodules.drtiosat = core
self.csr_devices.append("drtiosat") self.csr_devices.append("drtiosat")
@ -465,7 +387,7 @@ class GenericSatellite(SoCCore):
drtiorep_csr_group.append(corerep_name) drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater( core = cdr(DRTIORepeater(
self.rtio_tsc, self.gt_drtio.channels[i])) self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, corerep_name, core) setattr(self.submodules, corerep_name, core)
self.drtio_cri.append(core.cri) self.drtio_cri.append(core.cri)
self.csr_devices.append(corerep_name) self.csr_devices.append(corerep_name)
@ -483,34 +405,32 @@ class GenericSatellite(SoCCore):
# and registered in PS interface # and registered in PS interface
# manually, because software refers to rx/tx by halves of entire memory block, not names # 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.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.config["HAS_DRTIO"] = None self.rustc_cfg["has_drtio"] = None
self.config["HAS_DRTIO_ROUTING"] = None self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtioaux", drtioaux_csr_group) self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.add_csr_group("drtiorep", drtiorep_csr_group) self.add_csr_group("drtiorep", drtiorep_csr_group)
if self.acpki: if self.acpki:
self.config["KI_IMPL"] = "acp" self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.config["KI_IMPL"] = "csr" self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0) self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma") self.csr_devices.append("rtio_dma")
self.submodules.local_io = SyncRTIO( self.submodules.local_io = SyncRTIO(self.rtio_tsc, self.rtio_channels)
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.submodules.cri_con = rtio.CRIInterconnectShared( self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri, self.rtio_dma.cri, self.rtio.cri], [self.drtiosat.cri, self.rtio_dma.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")
@ -526,31 +446,48 @@ class GenericSatellite(SoCCore):
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
rtio_clk_period = 1e9/clk_freq rtio_clk_period = 1e9/clk_freq
self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6) self.rustc_cfg["rtio_frequency"] = str(clk_freq/1e6)
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,
ultrascale=False, ultrascale=False,
rtio_clk_freq=self.gt_drtio.rtio_clk_freq) rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
self.csr_devices.append("siphaser") self.csr_devices.append("siphaser")
self.config["HAS_SI5324"] = None self.rustc_cfg["has_si5324"] = None
self.config["SI5324_SOFT_RESET"] = None self.rustc_cfg["has_siphaser"] = None
self.rustc_cfg["si5324_soft_reset"] = None
gtx0 = self.gt_drtio.gtxs[0] gtx0 = self.drtio_transceiver.gtxs[0]
platform.add_false_path_constraints( platform.add_false_path_constraints(
gtx0.txoutclk, gtx0.rxoutclk) gtx0.txoutclk, gtx0.rxoutclk)
if has_grabber: if has_grabber:
self.config["HAS_GRABBER"] = None self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group) self.add_csr_group("grabber", self.grabber_csr_group)
# no RTIO CRG here # no RTIO CRG here
self.submodules.virtual_leds = virtual_leds.VirtualLeds()
self.csr_devices.append("virtual_leds") def write_mem_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_mem_rust(
soc.get_memory_regions(), soc.get_memory_groups(), None))
def write_csr_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_csr_rust(
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
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))
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
for i, channel in enumerate(self.gt_drtio.channels)]
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(

View File

@ -10,6 +10,7 @@ from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import zc706 from migen_axi.platforms import zc706
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
from misoc.integration import cpu_interface
from misoc.cores import gpio from misoc.cores import gpio
from artiq.gateware import rtio, nist_clock, nist_qc2 from artiq.gateware import rtio, nist_clock, nist_qc2
@ -25,7 +26,7 @@ import analyzer
import acpki import acpki
import drtio_aux_controller import drtio_aux_controller
import zynq_clocking import zynq_clocking
from config import write_csr_file, write_mem_file, write_rustc_cfg_file
class SMAClkinForward(Module): class SMAClkinForward(Module):
def __init__(self, platform): def __init__(self, platform):
@ -126,6 +127,7 @@ def prepare_zc706_platform(platform):
class ZC706(SoCCore): class ZC706(SoCCore):
def __init__(self, acpki=False): def __init__(self, acpki=False):
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict()
platform = zc706.Platform() platform = zc706.Platform()
prepare_zc706_platform(platform) prepare_zc706_platform(platform)
@ -152,9 +154,9 @@ class ZC706(SoCCore):
p_CLKSWING_CFG=3), p_CLKSWING_CFG=3),
Instance("BUFG", i_I=cdr_clk, o_O=cdr_clk_buf) Instance("BUFG", i_I=cdr_clk, o_O=cdr_clk_buf)
] ]
self.config["HAS_SI5324"] = None self.rustc_cfg["has_si5324"] = None
self.config["SI5324_AS_SYNTHESIZER"] = None self.rustc_cfg["si5324_as_synthesizer"] = None
self.config["SI5324_SOFT_RESET"] = None self.rustc_cfg["si5324_soft_reset"] = None
self.submodules.bootstrap = CLK200BootstrapClock(platform) self.submodules.bootstrap = CLK200BootstrapClock(platform)
self.submodules.sys_crg = zynq_clocking.SYSCRG(self.platform, self.ps7, cdr_clk_buf) self.submodules.sys_crg = zynq_clocking.SYSCRG(self.platform, self.ps7, cdr_clk_buf)
@ -168,14 +170,14 @@ class ZC706(SoCCore):
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
if self.acpki: if self.acpki:
self.config["KI_IMPL"] = "acp" self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.config["KI_IMPL"] = "csr" self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
@ -198,6 +200,7 @@ class ZC706(SoCCore):
class _MasterBase(SoCCore): class _MasterBase(SoCCore):
def __init__(self, acpki=False, drtio100mhz=False): def __init__(self, acpki=False, drtio100mhz=False):
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict()
clk_freq = 100e6 if drtio100mhz else 125e6 clk_freq = 100e6 if drtio100mhz else 125e6
@ -219,15 +222,15 @@ class _MasterBase(SoCCore):
self.submodules += SMAClkinForward(self.platform) self.submodules += SMAClkinForward(self.platform)
# 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock # 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock
self.submodules.gt_drtio = gtx_7series.GTX( self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"), clock_pads=platform.request("si5324_clkout"),
pads=data_pads, pads=data_pads,
clk_freq=clk_freq) clk_freq=clk_freq)
self.csr_devices.append("gt_drtio") self.csr_devices.append("drtio_transceiver")
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
txout_buf = Signal() txout_buf = Signal()
gtx0 = self.gt_drtio.gtxs[0] gtx0 = self.drtio_transceiver.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)
self.submodules.bootstrap = CLK200BootstrapClock(platform, clk_freq) self.submodules.bootstrap = CLK200BootstrapClock(platform, clk_freq)
self.submodules.sys_crg = zynq_clocking.SYSCRG( self.submodules.sys_crg = zynq_clocking.SYSCRG(
@ -244,7 +247,7 @@ class _MasterBase(SoCCore):
drtioaux_csr_group = [] drtioaux_csr_group = []
drtioaux_memory_group = [] drtioaux_memory_group = []
self.drtio_cri = [] self.drtio_cri = []
for i in range(len(self.gt_drtio.channels)): for i in range(len(self.drtio_transceiver.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"
@ -255,7 +258,7 @@ class _MasterBase(SoCCore):
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)}) cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
core = cdr(DRTIOMaster( core = cdr(DRTIOMaster(
self.rtio_tsc, self.gt_drtio.channels[i])) self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, core_name, core) setattr(self.submodules, core_name, core)
self.drtio_cri.append(core.cri) self.drtio_cri.append(core.cri)
self.csr_devices.append(core_name) self.csr_devices.append(core_name)
@ -268,18 +271,18 @@ class _MasterBase(SoCCore):
memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), mem_size) memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), mem_size)
self.axi2csr.register_port(coreaux.get_rx_port(), mem_size) self.axi2csr.register_port(coreaux.get_rx_port(), mem_size)
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2) self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.config["HAS_DRTIO"] = None self.rustc_cfg["has_drtio"] = None
self.config["HAS_DRTIO_ROUTING"] = None self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtio", drtio_csr_group) self.add_csr_group("drtio", drtio_csr_group)
self.add_csr_group("drtioaux", drtioaux_csr_group) self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.config["RTIO_FREQUENCY"] = str(self.gt_drtio.rtio_clk_freq/1e6) self.rustc_cfg["rtio_frequency"] = str(self.drtio_transceiver.rtio_clk_freq/1e6)
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n) self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n)
self.csr_devices.append("si5324_rst_n") self.csr_devices.append("si5324_rst_n")
self.config["HAS_SI5324"] = None self.rustc_cfg["has_si5324"] = None
self.config["SI5324_AS_SYNTHESIZER"] = None self.rustc_cfg["si5324_as_synthesizer"] = None
# Constrain TX & RX timing for the first transceiver channel # Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX) # (First channel acts as master for phase alignment for all channels' TX)
@ -287,7 +290,7 @@ class _MasterBase(SoCCore):
gtx0.txoutclk, gtx0.rxoutclk) gtx0.txoutclk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel # Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX) # (Each channel performs single-lane phase alignment for RX)
for gtx in self.gt_drtio.gtxs[1:]: for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_false_path_constraints( platform.add_false_path_constraints(
gtx0.txoutclk, gtx.rxoutclk) gtx0.txoutclk, gtx.rxoutclk)
@ -299,14 +302,14 @@ class _MasterBase(SoCCore):
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
if self.acpki: if self.acpki:
self.config["KI_IMPL"] = "acp" self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.config["KI_IMPL"] = "csr" self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
@ -333,6 +336,7 @@ class _MasterBase(SoCCore):
class _SatelliteBase(SoCCore): class _SatelliteBase(SoCCore):
def __init__(self, acpki=False, drtio100mhz=False): def __init__(self, acpki=False, drtio100mhz=False):
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict()
clk_freq = 100e6 if drtio100mhz else 125e6 clk_freq = 100e6 if drtio100mhz else 125e6
@ -355,15 +359,15 @@ class _SatelliteBase(SoCCore):
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
# 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock # 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock
self.submodules.gt_drtio = gtx_7series.GTX( self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"), clock_pads=platform.request("si5324_clkout"),
pads=data_pads, pads=data_pads,
clk_freq=clk_freq) clk_freq=clk_freq)
self.csr_devices.append("gt_drtio") self.csr_devices.append("drtio_transceiver")
txout_buf = Signal() txout_buf = Signal()
txout_buf.attr.add("keep") txout_buf.attr.add("keep")
gtx0 = self.gt_drtio.gtxs[0] gtx0 = self.drtio_transceiver.gtxs[0]
self.specials += Instance( self.specials += Instance(
"BUFG", "BUFG",
i_I=gtx0.txoutclk, i_I=gtx0.txoutclk,
@ -383,7 +387,7 @@ class _SatelliteBase(SoCCore):
drtioaux_memory_group = [] drtioaux_memory_group = []
drtiorep_csr_group = [] drtiorep_csr_group = []
self.drtio_cri = [] self.drtio_cri = []
for i in range(len(self.gt_drtio.channels)): for i in range(len(self.drtio_transceiver.channels)):
coreaux_name = "drtioaux" + str(i) coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem" memory_name = "drtioaux" + str(i) + "_mem"
drtioaux_csr_group.append(coreaux_name) drtioaux_csr_group.append(coreaux_name)
@ -395,7 +399,7 @@ class _SatelliteBase(SoCCore):
if i == 0: if i == 0:
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer()) self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
core = cdr(DRTIOSatellite( core = cdr(DRTIOSatellite(
self.rtio_tsc, self.gt_drtio.channels[0], self.rx_synchronizer)) self.rtio_tsc, self.drtio_transceiver.channels[0], self.rx_synchronizer))
self.submodules.drtiosat = core self.submodules.drtiosat = core
self.csr_devices.append("drtiosat") self.csr_devices.append("drtiosat")
# Repeaters # Repeaters
@ -403,7 +407,7 @@ class _SatelliteBase(SoCCore):
corerep_name = "drtiorep" + str(i-1) corerep_name = "drtiorep" + str(i-1)
drtiorep_csr_group.append(corerep_name) drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater( core = cdr(DRTIORepeater(
self.rtio_tsc, self.gt_drtio.channels[i])) self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, corerep_name, core) setattr(self.submodules, corerep_name, core)
self.drtio_cri.append(core.cri) self.drtio_cri.append(core.cri)
self.csr_devices.append(corerep_name) self.csr_devices.append(corerep_name)
@ -421,35 +425,36 @@ class _SatelliteBase(SoCCore):
# and registered in PS interface # and registered in PS interface
# manually, because software refers to rx/tx by halves of entire memory block, not names # 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.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.config["HAS_DRTIO"] = None self.rustc_cfg["has_drtio"] = None
self.config["HAS_DRTIO_ROUTING"] = None self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtioaux", drtioaux_csr_group) self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_csr_group("drtiorep", drtiorep_csr_group) self.add_csr_group("drtiorep", drtiorep_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.config["RTIO_FREQUENCY"] = str(self.gt_drtio.rtio_clk_freq/1e6) self.rustc_cfg["rtio_frequency"] = str(self.drtio_transceiver.rtio_clk_freq/1e6)
# Si5324 Phaser # Si5324 Phaser
self.submodules.siphaser = SiPhaser7Series( self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"), si5324_clkin=platform.request("si5324_clkin"),
rx_synchronizer=self.rx_synchronizer, rx_synchronizer=self.rx_synchronizer,
ultrascale=False, ultrascale=False,
rtio_clk_freq=self.gt_drtio.rtio_clk_freq) rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.sys_crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) self.sys_crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser") self.csr_devices.append("siphaser")
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n) self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n)
self.csr_devices.append("si5324_rst_n") self.csr_devices.append("si5324_rst_n")
self.config["HAS_SI5324"] = None self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["has_siphaser"] = None
rtio_clk_period = 1e9/self.gt_drtio.rtio_clk_freq rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel # Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX) # (First channel acts as master for phase alignment for all channels' TX)
platform.add_false_path_constraints( platform.add_false_path_constraints(
gtx0.txoutclk, gtx0.rxoutclk) gtx0.txoutclk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel # Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX) # (Each channel performs single-lane phase alignment for RX)
for gtx in self.gt_drtio.gtxs[1:]: for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.sys_crg.cd_sys.clk, gtx.rxoutclk) self.sys_crg.cd_sys.clk, gtx.rxoutclk)
@ -460,14 +465,14 @@ class _SatelliteBase(SoCCore):
self.csr_devices.append("rtio_moninj") self.csr_devices.append("rtio_moninj")
if self.acpki: if self.acpki:
self.config["KI_IMPL"] = "acp" self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.config["KI_IMPL"] = "csr" self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
@ -476,7 +481,7 @@ class _SatelliteBase(SoCCore):
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels) self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
self.submodules.cri_con = rtio.CRIInterconnectShared( self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri, self.rtio_dma.cri, self.rtio.cri], [self.drtiosat.cri, self.rtio_dma.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")
@ -671,6 +676,27 @@ class NIST_QC2_Satellite(_SatelliteBase, _NIST_QC2_RTIO):
VARIANTS = {cls.__name__.lower(): cls for cls in [NIST_CLOCK, NIST_CLOCK_Master, NIST_CLOCK_Satellite, VARIANTS = {cls.__name__.lower(): cls for cls in [NIST_CLOCK, NIST_CLOCK_Master, NIST_CLOCK_Satellite,
NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite]} NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite]}
def write_csr_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_csr_rust(
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
def write_mem_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_mem_rust(
soc.get_memory_regions(), soc.get_memory_groups(), None))
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))
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="ARTIQ port to the ZC706 Zynq development kit") description="ARTIQ port to the ZC706 Zynq development kit")

View File

@ -69,8 +69,6 @@ class SYSCRG(Module, AutoCSR):
# 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()
@ -80,6 +78,11 @@ 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:
@ -88,55 +91,32 @@ 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)
mmcm_locked = Signal()
mmcm_sys = Signal()
mmcm_sys4x = Signal()
mmcm_sys5x = Signal()
mmcm_clk208 = Signal()
mmcm_fb_clk = Signal()
self.specials += [ self.specials += [
Instance("MMCME2_ADV", Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=mmcm_locked, p_STARTUP_WAIT="FALSE", o_LOCKED=pll_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.25GHz # VCO @ 1.5GHz when using 125MHz input
p_CLKFBOUT_MULT_F=10, p_DIVCLK_DIVIDE=1, # 1.2GHz for 100MHz (zc706)
i_CLKFBIN=mmcm_fb_clk, p_CLKFBOUT_MULT=12, p_DIVCLK_DIVIDE=1,
i_RST=self.clk_sw_fsm.o_reset, i_CLKFBIN=fb_clk,
i_RST=self.clk_sw_fsm.o_reset,
o_CLKFBOUT=mmcm_fb_clk, o_CLKFBOUT=fb_clk,
p_CLKOUT0_DIVIDE_F=2.5, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=mmcm_sys4x, p_CLKOUT0_DIVIDE=3, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=pll_sys4x,
# 125MHz p_CLKOUT1_DIVIDE=12, p_CLKOUT1_PHASE=0.0,
p_CLKOUT1_DIVIDE=10, p_CLKOUT1_PHASE=0.0, o_CLKOUT1=mmcm_sys, o_CLKOUT1=pll_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),
# 625MHz AsyncResetSynchronizer(self.cd_sys, ~pll_locked),
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),
AsyncResetSynchronizer(self.cd_sys, ~mmcm_locked),
AsyncResetSynchronizer(self.cd_clk200, ~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)
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)

View File

@ -24,9 +24,8 @@ 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 = { path = "@@ZYNQ_RS@@/libboard_zynq" } libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"}
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] } 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"] }
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn"] } 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" }

View File

@ -1,233 +0,0 @@
use crate::pl;
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};
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 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);
pl::csr::eem_transceiver::rx_ready_write(1);
}
}
}

View File

@ -1,11 +1,8 @@
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};
// maximum size of arbitrary payloads pub const DMA_TRACE_MAX_SIZE: usize = /*max size*/512 - /*CRC*/4 - /*packet ID*/1 - /*trace ID*/4 - /*last*/1 -/*length*/2;
// used by satellite -> master analyzer, subkernel exceptions pub const ANALYZER_MAX_SIZE: usize = /*max size*/512 - /*CRC*/4 - /*packet ID*/1 - /*last*/1 - /*length*/2;
pub const SAT_PAYLOAD_MAX_SIZE: usize = /*max size*/512 - /*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 - /*destination*/1 - /*ID*/4;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -153,7 +150,7 @@ pub enum Packet {
AnalyzerData { AnalyzerData {
last: bool, last: bool,
length: u16, length: u16,
data: [u8; SAT_PAYLOAD_MAX_SIZE], data: [u8; ANALYZER_MAX_SIZE],
}, },
DmaAddTraceRequest { DmaAddTraceRequest {
@ -161,7 +158,7 @@ pub enum Packet {
id: u32, id: u32,
last: bool, last: bool,
length: u16, length: u16,
trace: [u8; MASTER_PAYLOAD_MAX_SIZE], trace: [u8; DMA_TRACE_MAX_SIZE],
}, },
DmaAddTraceReply { DmaAddTraceReply {
succeeded: bool, succeeded: bool,
@ -188,53 +185,6 @@ pub enum Packet {
channel: u32, channel: u32,
timestamp: u64, timestamp: u64,
}, },
SubkernelAddDataRequest {
destination: u8,
id: u32,
last: bool,
length: u16,
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
},
SubkernelAddDataReply {
succeeded: bool,
},
SubkernelLoadRunRequest {
destination: u8,
id: u32,
run: bool,
},
SubkernelLoadRunReply {
succeeded: bool,
},
SubkernelStopRequest {
destination: u8,
},
SubkernelStopReply {
succeeded: bool,
},
SubkernelFinished {
id: u32,
with_exception: bool,
},
SubkernelExceptionRequest {
destination: u8,
},
SubkernelException {
last: bool,
length: u16,
data: [u8; SAT_PAYLOAD_MAX_SIZE],
},
SubkernelMessage {
destination: u8,
id: u32,
last: bool,
length: u16,
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
},
SubkernelMessageAck {
destination: u8,
},
} }
impl Packet { impl Packet {
@ -379,7 +329,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; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE]; let mut data: [u8; ANALYZER_MAX_SIZE] = [0; ANALYZER_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,
@ -393,7 +343,7 @@ impl Packet {
let id = reader.read_u32()?; let id = reader.read_u32()?;
let last = reader.read_bool()?; let last = reader.read_bool()?;
let length = reader.read_u16()?; let length = reader.read_u16()?;
let mut trace: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE]; let mut trace: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
reader.read_exact(&mut trace[0..length as usize])?; reader.read_exact(&mut trace[0..length as usize])?;
Packet::DmaAddTraceRequest { Packet::DmaAddTraceRequest {
destination: destination, destination: destination,
@ -429,75 +379,6 @@ impl Packet {
timestamp: reader.read_u64()?, timestamp: reader.read_u64()?,
}, },
0xc0 => {
let destination = reader.read_u8()?;
let id = reader.read_u32()?;
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::SubkernelAddDataRequest {
destination: destination,
id: id,
last: last,
length: length as u16,
data: data,
}
}
0xc1 => Packet::SubkernelAddDataReply {
succeeded: reader.read_bool()?,
},
0xc4 => Packet::SubkernelLoadRunRequest {
destination: reader.read_u8()?,
id: reader.read_u32()?,
run: reader.read_bool()?,
},
0xc5 => Packet::SubkernelLoadRunReply {
succeeded: reader.read_bool()?,
},
0xc6 => Packet::SubkernelStopRequest {
destination: reader.read_u8()?,
},
0xc7 => Packet::SubkernelStopReply {
succeeded: reader.read_bool()?,
},
0xc8 => Packet::SubkernelFinished {
id: reader.read_u32()?,
with_exception: reader.read_bool()?,
},
0xc9 => Packet::SubkernelExceptionRequest {
destination: reader.read_u8()?,
},
0xca => {
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::SubkernelException {
last: last,
length: length,
data: data,
}
}
0xcb => {
let destination = reader.read_u8()?;
let id = reader.read_u32()?;
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::SubkernelMessage {
destination: destination,
id: id,
last: last,
length: length as u16,
data: data,
}
}
0xcc => Packet::SubkernelMessageAck {
destination: reader.read_u8()?,
},
ty => return Err(Error::UnknownPacket(ty)), ty => return Err(Error::UnknownPacket(ty)),
}) })
} }
@ -767,76 +648,6 @@ impl Packet {
writer.write_u32(channel)?; writer.write_u32(channel)?;
writer.write_u64(timestamp)?; writer.write_u64(timestamp)?;
} }
Packet::SubkernelAddDataRequest {
destination,
id,
last,
data,
length,
} => {
writer.write_u8(0xc0)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_bool(last)?;
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 { destination, id, run } => {
writer.write_u8(0xc4)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_bool(run)?;
}
Packet::SubkernelLoadRunReply { succeeded } => {
writer.write_u8(0xc5)?;
writer.write_bool(succeeded)?;
}
Packet::SubkernelStopRequest { destination } => {
writer.write_u8(0xc6)?;
writer.write_u8(destination)?;
}
Packet::SubkernelStopReply { succeeded } => {
writer.write_u8(0xc7)?;
writer.write_bool(succeeded)?;
}
Packet::SubkernelFinished { id, with_exception } => {
writer.write_u8(0xc8)?;
writer.write_u32(id)?;
writer.write_bool(with_exception)?;
}
Packet::SubkernelExceptionRequest { destination } => {
writer.write_u8(0xc9)?;
writer.write_u8(destination)?;
}
Packet::SubkernelException { last, length, data } => {
writer.write_u8(0xca)?;
writer.write_bool(last)?;
writer.write_u16(length)?;
writer.write_all(&data[0..length as usize])?;
}
Packet::SubkernelMessage {
destination,
id,
last,
data,
length,
} => {
writer.write_u8(0xcb)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_bool(last)?;
writer.write_u16(length)?;
writer.write_all(&data[0..length as usize])?;
}
Packet::SubkernelMessageAck { destination } => {
writer.write_u8(0xcc)?;
writer.write_u8(destination)?;
}
} }
Ok(()) Ok(())
} }

View File

@ -1,8 +1,6 @@
use libboard_zynq::i2c; use libboard_zynq::i2c;
use log::info; use log::info;
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.
struct Registers { struct Registers {
// PCA9539 equivalent register names in comments // PCA9539 equivalent register names in comments
@ -12,49 +10,23 @@ struct Registers {
gpiob: u8, // Output Port 1 gpiob: u8, // Output Port 1
} }
//IO expanders pins pub struct IoExpander<'a> {
const IODIR_OUT_SFP_TX_DISABLE: u8 = 0x02; i2c: &'a mut i2c::I2c,
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, address: u8,
virtual_led_mapping: &'static [(u8, u8, u8)],
iodir: [u8; 2], iodir: [u8; 2],
out_current: [u8; 2], out_current: [u8; 2],
out_target: [u8; 2], out_target: [u8; 2],
registers: Registers, registers: Registers,
} }
impl IoExpander { impl<'a> IoExpander<'a> {
pub fn new(i2c: &mut i2c::I2c, index: u8) -> Result<Self, &'static str> { pub fn new(i2c: &'a 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 // Both expanders on SHARED I2C bus
let mut io_expander = match index { let mut io_expander = match index {
0 => IoExpander { 0 => IoExpander {
i2c,
address: 0x40, address: 0x40,
virtual_led_mapping: &VIRTUAL_LED_MAPPING0, iodir: [0xff; 2],
iodir: IODIR0,
out_current: [0; 2], out_current: [0; 2],
out_target: [0; 2], out_target: [0; 2],
registers: Registers { registers: Registers {
@ -65,9 +37,9 @@ impl IoExpander {
}, },
}, },
1 => IoExpander { 1 => IoExpander {
i2c,
address: 0x42, address: 0x42,
virtual_led_mapping: &VIRTUAL_LED_MAPPING1, iodir: [0xff; 2],
iodir: IODIR1,
out_current: [0; 2], out_current: [0; 2],
out_target: [0; 2], out_target: [0; 2],
registers: Registers { registers: Registers {
@ -79,7 +51,7 @@ impl IoExpander {
}, },
_ => return Err("incorrect I/O expander index"), _ => return Err("incorrect I/O expander index"),
}; };
if !io_expander.check_ack(i2c)? { if !io_expander.check_ack()? {
info!("MCP23017 io expander {} not found. Checking for PCA9539.", index); info!("MCP23017 io expander {} not found. Checking for PCA9539.", index);
io_expander.address += 0xa8; // translate to PCA9539 addresses (see schematic) io_expander.address += 0xa8; // translate to PCA9539 addresses (see schematic)
io_expander.registers = Registers { io_expander.registers = Registers {
@ -88,58 +60,57 @@ impl IoExpander {
gpioa: 0x02, gpioa: 0x02,
gpiob: 0x03, gpiob: 0x03,
}; };
if !io_expander.check_ack(i2c)? { if !io_expander.check_ack()? {
return Err("Neither MCP23017 nor PCA9539 io expander found."); return Err("Neither MCP23017 nor PCA9539 io expander found.");
}; };
} }
Ok(io_expander) Ok(io_expander)
} }
fn select(&self, i2c: &mut i2c::I2c) -> Result<(), &'static str> { fn select(&mut self) -> Result<(), &'static str> {
i2c.pca954x_select(0x70, None)?; self.i2c.pca954x_select(0x70, None)?;
i2c.pca954x_select(0x71, Some(3))?; self.i2c.pca954x_select(0x71, Some(3))?;
Ok(()) Ok(())
} }
fn write(&self, i2c: &mut i2c::I2c, addr: u8, value: u8) -> Result<(), &'static str> { fn write(&mut self, addr: u8, value: u8) -> Result<(), &'static str> {
i2c.start()?; self.i2c.start()?;
i2c.write(self.address)?; self.i2c.write(self.address)?;
i2c.write(addr)?; self.i2c.write(addr)?;
i2c.write(value)?; self.i2c.write(value)?;
i2c.stop()?; self.i2c.stop()?;
Ok(()) Ok(())
} }
fn check_ack(&self, i2c: &mut i2c::I2c) -> Result<bool, &'static str> { fn check_ack(&mut self) -> Result<bool, &'static str> {
// Check for ack from io expander // Check for ack from io expander
self.select(i2c)?; self.select()?;
i2c.start()?; self.i2c.start()?;
let ack = i2c.write(self.address)?; let ack = self.i2c.write(self.address)?;
i2c.stop()?; self.i2c.stop()?;
Ok(ack) Ok(ack)
} }
fn update_iodir(&self, i2c: &mut i2c::I2c) -> Result<(), &'static str> { fn update_iodir(&mut self) -> Result<(), &'static str> {
self.write(i2c, self.registers.iodira, self.iodir[0])?; self.write(self.registers.iodira, self.iodir[0])?;
self.write(i2c, self.registers.iodirb, self.iodir[1])?; self.write(self.registers.iodirb, self.iodir[1])?;
Ok(()) Ok(())
} }
pub fn init(&mut self, i2c: &mut i2c::I2c) -> Result<(), &'static str> { pub fn init(&mut self) -> Result<(), &'static str> {
self.select(i2c)?; self.select()?;
self.update_iodir()?;
self.update_iodir(i2c)?;
self.out_current[0] = 0x00; self.out_current[0] = 0x00;
self.write(i2c, self.registers.gpioa, 0x00)?; self.write(self.registers.gpioa, 0x00)?;
self.out_current[1] = 0x00; self.out_current[1] = 0x00;
self.write(i2c, self.registers.gpiob, 0x00)?; self.write(self.registers.gpiob, 0x00)?;
Ok(()) Ok(())
} }
pub fn set_oe(&mut self, i2c: &mut i2c::I2c, port: u8, outputs: u8) -> Result<(), &'static str> { pub fn set_oe(&mut self, port: u8, outputs: u8) -> Result<(), &'static str> {
self.iodir[port as usize] &= !outputs; self.iodir[port as usize] &= !outputs;
self.update_iodir(i2c)?; self.update_iodir()?;
Ok(()) Ok(())
} }
@ -151,21 +122,15 @@ impl IoExpander {
} }
} }
pub fn service(&mut self, i2c: &mut i2c::I2c) -> Result<(), &'static str> { pub fn service(&mut self) -> 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 { if self.out_target != self.out_current {
self.select(i2c)?; self.select()?;
if self.out_target[0] != self.out_current[0] { if self.out_target[0] != self.out_current[0] {
self.write(i2c, self.registers.gpioa, self.out_target[0])?; self.write(self.registers.gpioa, self.out_target[0])?;
self.out_current[0] = self.out_target[0]; self.out_current[0] = self.out_target[0];
} }
if self.out_target[1] != self.out_current[1] { if self.out_target[1] != self.out_current[1] {
self.write(i2c, self.registers.gpiob, self.out_target[1])?; self.write(self.registers.gpiob, self.out_target[1])?;
self.out_current[1] = self.out_target[1]; self.out_current[1] = self.out_target[1];
} }
} }

View File

@ -31,8 +31,6 @@ pub mod mem;
pub mod pl; pub mod pl;
#[cfg(has_si5324)] #[cfg(has_si5324)]
pub mod si5324; pub mod si5324;
#[cfg(has_drtio_eem)]
pub mod drtio_eem;
use core::{cmp, str}; use core::{cmp, str};

View File

@ -6,7 +6,7 @@ edition = "2018"
build = "build.rs" build = "build.rs"
[dependencies] [dependencies]
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq" } libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
[build-dependencies] [build-dependencies]
cc = { version = "1.0.1" } cc = { version = "1.0.1" }

View File

@ -8,4 +8,4 @@ name = "dyld"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" } libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }

View File

@ -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 = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] } libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
[features] [features]
alloc = [] alloc = []

View File

@ -1,6 +1,3 @@
#[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)]
@ -67,7 +64,7 @@ impl Write for Cursor<&mut [u8]> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl Write for Cursor<Vec<u8>> { impl Write for Cursor<::alloc::Vec<u8>> {
#[inline] #[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);

View File

@ -1,10 +1,13 @@
#![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;

View File

@ -1,4 +1,3 @@
#[cfg(feature = "alloc")]
use alloc::{string::String, vec}; use alloc::{string::String, vec};
use core::str::Utf8Error; use core::str::Utf8Error;
@ -51,8 +50,7 @@ pub trait ProtoRead {
} }
#[inline] #[inline]
#[cfg(feature = "alloc")] fn read_bytes(&mut self) -> Result<::alloc::vec::Vec<u8>, Self::ReadError> {
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)?;
@ -60,8 +58,7 @@ pub trait ProtoRead {
} }
#[inline] #[inline]
#[cfg(feature = "alloc")] fn read_string(&mut self) -> Result<::alloc::string::String, ReadStringError<Self::ReadError>> {
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()))
} }
@ -138,7 +135,6 @@ 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())
} }

View File

@ -1,34 +0,0 @@
[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" }

View File

@ -1,5 +0,0 @@
extern crate build_zynq;
fn main() {
build_zynq::cfg();
}

View File

@ -1,109 +0,0 @@
use alloc::vec::Vec;
use cslice::CSlice;
use super::{Message, SubkernelStatus, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0};
use crate::{artiq_raise, rpc::send_args};
pub extern "C" fn load_run(id: u32, run: bool) {
unsafe {
KERNEL_CHANNEL_1TO0
.as_mut()
.unwrap()
.send(Message::SubkernelLoadRunRequest { id: id, run: run });
}
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: u64) {
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 {
status: SubkernelStatus::NoError,
} => (),
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::IncorrectState,
} => artiq_raise!("SubkernelError", "Subkernel not running"),
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::Timeout,
} => artiq_raise!("SubkernelError", "Subkernel timed out"),
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::CommLost,
} => artiq_raise!("SubkernelError", "Lost communication with satellite"),
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::OtherError,
} => artiq_raise!("SubkernelError", "An error occurred during subkernel operation"),
_ => panic!("expected SubkernelAwaitFinishReply after SubkernelAwaitFinishRequest"),
}
}
pub extern "C" fn send_message(id: u32, count: u8, tag: &CSlice<u8>, data: *const *const ()) {
let mut buffer = Vec::<u8>::new();
send_args(&mut buffer, 0, tag.as_ref(), data).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,
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: u32, timeout: u64, min: u8, max: u8) {
unsafe {
KERNEL_CHANNEL_1TO0
.as_mut()
.unwrap()
.send(Message::SubkernelMsgRecvRequest {
id: id,
timeout: timeout,
});
}
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::NoError,
count,
} => {
if min > count || count > max {
artiq_raise!("SubkernelError", "Received more or less arguments than required")
}
}
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::IncorrectState,
..
} => artiq_raise!("SubkernelError", "Subkernel not running"),
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::Timeout,
..
} => artiq_raise!("SubkernelError", "Subkernel timed out"),
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::CommLost,
..
} => artiq_raise!("SubkernelError", "Lost communication with satellite"),
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::OtherError,
..
} => artiq_raise!("SubkernelError", "An error occurred during subkernel operation"),
_ => panic!("expected SubkernelMsgRecvReply after SubkernelMsgRecvRequest"),
}
// RpcRecvRequest should be called after this to receive message data
}

View File

@ -1,163 +0,0 @@
#![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);
}
}

View File

@ -18,6 +18,7 @@ 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"] }
byteorder = { version = "1.3", default-features = false } byteorder = { version = "1.3", default-features = false }
@ -25,19 +26,19 @@ 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 = { path = "@@ZYNQ_RS@@/libboard_zynq", features = ["ipv6"]} libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["ipv6"]}
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] } libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" } libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libasync = { path = "@@ZYNQ_RS@@/libasync" } libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libregister = { path = "@@ZYNQ_RS@@/libregister" } libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn", "ipv6"] } libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", 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", features = ["alloc"] } io = { path = "../libio" }
ksupport = { path = "../libksupport" }
libboard_artiq = { path = "../libboard_artiq" } libboard_artiq = { path = "../libboard_artiq" }

View File

@ -1,14 +1,8 @@
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 futures::{future::FutureExt, select_biased}; use futures::{future::FutureExt, select_biased};
#[cfg(has_drtio)]
use io::{Cursor, ProtoRead};
#[cfg(has_drtio)]
use ksupport::rpc;
use ksupport::{kernel, resolve_channel_name};
use libasync::{smoltcp::{Sockets, TcpStream}, use libasync::{smoltcp::{Sockets, TcpStream},
task}; task};
use libboard_artiq::drtio_routing; use libboard_artiq::drtio_routing;
@ -30,19 +24,17 @@ use num_traits::{FromPrimitive, ToPrimitive};
#[cfg(has_drtio)] #[cfg(has_drtio)]
use crate::pl; use crate::pl;
use crate::{analyzer, mgmt, moninj, proto_async::*, rpc_async, rtio_dma, rtio_mgt}; use crate::{analyzer, kernel, mgmt, moninj,
#[cfg(has_drtio)] proto_async::*,
use crate::{subkernel, subkernel::Error as SubkernelError}; rpc, rtio_dma,
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),
} }
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
@ -51,12 +43,9 @@ 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),
} }
} }
} }
@ -67,19 +56,6 @@ 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,
@ -87,7 +63,6 @@ enum Request {
RunKernel = 6, RunKernel = 6,
RPCReply = 7, RPCReply = 7,
RPCException = 8, RPCException = 8,
UploadSubkernel = 9,
} }
#[derive(Debug, FromPrimitive, ToPrimitive)] #[derive(Debug, FromPrimitive, ToPrimitive)]
@ -207,7 +182,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_async::recv_return(stream, &tag, slot, &|size| { rpc::recv_return(stream, &tag, slot, &|size| {
let control = control.clone(); let control = control.clone();
async move { async move {
if size == 0 { if size == 0 {
@ -252,7 +227,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(ksupport::RPCException { .async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException {
id, id,
message, message,
param, param,
@ -390,124 +365,6 @@ 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, run } => {
let succeeded = match subkernel::load(aux_mutex, routing_table, timer, id, run).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 status = match res {
Ok(ref res) => {
if res.status == subkernel::FinishStatus::CommLost {
kernel::SubkernelStatus::CommLost
} else if let Some(exception) = &res.exception {
error!("Exception in subkernel");
match stream {
None => (),
Some(stream) => {
write_chunk(stream, exception).await?;
}
}
// will not be called after exception is served
kernel::SubkernelStatus::OtherError
} else {
kernel::SubkernelStatus::NoError
}
}
Err(SubkernelError::Timeout) => kernel::SubkernelStatus::Timeout,
Err(SubkernelError::IncorrectState) => kernel::SubkernelStatus::IncorrectState,
Err(_) => kernel::SubkernelStatus::OtherError,
};
control
.borrow_mut()
.tx
.async_send(kernel::Message::SubkernelAwaitFinishReply { status: status })
.await;
}
#[cfg(has_drtio)]
kernel::Message::SubkernelMsgSend { id, data } => {
let res = subkernel::message_send(aux_mutex, routing_table, timer, id, 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 } => {
let message_received = subkernel::message_await(id, timeout, timer).await;
let (status, count) = match message_received {
Ok(ref message) => (kernel::SubkernelStatus::NoError, message.count),
Err(SubkernelError::Timeout) => (kernel::SubkernelStatus::Timeout, 0),
Err(SubkernelError::IncorrectState) => (kernel::SubkernelStatus::IncorrectState, 0),
Err(SubkernelError::CommLost) => (kernel::SubkernelStatus::CommLost, 0),
Err(_) => (kernel::SubkernelStatus::OtherError, 0),
};
control
.borrow_mut()
.tx
.async_send(kernel::Message::SubkernelMsgRecvReply {
status: status,
count: count,
})
.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 tag: [u8; 1] = [message.tag];
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),
};
rpc::recv_return(&mut reader, &tag, 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 < count {
tag[0] = reader.read_u8()?;
} 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
@ -577,13 +434,9 @@ 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();
@ -607,29 +460,6 @@ 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);
@ -692,8 +522,7 @@ 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); rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer, &cfg);
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);

View File

@ -422,7 +422,7 @@ 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/language/embedding_map.py
static EXCEPTION_ID_LOOKUP: [(&str, u32); 12] = [ static EXCEPTION_ID_LOOKUP: [(&str, u32); 11] = [
("RuntimeError", 0), ("RuntimeError", 0),
("RTIOUnderflow", 1), ("RTIOUnderflow", 1),
("RTIOOverflow", 2), ("RTIOOverflow", 2),
@ -434,7 +434,6 @@ static EXCEPTION_ID_LOOKUP: [(&str, u32); 12] = [
("ZeroDivisionError", 8), ("ZeroDivisionError", 8),
("IndexError", 9), ("IndexError", 9),
("UnwrapNoneError", 10), ("UnwrapNoneError", 10),
("SubkernelError", 11),
]; ];
pub fn get_exception_id(name: &str) -> u32 { pub fn get_exception_id(name: &str) -> u32 {

View File

@ -5,8 +5,6 @@ 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,
@ -116,16 +114,6 @@ 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),

View File

@ -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, RPCException}; use crate::eh_artiq;
mod control; mod control;
pub use control::Control; pub use control::Control;
@ -13,17 +13,16 @@ mod dma;
mod rpc; mod rpc;
pub use dma::DmaRecorder; pub use dma::DmaRecorder;
mod cache; mod cache;
#[cfg(has_drtio)]
mod subkernel;
#[cfg(has_drtio)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum SubkernelStatus { pub struct RPCException {
NoError, pub id: u32,
Timeout, pub message: u32,
IncorrectState, pub param: [i64; 3],
CommLost, pub file: u32,
OtherError, pub line: i32,
pub column: i32,
pub function: u32,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -73,42 +72,6 @@ pub enum Message {
UpDestinationsRequest(i32), UpDestinationsRequest(i32),
#[cfg(has_drtio)] #[cfg(has_drtio)]
UpDestinationsReply(bool), UpDestinationsReply(bool),
#[cfg(has_drtio)]
SubkernelLoadRunRequest {
id: u32,
run: bool,
},
#[cfg(has_drtio)]
SubkernelLoadRunReply {
succeeded: bool,
},
#[cfg(has_drtio)]
SubkernelAwaitFinishRequest {
id: u32,
timeout: u64,
},
#[cfg(has_drtio)]
SubkernelAwaitFinishReply {
status: SubkernelStatus,
},
#[cfg(has_drtio)]
SubkernelMsgSend {
id: u32,
data: Vec<u8>,
},
#[cfg(has_drtio)]
SubkernelMsgSent,
#[cfg(has_drtio)]
SubkernelMsgRecvRequest {
id: u32,
timeout: u64,
},
#[cfg(has_drtio)]
SubkernelMsgRecvReply {
status: SubkernelStatus,
count: u8,
},
} }
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);

View File

@ -2,58 +2,103 @@
#![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(const_btree_new)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
#![feature(c_variadic)]
#![feature(const_btree_new)]
#![feature(const_in_array_repeat_expressions)]
#![feature(naked_functions)]
#![feature(asm)]
#[macro_use] #[macro_use]
extern crate alloc; extern crate alloc;
#[cfg(feature = "target_kasli_soc")] use libasync::{block_async, task};
use core::cell::RefCell;
use ksupport;
use libasync::task;
#[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};
#[cfg(has_drtio_eem)]
use libboard_artiq::drtio_eem;
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::ram;
use log::{info, warn}; use log::{error, 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_async; mod rpc;
#[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;
#[cfg(feature = "target_kasli_soc")] static mut SEEN_ASYNC_ERRORS: u8 = 0;
async fn io_expanders_service(
i2c_bus: RefCell<&mut libboard_zynq::i2c::I2c>, pub unsafe fn get_async_errors() -> u8 {
io_expander0: RefCell<io_expander::IoExpander>, let errors = SEEN_ASYNC_ERRORS;
io_expander1: RefCell<io_expander::IoExpander>, SEEN_ASYNC_ERRORS = 0;
) { errors
}
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
unsafe {
if pl::csr::rtio_core::async_error_read() != 0 {
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}
async fn report_async_rtio_errors() {
loop { loop {
task::r#yield().await; let _ = block_async!(wait_for_async_rtio_error()).await;
io_expander0 unsafe {
.borrow_mut() let errors = pl::csr::rtio_core::async_error_read();
.service(&mut i2c_bus.borrow_mut()) if errors & ASYNC_ERROR_COLLISION != 0 {
.expect("I2C I/O expander #0 service failed"); let channel = pl::csr::rtio_core::collision_channel_read();
io_expander1 error!(
.borrow_mut() "RTIO collision involving channel 0x{:04x}:{}",
.service(&mut i2c_bus.borrow_mut()) channel,
.expect("I2C I/O expander #1 service failed"); 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);
}
} }
} }
@ -76,29 +121,21 @@ pub fn main_core0() {
info!("gateware ident: {}", identifier_read(&mut [0; 64])); info!("gateware ident: {}", identifier_read(&mut [0; 64]));
ksupport::i2c::init(); i2c::init();
#[cfg(feature = "target_kasli_soc")]
let i2c_bus = unsafe { (ksupport::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 = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
io_expander1 = io_expander::IoExpander::new(i2c_bus, 1).unwrap(); for expander_i in 0..=1 {
io_expander0 let mut io_expander = io_expander::IoExpander::new(i2c, expander_i).unwrap();
.init(i2c_bus) io_expander.init().expect("I2C I/O expander #0 initialization failed");
.expect("I2C I/O expander #0 initialization failed"); // Actively drive TX_DISABLE to false on SFP0..3
io_expander1 io_expander.set_oe(0, 1 << 1).unwrap();
.init(i2c_bus) io_expander.set_oe(1, 1 << 1).unwrap();
.expect("I2C I/O expander #1 initialization failed"); io_expander.set(0, 1, false);
// Drive TX_DISABLE to false on SFP0..3 io_expander.set(1, 1, false);
io_expander0.set(0, 1, false); io_expander.service().unwrap();
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();
} }
let cfg = match Config::new() { let cfg = match Config::new() {
@ -111,16 +148,7 @@ pub fn main_core0() {
rtio_clocking::init(&mut timer, &cfg); rtio_clocking::init(&mut timer, &cfg);
#[cfg(has_drtio_eem)] task::spawn(report_async_rtio_errors());
drtio_eem::init(&mut timer, &cfg);
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);
} }

View File

@ -1,62 +1,71 @@
use core::str; use alloc::boxed::Box;
use core::{future::Future, str};
use async_recursion::async_recursion;
use byteorder::{ByteOrder, NativeEndian}; use byteorder::{ByteOrder, NativeEndian};
use core_io::{Error, Read, Write}; use core_io::{Error, Write};
use cslice::{CMutSlice, CSlice}; use cslice::{CMutSlice, CSlice};
use io::{ProtoRead, ProtoWrite}; use io::proto::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]
pub fn round_up(val: usize, power_of_two: usize) -> usize { 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]
pub unsafe fn round_up_mut<T>(ptr: *mut T, power_of_two: usize) -> *mut T { 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]
pub unsafe fn round_up_const<T>(ptr: *const T, power_of_two: usize) -> *const T { 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]
pub unsafe fn align_ptr<T>(ptr: *const ()) -> *const T { 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]
pub unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T { 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
} }
// versions for reader rather than TcpStream /// Reads (deserializes) `length` array or list elements of type `tag` from `stream`,
// they will be made into sync for satellite subkernels later /// writing them into the buffer given by `storage`.
unsafe fn recv_elements<F, R>( ///
reader: &mut R, /// `alloc` is used for nested allocations (if elements themselves contain
elt_tag: Tag, /// lists/arrays), see [recv_value].
#[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: &mut F, alloc: &(impl Fn(usize) -> F + 'async_recursion),
) -> Result<(), Error> ) -> Result<(), smoltcp::Error>
where where
F: FnMut(usize) -> *mut (), F: Future<Output = *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);
reader.read_exact(dest)?; proto_async::read_chunk(stream, dest).await?;
} }
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);
reader.read_exact(dest)?; proto_async::read_chunk(stream, dest).await?;
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);
@ -64,7 +73,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);
reader.read_exact(dest)?; proto_async::read_chunk(stream, dest).await?;
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);
@ -72,17 +81,27 @@ where
_ => { _ => {
let mut data = storage; let mut data = storage;
for _ in 0..length { for _ in 0..length {
recv_value(reader, elt_tag, &mut data, alloc)? recv_value(stream, elt_tag, &mut data, alloc).await?
} }
} }
} }
Ok(()) Ok(())
} }
unsafe fn recv_value<F, R>(reader: &mut R, tag: Tag, data: &mut *mut (), alloc: &mut F) -> Result<(), Error> /// 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 where
F: FnMut(usize) -> *mut (), F: Future<Output = *mut ()>,
R: Read + ?Sized,
{ {
macro_rules! consume_value { macro_rules! consume_value {
($ty:ty, | $ptr:ident | $map:expr) => {{ ($ty:ty, | $ptr:ident | $map:expr) => {{
@ -95,22 +114,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 = reader.read_u8()? as i8; *ptr = proto_async::read_i8(stream).await?;
Ok(()) Ok(())
}), }),
Tag::Int32 => consume_value!(i32, |ptr| { Tag::Int32 => consume_value!(i32, |ptr| {
*ptr = reader.read_u32()? as i32; *ptr = proto_async::read_i32(stream).await?;
Ok(()) Ok(())
}), }),
Tag::Int64 | Tag::Float64 => consume_value!(i64, |ptr| { Tag::Int64 | Tag::Float64 => consume_value!(i64, |ptr| {
*ptr = reader.read_u64()? as i64; *ptr = proto_async::read_i64(stream).await?;
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 = reader.read_u32()? as usize; let length = proto_async::read_i32(stream).await? as usize;
*ptr = CMutSlice::new(alloc(length) as *mut u8, length); *ptr = CMutSlice::new(alloc(length).await as *mut u8, length);
reader.read_exact((*ptr).as_mut())?; proto_async::read_chunk(stream, (*ptr).as_mut()).await?;
Ok(()) Ok(())
}) })
} }
@ -120,8 +139,10 @@ 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(reader, tag, data, alloc)? 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); *data = round_up_mut(*data, alignment);
Ok(()) Ok(())
} }
@ -133,41 +154,50 @@ 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 = reader.read_u32()? as usize; 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 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) as *mut u8; let allocation = alloc(storage_offset + storage_size).await 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(reader, tag, length, storage, alloc) recv_elements(stream, tag, length, storage, alloc).await
}) })
} }
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 = reader.read_u32()? as usize; let len = proto_async::read_i32(stream).await? 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); *buffer = alloc(elt_tag.size() * total_len).await;
recv_elements(reader, elt_tag, total_len, *buffer, alloc) recv_elements(stream, elt_tag, total_len, *buffer, alloc).await
}) })
} }
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(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)?; recv_value(stream, tag, data, alloc).await?;
Ok(()) Ok(())
} }
Tag::Keyword(_) => unreachable!(), Tag::Keyword(_) => unreachable!(),
@ -175,17 +205,21 @@ where
} }
} }
pub fn recv_return<F, R>(reader: &mut R, tag_bytes: &[u8], data: *mut (), alloc: &mut F) -> Result<(), Error> pub async fn recv_return<F>(
stream: &TcpStream,
tag_bytes: &[u8],
data: *mut (),
alloc: &impl Fn(usize) -> F,
) -> Result<(), smoltcp::Error>
where where
F: FnMut(usize) -> *mut (), F: Future<Output = *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(reader, tag, &mut data, alloc)? }; unsafe { recv_value(stream, tag, &mut data, alloc).await? };
Ok(()) Ok(())
} }
@ -333,7 +367,7 @@ where W: Write + ?Sized {
Ok(()) Ok(())
} }
pub mod tag { 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]) {

View File

@ -1,197 +0,0 @@
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(())
}

View File

@ -4,14 +4,13 @@ use cslice::CSlice;
use libcortex_a9::asm; use libcortex_a9::asm;
use vcell::VolatileCell; use vcell::VolatileCell;
use crate::{artiq_raise, pl::csr, resolve_channel_name, rtio_core}; use crate::{artiq_raise, pl::csr, rtio_mgt::resolve_channel_name};
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;
pub const RTIO_O_STATUS_DESTINATION_UNREACHABLE: i32 = 4; pub const RTIO_O_STATUS_DESTINATION_UNREACHABLE: i32 = 4;
pub const RTIO_I_STATUS_WAIT_EVENT: i32 = 1; pub const RTIO_I_STATUS_WAIT_EVENT: i32 = 1;
pub const RTIO_I_STATUS_OVERFLOW: i32 = 2; 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_WAIT_STATUS: i32 = 4; // TODO
pub const RTIO_I_STATUS_DESTINATION_UNREACHABLE: i32 = 8; pub const RTIO_I_STATUS_DESTINATION_UNREACHABLE: i32 = 8;
@ -52,7 +51,7 @@ static mut TRANSACTION_BUFFER: Transaction = Transaction {
pub extern "C" fn init() { pub extern "C" fn init() {
unsafe { unsafe {
rtio_core::reset_write(1); csr::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);
} }

View File

@ -1,6 +1,4 @@
use embedded_hal::blocking::delay::DelayMs; use embedded_hal::blocking::delay::DelayMs;
#[cfg(has_si5324)]
use ksupport::i2c;
use libboard_artiq::pl; use libboard_artiq::pl;
#[cfg(has_si5324)] #[cfg(has_si5324)]
use libboard_artiq::si5324; use libboard_artiq::si5324;
@ -10,6 +8,9 @@ use libboard_zynq::timer::GlobalTimer;
use libconfig::Config; use libconfig::Config;
use log::{info, warn}; use log::{info, warn};
#[cfg(has_si5324)]
use crate::i2c;
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum RtioClock { pub enum RtioClock {
@ -75,7 +76,7 @@ fn init_rtio(timer: &mut GlobalTimer) {
} }
// if it's not locked, it will hang at the CSR. // if it's not locked, it will hang at the CSR.
timer.delay_ms(50); // wait for CPLL/QPLL/SYS PLL lock timer.delay_ms(20); // wait for CPLL/QPLL/SYS PLL lock
let clk = unsafe { pl::csr::sys_crg::current_clock_read() }; let clk = unsafe { pl::csr::sys_crg::current_clock_read() };
if clk == 1 { if clk == 1 {
info!("SYS CLK switched successfully"); info!("SYS CLK switched successfully");
@ -91,10 +92,10 @@ fn init_rtio(timer: &mut GlobalTimer) {
#[cfg(has_drtio)] #[cfg(has_drtio)]
fn init_drtio(timer: &mut GlobalTimer) { fn init_drtio(timer: &mut GlobalTimer) {
unsafe { unsafe {
pl::csr::gt_drtio::stable_clkin_write(1); pl::csr::drtio_transceiver::stable_clkin_write(1);
} }
timer.delay_ms(50); // wait for CPLL/QPLL/SYS PLL lock timer.delay_ms(20); // wait for CPLL/QPLL/SYS PLL lock
let clk = unsafe { pl::csr::sys_crg::current_clock_read() }; let clk = unsafe { pl::csr::sys_crg::current_clock_read() };
if clk == 1 { if clk == 1 {
info!("SYS CLK switched successfully"); info!("SYS CLK switched successfully");
@ -103,10 +104,7 @@ 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::drtio_transceiver::txenable_write(0xffffffffu32 as _);
#[cfg(has_drtio_eem)]
pl::csr::eem_transceiver::txenable_write(0xffffffffu32 as _);
} }
} }
@ -266,10 +264,7 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
{ {
let i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() }; let i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
match clk { match clk {
RtioClock::Ext0_Bypass => { RtioClock::Ext0_Bypass => si5324::bypass(i2c, SI5324_EXT_INPUT, timer).expect("cannot bypass Si5324"),
info!("bypassing the PLL for RTIO clock");
si5324::bypass(i2c, SI5324_EXT_INPUT, timer).expect("cannot bypass Si5324")
}
_ => setup_si5324(i2c, timer, clk), _ => setup_si5324(i2c, timer, clk),
} }
} }

View File

@ -2,7 +2,7 @@ use core::ptr::{read_volatile, write_volatile};
use cslice::CSlice; use cslice::CSlice;
use crate::{artiq_raise, pl::csr, resolve_channel_name, rtio_core}; use crate::{artiq_raise, pl::csr, rtio_mgt::resolve_channel_name};
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 {
rtio_core::reset_write(1); csr::rtio_core::reset_write(1);
} }
} }

View File

@ -2,13 +2,14 @@ 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());

View File

@ -1,25 +1,28 @@
use alloc::rc::Rc; use alloc::{collections::BTreeMap, rc::Rc, string::String};
use core::cell::RefCell; use core::cell::RefCell;
use io::{Cursor, ProtoRead};
use libboard_artiq::{drtio_routing, pl::csr}; use libboard_artiq::{drtio_routing, pl::csr};
use libboard_zynq::timer::GlobalTimer; use libboard_zynq::timer::GlobalTimer;
use libconfig::Config;
use libcortex_a9::mutex::Mutex; use libcortex_a9::mutex::Mutex;
use log::warn;
static mut RTIO_DEVICE_MAP: BTreeMap<u32, String> = BTreeMap::new();
#[cfg(has_drtio)] #[cfg(has_drtio)]
pub mod drtio { pub mod drtio {
use alloc::vec::Vec; use alloc::vec::Vec;
use embedded_hal::blocking::delay::DelayMs; use embedded_hal::blocking::delay::DelayMs;
use ksupport::{resolve_channel_name, ASYNC_ERROR_BUSY, ASYNC_ERROR_COLLISION, ASYNC_ERROR_SEQUENCE_ERROR,
SEEN_ASYNC_ERRORS};
use libasync::{delay, task}; use libasync::{delay, task};
use libboard_artiq::{drtioaux::Error, drtioaux_async, drtioaux_async::Packet, use libboard_artiq::{drtioaux::Error, drtioaux_async, drtioaux_async::Packet, drtioaux_proto::DMA_TRACE_MAX_SIZE};
drtioaux_proto::MASTER_PAYLOAD_MAX_SIZE};
use libboard_zynq::time::Milliseconds; use libboard_zynq::time::Milliseconds;
use log::{error, info, warn}; use log::{error, info, warn};
use super::*; use super::*;
use crate::{analyzer::remote_analyzer::RemoteBuffer, rtio_dma::remote_dma, subkernel}; use crate::{analyzer::remote_analyzer::RemoteBuffer, rtio_dma::remote_dma, ASYNC_ERROR_BUSY,
ASYNC_ERROR_COLLISION, ASYNC_ERROR_SEQUENCE_ERROR, SEEN_ASYNC_ERRORS};
pub fn startup( pub fn startup(
aux_mutex: &Rc<Mutex<bool>>, aux_mutex: &Rc<Mutex<bool>>,
@ -41,42 +44,6 @@ pub mod drtio {
unsafe { (csr::DRTIO[linkno].rx_up_read)() == 1 } unsafe { (csr::DRTIO[linkno].rx_up_read)() == 1 }
} }
async fn process_async_packets(aux_mutex: &Mutex<bool>, linkno: u8, packet: Packet) -> Option<Packet> {
// returns None if an async packet has been consumed
match packet {
Packet::DmaPlaybackStatus {
id,
destination,
error,
channel,
timestamp,
} => {
remote_dma::playback_done(id, destination, error, channel, timestamp).await;
None
}
Packet::SubkernelFinished { id, with_exception } => {
subkernel::subkernel_finished(id, with_exception).await;
None
}
Packet::SubkernelMessage {
id,
destination: from,
last,
length,
data,
} => {
subkernel::message_handle_incoming(id, last, length as usize, &data).await;
// acknowledge receiving part of the message
let _lock = aux_mutex.async_lock().await;
drtioaux_async::send(linkno, &Packet::SubkernelMessageAck { destination: from })
.await
.unwrap();
None
}
other => Some(other),
}
}
async fn recv_aux_timeout(linkno: u8, timeout: u64, timer: GlobalTimer) -> Result<Packet, &'static str> { async fn recv_aux_timeout(linkno: u8, timeout: u64, timer: GlobalTimer) -> Result<Packet, &'static str> {
if !link_rx_up(linkno).await { if !link_rx_up(linkno).await {
return Err("link went down"); return Err("link went down");
@ -99,7 +66,22 @@ pub mod drtio {
} }
let _lock = aux_mutex.async_lock().await; let _lock = aux_mutex.async_lock().await;
drtioaux_async::send(linkno, request).await.unwrap(); drtioaux_async::send(linkno, request).await.unwrap();
Ok(recv_aux_timeout(linkno, 200, timer).await?) loop {
let reply = recv_aux_timeout(linkno, 200, timer).await;
match reply {
Ok(Packet::DmaPlaybackStatus {
id,
destination,
error,
channel,
timestamp,
}) => {
remote_dma::playback_done(id, destination, error, channel, timestamp).await;
}
Ok(packet) => return Ok(packet),
Err(e) => return Err(e),
}
}
} }
async fn drain_buffer(linkno: u8, draining_time: Milliseconds, timer: GlobalTimer) { async fn drain_buffer(linkno: u8, draining_time: Milliseconds, timer: GlobalTimer) {
@ -208,11 +190,14 @@ pub mod drtio {
async fn process_unsolicited_aux(aux_mutex: &Rc<Mutex<bool>>, linkno: u8) { async fn process_unsolicited_aux(aux_mutex: &Rc<Mutex<bool>>, linkno: u8) {
let _lock = aux_mutex.async_lock().await; let _lock = aux_mutex.async_lock().await;
match drtioaux_async::recv(linkno).await { match drtioaux_async::recv(linkno).await {
Ok(Some(packet)) => { Ok(Some(Packet::DmaPlaybackStatus {
if let Some(packet) = process_async_packets(aux_mutex, linkno, packet).await { id,
warn!("[LINK#{}] unsolicited aux packet: {:?}", linkno, packet); destination,
} error,
} channel,
timestamp,
})) => remote_dma::playback_done(id, destination, error, channel, timestamp).await,
Ok(Some(packet)) => warn!("[LINK#{}] unsolicited aux packet: {:?}", linkno, packet),
Ok(None) => (), Ok(None) => (),
Err(_) => warn!("[LINK#{}] aux packet error", linkno), Err(_) => warn!("[LINK#{}] aux packet error", linkno),
} }
@ -281,72 +266,55 @@ pub mod drtio {
let linkno = hop - 1; let linkno = hop - 1;
if destination_up(up_destinations, destination).await { if destination_up(up_destinations, destination).await {
if up_links[linkno as usize] { if up_links[linkno as usize] {
loop { let reply = aux_transact(
let reply = aux_transact( aux_mutex,
aux_mutex, linkno,
linkno, &Packet::DestinationStatusRequest {
&Packet::DestinationStatusRequest { destination: destination,
destination: destination, },
}, timer,
timer, )
) .await;
.await; match reply {
match reply { Ok(Packet::DestinationDownReply) => {
Ok(Packet::DestinationDownReply) => { destination_set_up(routing_table, up_destinations, destination, false).await;
destination_set_up(routing_table, up_destinations, destination, false).await; remote_dma::destination_changed(aux_mutex, routing_table, timer, destination, false)
remote_dma::destination_changed(
aux_mutex,
routing_table,
timer,
destination,
false,
)
.await; .await;
subkernel::destination_changed(aux_mutex, routing_table, timer, destination, false)
.await;
}
Ok(Packet::DestinationOkReply) => (),
Ok(Packet::DestinationSequenceErrorReply { channel }) => {
error!(
"[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
}
Ok(Packet::DestinationCollisionReply { channel }) => {
error!(
"[DEST#{}] RTIO collision involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
}
Ok(Packet::DestinationBusyReply { channel }) => {
error!(
"[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
}
Ok(packet) => match process_async_packets(aux_mutex, linkno, packet).await {
Some(packet) => {
error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet)
}
None => continue,
},
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e),
} }
break; Ok(Packet::DestinationOkReply) => (),
Ok(Packet::DestinationSequenceErrorReply { channel }) => {
error!(
"[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
}
Ok(Packet::DestinationCollisionReply { channel }) => {
error!(
"[DEST#{}] RTIO collision involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
}
Ok(Packet::DestinationBusyReply { channel }) => {
error!(
"[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
}
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e),
} }
} else { } else {
destination_set_up(routing_table, up_destinations, destination, false).await; destination_set_up(routing_table, up_destinations, destination, false).await;
remote_dma::destination_changed(aux_mutex, routing_table, timer, destination, false).await; remote_dma::destination_changed(aux_mutex, routing_table, timer, destination, false).await;
subkernel::destination_changed(aux_mutex, routing_table, timer, destination, false).await;
} }
} else { } else {
if up_links[linkno as usize] { if up_links[linkno as usize] {
@ -366,8 +334,6 @@ pub mod drtio {
init_buffer_space(destination as u8, linkno).await; init_buffer_space(destination as u8, linkno).await;
remote_dma::destination_changed(aux_mutex, routing_table, timer, destination, true) remote_dma::destination_changed(aux_mutex, routing_table, timer, destination, true)
.await; .await;
subkernel::destination_changed(aux_mutex, routing_table, timer, destination, true)
.await;
} }
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet), Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e), Err(e) => error!("[DEST#{}] communication failed ({})", destination, e),
@ -454,36 +420,6 @@ pub mod drtio {
} }
} }
async fn partition_data<PacketF, HandlerF>(
linkno: u8,
aux_mutex: &Rc<Mutex<bool>>,
timer: GlobalTimer,
data: &[u8],
packet_f: PacketF,
reply_handler_f: HandlerF,
) -> Result<(), &'static str>
where
PacketF: Fn(&[u8; MASTER_PAYLOAD_MAX_SIZE], bool, usize) -> Packet,
HandlerF: Fn(&Packet) -> Result<(), &'static str>,
{
let mut i = 0;
while i < data.len() {
let mut slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
let len: usize = if i + MASTER_PAYLOAD_MAX_SIZE < data.len() {
MASTER_PAYLOAD_MAX_SIZE
} else {
data.len() - i
} as usize;
let last = i + len == data.len();
slice[..len].clone_from_slice(&data[i..i + len]);
i += len;
let packet = packet_f(&slice, last, len);
let reply = aux_transact(aux_mutex, linkno, &packet, timer).await?;
reply_handler_f(&reply)?;
}
Ok(())
}
pub async fn ddma_upload_trace( pub async fn ddma_upload_trace(
aux_mutex: &Rc<Mutex<bool>>, aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable, routing_table: &drtio_routing::RoutingTable,
@ -493,25 +429,44 @@ pub mod drtio {
trace: &Vec<u8>, trace: &Vec<u8>,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1; let linkno = routing_table.0[destination as usize][0] - 1;
partition_data( let mut i = 0;
linkno, while i < trace.len() {
aux_mutex, let mut trace_slice: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
timer, let len: usize = if i + DMA_TRACE_MAX_SIZE < trace.len() {
trace, DMA_TRACE_MAX_SIZE
|slice, last, len| Packet::DmaAddTraceRequest { } else {
id: id, trace.len() - i
destination: destination, } as usize;
last: last, let last = i + len == trace.len();
length: len as u16, trace_slice[..len].clone_from_slice(&trace[i..i + len]);
trace: *slice, i += len;
}, let reply = aux_transact(
|reply| match reply { aux_mutex,
Packet::DmaAddTraceReply { succeeded: true } => Ok(()), linkno,
Packet::DmaAddTraceReply { succeeded: false } => Err("error adding trace on satellite"), &Packet::DmaAddTraceRequest {
_ => Err("adding DMA trace failed, unexpected aux packet"), id: id,
}, destination: destination,
) last: last,
.await length: len as u16,
trace: trace_slice,
},
timer,
)
.await;
match reply {
Ok(Packet::DmaAddTraceReply { succeeded: true }) => (),
Ok(Packet::DmaAddTraceReply { succeeded: false }) => {
return Err("error adding trace on satellite");
}
Ok(_) => {
return Err("adding DMA trace failed, unexpected aux packet");
}
Err(_) => {
return Err("adding DMA trace failed, aux error");
}
}
}
Ok(())
} }
pub async fn ddma_send_erase( pub async fn ddma_send_erase(
@ -640,124 +595,48 @@ pub mod drtio {
} }
Ok(remote_buffers) Ok(remote_buffers)
} }
}
pub async fn subkernel_upload( fn read_device_map(cfg: &Config) -> BTreeMap<u32, String> {
aux_mutex: &Rc<Mutex<bool>>, let mut device_map: BTreeMap<u32, String> = BTreeMap::new();
routing_table: &drtio_routing::RoutingTable, let _ = cfg
timer: GlobalTimer, .read("device_map")
id: u32, .and_then(|raw_bytes| {
destination: u8, let mut bytes_cr = Cursor::new(raw_bytes);
data: &Vec<u8>, let size = bytes_cr.read_u32().unwrap();
) -> Result<(), &'static str> { for _ in 0..size {
let linkno = routing_table.0[destination as usize][0] - 1; let channel = bytes_cr.read_u32().unwrap();
partition_data( let device_name = bytes_cr.read_string().unwrap();
linkno, if let Some(old_entry) = device_map.insert(channel, device_name.clone()) {
aux_mutex, warn!(
timer, "conflicting device map entries for RTIO channel {}: '{}' and '{}'",
data, channel, old_entry, device_name
|slice, last, len| Packet::SubkernelAddDataRequest { );
id: id,
destination: destination,
last: last,
length: len as u16,
data: *slice,
},
|reply| match reply {
Packet::SubkernelAddDataReply { succeeded: true } => Ok(()),
Packet::SubkernelAddDataReply { succeeded: false } => Err("error adding subkernel on satellite"),
_ => Err("adding subkernel failed, unexpected aux packet"),
},
)
.await
}
pub async fn subkernel_load(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer,
id: u32,
destination: u8,
run: bool,
) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1;
let reply = aux_transact(
aux_mutex,
linkno,
&Packet::SubkernelLoadRunRequest {
id: id,
destination: destination,
run: run,
},
timer,
)
.await?;
match reply {
Packet::SubkernelLoadRunReply { succeeded: true } => return Ok(()),
Packet::SubkernelLoadRunReply { succeeded: false } => return Err("error on subkernel run request"),
_ => return Err("received unexpected aux packet during subkernel run"),
}
}
pub async fn subkernel_retrieve_exception(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer,
destination: u8,
) -> Result<Vec<u8>, &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1;
let mut remote_data: Vec<u8> = Vec::new();
loop {
let reply = aux_transact(
aux_mutex,
linkno,
&Packet::SubkernelExceptionRequest {
destination: destination,
},
timer,
)
.await?;
match reply {
Packet::SubkernelException { last, length, data } => {
remote_data.extend(&data[0..length as usize]);
if last {
return Ok(remote_data);
}
} }
_ => return Err("received unexpected aux packet during subkernel exception request"),
} }
} Ok(())
} })
.or_else(|err| {
warn!(
"error reading device map ({}), device names will not be available in RTIO error messages",
err
);
Err(err)
});
device_map
}
pub async fn subkernel_send_message( fn _resolve_channel_name(channel: u32, device_map: &BTreeMap<u32, String>) -> String {
aux_mutex: &Rc<Mutex<bool>>, match device_map.get(&channel) {
routing_table: &drtio_routing::RoutingTable, Some(val) => val.clone(),
timer: GlobalTimer, None => String::from("unknown"),
id: u32,
destination: u8,
message: &[u8],
) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1;
partition_data(
linkno,
aux_mutex,
timer,
message,
|slice, last, len| Packet::SubkernelMessage {
destination: destination,
id: id,
last: last,
length: len as u16,
data: *slice,
},
|reply| match reply {
Packet::SubkernelMessageAck { .. } => Ok(()),
_ => Err("sending message to subkernel failed, unexpected aux packet"),
},
)
.await
} }
} }
pub fn resolve_channel_name(channel: u32) -> String {
_resolve_channel_name(channel, unsafe { &RTIO_DEVICE_MAP })
}
#[cfg(not(has_drtio))] #[cfg(not(has_drtio))]
pub mod drtio { pub mod drtio {
use super::*; use super::*;
@ -779,7 +658,11 @@ pub fn startup(
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>, routing_table: &Rc<RefCell<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,
cfg: &Config,
) { ) {
unsafe {
RTIO_DEVICE_MAP = read_device_map(cfg);
}
drtio::startup(aux_mutex, routing_table, up_destinations, timer); drtio::startup(aux_mutex, routing_table, up_destinations, timer);
unsafe { unsafe {
csr::rtio_core::reset_phy_write(1); csr::rtio_core::reset_phy_write(1);

View File

@ -1,289 +0,0 @@
use alloc::{collections::BTreeMap, rc::Rc, vec::Vec};
use libasync::task;
use libboard_artiq::{drtio_routing::RoutingTable, drtioaux_proto::MASTER_PAYLOAD_MAX_SIZE};
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
use libcortex_a9::mutex::Mutex;
use log::error;
use crate::rtio_mgt::drtio;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum FinishStatus {
Ok,
CommLost,
Exception,
}
#[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,
CommLost,
DrtioError(&'static str),
}
impl From<&'static str> for Error {
fn from(value: &'static str) -> 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,
) -> 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).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) {
// 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) {
subkernel.state = SubkernelState::Finished {
status: match with_exception {
true => FinishStatus::Exception,
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: u64,
) -> Result<SubkernelFinished, Error> {
match SUBKERNELS.async_lock().await.get(&id).unwrap().state {
SubkernelState::Running | SubkernelState::Finished { .. } => (),
_ => return Err(Error::IncorrectState),
}
let max_time = timer.get_time() + Milliseconds(timeout);
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);
}
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 status == FinishStatus::Exception {
Some(
drtio::subkernel_retrieve_exception(aux_mutex, routing_table, timer, subkernel.destination)
.await?,
)
} else {
None
},
})
}
_ => Err(Error::IncorrectState),
}
} else {
Err(Error::SubkernelNotFound)
}
}
pub struct Message {
from_id: u32,
pub count: u8,
pub tag: 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, last: bool, length: usize, data: &[u8; MASTER_PAYLOAD_MAX_SIZE]) {
// called when receiving a message from satellite
if SUBKERNELS.async_lock().await.get(&id).is_none() {
// do not add messages for non-existing or deleted subkernels
return;
}
let mut current_messages = CURRENT_MESSAGES.async_lock().await;
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],
tag: data[1],
data: data[2..length].to_vec(),
},
);
}
};
if 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: u64, timer: GlobalTimer) -> Result<Message, Error> {
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);
while 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);
}
}
}
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,
message: Vec<u8>,
) -> Result<(), Error> {
let destination = SUBKERNELS.async_lock().await.get(&id).unwrap().destination;
// rpc data prepared by the kernel core already
Ok(drtio::subkernel_send_message(aux_mutex, routing_table, timer, id, destination, &message).await?)
}

View File

@ -14,19 +14,15 @@ build_zynq = { path = "../libbuild_zynq" }
[dependencies] [dependencies]
log = { version = "0.4", default-features = false } log = { version = "0.4", default-features = false }
core_io = { version = "0.1", features = ["collections"] }
cslice = "0.3"
embedded-hal = "0.2" embedded-hal = "0.2"
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq", features = ["ipv6"]} libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["ipv6"]}
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] } libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" } libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libasync = { path = "@@ZYNQ_RS@@/libasync" } libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libregister = { path = "@@ZYNQ_RS@@/libregister" } libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn", "ipv6"] } libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn", "ipv6"] }
libboard_artiq = { path = "../libboard_artiq" } libboard_artiq = { path = "../libboard_artiq" }
unwind = { path = "../libunwind" } unwind = { path = "../libunwind" }
libc = { path = "../libc" } libc = { path = "../libc" }
io = { path = "../libio", features = ["alloc"] }
ksupport = { path = "../libksupport" }

View File

@ -1,6 +1,6 @@
use core::cmp::min; use core::cmp::min;
use libboard_artiq::{drtioaux_proto::SAT_PAYLOAD_MAX_SIZE, pl::csr}; use libboard_artiq::{drtioaux_proto::ANALYZER_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; SAT_PAYLOAD_MAX_SIZE]) -> AnalyzerSliceMeta { pub fn get_data(&mut self, data_slice: &mut [u8; ANALYZER_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(SAT_PAYLOAD_MAX_SIZE, self.data_len - self.sent_bytes); let len = min(ANALYZER_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 {

View File

@ -170,8 +170,4 @@ impl Manager {
} }
} }
} }
pub fn running(&self) -> bool {
self.state == ManagerState::Playback
}
} }

View File

@ -1,15 +1,13 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(alloc_error_handler, try_trait, never_type, panic_info_message)] #![feature(never_type, panic_info_message, asm, naked_functions)]
#![feature(alloc_error_handler)]
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate core_io;
extern crate cslice;
extern crate embedded_hal; extern crate embedded_hal;
extern crate io;
extern crate ksupport;
extern crate libboard_artiq; extern crate libboard_artiq;
extern crate libboard_zynq; extern crate libboard_zynq;
extern crate libcortex_a9; extern crate libcortex_a9;
@ -20,6 +18,8 @@ extern crate unwind;
extern crate alloc; extern crate alloc;
use core::sync::atomic::{AtomicBool, Ordering};
use analyzer::Analyzer; use analyzer::Analyzer;
use dma::Manager as DmaManager; use dma::Manager as DmaManager;
use embedded_hal::blocking::delay::DelayUs; use embedded_hal::blocking::delay::DelayUs;
@ -27,22 +27,21 @@ use embedded_hal::blocking::delay::DelayUs;
use libboard_artiq::io_expander; use libboard_artiq::io_expander;
#[cfg(has_si5324)] #[cfg(has_si5324)]
use libboard_artiq::si5324; use libboard_artiq::si5324;
use libboard_artiq::{drtio_routing, drtioaux, use libboard_artiq::{drtio_routing, drtioaux, drtioaux_proto::ANALYZER_MAX_SIZE, identifier_read, logger, pl::csr};
drtioaux_proto::{MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE},
identifier_read, logger,
pl::csr};
#[cfg(feature = "target_kasli_soc")] #[cfg(feature = "target_kasli_soc")]
use libboard_zynq::error_led::ErrorLED; use libboard_zynq::error_led::ErrorLED;
use libboard_zynq::{i2c::I2c, print, println, time::Milliseconds, timer::GlobalTimer}; use libboard_zynq::{gic, i2c::I2c, mpcore, print, println, stdio, time::Milliseconds, timer::GlobalTimer};
use libcortex_a9::{l2c::enable_l2_cache, regs::MPIDR}; use libcortex_a9::{asm, interrupt_handler,
use libregister::RegisterR; l2c::enable_l2_cache,
notify_spin_lock,
regs::{MPIDR, SP},
spin_lock_yield};
use libregister::{RegisterR, RegisterW};
use libsupport_zynq::ram; use libsupport_zynq::ram;
use subkernel::Manager as KernelManager;
mod analyzer; mod analyzer;
mod dma; mod dma;
mod repeater; mod repeater;
mod subkernel;
fn drtiosat_reset(reset: bool) { fn drtiosat_reset(reset: bool) {
unsafe { unsafe {
@ -99,7 +98,6 @@ fn process_aux_packet(
i2c: &mut I2c, i2c: &mut I2c,
dma_manager: &mut DmaManager, dma_manager: &mut DmaManager,
analyzer: &mut Analyzer, analyzer: &mut Analyzer,
kernel_manager: &mut KernelManager,
) -> Result<(), drtioaux::Error> { ) -> Result<(), drtioaux::Error> {
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels, // In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
// and u16 otherwise; hence the `as _` conversion. // and u16 otherwise; hence the `as _` conversion.
@ -118,84 +116,42 @@ fn process_aux_packet(
drtioaux::send(0, &drtioaux::Packet::ResetAck) drtioaux::send(0, &drtioaux::Packet::ResetAck)
} }
drtioaux::Packet::DestinationStatusRequest { destination } => { drtioaux::Packet::DestinationStatusRequest {
destination: _destination,
} => {
#[cfg(has_drtio_routing)] #[cfg(has_drtio_routing)]
let hop = _routing_table.0[destination as usize][*_rank as usize]; let hop = _routing_table.0[_destination as usize][*_rank as usize];
#[cfg(not(has_drtio_routing))] #[cfg(not(has_drtio_routing))]
let hop = 0; let hop = 0;
if hop == 0 { if hop == 0 {
if let Some(status) = dma_manager.check_state() { let errors;
info!( unsafe {
"playback done, error: {}, channel: {}, timestamp: {}", errors = csr::drtiosat::rtio_error_read();
status.error, status.channel, status.timestamp }
); if errors & 1 != 0 {
drtioaux::send( let channel;
0,
&drtioaux::Packet::DmaPlaybackStatus {
destination: destination,
id: status.id,
error: status.error,
channel: status.channel,
timestamp: status.timestamp,
},
)?;
} else if let Some(subkernel_finished) = kernel_manager.get_last_finished() {
info!(
"subkernel {} finished, with exception: {}",
subkernel_finished.id, subkernel_finished.with_exception
);
drtioaux::send(
0,
&drtioaux::Packet::SubkernelFinished {
id: subkernel_finished.id,
with_exception: subkernel_finished.with_exception,
},
)?;
} else if kernel_manager.message_is_ready() {
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
match kernel_manager.message_get_slice(&mut data_slice) {
Some(meta) => drtioaux::send(
0,
&drtioaux::Packet::SubkernelMessage {
destination: destination,
id: kernel_manager.get_current_id().unwrap(),
last: meta.last,
length: meta.len as u16,
data: data_slice,
},
)?,
None => warn!("subkernel message is ready but no message is present"),
}
} else {
let errors;
unsafe { unsafe {
errors = csr::drtiosat::rtio_error_read(); channel = csr::drtiosat::sequence_error_channel_read();
csr::drtiosat::rtio_error_write(1);
} }
if errors & 1 != 0 { drtioaux::send(0, &drtioaux::Packet::DestinationSequenceErrorReply { channel })?;
let channel; } else if errors & 2 != 0 {
unsafe { let channel;
channel = csr::drtiosat::sequence_error_channel_read(); unsafe {
csr::drtiosat::rtio_error_write(1); channel = csr::drtiosat::collision_channel_read();
} csr::drtiosat::rtio_error_write(2);
drtioaux::send(0, &drtioaux::Packet::DestinationSequenceErrorReply { channel })?;
} else if errors & 2 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::collision_channel_read();
csr::drtiosat::rtio_error_write(2);
}
drtioaux::send(0, &drtioaux::Packet::DestinationCollisionReply { channel })?;
} else if errors & 4 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::busy_channel_read();
csr::drtiosat::rtio_error_write(4);
}
drtioaux::send(0, &drtioaux::Packet::DestinationBusyReply { channel })?;
} else {
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?;
} }
drtioaux::send(0, &drtioaux::Packet::DestinationCollisionReply { channel })?;
} else if errors & 4 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::busy_channel_read();
csr::drtiosat::rtio_error_write(4);
}
drtioaux::send(0, &drtioaux::Packet::DestinationBusyReply { channel })?;
} else {
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?;
} }
} }
@ -207,7 +163,7 @@ fn process_aux_packet(
let repno = hop - 1; let repno = hop - 1;
match _repeaters[repno].aux_forward( match _repeaters[repno].aux_forward(
&drtioaux::Packet::DestinationStatusRequest { &drtioaux::Packet::DestinationStatusRequest {
destination: destination, destination: _destination,
}, },
timer, timer,
) { ) {
@ -267,8 +223,8 @@ fn process_aux_packet(
drtioaux::Packet::MonitorRequest { drtioaux::Packet::MonitorRequest {
destination: _destination, destination: _destination,
channel, channel: _channel,
probe, probe: _probe,
} => { } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer); forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let value; let value;
@ -288,9 +244,9 @@ fn process_aux_packet(
} }
drtioaux::Packet::InjectionRequest { drtioaux::Packet::InjectionRequest {
destination: _destination, destination: _destination,
channel, channel: _channel,
overrd, overrd: _overrd,
value, value: _value,
} => { } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer); forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
#[cfg(has_rtio_moninj)] #[cfg(has_rtio_moninj)]
@ -303,8 +259,8 @@ fn process_aux_packet(
} }
drtioaux::Packet::InjectionStatusRequest { drtioaux::Packet::InjectionStatusRequest {
destination: _destination, destination: _destination,
channel, channel: _channel,
overrd, overrd: _overrd,
} => { } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer); forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let value; let value;
@ -477,7 +433,7 @@ fn process_aux_packet(
destination: _destination, destination: _destination,
} => { } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer); forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let mut data_slice: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE]; let mut data_slice: [u8; ANALYZER_MAX_SIZE] = [0; ANALYZER_MAX_SIZE];
let meta = analyzer.get_data(&mut data_slice); let meta = analyzer.get_data(&mut data_slice);
drtioaux::send( drtioaux::send(
0, 0,
@ -514,98 +470,10 @@ fn process_aux_packet(
timestamp, timestamp,
} => { } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer); forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = if !kernel_manager.running() { let succeeded = dma_manager.playback(id, timestamp).is_ok();
dma_manager.playback(id, timestamp).is_ok()
} else {
false
};
drtioaux::send(0, &drtioaux::Packet::DmaPlaybackReply { succeeded: succeeded }) drtioaux::send(0, &drtioaux::Packet::DmaPlaybackReply { succeeded: succeeded })
} }
drtioaux::Packet::SubkernelAddDataRequest {
destination: _destination,
id,
last,
length,
data,
} => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = kernel_manager.add(id, last, &data, length as usize).is_ok();
drtioaux::send(0, &drtioaux::Packet::SubkernelAddDataReply { succeeded: succeeded })
}
drtioaux::Packet::SubkernelLoadRunRequest {
destination: _destination,
id,
run,
} => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let mut succeeded = kernel_manager.load(id).is_ok();
// allow preloading a kernel with delayed run
if run {
if dma_manager.running() {
// cannot run kernel while DDMA is running
succeeded = false;
} else {
succeeded |= kernel_manager.run(id).is_ok();
}
}
drtioaux::send(0, &drtioaux::Packet::SubkernelLoadRunReply { succeeded: succeeded })
}
drtioaux::Packet::SubkernelExceptionRequest {
destination: _destination,
} => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let mut data_slice: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
let meta = kernel_manager.exception_get_slice(&mut data_slice);
drtioaux::send(
0,
&drtioaux::Packet::SubkernelException {
last: meta.last,
length: meta.len,
data: data_slice,
},
)
}
drtioaux::Packet::SubkernelMessage {
destination,
id: _id,
last,
length,
data,
} => {
forward!(_routing_table, destination, *_rank, _repeaters, &packet, timer);
kernel_manager.message_handle_incoming(last, length as usize, &data);
drtioaux::send(
0,
&drtioaux::Packet::SubkernelMessageAck {
destination: destination,
},
)
}
drtioaux::Packet::SubkernelMessageAck {
destination: _destination,
} => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
if kernel_manager.message_ack_slice() {
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
if let Some(meta) = kernel_manager.message_get_slice(&mut data_slice) {
drtioaux::send(
0,
&drtioaux::Packet::SubkernelMessage {
destination: *_rank,
id: kernel_manager.get_current_id().unwrap(),
last: meta.last,
length: meta.len as u16,
data: data_slice,
},
)?;
} else {
error!("Error receiving message slice");
}
}
Ok(())
}
_ => { _ => {
warn!("received unexpected aux packet"); warn!("received unexpected aux packet");
Ok(()) Ok(())
@ -621,7 +489,6 @@ fn process_aux_packets(
i2c: &mut I2c, i2c: &mut I2c,
dma_manager: &mut DmaManager, dma_manager: &mut DmaManager,
analyzer: &mut Analyzer, analyzer: &mut Analyzer,
kernel_manager: &mut KernelManager,
) { ) {
let result = drtioaux::recv(0).and_then(|packet| { let result = drtioaux::recv(0).and_then(|packet| {
if let Some(packet) = packet { if let Some(packet) = packet {
@ -634,7 +501,6 @@ fn process_aux_packets(
i2c, i2c,
dma_manager, dma_manager,
analyzer, analyzer,
kernel_manager,
) )
} else { } else {
Ok(()) Ok(())
@ -743,28 +609,20 @@ pub extern "C" fn main_core0() -> i32 {
ram::init_alloc_core0(); ram::init_alloc_core0();
ksupport::i2c::init(); let mut i2c = I2c::i2c0();
let mut i2c = unsafe { (ksupport::i2c::I2C_BUS).as_mut().unwrap() }; i2c.init().expect("I2C initialization failed");
#[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(&mut i2c, 0).unwrap(); for expander_i in 0..=1 {
io_expander1 = io_expander::IoExpander::new(&mut i2c, 1).unwrap(); let mut io_expander = io_expander::IoExpander::new(&mut i2c, expander_i).unwrap();
io_expander0 io_expander.init().expect("I2C I/O expander #0 initialization failed");
.init(&mut i2c) // Actively drive TX_DISABLE to false on SFP0..3
.expect("I2C I/O expander #0 initialization failed"); io_expander.set_oe(0, 1 << 1).unwrap();
io_expander1 io_expander.set_oe(1, 1 << 1).unwrap();
.init(&mut i2c) io_expander.set(0, 1, false);
.expect("I2C I/O expander #1 initialization failed"); io_expander.set(1, 1, false);
// Drive TX_DISABLE to false on SFP0..3 io_expander.service().unwrap();
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)] #[cfg(has_si5324)]
@ -773,7 +631,7 @@ pub extern "C" fn main_core0() -> i32 {
timer.delay_us(100_000); timer.delay_us(100_000);
info!("Switching SYS clocks..."); info!("Switching SYS clocks...");
unsafe { unsafe {
csr::gt_drtio::stable_clkin_write(1); csr::drtio_transceiver::stable_clkin_write(1);
} }
timer.delay_us(50_000); // wait for CPLL/QPLL/MMCM lock timer.delay_us(50_000); // wait for CPLL/QPLL/MMCM lock
let clk = unsafe { csr::sys_crg::current_clock_read() }; let clk = unsafe { csr::sys_crg::current_clock_read() };
@ -784,7 +642,7 @@ pub extern "C" fn main_core0() -> i32 {
} }
unsafe { unsafe {
csr::gt_drtio::txenable_write(0xffffffffu32 as _); csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
} }
#[cfg(has_drtio_routing)] #[cfg(has_drtio_routing)]
@ -799,8 +657,6 @@ pub extern "C" fn main_core0() -> i32 {
let mut hardware_tick_ts = 0; let mut hardware_tick_ts = 0;
let mut control = ksupport::kernel::Control::start();
loop { loop {
while !drtiosat_link_rx_up() { while !drtiosat_link_rx_up() {
drtiosat_process_errors(); drtiosat_process_errors();
@ -808,16 +664,6 @@ pub extern "C" fn main_core0() -> i32 {
for mut rep in repeaters.iter_mut() { for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer); 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); hardware_tick(&mut hardware_tick_ts, &mut timer);
} }
@ -828,12 +674,12 @@ pub extern "C" fn main_core0() -> i32 {
si5324::siphaser::calibrate_skew(&mut timer).expect("failed to calibrate skew"); si5324::siphaser::calibrate_skew(&mut timer).expect("failed to calibrate skew");
} }
// Various managers created here, so when link is dropped, all DMA traces // DMA manager created here, so when link is dropped, all DMA traces
// are cleared out for a clean slate on subsequent connections, // are cleared out for a clean slate on subsequent connections,
// without a manual intervention. // without a manual intervention.
let mut dma_manager = DmaManager::new(); let mut dma_manager = DmaManager::new();
// same for RTIO Analyzer
let mut analyzer = Analyzer::new(); let mut analyzer = Analyzer::new();
let mut kernel_manager = KernelManager::new(&mut control);
drtioaux::reset(0); drtioaux::reset(0);
drtiosat_reset(false); drtiosat_reset(false);
@ -849,21 +695,11 @@ pub extern "C" fn main_core0() -> i32 {
&mut i2c, &mut i2c,
&mut dma_manager, &mut dma_manager,
&mut analyzer, &mut analyzer,
&mut kernel_manager,
); );
#[allow(unused_mut)] #[allow(unused_mut)]
for mut rep in repeaters.iter_mut() { for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer); 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); hardware_tick(&mut hardware_tick_ts, &mut timer);
if drtiosat_tsc_loaded() { if drtiosat_tsc_loaded() {
info!("TSC loaded from uplink"); info!("TSC loaded from uplink");
@ -876,7 +712,24 @@ pub extern "C" fn main_core0() -> i32 {
error!("aux packet error: {:?}", e); error!("aux packet error: {:?}", e);
} }
} }
kernel_manager.process_kern_requests(rank, timer); if let Some(status) = dma_manager.check_state() {
info!(
"playback done, error: {}, channel: {}, timestamp: {}",
status.error, status.channel, status.timestamp
);
if let Err(e) = drtioaux::send(
0,
&drtioaux::Packet::DmaPlaybackStatus {
destination: rank,
id: status.id,
error: status.error,
channel: status.channel,
timestamp: status.timestamp,
},
) {
error!("error sending DMA playback status: {:?}", e);
}
}
} }
drtiosat_reset_phy(true); drtiosat_reset_phy(true);
@ -892,8 +745,46 @@ extern "C" {
static mut __stack1_start: u32; static mut __stack1_start: u32;
} }
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
if MPIDR.read().cpu_id() == 1 {
let mpcore = mpcore::RegisterBlock::mpcore();
let mut gic = gic::InterruptController::gic(mpcore);
let id = gic.get_interrupt_id();
if id.0 == 0 {
gic.end_interrupt(id);
asm::exit_irq();
SP.write(&mut __stack1_start as *mut _ as u32);
asm::enable_irq();
CORE1_RESTART.store(false, Ordering::Relaxed);
notify_spin_lock();
main_core1();
}
stdio::drop_uart();
}
loop {}
});
static mut PANICKED: [bool; 2] = [false; 2]; static mut PANICKED: [bool; 2] = [false; 2];
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
pub fn restart_core1() {
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
CORE1_RESTART.store(true, Ordering::Relaxed);
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core1.into());
while CORE1_RESTART.load(Ordering::Relaxed) {
spin_lock_yield();
}
}
#[no_mangle]
pub fn main_core1() {
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
interrupt_controller.enable_interrupts();
loop {}
}
#[no_mangle] #[no_mangle]
pub extern "C" fn exception(_vect: u32, _regs: *const u32, pc: u32, ea: u32) { pub extern "C" fn exception(_vect: u32, _regs: *const u32, pc: u32, ea: u32) {
fn hexdump(addr: u32) { fn hexdump(addr: u32) {
@ -949,3 +840,23 @@ pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
loop {} loop {}
} }
// linker symbols
extern "C" {
static __text_start: u32;
static __text_end: u32;
static __exidx_start: u32;
static __exidx_end: u32;
}
#[no_mangle]
extern "C" fn dl_unwind_find_exidx(_pc: *const u32, len_ptr: *mut u32) -> *const u32 {
let length;
let start: *const u32;
unsafe {
length = (&__exidx_end as *const u32).offset_from(&__exidx_start) as u32;
start = &__exidx_start;
*len_ptr = length;
}
start
}

View File

@ -1,692 +0,0 @@
use alloc::{collections::{BTreeMap, VecDeque},
format,
string::{String, ToString},
vec::Vec};
use core::{cmp::min, option::NoneError, slice, str};
use core_io::{Error as IoError, Write};
use cslice::AsCSlice;
use io::{Cursor, ProtoRead, ProtoWrite};
use ksupport::{eh_artiq, kernel, rpc};
use libboard_artiq::{drtioaux_proto::{MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE},
pl::csr};
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
use libcortex_a9::sync_channel::Receiver;
use log::warn;
#[derive(Debug, Clone, Copy, PartialEq)]
enum KernelState {
Absent,
Loaded,
Running,
MsgAwait(Milliseconds),
MsgSending,
}
#[derive(Debug)]
pub enum Error {
Load(String),
KernelNotFound,
Unexpected(String),
NoMessage,
AwaitingMessage,
SubkernelIoError,
KernelException(Sliceable),
}
impl From<NoneError> for Error {
fn from(_: NoneError) -> Error {
Error::KernelNotFound
}
}
impl From<IoError> for Error {
fn from(_value: IoError) -> Error {
Error::SubkernelIoError
}
}
impl From<()> for Error {
fn from(_: ()) -> Error {
Error::NoMessage
}
}
macro_rules! unexpected {
($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*))));
}
/* represents data that has to be sent to Master */
#[derive(Debug)]
pub struct Sliceable {
it: usize,
data: Vec<u8>,
}
/* represents interkernel messages */
struct Message {
count: u8,
tag: u8,
data: Vec<u8>,
}
#[derive(PartialEq)]
enum OutMessageState {
NoMessage,
MessageReady,
MessageBeingSent,
MessageSent,
MessageAcknowledged,
}
/* for dealing with incoming and outgoing interkernel messages */
struct MessageManager {
out_message: Option<Sliceable>,
out_state: OutMessageState,
in_queue: VecDeque<Message>,
in_buffer: Option<Message>,
}
// Per-run state
struct Session {
id: u32,
kernel_state: KernelState,
last_exception: Option<Sliceable>,
messages: MessageManager,
}
impl Session {
pub fn new(id: u32) -> Session {
Session {
id: id,
kernel_state: KernelState::Absent,
last_exception: None,
messages: MessageManager::new(),
}
}
fn running(&self) -> bool {
match self.kernel_state {
KernelState::Absent | KernelState::Loaded => false,
KernelState::Running | KernelState::MsgAwait { .. } | KernelState::MsgSending => true,
}
}
}
#[derive(Debug)]
struct KernelLibrary {
library: Vec<u8>,
complete: bool,
}
pub struct Manager<'a> {
kernels: BTreeMap<u32, KernelLibrary>,
session: Session,
control: &'a mut kernel::Control,
cache: BTreeMap<String, Vec<i32>>,
last_finished: Option<SubkernelFinished>,
}
pub struct SubkernelFinished {
pub id: u32,
pub with_exception: bool,
}
pub struct SliceMeta {
pub len: u16,
pub last: bool,
}
macro_rules! get_slice_fn {
($name:tt, $size:expr) => {
pub fn $name(&mut self, data_slice: &mut [u8; $size]) -> SliceMeta {
if self.data.len() == 0 {
return SliceMeta { len: 0, last: true };
}
let len = min($size, self.data.len() - self.it);
let last = self.it + len == self.data.len();
data_slice[..len].clone_from_slice(&self.data[self.it..self.it + len]);
self.it += len;
SliceMeta {
len: len as u16,
last: last,
}
}
};
}
impl Sliceable {
pub fn new(data: Vec<u8>) -> Sliceable {
Sliceable { it: 0, data: data }
}
get_slice_fn!(get_slice_sat, SAT_PAYLOAD_MAX_SIZE);
get_slice_fn!(get_slice_master, MASTER_PAYLOAD_MAX_SIZE);
}
impl MessageManager {
pub fn new() -> MessageManager {
MessageManager {
out_message: None,
out_state: OutMessageState::NoMessage,
in_queue: VecDeque::new(),
in_buffer: None,
}
}
pub fn handle_incoming(&mut self, last: bool, length: usize, data: &[u8; MASTER_PAYLOAD_MAX_SIZE]) {
// called when receiving a message from master
match self.in_buffer.as_mut() {
Some(message) => message.data.extend(&data[..length]),
None => {
self.in_buffer = Some(Message {
count: data[0],
tag: data[1],
data: data[2..length].to_vec(),
});
}
};
if last {
// when done, remove from working queue
self.in_queue.push_back(self.in_buffer.take().unwrap());
}
}
pub fn is_outgoing_ready(&mut self) -> bool {
// called by main loop, to see if there's anything to send, will send it afterwards
match self.out_state {
OutMessageState::MessageReady => {
self.out_state = OutMessageState::MessageBeingSent;
true
}
_ => false,
}
}
pub fn was_message_acknowledged(&mut self) -> bool {
match self.out_state {
OutMessageState::MessageAcknowledged => {
self.out_state = OutMessageState::NoMessage;
true
}
_ => false,
}
}
pub fn get_outgoing_slice(&mut self, data_slice: &mut [u8; MASTER_PAYLOAD_MAX_SIZE]) -> Option<SliceMeta> {
if self.out_state != OutMessageState::MessageBeingSent {
return None;
}
let meta = self.out_message.as_mut()?.get_slice_master(data_slice);
if meta.last {
// clear the message slot
self.out_message = None;
// notify kernel with a flag that message is sent
self.out_state = OutMessageState::MessageSent;
}
Some(meta)
}
pub fn ack_slice(&mut self) -> bool {
// returns whether or not there's more to be sent
match self.out_state {
OutMessageState::MessageBeingSent => true,
OutMessageState::MessageSent => {
self.out_state = OutMessageState::MessageAcknowledged;
false
}
_ => {
warn!("received unsolicited SubkernelMessageAck");
false
}
}
}
pub fn accept_outgoing(&mut self, message: Vec<u8>) -> Result<(), Error> {
// service tag skipped in kernel
self.out_message = Some(Sliceable::new(message));
self.out_state = OutMessageState::MessageReady;
Ok(())
}
pub fn get_incoming(&mut self) -> Option<Message> {
self.in_queue.pop_front()
}
}
impl<'a> Manager<'_> {
pub fn new(control: &mut kernel::Control) -> Manager {
Manager {
kernels: BTreeMap::new(),
session: Session::new(0),
control: control,
cache: BTreeMap::new(),
last_finished: None,
}
}
pub fn add(&mut self, id: u32, last: bool, data: &[u8], data_len: usize) -> Result<(), Error> {
let kernel = match self.kernels.get_mut(&id) {
Some(kernel) => {
if kernel.complete {
// replace entry
self.kernels.remove(&id);
self.kernels.insert(
id,
KernelLibrary {
library: Vec::new(),
complete: false,
},
);
self.kernels.get_mut(&id)?
} else {
kernel
}
}
None => {
self.kernels.insert(
id,
KernelLibrary {
library: Vec::new(),
complete: false,
},
);
self.kernels.get_mut(&id)?
}
};
kernel.library.extend(&data[0..data_len]);
kernel.complete = last;
Ok(())
}
pub fn running(&self) -> bool {
self.session.running()
}
pub fn get_current_id(&self) -> Option<u32> {
match self.running() {
true => Some(self.session.id),
false => None,
}
}
pub fn run(&mut self, id: u32) -> Result<(), Error> {
info!("starting subkernel #{}", id);
if self.session.kernel_state != KernelState::Loaded || self.session.id != id {
self.load(id)?;
}
self.session.kernel_state = KernelState::Running;
unsafe {
csr::cri_con::selected_write(2);
}
self.control.tx.send(kernel::Message::StartRequest);
Ok(())
}
pub fn message_handle_incoming(&mut self, last: bool, length: usize, slice: &[u8; MASTER_PAYLOAD_MAX_SIZE]) {
if !self.running() {
return;
}
self.session.messages.handle_incoming(last, length, slice);
}
pub fn message_get_slice(&mut self, slice: &mut [u8; MASTER_PAYLOAD_MAX_SIZE]) -> Option<SliceMeta> {
if !self.running() {
return None;
}
self.session.messages.get_outgoing_slice(slice)
}
pub fn message_ack_slice(&mut self) -> bool {
if !self.running() {
warn!("received unsolicited SubkernelMessageAck");
return false;
}
self.session.messages.ack_slice()
}
pub fn message_is_ready(&mut self) -> bool {
self.session.messages.is_outgoing_ready()
}
pub fn load(&mut self, id: u32) -> Result<(), Error> {
if self.session.id == id && self.session.kernel_state == KernelState::Loaded {
return Ok(());
}
if !self.kernels.get(&id)?.complete {
return Err(Error::KernelNotFound);
}
self.session = Session::new(id);
self.control.restart();
self.control
.tx
.send(kernel::Message::LoadRequest(self.kernels.get(&id)?.library.clone()));
let reply = self.control.rx.recv();
match reply {
kernel::Message::LoadCompleted => Ok(()),
kernel::Message::LoadFailed => Err(Error::Load("kernel load failed".to_string())),
_ => Err(Error::Load(format!(
"unexpected kernel CPU reply to load request: {:?}",
reply
))),
}
}
pub fn exception_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta {
match self.session.last_exception.as_mut() {
Some(exception) => exception.get_slice_sat(data_slice),
None => SliceMeta { len: 0, last: true },
}
}
pub fn get_last_finished(&mut self) -> Option<SubkernelFinished> {
self.last_finished.take()
}
fn kernel_stop(&mut self) {
self.session.kernel_state = KernelState::Absent;
unsafe {
csr::cri_con::selected_write(0);
}
}
fn runtime_exception(&mut self, cause: Error) {
let raw_exception: Vec<u8> = Vec::new();
let mut writer = Cursor::new(raw_exception);
match write_exception(
&mut writer,
&[Some(eh_artiq::Exception {
id: 11, // SubkernelError, defined in ksupport
message: format!("in subkernel id {}: {:?}", self.session.id, cause).as_c_slice(),
param: [0, 0, 0],
file: file!().as_c_slice(),
line: line!(),
column: column!(),
function: format!("subkernel id {}", self.session.id).as_c_slice(),
})],
&[eh_artiq::StackPointerBacktrace {
stack_pointer: 0,
initial_backtrace_size: 0,
current_backtrace_size: 0,
}],
&[],
0,
) {
Ok(_) => self.session.last_exception = Some(Sliceable::new(writer.into_inner())),
Err(_) => error!("Error writing exception data"),
}
self.kernel_stop();
}
pub fn process_kern_requests(&mut self, rank: u8, timer: GlobalTimer) {
if !self.running() {
return;
}
match self.process_external_messages(timer) {
Ok(()) => (),
Err(Error::AwaitingMessage) => return, // kernel still waiting, do not process kernel messages
Err(Error::KernelException(exception)) => {
self.session.last_exception = Some(exception);
self.last_finished = Some(SubkernelFinished {
id: self.session.id,
with_exception: true,
});
}
Err(e) => {
error!("Error while running processing external messages: {:?}", e);
self.runtime_exception(e);
self.last_finished = Some(SubkernelFinished {
id: self.session.id,
with_exception: true,
});
}
}
match self.process_kern_message(rank, timer) {
Ok(true) => {
self.last_finished = Some(SubkernelFinished {
id: self.session.id,
with_exception: false,
});
}
Ok(false) | Err(Error::NoMessage) => (),
Err(Error::KernelException(exception)) => {
self.session.last_exception = Some(exception);
self.last_finished = Some(SubkernelFinished {
id: self.session.id,
with_exception: true,
});
}
Err(e) => {
error!("Error while running kernel: {:?}", e);
self.runtime_exception(e);
self.last_finished = Some(SubkernelFinished {
id: self.session.id,
with_exception: true,
});
}
}
}
fn process_kern_message(&mut self, rank: u8, timer: GlobalTimer) -> Result<bool, Error> {
let reply = self.control.rx.try_recv()?;
match reply {
kernel::Message::KernelFinished(_async_errors) => {
self.kernel_stop();
return Ok(true);
}
kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors) => {
error!("exception in kernel");
for exception in exceptions {
error!("{:?}", exception.unwrap());
}
error!("stack pointers: {:?}", stack_pointers);
error!("backtrace: {:?}", backtrace);
let buf: Vec<u8> = Vec::new();
let mut writer = Cursor::new(buf);
match write_exception(&mut writer, exceptions, stack_pointers, backtrace, async_errors) {
Ok(()) => (),
Err(_) => error!("Error writing exception data"),
}
self.kernel_stop();
return Err(Error::KernelException(Sliceable::new(writer.into_inner())));
}
kernel::Message::CachePutRequest(key, value) => {
self.cache.insert(key, value);
}
kernel::Message::CacheGetRequest(key) => {
const DEFAULT: Vec<i32> = Vec::new();
let value = self.cache.get(&key).unwrap_or(&DEFAULT).clone();
self.control.tx.send(kernel::Message::CacheGetReply(value));
}
kernel::Message::SubkernelMsgSend { id: _, data } => {
self.session.messages.accept_outgoing(data)?;
self.session.kernel_state = KernelState::MsgSending;
}
kernel::Message::SubkernelMsgRecvRequest { id: _, timeout } => {
let max_time = timer.get_time() + Milliseconds(timeout);
self.session.kernel_state = KernelState::MsgAwait(max_time);
}
kernel::Message::UpDestinationsRequest(destination) => {
self.control
.tx
.send(kernel::Message::UpDestinationsReply(destination == (rank as i32)));
}
_ => {
unexpected!("unexpected message from core1 while kernel was running: {:?}", reply);
}
}
Ok(false)
}
fn process_external_messages(&mut self, timer: GlobalTimer) -> Result<(), Error> {
match self.session.kernel_state {
KernelState::MsgAwait(timeout) => {
if timer.get_time() > timeout {
self.control.tx.send(kernel::Message::SubkernelMsgRecvReply {
status: kernel::SubkernelStatus::Timeout,
count: 0,
});
self.session.kernel_state = KernelState::Running;
return Ok(());
}
if let Some(message) = self.session.messages.get_incoming() {
self.control.tx.send(kernel::Message::SubkernelMsgRecvReply {
status: kernel::SubkernelStatus::NoError,
count: message.count,
});
self.session.kernel_state = KernelState::Running;
self.pass_message_to_kernel(&message, timer)
} else {
Err(Error::AwaitingMessage)
}
}
KernelState::MsgSending => {
if self.session.messages.was_message_acknowledged() {
self.session.kernel_state = KernelState::Running;
self.control.tx.send(kernel::Message::SubkernelMsgSent);
Ok(())
} else {
Err(Error::AwaitingMessage)
}
}
_ => Ok(()),
}
}
fn pass_message_to_kernel(&mut self, message: &Message, timer: GlobalTimer) -> Result<(), Error> {
let mut reader = Cursor::new(&message.data);
let mut tag: [u8; 1] = [message.tag];
let mut i = message.count;
loop {
let slot = match recv_w_timeout(&mut self.control.rx, timer, 100)? {
kernel::Message::RpcRecvRequest(slot) => slot,
other => unexpected!("expected root value slot from core1, not {:?}", other),
};
let mut exception: Option<Sliceable> = None;
let mut unexpected: Option<String> = None;
rpc::recv_return(&mut reader, &tag, slot, &mut |size| {
if size == 0 {
0 as *mut ()
} else {
self.control.tx.send(kernel::Message::RpcRecvReply(Ok(size)));
match recv_w_timeout(&mut self.control.rx, timer, 100) {
Ok(kernel::Message::RpcRecvRequest(slot)) => slot,
Ok(kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors)) => {
let buf: Vec<u8> = Vec::new();
let mut writer = Cursor::new(buf);
match write_exception(&mut writer, exceptions, stack_pointers, backtrace, async_errors) {
Ok(()) => {
exception = Some(Sliceable::new(writer.into_inner()));
}
Err(_) => {
unexpected = Some("Error writing exception data".to_string());
}
};
0 as *mut ()
}
other => {
unexpected = Some(format!("expected nested value slot from kernel CPU, not {:?}", other));
0 as *mut ()
}
}
}
})?;
if let Some(exception) = exception {
self.kernel_stop();
return Err(Error::KernelException(exception));
} else if let Some(unexpected) = unexpected {
self.kernel_stop();
unexpected!("{}", unexpected);
}
self.control.tx.send(kernel::Message::RpcRecvReply(Ok(0)));
i -= 1;
if i == 0 {
break;
} else {
// update the tag for next read
tag[0] = reader.read_u8()?;
}
}
Ok(())
}
}
fn write_exception<W>(
writer: &mut W,
exceptions: &[Option<eh_artiq::Exception>],
stack_pointers: &[eh_artiq::StackPointerBacktrace],
backtrace: &[(usize, usize)],
async_errors: u8,
) -> Result<(), Error>
where
W: Write + ?Sized,
{
/* header */
writer.write_bytes(&[0x5a, 0x5a, 0x5a, 0x5a, /*Reply::KernelException*/ 9])?;
writer.write_u32(exceptions.len() as u32)?;
for exception in exceptions.iter() {
let exception = exception.as_ref().unwrap();
writer.write_u32(exception.id)?;
if exception.message.len() == usize::MAX {
// exception with host string
writer.write_u32(u32::MAX)?;
writer.write_u32(exception.message.as_ptr() as u32)?;
} else {
let msg =
str::from_utf8(unsafe { slice::from_raw_parts(exception.message.as_ptr(), exception.message.len()) })
.unwrap()
.replace(
"{rtio_channel_info:0}",
&format!(
"0x{:04x}:{}",
exception.param[0],
ksupport::resolve_channel_name(exception.param[0] as u32)
),
);
writer.write_string(&msg)?;
}
writer.write_u64(exception.param[0] as u64)?;
writer.write_u64(exception.param[1] as u64)?;
writer.write_u64(exception.param[2] as u64)?;
writer.write_bytes(exception.file.as_ref())?;
writer.write_u32(exception.line)?;
writer.write_u32(exception.column)?;
writer.write_bytes(exception.function.as_ref())?;
}
for sp in stack_pointers.iter() {
writer.write_u32(sp.stack_pointer as u32)?;
writer.write_u32(sp.initial_backtrace_size as u32)?;
writer.write_u32(sp.current_backtrace_size as u32)?;
}
writer.write_u32(backtrace.len() as u32)?;
for &(addr, sp) in backtrace {
writer.write_u32(addr as u32)?;
writer.write_u32(sp as u32)?;
}
writer.write_u8(async_errors as u8)?;
Ok(())
}
fn recv_w_timeout(
rx: &mut Receiver<'_, kernel::Message>,
timer: GlobalTimer,
timeout: u64,
) -> Result<kernel::Message, Error> {
let max_time = timer.get_time() + Milliseconds(timeout);
while timer.get_time() < max_time {
match rx.try_recv() {
Err(_) => (),
Ok(message) => return Ok(message),
}
}
Err(Error::NoMessage)
}