Compare commits

..

9 Commits

Author SHA1 Message Date
Sebastien Bourdeauducq 49d9a644c6 mgmt: remove spurious config write warning 2022-03-16 08:25:05 +08:00
pca006132 14c2abe578 runtime/rpc: fixes alignment and size problem 2022-01-05 08:24:41 +08:00
Sebastien Bourdeauducq 1155802cf7 Revert "runtime: expose rint from libm"
This reverts commit 95d024e38b.
2021-10-11 08:13:15 +08:00
Sebastien Bourdeauducq 95d024e38b runtime: expose rint from libm 2021-10-10 20:40:43 +08:00
pca006132 d765b83e36 libdyld: fixed symbol relocation
Note that in libdyld/src/lib.rs #117-118, image pointer is already added
to the symbol offset, so we do not need to add the pointer again
2021-09-25 13:50:30 +08:00
Sebastien Bourdeauducq 62a30bda9b update dependencies 2021-07-27 16:29:09 +08:00
Sebastien Bourdeauducq 801d7ac911 nixpkgs 21.05 2021-07-27 15:46:32 +08:00
Sebastien Bourdeauducq 298085b62d fix compilation with nixpkgs 21.05
The environment variable is optional to keep compatibility with other build environments.

Closes #131
2021-07-27 15:40:56 +08:00
Sebastien Bourdeauducq 93e26087ee update copyright year 2021-07-07 18:07:34 +08:00
67 changed files with 1270 additions and 6073 deletions

View File

@ -24,7 +24,7 @@ The following configuration keys are available:
- ``ip``: IPv4 address. - ``ip``: IPv4 address.
- ``ip6``: IPv6 address. - ``ip6``: IPv6 address.
- ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``). - ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``).
- ``rtio_clock``: source of RTIO clock; valid values are ``ext0_bypass`` and ``int_125``. - ``rtioclk``: source of RTIO clock; valid values are ``external`` and ``internal``.
- ``boot``: SD card "boot.bin" file, for replacing the boot firmware/gateware. Write only. - ``boot``: SD card "boot.bin" file, for replacing the boot firmware/gateware. Write only.
Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is
@ -33,38 +33,44 @@ not implemented as it seems not very useful.
Development instructions Development instructions
------------------------ ------------------------
ARTIQ on Zynq is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``). Configure Nix channels:
```shell
nix-channel --add https://nixbld.m-labs.hk/channel/custom/artiq/fast-beta/artiq-fast
nix-channel --update
```
Note: if you are using Nix channels the first time, you need to be aware of this bug: https://github.com/NixOS/nix/issues/3831
Pure build with Nix and execution on a remote JTAG server: Pure build with Nix and execution on a remote JTAG server:
```shell ```shell
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock_satellite-jtag etc. nix-build -A zc706-simple-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock-jtag
./remote_run.sh ./remote_run.sh
``` ```
Impure incremental build and execution on a remote JTAG server: Impure incremental build and execution on a remote JTAG server:
```shell ```shell
nix develop nix-shell
cd src cd src
gateware/zc706.py -g ../build/gateware -V <variant> # build gateware gateware/zc706.py -g ../build/gateware # build gateware
make GWARGS="-V <variant>" <runtime/satman> # build firmware make # build firmware
cd .. cd ..
./remote_run.sh -i ./remote_run.sh -i
``` ```
Notes: Notes:
- This is developed with Nixpkgs 21.05, and the ``nixbld.m-labs.hk`` binary substituter can also be used here (see the ARTIQ manual for the public key and instructions).
- The impure build process is also compatible with non-Nix systems. - The impure build process is also compatible with non-Nix systems.
- When calling make, you need to specify both the variant and firmware type.
- Firmware type must be either ``runtime`` for DRTIO-less or DRTIO master variants, or ``satman`` for DRTIO satellite.
- 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. - To update ``zynq-rs``, update the cargo files as per usual for Rust projects, but also keep ``zynq-rs.nix`` in sync.
License License
------- -------
Copyright (C) 2019-2022 M-Labs Limited. Copyright (C) 2019-2021 M-Labs Limited.
ARTIQ is free software: you can redistribute it and/or modify ARTIQ is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

147
default.nix Normal file
View File

@ -0,0 +1,147 @@
let
zynq-rs = (import ./zynq-rs.nix);
pkgs = import <nixpkgs> { overlays = [ (import "${zynq-rs}/nix/mozilla-overlay.nix") ]; };
rustPlatform = (import "${zynq-rs}/nix/rust-platform.nix" { inherit pkgs; });
cargo-xbuild = (import zynq-rs).cargo-xbuild;
mkbootimage = import "${zynq-rs}/nix/mkbootimage.nix" { inherit pkgs; };
artiqpkgs = import <artiq-fast/default.nix> { inherit pkgs; };
vivado = import <artiq-fast/vivado.nix> { inherit pkgs; };
# FSBL configuration supplied by Vivado 2020.1 for these boards:
fsblTargets = ["zc702" "zc706" "zed"];
build = { target, variant }: let
szl = (import zynq-rs)."${target}-szl";
fsbl = import "${zynq-rs}/nix/fsbl.nix" {
inherit pkgs;
board = target;
};
firmware = rustPlatform.buildRustPackage rec {
# note: due to fetchCargoTarball, cargoSha256 depends on package name
name = "firmware";
src = ./src;
cargoSha256 = "1g1nb8jz1na1v60hq3zrpgrwnw9qz7xrnnr2lkiyhsp72bg52ssl";
nativeBuildInputs = [
pkgs.gnumake
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
cargo-xbuild
pkgs.llvmPackages_9.llvm
pkgs.llvmPackages_9.clang-unwrapped
];
buildPhase = ''
export XARGO_RUST_SRC="${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library"
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)
make TARGET=${target} VARIANT=${variant}
'';
installPhase = ''
mkdir -p $out $out/nix-support
cp ../build/runtime.bin $out/runtime.bin
cp ../build/firmware/armv7-none-eabihf/release/runtime $out/runtime.elf
echo file binary-dist $out/runtime.bin >> $out/nix-support/hydra-build-products
echo file binary-dist $out/runtime.elf >> $out/nix-support/hydra-build-products
'';
doCheck = false;
dontFixup = true;
};
gateware = pkgs.runCommand "${target}-${variant}-gateware"
{
nativeBuildInputs = [
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
vivado
];
}
''
python ${./src/gateware}/${target}.py -g build -V ${variant}
mkdir -p $out $out/nix-support
cp build/top.bit $out
echo file binary-dist $out/top.bit >> $out/nix-support/hydra-build-products
'';
# SZL startup
jtag = pkgs.runCommand "${target}-${variant}-jtag" {}
''
mkdir $out
ln -s ${szl}/szl.elf $out
ln -s ${firmware}/runtime.bin $out
ln -s ${gateware}/top.bit $out
'';
sd = pkgs.runCommand "${target}-${variant}-sd"
{
buildInputs = [ mkbootimage ];
}
''
# Do not use "long" paths in boot.bif, because embedded developers
# can't write software (mkbootimage will segfault).
bifdir=`mktemp -d`
cd $bifdir
ln -s ${szl}/szl.elf szl.elf
ln -s ${firmware}/runtime.elf runtime.elf
ln -s ${gateware}/top.bit top.bit
cat > boot.bif << EOF
the_ROM_image:
{
[bootloader]szl.elf
top.bit
runtime.elf
}
EOF
mkdir $out $out/nix-support
mkbootimage boot.bif $out/boot.bin
echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products
'';
# FSBL startup
fsbl-sd = pkgs.runCommand "${target}-${variant}-fsbl-sd"
{
buildInputs = [ mkbootimage ];
}
''
bifdir=`mktemp -d`
cd $bifdir
ln -s ${fsbl}/fsbl.elf fsbl.elf
ln -s ${gateware}/top.bit top.bit
ln -s ${firmware}/runtime.elf runtime.elf
cat > boot.bif << EOF
the_ROM_image:
{
[bootloader]fsbl.elf
top.bit
runtime.elf
}
EOF
mkdir $out $out/nix-support
mkbootimage boot.bif $out/boot.bin
echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products
'';
in {
"${target}-${variant}-firmware" = firmware;
"${target}-${variant}-gateware" = gateware;
"${target}-${variant}-jtag" = jtag;
"${target}-${variant}-sd" = sd;
} // (
if builtins.elem target fsblTargets
then {
"${target}-${variant}-fsbl-sd" = fsbl-sd;
}
else {}
);
in
(
(build { target = "zc706"; variant = "simple"; }) //
(build { target = "zc706"; variant = "nist_clock"; }) //
(build { target = "zc706"; variant = "nist_qc2"; }) //
(build { target = "zc706"; variant = "acpki_simple"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2"; }) //
(build { target = "coraz7"; variant = "10"; }) //
(build { target = "coraz7"; variant = "07s"; }) //
(build { target = "coraz7"; variant = "acpki_10"; }) //
(build { target = "coraz7"; variant = "acpki_07s"; }) //
(build { target = "redpitaya"; variant = "simple"; }) //
(build { target = "redpitaya"; variant = "acpki_simple"; }) //
{ inherit zynq-rs; }
)

View File

@ -1,60 +0,0 @@
{
"target": "kasli_soc",
"variant": "demo",
"hw_rev": "v1.0",
"base": "standalone",
"peripherals": [
{
"type": "grabber",
"ports": [0]
},
{
"type": "dio",
"ports": [1],
"bank_direction_low": "input",
"bank_direction_high": "output"
},
{
"type": "dio",
"ports": [2],
"bank_direction_low": "output",
"bank_direction_high": "output"
},
{
"type": "urukul",
"dds": "ad9910",
"ports": [3, 4],
"clk_sel": 2
},
{
"type": "zotino",
"ports": [5]
},
{
"type": "sampler",
"ports": [6, 7]
},
{
"type": "mirny",
"ports": [8],
"clk_sel": 1,
"refclk": 125e6
},
{
"type": "fastino",
"ports": [9]
},
{
"type": "dio",
"ports": [10],
"bank_direction_low": "input",
"bank_direction_high": "input"
},
{
"type": "dio",
"ports": [11],
"bank_direction_low": "output",
"bank_direction_high": "input"
}
]
}

View File

@ -29,11 +29,30 @@ device_db = {
"class": "PCA9548" "class": "PCA9548"
}, },
# led? are common to all variants
"led0": { "led0": {
"type": "local", "type": "local",
"module": "artiq.coredevice.ttl", "module": "artiq.coredevice.ttl",
"class": "TTLOut", "class": "TTLOut",
"arguments": {"channel": 41}, "arguments": {"channel": 0},
},
"led1": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 1},
},
"led2": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 2}
},
"led3": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 3}
}, },
} }
@ -43,7 +62,7 @@ for i in range(40):
"type": "local", "type": "local",
"module": "artiq.coredevice.ttl", "module": "artiq.coredevice.ttl",
"class": "TTLInOut", "class": "TTLInOut",
"arguments": {"channel": i} "arguments": {"channel": 4+i}
} }
device_db["ad9914dds0"] = { device_db["ad9914dds0"] = {

View File

@ -1,219 +0,0 @@
{
"nodes": {
"artiq": {
"inputs": {
"artiq-comtools": "artiq-comtools",
"mozilla-overlay": "mozilla-overlay",
"nixpkgs": "nixpkgs",
"sipyco": "sipyco",
"src-migen": "src-migen",
"src-misoc": "src-misoc",
"src-pythonparser": "src-pythonparser"
},
"locked": {
"lastModified": 1654087576,
"narHash": "sha256-d+LCvCXikhhEjsf4qBZakx3Sda85p+4vVZVwE1YuS90=",
"ref": "master",
"rev": "68ef0073ea66fe9c7f7c178979ecb8c681c47caa",
"revCount": 8068,
"type": "git",
"url": "https://github.com/m-labs/artiq.git"
},
"original": {
"type": "git",
"url": "https://github.com/m-labs/artiq.git"
}
},
"artiq-comtools": {
"inputs": {
"nixpkgs": [
"artiq",
"nixpkgs"
],
"sipyco": [
"artiq",
"sipyco"
]
},
"locked": {
"lastModified": 1654007592,
"narHash": "sha256-vaDFhE1ItjqtIcinC/6RAJGbj44pxxMUEeQUa3FtgEE=",
"owner": "m-labs",
"repo": "artiq-comtools",
"rev": "cb73281154656ee8f74db1866859e31bf42755cd",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "artiq-comtools",
"type": "github"
}
},
"mozilla-overlay": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"mozilla-overlay_2": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"mozilla-overlay_3": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1653920503,
"narHash": "sha256-BBeCZwZImtjP3oYy4WogkQYy5OxNyfNciVSc1AfZgLQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a634c8f6c1fbf9b9730e01764999666f3436f10a",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"artiq": "artiq",
"mozilla-overlay": "mozilla-overlay_2",
"zynq-rs": "zynq-rs"
}
},
"sipyco": {
"inputs": {
"nixpkgs": [
"artiq",
"nixpkgs"
]
},
"locked": {
"lastModified": 1654006751,
"narHash": "sha256-OWAnoTCutvTQcYdtdtLQuL6uRtG+7Jz7sbRhcScv8bo=",
"owner": "m-labs",
"repo": "sipyco",
"rev": "b3d03a94c751a24769c54a61a0dbe9d6af52dade",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "sipyco",
"type": "github"
}
},
"src-migen": {
"flake": false,
"locked": {
"lastModified": 1650337393,
"narHash": "sha256-rm1SlFmF2ASz0vIy2nDEzGlyRw2oYNeJRr8Kh8Mg2Qc=",
"owner": "m-labs",
"repo": "migen",
"rev": "d4e3f34177c32f09904397179e6ed9c83175e528",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "migen",
"type": "github"
}
},
"src-misoc": {
"flake": false,
"locked": {
"lastModified": 1649324486,
"narHash": "sha256-Mw/fQS3lHFvCm7L1k63joRkz5uyijQfywcOq+X2+o2s=",
"ref": "master",
"rev": "f1dc58d2b8c222ba41c25cee4301626625f46e43",
"revCount": 2420,
"submodules": true,
"type": "git",
"url": "https://github.com/m-labs/misoc.git"
},
"original": {
"submodules": true,
"type": "git",
"url": "https://github.com/m-labs/misoc.git"
}
},
"src-pythonparser": {
"flake": false,
"locked": {
"lastModified": 1628745371,
"narHash": "sha256-p6TgeeaK4NEmbhimEXp31W8hVRo4DgWmcCoqZ+UdN60=",
"owner": "m-labs",
"repo": "pythonparser",
"rev": "5413ee5c9f8760e95c6acd5d6e88dabb831ad201",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "pythonparser",
"type": "github"
}
},
"zynq-rs": {
"inputs": {
"mozilla-overlay": "mozilla-overlay_3",
"nixpkgs": [
"artiq",
"nixpkgs"
]
},
"locked": {
"lastModified": 1654002148,
"narHash": "sha256-Ztzx7ze8o0gdu+sDpc+PS2M88h/b22rwFq0b12wMqZQ=",
"ref": "master",
"rev": "6cd32f6ee0e7bb7ce5a279f8be943903a393a476",
"revCount": 617,
"type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
},
"original": {
"type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
}
}
},
"root": "root",
"version": 7
}

381
flake.nix
View File

@ -1,381 +0,0 @@
{
description = "ARTIQ port to the Zynq-7000 platform";
inputs.artiq.url = git+https://github.com/m-labs/artiq.git;
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
inputs.zynq-rs.url = git+https://git.m-labs.hk/m-labs/zynq-rs;
inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs";
outputs = { self, mozilla-overlay, zynq-rs, artiq }:
let
pkgs = import artiq.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
zynqpkgs = zynq-rs.packages.x86_64-linux;
artiqpkgs = artiq.packages.x86_64-linux;
rustPlatform = zynq-rs.rustPlatform;
fastnumbers = pkgs.python3Packages.buildPythonPackage rec {
pname = "fastnumbers";
version = "2.2.1";
src = pkgs.python3Packages.fetchPypi {
inherit pname version;
sha256 = "0j15i54p7nri6hkzn1wal9pxri4pgql01wgjccig6ar0v5jjbvsy";
};
};
artiq-netboot = pkgs.python3Packages.buildPythonPackage rec {
pname = "artiq-netboot";
version = "unstable-2020-10-15";
src = pkgs.fetchgit {
url = "https://git.m-labs.hk/m-labs/artiq-netboot.git";
rev = "04f69eb07df73abe4b89fde2c24084f7664f2104";
sha256 = "0ql4fr8m8gpb2yql8aqsdqsssxb8zqd6l65kl1f6s9845zy7shs9";
};
};
ramda = pkgs.python3Packages.buildPythonPackage {
pname = "ramda";
version = "unstable-2019-02-01";
src = pkgs.fetchFromGitHub {
owner = "peteut";
repo = "ramda.py";
rev = "bd58f8e69d0e9a713d9c1f286a1ac5e5603956b1";
sha256 = "0qzd5yp9lbaham8p1wiymdjapzbqsli7lvngv24c3z4ybd9jlq9g";
};
nativeBuildInputs = with pkgs.python3Packages; [ pbr ];
propagatedBuildInputs = with pkgs.python3Packages; [ future fastnumbers ];
checkInputs = with pkgs.python3Packages; [ pytest pytest-flake8 ];
checkPhase = "pytest";
doCheck = false;
preBuild = ''
export PBR_VERSION=0.0.1
'';
};
migen-axi = pkgs.python3Packages.buildPythonPackage {
pname = "migen-axi";
version = "unstable-2021-09-15";
src = pkgs.fetchFromGitHub {
owner = "peteut";
repo = "migen-axi";
rev = "9763505ee96acd7572280a2d1233721342dc7c3f";
sha256 = "15c7g05n183rka66fl1glzp6h7xjlpy1p6k8biry24dangsmxmvg";
};
nativeBuildInputs = with pkgs.python3Packages; [ pbr ];
propagatedBuildInputs = with pkgs.python3Packages; [ setuptools click numpy toolz jinja2 ramda artiqpkgs.migen artiqpkgs.misoc ];
postPatch = ''
substituteInPlace requirements.txt \
--replace "jinja2==2.11.3" "jinja2"
substituteInPlace requirements.txt \
--replace "future==0.18.2" "future"
substituteInPlace requirements.txt \
--replace "ramda==0.5.5" "ramda"
substituteInPlace requirements.txt \
--replace "colorama==0.4.3" "colorama"
substituteInPlace requirements.txt \
--replace "toolz==0.10.0" "toolz"
substituteInPlace requirements.txt \
--replace "pyserial==3.4" "pyserial"
substituteInPlace requirements.txt \
--replace "markupsafe==1.1.1" "markupsafe"
'';
checkInputs = with pkgs.python3Packages; [ pytest pytest-timeout pytest-flake8 ];
checkPhase = "pytest";
preBuild = ''
export PBR_VERSION=0.0.1
'';
};
binutils = { platform, target, zlib }: pkgs.stdenv.mkDerivation rec {
basename = "binutils";
version = "2.30";
name = "${basename}-${platform}-${version}";
src = pkgs.fetchurl {
url = "https://ftp.gnu.org/gnu/binutils/binutils-${version}.tar.bz2";
sha256 = "028cklfqaab24glva1ks2aqa1zxa6w6xmc8q34zs1sb7h22dxspg";
};
configureFlags =
[ "--enable-shared" "--enable-deterministic-archives" "--target=${target}"];
outputs = [ "out" "info" "man" ];
depsBuildBuild = [ pkgs.buildPackages.stdenv.cc ];
buildInputs = [ zlib ];
enableParallelBuilding = true;
};
binutils-arm = pkgs.callPackage binutils { platform = "arm"; target = "armv7-unknown-linux-gnueabihf"; };
# FSBL configuration supplied by Vivado 2020.1 for these boards:
fsblTargets = ["zc702" "zc706" "zed"];
sat_variants = [
# kasli-soc satellite variants
"satellite"
# zc706 satellite variants
"nist_clock_satellite" "nist_qc2_satellite" "acpki_nist_clock_satellite" "acpki_nist_qc2_satellite"
"nist_clock_satellite_100mhz" "nist_qc2_satellite_100mhz" "acpki_nist_clock_satellite_100mhz" "acpki_nist_qc2_satellite_100mhz"
];
build = { target, variant, json ? null }: let
szl = zynqpkgs."${target}-szl";
fsbl = zynqpkgs."${target}-fsbl";
fwtype = if builtins.elem variant sat_variants then "satman" else "runtime";
firmware = rustPlatform.buildRustPackage rec {
name = "firmware";
src = ./src;
cargoLock = {
lockFile = src/Cargo.lock;
outputHashes = {
"libasync-0.0.0" = "sha256-xuwesRrQiccopPTCkwGqQxld74X74q7EVsKIrE0zirc=";
};
};
nativeBuildInputs = [
pkgs.gnumake
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ ps.jsonschema migen migen-axi misoc artiq ])))
artiqpkgs.artiq
zynqpkgs.cargo-xbuild
pkgs.llvmPackages_9.llvm
pkgs.llvmPackages_9.clang-unwrapped
];
buildPhase = ''
export XARGO_RUST_SRC="${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library"
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)
make TARGET=${target} GWARGS="${if json == null then "-V ${variant}" else json}" ${fwtype}
'';
installPhase = ''
mkdir -p $out $out/nix-support
cp ../build/${fwtype}.bin $out/${fwtype}.bin
cp ../build/firmware/armv7-none-eabihf/release/${fwtype} $out/${fwtype}.elf
echo file binary-dist $out/${fwtype}.bin >> $out/nix-support/hydra-build-products
echo file binary-dist $out/${fwtype}.elf >> $out/nix-support/hydra-build-products
'';
doCheck = false;
dontFixup = true;
};
gateware = pkgs.runCommand "${target}-${variant}-gateware"
{
nativeBuildInputs = [
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ ps.jsonschema migen migen-axi misoc artiq ])))
artiqpkgs.artiq
artiqpkgs.vivado
];
}
''
python ${./src/gateware}/${target}.py -g build ${if json == null then "-V ${variant}" else json}
mkdir -p $out $out/nix-support
cp build/top.bit $out
echo file binary-dist $out/top.bit >> $out/nix-support/hydra-build-products
'';
# SZL startup
jtag = pkgs.runCommand "${target}-${variant}-jtag" {}
''
mkdir $out
ln -s ${szl}/szl.elf $out
ln -s ${firmware}/${fwtype}.bin $out
ln -s ${gateware}/top.bit $out
'';
sd = pkgs.runCommand "${target}-${variant}-sd"
{
buildInputs = [ zynqpkgs.mkbootimage ];
}
''
# Do not use "long" paths in boot.bif, because embedded developers
# can't write software (mkbootimage will segfault).
bifdir=`mktemp -d`
cd $bifdir
ln -s ${szl}/szl.elf szl.elf
ln -s ${firmware}/${fwtype}.elf ${fwtype}.elf
ln -s ${gateware}/top.bit top.bit
cat > boot.bif << EOF
the_ROM_image:
{
[bootloader]szl.elf
top.bit
${fwtype}.elf
}
EOF
mkdir $out $out/nix-support
mkbootimage boot.bif $out/boot.bin
echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products
'';
# FSBL startup
fsbl-sd = pkgs.runCommand "${target}-${variant}-fsbl-sd"
{
buildInputs = [ zynqpkgs.mkbootimage ];
}
''
bifdir=`mktemp -d`
cd $bifdir
ln -s ${fsbl}/fsbl.elf fsbl.elf
ln -s ${gateware}/top.bit top.bit
ln -s ${firmware}/${fwtype}.elf ${fwtype}.elf
cat > boot.bif << EOF
the_ROM_image:
{
[bootloader]fsbl.elf
top.bit
${fwtype}.elf
}
EOF
mkdir $out $out/nix-support
mkbootimage boot.bif $out/boot.bin
echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products
'';
in {
"${target}-${variant}-firmware" = firmware;
"${target}-${variant}-gateware" = gateware;
"${target}-${variant}-jtag" = jtag;
"${target}-${variant}-sd" = sd;
} // (
if builtins.elem target fsblTargets
then {
"${target}-${variant}-fsbl-sd" = fsbl-sd;
}
else {}
);
gateware-sim = pkgs.stdenv.mkDerivation {
name = "gateware-sim";
nativeBuildInputs = [
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi artiq ])))
artiqpkgs.artiq
];
phases = [ "buildPhase" ];
buildPhase =
''
python -m unittest discover ${self}/src/gateware -v
touch $out
'';
};
# for hitl-tests
zc706-nist_qc2 = (build { target = "zc706"; variant = "nist_qc2"; });
zc706-hitl-tests = pkgs.stdenv.mkDerivation {
name = "zc706-hitl-tests";
__networked = true; # compatibility with old patched Nix
# breaks hydra, https://github.com/NixOS/hydra/issues/1216
#__impure = true; # Nix 2.8+
buildInputs = [
pkgs.netcat pkgs.openssh pkgs.rsync artiqpkgs.artiq artiq-netboot zynqpkgs.zc706-szl
];
phases = [ "buildPhase" ];
buildPhase =
''
export NIX_SSHOPTS="-F /dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -i /opt/hydra_id_ed25519"
LOCKCTL=$(mktemp -d)
mkfifo $LOCKCTL/lockctl
cat $LOCKCTL/lockctl | ${pkgs.openssh}/bin/ssh \
$NIX_SSHOPTS \
rpi-4 \
'mkdir -p /tmp/board_lock && flock /tmp/board_lock/zc706-1 -c "echo Ok; cat"' \
| (
# End remote flock via FIFO
atexit_unlock() {
echo > $LOCKCTL/lockctl
}
trap atexit_unlock EXIT
# Read "Ok" line when remote successfully locked
read LOCK_OK
echo Power cycling board...
(echo b; sleep 5; echo B; sleep 5) | nc -N -w6 192.168.1.31 3131
echo Power cycle done.
export USER=hydra
export OPENOCD_ZYNQ=${zynq-rs}/openocd
export SZL=${zynqpkgs.szl}
bash ${self}/remote_run.sh -h rpi-4 -o "$NIX_SSHOPTS" -d ${zc706-nist_qc2.zc706-nist_qc2-jtag}
echo Waiting for the firmware to boot...
sleep 15
echo Running test kernel...
artiq_run --device-db ${self}/examples/device_db.py ${self}/examples/mandelbrot.py
echo Running ARTIQ unit tests...
export ARTIQ_ROOT=${self}/examples
export ARTIQ_LOW_LATENCY=1
python -m unittest discover artiq.test.coredevice -v
touch $out
echo Completed
(echo b; sleep 5) | nc -N -w6 192.168.1.31 3131
echo Board powered off
)
'';
};
in rec {
packages.x86_64-linux = (build { target = "zc706"; variant = "nist_clock"; }) //
(build { target = "zc706"; variant = "nist_clock_master"; }) //
(build { target = "zc706"; variant = "nist_clock_satellite"; }) //
(build { target = "zc706"; variant = "nist_clock_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "nist_qc2"; }) //
(build { target = "zc706"; variant = "nist_qc2_master"; }) //
(build { target = "zc706"; variant = "nist_qc2_satellite"; }) //
(build { target = "zc706"; variant = "nist_qc2_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_master"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_satellite"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_master"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) //
(build { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) //
(build { target = "kasli_soc"; variant = "master"; json = ./kasli-soc-master.json; }) //
(build { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; });
hydraJobs = packages.x86_64-linux // { inherit zc706-hitl-tests; inherit gateware-sim; };
devShell.x86_64-linux = pkgs.mkShell {
name = "artiq-zynq-dev-shell";
buildInputs = with pkgs; [
rustPlatform.rust.rustc
rustPlatform.rust.cargo
llvmPackages_9.llvm
llvmPackages_9.clang-unwrapped
gnumake
cacert
zynqpkgs.cargo-xbuild
zynqpkgs.mkbootimage
openocd
openssh rsync
(python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq artiq-netboot ps.jsonschema ps.pyftdi ])))
artiqpkgs.artiq
artiqpkgs.vivado
binutils-arm
];
XARGO_RUST_SRC = "${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library";
CLANG_EXTRA_INCLUDE_DIR = "${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include";
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
SZL = "${zynqpkgs.szl}";
};
makeArtiqZynqPackage = build;
};
}

View File

@ -1,60 +0,0 @@
{
"target": "kasli_soc",
"variant": "master",
"hw_rev": "v1.0",
"base": "master",
"peripherals": [
{
"type": "grabber",
"ports": [0]
},
{
"type": "dio",
"ports": [1],
"bank_direction_low": "input",
"bank_direction_high": "output"
},
{
"type": "dio",
"ports": [2],
"bank_direction_low": "output",
"bank_direction_high": "output"
},
{
"type": "urukul",
"dds": "ad9910",
"ports": [3, 4],
"clk_sel": 2
},
{
"type": "zotino",
"ports": [5]
},
{
"type": "sampler",
"ports": [6, 7]
},
{
"type": "mirny",
"ports": [8],
"clk_sel": 1,
"refclk": 125e6
},
{
"type": "fastino",
"ports": [9]
},
{
"type": "dio",
"ports": [10],
"bank_direction_low": "input",
"bank_direction_high": "input"
},
{
"type": "dio",
"ports": [11],
"bank_direction_low": "output",
"bank_direction_high": "input"
}
]
}

View File

@ -1,60 +0,0 @@
{
"target": "kasli_soc",
"variant": "satellite",
"hw_rev": "v1.0",
"base": "satellite",
"peripherals": [
{
"type": "grabber",
"ports": [0]
},
{
"type": "dio",
"ports": [1],
"bank_direction_low": "input",
"bank_direction_high": "output"
},
{
"type": "dio",
"ports": [2],
"bank_direction_low": "output",
"bank_direction_high": "output"
},
{
"type": "urukul",
"dds": "ad9910",
"ports": [3, 4],
"clk_sel": 2
},
{
"type": "zotino",
"ports": [5]
},
{
"type": "sampler",
"ports": [6, 7]
},
{
"type": "mirny",
"ports": [8],
"clk_sel": 1,
"refclk": 125e6
},
{
"type": "fastino",
"ports": [9]
},
{
"type": "dio",
"ports": [10],
"bank_direction_low": "input",
"bank_direction_high": "input"
},
{
"type": "dio",
"ports": [11],
"bank_direction_low": "output",
"bank_direction_high": "input"
}
]
}

View File

@ -13,10 +13,9 @@ fi
impure=0 impure=0
load_bitstream=1 load_bitstream=1
board_type="kasli_soc" board_host="192.168.1.52"
fw_type="runtime"
while getopts "ilb:t:f:" opt; do while getopts "ilb:" opt; do
case "$opt" in case "$opt" in
\?) exit 1 \?) exit 1
;; ;;
@ -26,36 +25,24 @@ while getopts "ilb:t:f:" opt; do
;; ;;
b) board_host=$OPTARG b) board_host=$OPTARG
;; ;;
t) board_type=$OPTARG
;;
f) fw_type=$OPTARG
;;
esac esac
done done
if [ -z "$board_host" ]; then
case $board_type in
kasli_soc) board_host="192.168.1.56";;
zc706) board_host="192.168.1.52";;
*) echo "Unknown board type"; exit 1;;
esac
fi
load_bitstream_cmd="" load_bitstream_cmd=""
build_dir=`pwd`/build build_dir=`pwd`/build
result_dir=`pwd`/result result_dir=`pwd`/result
cd $OPENOCD_ZYNQ cd $OPENOCD_ZYNQ
openocd -f $board_type.cfg -c "load_image $SZL/szl-$board_type.elf; resume 0; exit" openocd -f zc706.cfg -c "load_image $SZL; resume 0; exit"
sleep 5 sleep 5
if [ $impure -eq 1 ]; then if [ $impure -eq 1 ]; then
if [ $load_bitstream -eq 1 ]; then if [ $load_bitstream -eq 1 ]; then
load_bitstream_cmd="-g $build_dir/gateware/top.bit" load_bitstream_cmd="-g $build_dir/gateware/top.bit"
fi fi
artiq_netboot $load_bitstream_cmd -f $build_dir/$fw_type.bin -b $board_host artiq_netboot $load_bitstream_cmd -f $build_dir/runtime.bin -b $board_host
else else
if [ $load_bitstream -eq 1 ]; then if [ $load_bitstream -eq 1 ]; then
load_bitstream_cmd="-g $result_dir/top.bit" load_bitstream_cmd="-g $result_dir/top.bit"
fi fi
artiq_netboot $load_bitstream_cmd -f $result_dir/$fw_type.bin -b $board_host artiq_netboot $load_bitstream_cmd -f $result_dir/runtime.bin -b $board_host
fi fi

View File

@ -1,7 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Only ZC706 supported for now.
set -e set -e
if [ -z "$OPENOCD_ZYNQ" ]; then if [ -z "$OPENOCD_ZYNQ" ]; then
@ -20,9 +18,8 @@ impure_dir="build"
sshopts="" sshopts=""
load_bitstream=1 load_bitstream=1
board_host="192.168.1.52" board_host="192.168.1.52"
fw_type="runtime"
while getopts "h:id:o:lt:" opt; do while getopts "h:id:o:l" opt; do
case "$opt" in case "$opt" in
\?) exit 1 \?) exit 1
;; ;;
@ -39,8 +36,6 @@ while getopts "h:id:o:lt:" opt; do
;; ;;
b) board_host=$OPTARG b) board_host=$OPTARG
;; ;;
t) fw_type=$OPTARG
;;
esac esac
done done
@ -51,17 +46,17 @@ echo "Creating $target_folder..."
ssh $sshopts $target_host "mkdir -p $target_folder" ssh $sshopts $target_host "mkdir -p $target_folder"
echo "Copying files..." echo "Copying files..."
rsync -e "ssh $sshopts" -Lc $OPENOCD_ZYNQ/* $target_host:$target_folder rsync -e "ssh $sshopts" -Lc $OPENOCD_ZYNQ/* $target_host:$target_folder
rsync -e "ssh $sshopts" -Lc $SZL/szl-zc706.elf $target_host:$target_folder/szl.elf rsync -e "ssh $sshopts" -Lc $SZL $target_host:$target_folder
if [ $impure -eq 1 ]; then if [ $impure -eq 1 ]; then
if [ $load_bitstream -eq 1 ]; then if [ $load_bitstream -eq 1 ]; then
load_bitstream_cmd="-g build/gateware/top.bit" load_bitstream_cmd="-g build/gateware/top.bit"
fi fi
firmware="build/$fw_type.bin" firmware="build/runtime.bin"
else else
if [ $load_bitstream -eq 1 ]; then if [ $load_bitstream -eq 1 ]; then
load_bitstream_cmd="-g $pure_dir/top.bit" load_bitstream_cmd="-g $pure_dir/top.bit"
fi fi
firmware="$pure_dir/$fw_type.bin" firmware="$pure_dir/runtime.bin"
fi fi
echo "Programming board..." echo "Programming board..."
ssh $sshopts $target_host "cd $target_folder; openocd -f zc706.cfg -c'load_image szl.elf; resume 0; exit'" ssh $sshopts $target_host "cd $target_folder; openocd -f zc706.cfg -c'load_image szl.elf; resume 0; exit'"

36
shell.nix Normal file
View File

@ -0,0 +1,36 @@
let
zynq-rs = (import ./zynq-rs.nix);
pkgs = import <nixpkgs> { overlays = [ (import "${zynq-rs}/nix/mozilla-overlay.nix") ]; };
rustPlatform = (import "${zynq-rs}/nix/rust-platform.nix" { inherit pkgs; });
cargo-xbuild = (import zynq-rs).cargo-xbuild;
artiq-fast = <artiq-fast>;
artiqpkgs = import "${artiq-fast}/default.nix" { inherit pkgs; };
vivado = import "${artiq-fast}/vivado.nix" { inherit pkgs; };
zc706-szl = (import zynq-rs).zc706-szl;
in
pkgs.stdenv.mkDerivation {
name = "artiq-zynq-env";
buildInputs = [
pkgs.gnumake
rustPlatform.rust.rustc
rustPlatform.rust.cargo
pkgs.llvmPackages_9.llvm
pkgs.llvmPackages_9.clang-unwrapped
pkgs.cacert
cargo-xbuild
pkgs.openocd
pkgs.openssh pkgs.rsync
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq artiq-netboot ])))
vivado
artiqpkgs.binutils-arm
(import "${zynq-rs}/nix/mkbootimage.nix" { inherit pkgs; })
];
XARGO_RUST_SRC = "${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library";
CLANG_EXTRA_INCLUDE_DIR = "${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include";
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
SZL = "${zc706-szl}/szl.elf";
}

173
src/Cargo.lock generated
View File

@ -13,9 +13,9 @@ dependencies = [
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]] [[package]]
name = "bit_field" name = "bit_field"
@ -25,19 +25,9 @@ checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "build_const"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
[[package]]
name = "build_zynq"
version = "0.0.0"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -47,9 +37,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.73" version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -78,15 +68,6 @@ dependencies = [
"rustc_version", "rustc_version",
] ]
[[package]]
name = "crc"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
dependencies = [
"build_const",
]
[[package]] [[package]]
name = "cslice" name = "cslice"
version = "0.3.0" version = "0.3.0"
@ -99,7 +80,6 @@ version = "0.0.0"
dependencies = [ dependencies = [
"cfg-if 0.1.10", "cfg-if 0.1.10",
"compiler_builtins", "compiler_builtins",
"cslice",
"libc", "libc",
"unwind", "unwind",
] ]
@ -114,9 +94,9 @@ dependencies = [
[[package]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "0.2.7" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" checksum = "db184d3fa27bc7a2344250394c0264144dfe0bc81a4401801dcb964b8dd172ad"
dependencies = [ dependencies = [
"nb 0.1.3", "nb 0.1.3",
"void", "void",
@ -136,9 +116,9 @@ dependencies = [
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.21" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -150,9 +130,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.21" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
@ -160,22 +140,24 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.21" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.21" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582"
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.21" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57"
dependencies = [ dependencies = [
"autocfg",
"proc-macro-hack",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@ -183,43 +165,37 @@ dependencies = [
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.21" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.21" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.21" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78"
dependencies = [ dependencies = [
"autocfg",
"futures-core", "futures-core",
"futures-macro", "futures-macro",
"futures-sink", "futures-sink",
"futures-task", "futures-task",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
] "proc-macro-hack",
"proc-macro-nested",
[[package]]
name = "io"
version = "0.0.0"
dependencies = [
"byteorder",
"core_io",
"libsupport_zynq",
] ]
[[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#605c8f73a60cf41a6e2d535c5850933d3154e935" source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
dependencies = [ dependencies = [
"embedded-hal", "embedded-hal",
"libcortex_a9", "libcortex_a9",
@ -228,30 +204,10 @@ dependencies = [
"smoltcp", "smoltcp",
] ]
[[package]]
name = "libboard_artiq"
version = "0.0.0"
dependencies = [
"build_zynq",
"core_io",
"crc",
"embedded-hal",
"io",
"libasync",
"libboard_zynq",
"libconfig",
"libcortex_a9",
"libregister",
"log",
"log_buffer",
"nb 1.0.0",
"void",
]
[[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#605c8f73a60cf41a6e2d535c5850933d3154e935" source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"embedded-hal", "embedded-hal",
@ -276,7 +232,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#605c8f73a60cf41a6e2d535c5850933d3154e935" source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
dependencies = [ dependencies = [
"core_io", "core_io",
"fatfs", "fatfs",
@ -287,7 +243,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#605c8f73a60cf41a6e2d535c5850933d3154e935" source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"libregister", "libregister",
@ -296,14 +252,14 @@ dependencies = [
[[package]] [[package]]
name = "libm" name = "libm"
version = "0.2.2" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
[[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#605c8f73a60cf41a6e2d535c5850933d3154e935" source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"vcell", "vcell",
@ -313,7 +269,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#605c8f73a60cf41a6e2d535c5850933d3154e935" source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
dependencies = [ dependencies = [
"cc", "cc",
"compiler_builtins", "compiler_builtins",
@ -388,9 +344,9 @@ dependencies = [
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.8" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
[[package]] [[package]]
name = "pin-utils" name = "pin-utils"
@ -399,19 +355,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro-hack"
version = "1.0.36" version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.15" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -427,7 +395,6 @@ name = "runtime"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-recursion", "async-recursion",
"build_zynq",
"byteorder", "byteorder",
"core_io", "core_io",
"cslice", "cslice",
@ -435,9 +402,7 @@ dependencies = [
"dyld", "dyld",
"embedded-hal", "embedded-hal",
"futures", "futures",
"io",
"libasync", "libasync",
"libboard_artiq",
"libboard_zynq", "libboard_zynq",
"libc", "libc",
"libconfig", "libconfig",
@ -464,24 +429,6 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "satman"
version = "0.0.0"
dependencies = [
"build_zynq",
"embedded-hal",
"libasync",
"libboard_artiq",
"libboard_zynq",
"libc",
"libconfig",
"libcortex_a9",
"libregister",
"libsupport_zynq",
"log",
"unwind",
]
[[package]] [[package]]
name = "semver" name = "semver"
version = "0.1.20" version = "0.1.20"
@ -501,9 +448,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -540,9 +487,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]] [[package]]
name = "volatile-register" name = "volatile-register"
version = "0.2.1" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
dependencies = [ dependencies = [
"vcell", "vcell",
] ]

View File

@ -3,10 +3,8 @@ members = [
"libc", "libc",
"libdyld", "libdyld",
"libdwarf", "libdwarf",
"libio",
"libunwind", "libunwind",
"runtime", "runtime",
"satman"
] ]
[profile.release] [profile.release]

View File

@ -1,19 +1,16 @@
TARGET := zc706 TARGET := zc706
GWARGS := -V nist_clock VARIANT := simple
all: runtime all: ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
runtime: ../build/runtime.bin .PHONY: all
satman: ../build/satman.bin
.PHONY: all runtime_target satman_target ../build/pl.rs ../build/rustc-cfg: 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 -V $(VARIANT)
../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print) ../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg $(shell find . -print)
cd runtime && \ cd runtime && \
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \ XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
cargo xbuild --release \ cargo xbuild --release \
@ -22,13 +19,3 @@ satman: ../build/satman.bin
../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
../build/firmware/armv7-none-eabihf/release/satman: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
cd satman && \
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
cargo xbuild --release \
--target-dir ../../build/firmware \
--no-default-features --features=target_$(TARGET)
../build/satman.bin: ../build/firmware/armv7-none-eabihf/release/satman
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/satman ../build/satman.bin

191
src/gateware/coraz7.py Executable file
View File

@ -0,0 +1,191 @@
#!/usr/bin/env python
import argparse
from operator import itemgetter
from migen import *
from migen.build.generic_platform import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import coraz7
from misoc.interconnect.csr import *
from misoc.integration import cpu_interface
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
import dma
import analyzer
import acpki
class RTIOCRG(Module, AutoCSR):
def __init__(self, platform, rtio_internal_clk):
self.clock_sel = CSRStorage()
self.pll_reset = CSRStorage(reset=1)
self.pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
rtio_external_clk = Signal()
# user_sma_clock = platform.request("user_sma_clock")
# platform.add_period_constraint(user_sma_clock.p, 8.0)
# self.specials += Instance("IBUFDS",
# i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
# o_O=rtio_external_clk)
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_REF_JITTER1=0.01,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self.clock_sel.storage,
# VCO @ 1GHz when using 125MHz input
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=self.cd_rtio.clk,
i_RST=self.pll_reset.storage,
o_CLKFBOUT=rtio_clk,
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self.pll_locked.status)
]
class CoraZ7(SoCCore):
def __init__(self, device_variant="10", acpki=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = coraz7.Platform(device_variant=device_variant)
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
self.csr_devices.append("rtio_crg")
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
def add_rtio(self, rtio_channels):
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri])
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
class Simple(CoraZ7):
def __init__(self, **kwargs):
CoraZ7.__init__(self, **kwargs)
platform = self.platform
rtio_channels = []
for i in range(2):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels)
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))
def main():
parser = argparse.ArgumentParser(
description="ARTIQ port to the Cora Z7 Zynq development kit")
parser.add_argument("-r", default=None,
help="build Rust interface into the specified file")
parser.add_argument("-c", default=None,
help="build Rust compiler configuration into the specified file")
parser.add_argument("-g", default=None,
help="build gateware into the specified directory")
parser.add_argument("-V", "--variant", default="10",
help="variant: "
"[acpki_]10/07s "
"(default: %(default)s)")
args = parser.parse_args()
variant = args.variant.lower()
acpki = variant.startswith("acpki_")
if acpki:
variant = variant[6:]
try:
soc = Simple(device_variant=variant, acpki=acpki)
except KeyError:
raise SystemExit("Invalid variant (-V/--variant)")
soc.finalize()
if args.r is not None:
write_csr_file(soc, args.r)
if args.c is not None:
write_rustc_cfg_file(soc, args.c)
if args.g is not None:
soc.build(build_dir=args.g)
if __name__ == "__main__":
main()

View File

@ -1,85 +0,0 @@
"""Auxiliary controller, common to satellite and master"""
from artiq.gateware.drtio.aux_controller import Transmitter, Receiver
from migen.fhdl.simplify import FullMemoryWE
from misoc.interconnect.csr import *
from migen_axi.interconnect.sram import SRAM
from migen_axi.interconnect import axi
max_packet = 1024
class _DRTIOAuxControllerBase(Module):
def __init__(self, link_layer):
self.bus = axi.Interface()
self.submodules.transmitter = Transmitter(link_layer, len(self.bus.w.data))
self.submodules.receiver = Receiver(link_layer, len(self.bus.w.data))
def get_csrs(self):
return self.transmitter.get_csrs() + self.receiver.get_csrs()
# TODO: FullMemoryWE should be applied by migen.build
@FullMemoryWE()
class DRTIOAuxControllerAxi(_DRTIOAuxControllerBase):
def __init__(self, link_layer):
_DRTIOAuxControllerBase.__init__(self, link_layer)
tx_sdram_if = SRAM(self.transmitter.mem, read_only=False)
rx_sdram_if = SRAM(self.receiver.mem, read_only=True)
aw_decoder = axi.AddressDecoder(self.bus.aw,
[(lambda a: a[log2_int(max_packet)] == 0, tx_sdram_if.bus.aw),
(lambda a: a[log2_int(max_packet)] == 1, rx_sdram_if.bus.aw)],
register=True)
ar_decoder = axi.AddressDecoder(self.bus.ar,
[(lambda a: a[log2_int(max_packet)] == 0, tx_sdram_if.bus.ar),
(lambda a: a[log2_int(max_packet)] == 1, rx_sdram_if.bus.ar)],
register=True)
# unlike wb, axi address decoder only connects ar/aw lanes,
# the rest must also be connected!
# not quite unlike an address decoder itself.
# connect bus.b with tx.b
self.comb += [tx_sdram_if.bus.b.ready.eq(self.bus.b.ready),
self.bus.b.id.eq(tx_sdram_if.bus.b.id),
self.bus.b.resp.eq(tx_sdram_if.bus.b.resp),
self.bus.b.valid.eq(tx_sdram_if.bus.b.valid)]
# connect bus.w with tx.w
# no worries about w.valid and slave sel here, only tx will be written to
self.comb += [tx_sdram_if.bus.w.id.eq(self.bus.w.id),
tx_sdram_if.bus.w.data.eq(self.bus.w.data),
tx_sdram_if.bus.w.strb.eq(self.bus.w.strb),
tx_sdram_if.bus.w.last.eq(self.bus.w.last),
tx_sdram_if.bus.w.valid.eq(self.bus.w.valid),
self.bus.w.ready.eq(tx_sdram_if.bus.w.ready)]
# connect bus.r with rx.r and tx.r w/o data
self.comb += [self.bus.r.id.eq(rx_sdram_if.bus.r.id | tx_sdram_if.bus.r.id),
#self.bus.r.data.eq(rx_sdram_if.bus.r.data | tx_sdram_if.bus.r.data),
self.bus.r.resp.eq(rx_sdram_if.bus.r.resp | tx_sdram_if.bus.r.resp),
self.bus.r.last.eq(rx_sdram_if.bus.r.last | tx_sdram_if.bus.r.last),
self.bus.r.valid.eq(rx_sdram_if.bus.r.valid | tx_sdram_if.bus.r.valid),
rx_sdram_if.bus.r.ready.eq(self.bus.r.ready),
tx_sdram_if.bus.r.ready.eq(self.bus.r.ready)]
# connect read data after being masked
masked = [Replicate(rx_sdram_if.bus.r.valid,
len(self.bus.r.data)
) & rx_sdram_if.bus.r.data,
Replicate(tx_sdram_if.bus.r.valid,
len(self.bus.r.data)
) & tx_sdram_if.bus.r.data]
self.comb += self.bus.r.data.eq(reduce(or_, masked))
self.submodules += tx_sdram_if, rx_sdram_if, aw_decoder, ar_decoder
@FullMemoryWE()
class DRTIOAuxControllerBare(_DRTIOAuxControllerBase):
# Barebones version of the AuxController. No SRAM, no decoders.
# add memories manually from tx and rx in target code.
def get_tx_port(self):
return self.transmitter.mem.get_port(write_capable=True)
def get_rx_port(self):
return self.receiver.mem.get_port(write_capable=False)
def get_mem_size(self):
return max_packet

View File

@ -1,544 +0,0 @@
#!/usr/bin/env python
import argparse
from operator import itemgetter
from migen import *
from migen.build.generic_platform import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import kasli_soc
from misoc.interconnect.csr import *
from misoc.integration import cpu_interface
from artiq.coredevice import jsondesc
from artiq.gateware import rtio, eem_7series
from artiq.gateware.rtio.phy import ttl_simple
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier
from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import *
import dma
import analyzer
import acpki
import drtio_aux_controller
class RTIOCRG(Module, AutoCSR):
def __init__(self, platform):
self.pll_reset = CSRStorage(reset=1)
self.pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
clk_synth = platform.request("cdr_clk_clean_fabric")
clk_synth_se = Signal()
platform.add_period_constraint(clk_synth.p, 8.0)
self.specials += [
Instance("IBUFGDS",
p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE",
i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se),
]
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
fb_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_BANDWIDTH="HIGH",
p_REF_JITTER1=0.001,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN2=clk_synth_se,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=0,
# VCO @ 1.5GHz when using 125MHz input
p_CLKFBOUT_MULT=12, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=fb_clk,
i_RST=self.pll_reset.storage,
o_CLKFBOUT=fb_clk,
p_CLKOUT0_DIVIDE=3, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk,
p_CLKOUT1_DIVIDE=12, p_CLKOUT1_PHASE=0.0,
o_CLKOUT1=rtio_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self.pll_locked.status)
]
eem_iostandard_dict = {
0: "LVDS_25",
1: "LVDS_25",
2: "LVDS",
3: "LVDS",
4: "LVDS",
5: "LVDS",
6: "LVDS",
7: "LVDS",
8: "LVDS_25",
9: "LVDS_25",
10: "LVDS",
11: "LVDS",
}
def eem_iostandard(eem):
return IOStandard(eem_iostandard_dict[eem])
class SMAClkinForward(Module):
def __init__(self, platform):
sma_clkin = platform.request("sma_clkin")
sma_clkin_se = Signal()
cdr_clk_se = Signal()
cdr_clk = platform.request("cdr_clk")
self.specials += [
Instance("IBUFDS", i_I=sma_clkin.p, i_IB=sma_clkin.n, o_O=sma_clkin_se),
Instance("ODDR", i_C=sma_clkin_se, i_CE=1, i_D1=1, i_D2=0, o_Q=cdr_clk_se),
Instance("OBUFDS", i_I=cdr_clk_se, o_O=cdr_clk.p, o_OB=cdr_clk.n)
]
class GenericStandalone(SoCCore):
def __init__(self, description, acpki=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules += SMAClkinForward(self.platform)
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["si5324_soft_reset"] = None
self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOCRG(self.platform)
self.csr_devices.append("rtio_crg")
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
if has_grabber:
self.grabber_csr_group = []
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
for i in (0, 1):
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
user_led = self.platform.request("user_led", i)
phy = ttl_simple.Output(user_led)
self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
self.rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri])
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
if has_grabber:
self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group)
for grabber in self.grabber_csr_group:
self.platform.add_false_path_constraints(
self.rtio_crg.cd_rtio.clk, getattr(self, grabber).deserializer.cd_cl.clk)
class GenericMaster(SoCCore):
def __init__(self, description, acpki=False):
sys_clk_freq = 125e6
rtio_clk_freq = description["rtio_frequency"]
self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules += SMAClkinForward(self.platform)
data_pads = [platform.request("sfp", i) for i in range(4)]
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("clk_gtp"),
pads=data_pads,
sys_clk_freq=sys_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["si5324_soft_reset"] = None
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
if has_grabber:
self.grabber_csr_group = []
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
for i in (0, 1):
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
user_led = self.platform.request("user_led", i)
phy = ttl_simple.Output(user_led)
self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
self.rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
drtio_csr_group = []
drtioaux_csr_group = []
drtioaux_memory_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
core_name = "drtio" + str(i)
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtio_csr_group.append(core_name)
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
core = cdr(DRTIOMaster(self.rtio_tsc, self.drtio_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)
self.rustc_cfg["has_drtio"] = 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)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri] + self.drtio_cri,
enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
if has_grabber:
self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group)
class GenericSatellite(SoCCore):
def __init__(self, description, acpki=False):
sys_clk_freq = 125e6
rtio_clk_freq = description["rtio_frequency"]
self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_rtio_crg"] = None
data_pads = [platform.request("sfp", i) for i in range(4)]
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("clk_gtp"),
pads=data_pads,
sys_clk_freq=sys_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
if has_grabber:
self.grabber_csr_group = []
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
for i in (0, 1):
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
user_led = self.platform.request("user_led", i)
phy = ttl_simple.Output(user_led)
self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
self.rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
drtioaux_csr_group = []
drtioaux_memory_group = []
drtiorep_csr_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
if i == 0:
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
core = cdr(DRTIOSatellite(
self.rtio_tsc, self.drtio_transceiver.channels[i],
self.rx_synchronizer))
self.submodules.drtiosat = core
self.csr_devices.append("drtiosat")
else:
corerep_name = "drtiorep" + str(i-1)
drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater(
self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, corerep_name, core)
self.drtio_cri.append(core.cri)
self.csr_devices.append(corerep_name)
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
setattr(self.submodules, coreaux_name, coreaux)
self.csr_devices.append(coreaux_name)
mem_size = coreaux.get_mem_size()
tx_port = coreaux.get_tx_port()
rx_port = coreaux.get_rx_port()
memory_address = self.axi2csr.register_port(tx_port, mem_size)
# rcv in upper half of the memory, thus added second
self.axi2csr.register_port(rx_port, mem_size)
# and registered in PS interface
# manually, because software refers to rx/tx by halves of entire memory block, not names
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.rustc_cfg["has_drtio"] = None
self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.add_csr_group("drtiorep", drtiorep_csr_group)
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.local_io = SyncRTIO(self.rtio_tsc, self.rtio_channels)
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
self.csr_devices.append("rtio_moninj")
rtio_clk_period = 1e9/rtio_clk_freq
self.rustc_cfg["rtio_frequency"] = str(rtio_clk_freq/1e6)
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("cdr_clk"),
rx_synchronizer=self.rx_synchronizer,
ultrascale=False,
rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser")
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["has_siphaser"] = None
self.rustc_cfg["si5324_soft_reset"] = None
gtx0 = self.drtio_transceiver.gtxs[0]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gtx0.txoutclk, gtx0.rxoutclk)
for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, gtx.rxoutclk)
if has_grabber:
self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group)
# no RTIO CRG here
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))
def main():
parser = argparse.ArgumentParser(
description="ARTIQ device binary builder for generic Kasli-SoC systems")
parser.add_argument("-r", default=None,
help="build Rust interface into the specified file")
parser.add_argument("-c", default=None,
help="build Rust compiler configuration into the specified file")
parser.add_argument("-m", default=None,
help="build Rust memory interface into the specified file")
parser.add_argument("-g", default=None,
help="build gateware into the specified directory")
parser.add_argument("--acpki", default=False, action="store_true",
help="enable ACPKI")
parser.add_argument("description", metavar="DESCRIPTION",
help="JSON system description file")
args = parser.parse_args()
description = jsondesc.load(args.description)
if description["target"] != "kasli_soc":
raise ValueError("Description is for a different target")
if description["base"] == "standalone":
cls = GenericStandalone
elif description["base"] == "master":
cls = GenericMaster
elif description["base"] == "satellite":
cls = GenericSatellite
else:
raise ValueError("Invalid base")
soc = cls(description, acpki=args.acpki)
soc.finalize()
if args.r is not None:
write_csr_file(soc, args.r)
if args.m is not None:
write_mem_file(soc, args.m)
if args.c is not None:
write_rustc_cfg_file(soc, args.c)
if args.g is not None:
soc.build(build_dir=args.g)
if __name__ == "__main__":
main()

191
src/gateware/redpitaya.py Executable file
View File

@ -0,0 +1,191 @@
#!/usr/bin/env python
import argparse
from operator import itemgetter
from migen import *
from migen.build.generic_platform import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import redpitaya
from misoc.interconnect.csr import *
from misoc.integration import cpu_interface
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
import dma
import analyzer
import acpki
class RTIOCRG(Module, AutoCSR):
def __init__(self, platform, rtio_internal_clk):
self.clock_sel = CSRStorage()
self.pll_reset = CSRStorage(reset=1)
self.pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
rtio_external_clk = Signal()
# user_sma_clock = platform.request("user_sma_clock")
# platform.add_period_constraint(user_sma_clock.p, 8.0)
# self.specials += Instance("IBUFDS",
# i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
# o_O=rtio_external_clk)
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_REF_JITTER1=0.01,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self.clock_sel.storage,
# VCO @ 1GHz when using 125MHz input
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=self.cd_rtio.clk,
i_RST=self.pll_reset.storage,
o_CLKFBOUT=rtio_clk,
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self.pll_locked.status)
]
class Redpitaya(SoCCore):
def __init__(self, acpki=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = redpitaya.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
self.csr_devices.append("rtio_crg")
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
def add_rtio(self, rtio_channels):
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri])
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
class Simple(Redpitaya):
def __init__(self, **kwargs):
Redpitaya.__init__(self, **kwargs)
platform = self.platform
rtio_channels = []
for i in range(2):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels)
VARIANTS = {cls.__name__.lower(): cls for cls in [Simple]}
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))
def main():
parser = argparse.ArgumentParser(
description="ARTIQ port to the Redpitaya Zynq development kit")
parser.add_argument("-r", default=None,
help="build Rust interface into the specified file")
parser.add_argument("-c", default=None,
help="build Rust compiler configuration into the specified file")
parser.add_argument("-g", default=None,
help="build gateware into the specified directory")
parser.add_argument("-V", "--variant", default="10",
help="variant: "
"[acpki_]simple "
"(default: %(default)s)")
args = parser.parse_args()
variant = args.variant.lower()
acpki = variant.startswith("acpki_")
if acpki:
variant = variant[6:]
soc = Simple(acpki=acpki)
soc.finalize()
if args.r is not None:
write_csr_file(soc, args.r)
if args.c is not None:
write_rustc_cfg_file(soc, args.c)
if args.g is not None:
soc.build(build_dir=args.g)
if __name__ == "__main__":
main()

View File

@ -11,20 +11,13 @@ 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.integration import cpu_interface
from misoc.cores import gpio
from artiq.gateware import rtio, nist_clock, nist_qc2 from artiq.gateware import rtio, nist_clock, nist_qc2
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2 from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import *
import dma import dma
import analyzer import analyzer
import acpki import acpki
import drtio_aux_controller
class RTIOCRG(Module, AutoCSR): class RTIOCRG(Module, AutoCSR):
@ -71,90 +64,25 @@ class RTIOCRG(Module, AutoCSR):
] ]
class SMAClkinForward(Module):
def __init__(self, platform):
sma_clkin = platform.request("user_sma_clock")
sma_clkin_se = Signal()
si5324_clkin_se = Signal()
si5324_clkin = platform.request("si5324_clkin")
self.specials += [
Instance("IBUFDS", i_I=sma_clkin.p, i_IB=sma_clkin.n, o_O=sma_clkin_se),
Instance("ODDR", i_C=sma_clkin_se, i_CE=1, i_D1=1, i_D2=0, o_Q=si5324_clkin_se),
Instance("OBUFDS", i_I=si5324_clkin_se, o_O=si5324_clkin.p, o_OB=si5324_clkin.n)
]
# The NIST backplanes require setting VADJ to 3.3V by reprogramming the power supply.
# This also changes the I/O standard for some on-board LEDs.
leds_fmc33 = [
("user_led_33", 0, Pins("Y21"), IOStandard("LVCMOS33")),
("user_led_33", 1, Pins("G2"), IOStandard("LVCMOS15")),
("user_led_33", 2, Pins("W21"), IOStandard("LVCMOS33")),
("user_led_33", 3, Pins("A17"), IOStandard("LVCMOS15")),
]
# same deal as with LEDs - changed I/O standard.
si5324_fmc33 = [
("si5324_33", 0,
Subsignal("rst_n", Pins("W23"), IOStandard("LVCMOS33")),
Subsignal("int", Pins("AJ25"), IOStandard("LVCMOS33"))
),
]
pmod1_33 = [
("pmod1_33", 0, Pins("AJ21"), IOStandard("LVCMOS33")),
("pmod1_33", 1, Pins("AK21"), IOStandard("LVCMOS33")),
("pmod1_33", 2, Pins("AB21"), IOStandard("LVCMOS33")),
("pmod1_33", 3, Pins("AB16"), IOStandard("LVCMOS33")),
# rest removed for use with dummy spi
]
_ams101_dac = [
("ams101_dac", 0,
Subsignal("ldac", Pins("XADC:GPIO0")),
Subsignal("clk", Pins("XADC:GPIO1")),
Subsignal("mosi", Pins("XADC:GPIO2")),
Subsignal("cs_n", Pins("XADC:GPIO3")),
IOStandard("LVCMOS15")
)
]
_pmod_spi = [
("pmod_spi", 0,
# PMOD_1 4-7 pins, same bank as sfp_tx_disable or user_sma_clock
Subsignal("miso", Pins("Y20"), IOStandard("LVCMOS25")),
Subsignal("clk", Pins("AA20"), IOStandard("LVCMOS25")),
Subsignal("mosi", Pins("AC18"), IOStandard("LVCMOS25")),
Subsignal("cs_n", Pins("AC19"), IOStandard("LVCMOS25")),
IOStandard("LVCMOS25")
)
]
def prepare_zc706_platform(platform):
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
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() self.rustc_cfg = dict()
platform = zc706.Platform() platform = zc706.Platform()
prepare_zc706_platform(platform) platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__ ident = self.__class__.__name__
if self.acpki: if self.acpki:
ident = "acpki_" + ident ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident) SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk) self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
self.csr_devices.append("rtio_crg") self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_rtio_crg_clock_sel"] = None
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.) self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints( self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk, self.ps7.cd_sys.clk,
@ -193,303 +121,52 @@ class ZC706(SoCCore):
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
class _MasterBase(SoCCore): class Simple(ZC706):
def __init__(self, acpki=False, drtio100mhz=False): def __init__(self, **kwargs):
self.acpki = acpki ZC706.__init__(self, **kwargs)
self.rustc_cfg = dict()
platform = zc706.Platform()
prepare_zc706_platform(platform)
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_extension(si5324_fmc33)
self.sys_clk_freq = 125e6
rtio_clk_freq = 100e6 if drtio100mhz else self.sys_clk_freq
platform = self.platform platform = self.platform
self.comb += platform.request("sfp_tx_disable_n").eq(1) rtio_channels = []
data_pads = [ for i in range(4):
platform.request("sfp"), phy = ttl_simple.Output(platform.request("user_led", i))
platform.request("user_sma_mgt") self.submodules += phy
] rtio_channels.append(rtio.Channel.from_phy(phy))
self.submodules += SMAClkinForward(self.platform) self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
# 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock self.add_rtio(rtio_channels)
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"),
pads=data_pads,
sys_clk_freq=self.sys_clk_freq,
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
drtio_csr_group = []
drtioaux_csr_group = []
drtioaux_memory_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
core_name = "drtio" + str(i)
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtio_csr_group.append(core_name)
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
core = cdr(DRTIOMaster(
self.rtio_tsc, self.drtio_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)
mem_size = coreaux.get_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.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.rustc_cfg["has_drtio"] = None
self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtio", drtio_csr_group)
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.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.csr_devices.append("si5324_rst_n")
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["si5324_as_synthesizer"] = None
rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX)
gtx0 = self.drtio_transceiver.gtxs[0]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
gtx0.txoutclk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX)
for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk, gtx0.txoutclk, gtx.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(self.sys_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(self.platform)
def add_rtio(self, rtio_channels):
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
class _SatelliteBase(SoCCore): # The NIST backplanes require setting VADJ to 3.3V by reprogramming the power supply.
def __init__(self, acpki=False, drtio100mhz=False): # This also changes the I/O standard for some on-board LEDs.
self.acpki = acpki leds_fmc33 = [
self.rustc_cfg = dict() ("user_led_33", 0, Pins("Y21"), IOStandard("LVCMOS33")),
("user_led_33", 1, Pins("G2"), IOStandard("LVCMOS15")),
platform = zc706.Platform() ("user_led_33", 2, Pins("W21"), IOStandard("LVCMOS33")),
prepare_zc706_platform(platform) ("user_led_33", 3, Pins("A17"), IOStandard("LVCMOS15")),
ident = self.__class__.__name__ ]
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_extension(si5324_fmc33)
self.sys_clk_freq = 125e6
rtio_clk_freq = 100e6 if drtio100mhz else self.sys_clk_freq
platform = self.platform
# SFP
self.comb += platform.request("sfp_tx_disable_n").eq(0)
data_pads = [
platform.request("sfp"),
platform.request("user_sma_mgt")
]
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
# 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"),
pads=data_pads,
sys_clk_freq=self.sys_clk_freq,
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver")
drtioaux_csr_group = []
drtioaux_memory_group = []
drtiorep_csr_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
# Satellite
if i == 0:
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
core = cdr(DRTIOSatellite(
self.rtio_tsc, self.drtio_transceiver.channels[0], self.rx_synchronizer))
self.submodules.drtiosat = core
self.csr_devices.append("drtiosat")
# Repeaters
else:
corerep_name = "drtiorep" + str(i-1)
drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater(
self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, corerep_name, core)
self.drtio_cri.append(core.cri)
self.csr_devices.append(corerep_name)
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
setattr(self.submodules, coreaux_name, coreaux)
self.csr_devices.append(coreaux_name)
mem_size = coreaux.get_mem_size()
tx_port = coreaux.get_tx_port()
rx_port = coreaux.get_rx_port()
memory_address = self.axi2csr.register_port(tx_port, mem_size)
# rcv in upper half of the memory, thus added second
self.axi2csr.register_port(rx_port, mem_size)
# and registered in PS interface
# manually, because software refers to rx/tx by halves of entire memory block, not names
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.rustc_cfg["has_drtio"] = None
self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_csr_group("drtiorep", drtiorep_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.rustc_cfg["rtio_frequency"] = str(self.drtio_transceiver.rtio_clk_freq/1e6)
# Si5324 Phaser
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"),
rx_synchronizer=self.rx_synchronizer,
ultrascale=False,
rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser")
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n)
self.csr_devices.append("si5324_rst_n")
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["has_siphaser"] = None
rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX)
gtx0 = self.drtio_transceiver.gtxs[0]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
gtx0.txoutclk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX)
for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk, gtx.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(self.sys_clk_freq)
self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_rtio_crg"] = None
fix_serdes_timing_path(self.platform)
def add_rtio(self, rtio_channels):
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
class NIST_CLOCK(ZC706):
class _NIST_CLOCK_RTIO:
""" """
NIST clock hardware, with old backplane and 11 DDS channels NIST clock hardware, with old backplane and 11 DDS channels
""" """
def __init__(self): def __init__(self, **kwargs):
ZC706.__init__(self, **kwargs)
platform = self.platform platform = self.platform
platform.add_extension(nist_clock.fmc_adapter_io) platform.add_extension(nist_clock.fmc_adapter_io)
platform.add_extension(leds_fmc33) platform.add_extension(leds_fmc33)
platform.add_extension(pmod1_33)
platform.add_extension(_ams101_dac)
platform.add_extension(_pmod_spi)
rtio_channels = [] rtio_channels = []
for i in range(4):
phy = ttl_simple.Output(platform.request("user_led_33", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
for i in range(16): for i in range(16):
if i % 4 == 3: if i % 4 == 3:
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i)) phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
@ -505,40 +182,16 @@ class _NIST_CLOCK_RTIO:
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512)) rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
# no SMA GPIO, replaced with PMOD1_0
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Output(platform.request("user_led_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
ams101_dac = self.platform.request("ams101_dac", 0)
phy = ttl_simple.Output(ams101_dac.ldac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = ttl_simple.ClockGen(platform.request("la32_p")) phy = ttl_simple.ClockGen(platform.request("la32_p"))
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy)) rtio_channels.append(rtio.Channel.from_phy(phy))
phy = spi2.SPIMaster(ams101_dac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(
phy, ififo_depth=4))
for i in range(3): for i in range(3):
phy = spi2.SPIMaster(self.platform.request("spi", i)) phy = spi2.SPIMaster(self.platform.request("spi", i))
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy( rtio_channels.append(rtio.Channel.from_phy(
phy, ififo_depth=128)) phy, ififo_depth=128))
# no SDIO on PL side, dummy SPI placeholder instead
phy = spi2.SPIMaster(platform.request("pmod_spi"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
phy = dds.AD9914(platform.request("dds"), 11, onehot=True) phy = dds.AD9914(platform.request("dds"), 11, onehot=True)
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4)) rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
@ -549,40 +202,31 @@ class _NIST_CLOCK_RTIO:
self.add_rtio(rtio_channels) self.add_rtio(rtio_channels)
class _NIST_QC2_RTIO: class NIST_QC2(ZC706):
""" """
NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane
and 24 DDS channels. Two backplanes are used. and 24 DDS channels. Two backplanes are used.
""" """
def __init__(self): def __init__(self, **kwargs):
ZC706.__init__(self, **kwargs)
platform = self.platform platform = self.platform
platform.add_extension(nist_qc2.fmc_adapter_io) platform.add_extension(nist_qc2.fmc_adapter_io)
platform.add_extension(leds_fmc33) platform.add_extension(leds_fmc33)
platform.add_extension(_ams101_dac)
platform.add_extension(pmod1_33)
rtio_channels = [] rtio_channels = []
for i in range(4):
phy = ttl_simple.Output(platform.request("user_led_33", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
# All TTL channels are In+Out capable # All TTL channels are In+Out capable
for i in range(40): for i in range(40):
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i)) phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512)) rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
# no SMA GPIO, replaced with PMOD1_0
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Output(platform.request("user_led_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
ams101_dac = self.platform.request("ams101_dac", 0)
phy = ttl_simple.Output(ams101_dac.ldac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
# CLK0, CLK1 are for clock generators, on backplane SMP connectors # CLK0, CLK1 are for clock generators, on backplane SMP connectors
for i in range(2): for i in range(2):
phy = ttl_simple.ClockGen( phy = ttl_simple.ClockGen(
@ -590,11 +234,6 @@ class _NIST_QC2_RTIO:
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy)) rtio_channels.append(rtio.Channel.from_phy(phy))
phy = spi2.SPIMaster(ams101_dac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(
phy, ififo_depth=4))
for i in range(4): for i in range(4):
phy = spi2.SPIMaster(self.platform.request("spi", i)) phy = spi2.SPIMaster(self.platform.request("spi", i))
self.submodules += phy self.submodules += phy
@ -613,38 +252,7 @@ class _NIST_QC2_RTIO:
self.add_rtio(rtio_channels) self.add_rtio(rtio_channels)
class NIST_CLOCK(ZC706, _NIST_CLOCK_RTIO): VARIANTS = {cls.__name__.lower(): cls for cls in [Simple, NIST_CLOCK, NIST_QC2]}
def __init__(self, acpki, drtio100mhz):
ZC706.__init__(self, acpki)
_NIST_CLOCK_RTIO.__init__(self)
class NIST_CLOCK_Master(_MasterBase, _NIST_CLOCK_RTIO):
def __init__(self, acpki, drtio100mhz):
_MasterBase.__init__(self, acpki, drtio100mhz)
_NIST_CLOCK_RTIO.__init__(self)
class NIST_CLOCK_Satellite(_SatelliteBase, _NIST_CLOCK_RTIO):
def __init__(self, acpki, drtio100mhz):
_SatelliteBase.__init__(self, acpki, drtio100mhz)
_NIST_CLOCK_RTIO.__init__(self)
class NIST_QC2(ZC706, _NIST_QC2_RTIO):
def __init__(self, acpki, drtio100mhz):
ZC706.__init__(self, acpki)
_NIST_QC2_RTIO.__init__(self)
class NIST_QC2_Master(_MasterBase, _NIST_QC2_RTIO):
def __init__(self, acpki, drtio100mhz):
_MasterBase.__init__(self, acpki, drtio100mhz)
_NIST_QC2_RTIO.__init__(self)
class NIST_QC2_Satellite(_SatelliteBase, _NIST_QC2_RTIO):
def __init__(self, acpki, drtio100mhz):
_SatelliteBase.__init__(self, acpki, drtio100mhz)
_NIST_QC2_RTIO.__init__(self)
VARIANTS = {cls.__name__.lower(): cls for cls in [NIST_CLOCK, NIST_CLOCK_Master, NIST_CLOCK_Satellite,
NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite]}
def write_csr_file(soc, filename): def write_csr_file(soc, filename):
@ -652,11 +260,6 @@ def write_csr_file(soc, filename):
f.write(cpu_interface.get_csr_rust( f.write(cpu_interface.get_csr_rust(
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants())) 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): def write_rustc_cfg_file(soc, filename):
with open(filename, "w") as f: with open(filename, "w") as f:
@ -672,15 +275,13 @@ def main():
description="ARTIQ port to the ZC706 Zynq development kit") description="ARTIQ port to the ZC706 Zynq development kit")
parser.add_argument("-r", default=None, parser.add_argument("-r", default=None,
help="build Rust interface into the specified file") help="build Rust interface into the specified file")
parser.add_argument("-m", default=None,
help="build Rust memory interface into the specified file")
parser.add_argument("-c", default=None, parser.add_argument("-c", default=None,
help="build Rust compiler configuration into the specified file") help="build Rust compiler configuration into the specified file")
parser.add_argument("-g", default=None, parser.add_argument("-g", default=None,
help="build gateware into the specified directory") help="build gateware into the specified directory")
parser.add_argument("-V", "--variant", default="nist_clock", parser.add_argument("-V", "--variant", default="simple",
help="variant: " help="variant: "
"[acpki_]nist_clock/nist_qc2[_master/_satellite][_100mhz]" "[acpki_]simple/nist_clock/nist_qc2 "
"(default: %(default)s)") "(default: %(default)s)")
args = parser.parse_args() args = parser.parse_args()
@ -688,25 +289,21 @@ def main():
acpki = variant.startswith("acpki_") acpki = variant.startswith("acpki_")
if acpki: if acpki:
variant = variant[6:] variant = variant[6:]
drtio100mhz = variant.endswith("_100mhz")
if drtio100mhz:
variant = variant[:-7]
try: try:
cls = VARIANTS[variant] cls = VARIANTS[variant]
except KeyError: except KeyError:
raise SystemExit("Invalid variant (-V/--variant)") raise SystemExit("Invalid variant (-V/--variant)")
soc = cls(acpki=acpki, drtio100mhz=drtio100mhz) soc = cls(acpki=acpki)
soc.finalize() soc.finalize()
if args.r is not None: if args.r is not None:
write_csr_file(soc, args.r) write_csr_file(soc, args.r)
if args.m is not None:
write_mem_file(soc, args.m)
if args.c is not None: if args.c is not None:
write_rustc_cfg_file(soc, args.c) write_rustc_cfg_file(soc, args.c)
if args.g is not None: if args.g is not None:
soc.build(build_dir=args.g) soc.build(build_dir=args.g)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,31 +0,0 @@
[package]
name = "libboard_artiq"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[lib]
name = "libboard_artiq"
[features]
target_zc706 = ["libboard_zynq/target_zc706", "libconfig/target_zc706"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
[build-dependencies]
build_zynq = { path = "../libbuild_zynq" }
[dependencies]
log = "0.4"
log_buffer = { version = "1.2" }
crc = { version = "1.7", default-features = false }
core_io = { version = "0.1", features = ["collections"] }
embedded-hal = "0.2"
nb = "1.0"
void = { version = "1", default-features = false }
io = { path = "../libio", features = ["byteorder"] }
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"}
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn"] }
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }

View File

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

View File

@ -1,106 +0,0 @@
use libconfig::Config;
#[cfg(has_drtio_routing)]
use crate::pl::csr;
use core::fmt;
use log::{warn, info};
#[cfg(has_drtio_routing)]
pub const DEST_COUNT: usize = 256;
#[cfg(not(has_drtio_routing))]
pub const DEST_COUNT: usize = 0;
pub const MAX_HOPS: usize = 32;
pub const INVALID_HOP: u8 = 0xff;
pub struct RoutingTable(pub [[u8; MAX_HOPS]; DEST_COUNT]);
impl RoutingTable {
// default routing table is for star topology with no repeaters
pub fn default_master(default_n_links: usize) -> RoutingTable {
let mut ret = RoutingTable([[INVALID_HOP; MAX_HOPS]; DEST_COUNT]);
let n_entries = default_n_links + 1; // include local RTIO
for i in 0..n_entries {
ret.0[i][0] = i as u8;
}
for i in 1..n_entries {
ret.0[i][1] = 0x00;
}
ret
}
// use this by default on satellite, as they receive
// the routing table from the master
pub fn default_empty() -> RoutingTable {
RoutingTable([[INVALID_HOP; MAX_HOPS]; DEST_COUNT])
}
}
impl fmt::Display for RoutingTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RoutingTable {{")?;
for i in 0..DEST_COUNT {
if self.0[i][0] != INVALID_HOP {
write!(f, " {}:", i)?;
for j in 0..MAX_HOPS {
if self.0[i][j] == INVALID_HOP {
break;
}
write!(f, " {}", self.0[i][j])?;
}
write!(f, ";")?;
}
}
write!(f, " }}")?;
Ok(())
}
}
pub fn config_routing_table(default_n_links: usize, cfg: &Config) -> RoutingTable {
let mut ret = RoutingTable::default_master(default_n_links);
if let Ok(data) = cfg.read("routing_table") {
if data.len() == DEST_COUNT*MAX_HOPS {
for i in 0..DEST_COUNT {
for j in 0..MAX_HOPS {
ret.0[i][j] = data[i*MAX_HOPS+j];
}
}
} else {
warn!("length of the configured routing table is incorrect, using default");
}
} else {
info!("could not read routing table from configuration, using default");
}
info!("routing table: {}", ret);
ret
}
#[cfg(has_drtio_routing)]
pub fn interconnect_enable(routing_table: &RoutingTable, rank: u8, destination: u8) {
let hop = routing_table.0[destination as usize][rank as usize];
unsafe {
csr::routing_table::destination_write(destination);
csr::routing_table::hop_write(hop);
}
}
#[cfg(has_drtio_routing)]
pub fn interconnect_disable(destination: u8) {
unsafe {
csr::routing_table::destination_write(destination);
csr::routing_table::hop_write(INVALID_HOP);
}
}
#[cfg(has_drtio_routing)]
pub fn interconnect_enable_all(routing_table: &RoutingTable, rank: u8) {
for i in 0..DEST_COUNT {
interconnect_enable(routing_table, rank, i as u8);
}
}
#[cfg(has_drtio_routing)]
pub fn interconnect_disable_all() {
for i in 0..DEST_COUNT {
interconnect_disable(i as u8);
}
}

View File

@ -1,167 +0,0 @@
use crc;
use core_io::{ErrorKind as IoErrorKind, Error as IoError};
use io::{proto::ProtoRead, proto::ProtoWrite, Cursor};
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds};
use libcortex_a9::asm::dmb;
use crate::mem::mem::DRTIOAUX_MEM;
use crate::pl::csr::DRTIOAUX;
use crate::drtioaux_proto::Error as ProtocolError;
pub use crate::drtioaux_proto::Packet;
#[derive(Debug)]
pub enum Error {
GatewareError,
CorruptedPacket,
LinkDown,
TimedOut,
UnexpectedReply,
RoutingError,
Protocol(ProtocolError)
}
impl From<ProtocolError> for Error {
fn from(value: ProtocolError) -> Error {
Error::Protocol(value)
}
}
impl From<IoError> for Error {
fn from(value: IoError) -> Error {
Error::Protocol(ProtocolError::Io(value))
}
}
pub fn reset(linkno: u8) {
let linkno = linkno as usize;
unsafe {
// clear buffer first to limit race window with buffer overflow
// error. We assume the CPU is fast enough so that no two packets
// will be received between the buffer and the error flag are cleared.
(DRTIOAUX[linkno].aux_rx_present_write)(1);
(DRTIOAUX[linkno].aux_rx_error_write)(1);
}
}
pub fn has_rx_error(linkno: u8) -> bool {
let linkno = linkno as usize;
unsafe {
let error = (DRTIOAUX[linkno].aux_rx_error_read)() != 0;
if error {
(DRTIOAUX[linkno].aux_rx_error_write)(1)
}
error
}
}
pub fn copy_work_buffer(src: *mut u32, dst: *mut u32, len: isize) {
// AXI writes must be 4-byte aligned (drtio proto doesn't care for that),
// and AXI burst reads/writes are not implemented yet in gateware
// thus the need for a work buffer for transmitting and copying it over
unsafe {
for i in 0..(len/4) {
*dst.offset(i) = *src.offset(i);
//data memory barrier to prevent bursts
dmb();
}
}
}
fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
where F: FnOnce(&[u8]) -> Result<T, Error>
{
let linkidx = linkno as usize;
unsafe {
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u32;
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize;
// work buffer to accomodate axi burst reads
let mut buf: [u8; 1024] = [0; 1024];
copy_work_buffer(ptr, buf.as_mut_ptr() as *mut u32, len as isize);
let result = f(&buf[0..len]);
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
Ok(Some(result?))
} else {
Ok(None)
}
}
}
pub fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
if has_rx_error(linkno) {
return Err(Error::GatewareError)
}
receive(linkno, |buffer| {
if buffer.len() < 8 {
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into())
}
let mut reader = Cursor::new(buffer);
let checksum_at = buffer.len() - 4;
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
reader.set_position(checksum_at);
if reader.read_u32()? != checksum {
return Err(Error::CorruptedPacket)
}
reader.set_position(0);
Ok(Packet::read_from(&mut reader)?)
})
}
pub fn recv_timeout(linkno: u8, timeout_ms: Option<u64>,
timer: GlobalTimer) -> Result<Packet, Error>
{
let timeout_ms = Milliseconds(timeout_ms.unwrap_or(10));
let limit = timer.get_time() + timeout_ms;
while timer.get_time() < limit {
match recv(linkno)? {
None => (),
Some(packet) => return Ok(packet),
}
}
Err(Error::TimedOut)
}
fn transmit<F>(linkno: u8, f: F) -> Result<(), Error>
where F: FnOnce(&mut [u8]) -> Result<usize, Error>
{
let linkno = linkno as usize;
unsafe {
while (DRTIOAUX[linkno].aux_tx_read)() != 0 {}
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
let len = DRTIOAUX_MEM[linkno].size / 2;
// work buffer, works with unaligned mem access
let mut buf: [u8; 1024] = [0; 1024];
let len = f(&mut buf[0..len])?;
copy_work_buffer(buf.as_mut_ptr() as *mut u32, ptr, len as isize);
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
(DRTIOAUX[linkno].aux_tx_write)(1);
Ok(())
}
}
pub fn send(linkno: u8, packet: &Packet) -> Result<(), Error> {
transmit(linkno, |buffer| {
let mut writer = Cursor::new(buffer);
packet.write_to(&mut writer)?;
// Pad till offset 4, insert checksum there
let padding = (12 - (writer.position() % 8)) % 8;
for _ in 0..padding {
writer.write_u8(0)?;
}
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
writer.write_u32(checksum)?;
Ok(writer.position())
})
}

View File

@ -1,138 +0,0 @@
use crc;
use core_io::{ErrorKind as IoErrorKind, Error as IoError};
use void::Void;
use nb;
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds};
use libasync::{task, block_async};
use io::{proto::ProtoRead, proto::ProtoWrite, Cursor};
use crate::mem::mem::DRTIOAUX_MEM;
use crate::pl::csr::DRTIOAUX;
use crate::drtioaux::{Error, has_rx_error, copy_work_buffer};
pub use crate::drtioaux_proto::Packet;
pub async fn reset(linkno: u8) {
let linkno = linkno as usize;
unsafe {
// clear buffer first to limit race window with buffer overflow
// error. We assume the CPU is fast enough so that no two packets
// will be received between the buffer and the error flag are cleared.
(DRTIOAUX[linkno].aux_rx_present_write)(1);
(DRTIOAUX[linkno].aux_rx_error_write)(1);
}
}
fn tx_ready(linkno: usize) -> nb::Result<(), Void> {
unsafe {
if (DRTIOAUX[linkno].aux_tx_read)() != 0 {
Err(nb::Error::WouldBlock)
}
else {
Ok(())
}
}
}
async fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
where F: FnOnce(&[u8]) -> Result<T, Error>
{
let linkidx = linkno as usize;
unsafe {
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u32;
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize;
// work buffer to accomodate axi burst reads
let mut buf: [u8; 1024] = [0; 1024];
copy_work_buffer(ptr, buf.as_mut_ptr() as *mut u32, len as isize);
let result = f(&buf[0..len]);
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
Ok(Some(result?))
} else {
Ok(None)
}
}
}
pub async fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
if has_rx_error(linkno) {
return Err(Error::GatewareError)
}
receive(linkno, |buffer| {
if buffer.len() < 8 {
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into())
}
let mut reader = Cursor::new(buffer);
let checksum_at = buffer.len() - 4;
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
reader.set_position(checksum_at);
if reader.read_u32()? != checksum {
return Err(Error::CorruptedPacket)
}
reader.set_position(0);
Ok(Packet::read_from(&mut reader)?)
}).await
}
pub async fn recv_timeout(linkno: u8, timeout_ms: Option<u64>,
timer: GlobalTimer) -> Result<Packet, Error>
{
let timeout_ms = Milliseconds(timeout_ms.unwrap_or(10));
let limit = timer.get_time() + timeout_ms;
let mut would_block = false;
while timer.get_time() < limit {
// to ensure one last time recv would run one last time
// in case async would return after timeout
if would_block {
task::r#yield().await;
}
match recv(linkno).await? {
None => { would_block = true; },
Some(packet) => return Ok(packet),
}
}
Err(Error::TimedOut)
}
async fn transmit<F>(linkno: u8, f: F) -> Result<(), Error>
where F: FnOnce(&mut [u8]) -> Result<usize, Error>
{
let linkno = linkno as usize;
unsafe {
let _ = block_async!(tx_ready(linkno)).await;
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
let len = DRTIOAUX_MEM[linkno].size / 2;
// work buffer, works with unaligned mem access
let mut buf: [u8; 1024] = [0; 1024];
let len = f(&mut buf[0..len])?;
copy_work_buffer(buf.as_mut_ptr() as *mut u32, ptr, len as isize);
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
(DRTIOAUX[linkno].aux_tx_write)(1);
Ok(())
}
}
pub async fn send(linkno: u8, packet: &Packet) -> Result<(), Error> {
transmit(linkno, |buffer| {
let mut writer = Cursor::new(buffer);
packet.write_to(&mut writer)?;
// Pad till offset 4, insert checksum there
let padding = (12 - (writer.position() % 8)) % 8;
for _ in 0..padding {
writer.write_u8(0)?;
}
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
writer.write_u32(checksum)?;
Ok(writer.position())
}).await
}

View File

@ -1,353 +0,0 @@
use core_io::{Write, Read, Error as IoError};
use io::proto::{ProtoWrite, ProtoRead};
#[derive(Debug)]
pub enum Error {
UnknownPacket(u8),
Io(IoError)
}
impl From<IoError> for Error {
fn from(value: IoError) -> Error {
Error::Io(value)
}
}
#[derive(PartialEq, Debug)]
pub enum Packet {
EchoRequest,
EchoReply,
ResetRequest,
ResetAck,
TSCAck,
DestinationStatusRequest { destination: u8 },
DestinationDownReply,
DestinationOkReply,
DestinationSequenceErrorReply { channel: u16 },
DestinationCollisionReply { channel: u16 },
DestinationBusyReply { channel: u16 },
RoutingSetPath { destination: u8, hops: [u8; 32] },
RoutingSetRank { rank: u8 },
RoutingAck,
MonitorRequest { destination: u8, channel: u16, probe: u8 },
MonitorReply { value: u64 },
InjectionRequest { destination: u8, channel: u16, overrd: u8, value: u8 },
InjectionStatusRequest { destination: u8, channel: u16, overrd: u8 },
InjectionStatusReply { value: u8 },
I2cStartRequest { destination: u8, busno: u8 },
I2cRestartRequest { destination: u8, busno: u8 },
I2cStopRequest { destination: u8, busno: u8 },
I2cWriteRequest { destination: u8, busno: u8, data: u8 },
I2cWriteReply { succeeded: bool, ack: bool },
I2cReadRequest { destination: u8, busno: u8, ack: bool },
I2cReadReply { succeeded: bool, data: u8 },
I2cBasicReply { succeeded: bool },
I2cSwitchSelectRequest { destination: u8, busno: u8, address: u8, mask: u8 },
SpiSetConfigRequest { destination: u8, busno: u8, flags: u8, length: u8, div: u8, cs: u8 },
SpiWriteRequest { destination: u8, busno: u8, data: u32 },
SpiReadRequest { destination: u8, busno: u8 },
SpiReadReply { succeeded: bool, data: u32 },
SpiBasicReply { succeeded: bool },
}
impl Packet {
pub fn read_from<R>(reader: &mut R) -> Result<Self, Error>
where R: Read + ?Sized
{
Ok(match reader.read_u8()? {
0x00 => Packet::EchoRequest,
0x01 => Packet::EchoReply,
0x02 => Packet::ResetRequest,
0x03 => Packet::ResetAck,
0x04 => Packet::TSCAck,
0x20 => Packet::DestinationStatusRequest {
destination: reader.read_u8()?
},
0x21 => Packet::DestinationDownReply,
0x22 => Packet::DestinationOkReply,
0x23 => Packet::DestinationSequenceErrorReply {
channel: reader.read_u16()?
},
0x24 => Packet::DestinationCollisionReply {
channel: reader.read_u16()?
},
0x25 => Packet::DestinationBusyReply {
channel: reader.read_u16()?
},
0x30 => {
let destination = reader.read_u8()?;
let mut hops = [0; 32];
reader.read_exact(&mut hops)?;
Packet::RoutingSetPath {
destination: destination,
hops: hops
}
},
0x31 => Packet::RoutingSetRank {
rank: reader.read_u8()?
},
0x32 => Packet::RoutingAck,
0x40 => Packet::MonitorRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
probe: reader.read_u8()?
},
0x41 => Packet::MonitorReply {
value: reader.read_u64()?
},
0x50 => Packet::InjectionRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
overrd: reader.read_u8()?,
value: reader.read_u8()?
},
0x51 => Packet::InjectionStatusRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
overrd: reader.read_u8()?
},
0x52 => Packet::InjectionStatusReply {
value: reader.read_u8()?
},
0x80 => Packet::I2cStartRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?
},
0x81 => Packet::I2cRestartRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?
},
0x82 => Packet::I2cStopRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?
},
0x83 => Packet::I2cWriteRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
data: reader.read_u8()?
},
0x84 => Packet::I2cWriteReply {
succeeded: reader.read_bool()?,
ack: reader.read_bool()?
},
0x85 => Packet::I2cReadRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
ack: reader.read_bool()?
},
0x86 => Packet::I2cReadReply {
succeeded: reader.read_bool()?,
data: reader.read_u8()?
},
0x87 => Packet::I2cBasicReply {
succeeded: reader.read_bool()?
},
0x88 => Packet::I2cSwitchSelectRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
address: reader.read_u8()?,
mask: reader.read_u8()?
},
0x90 => Packet::SpiSetConfigRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
flags: reader.read_u8()?,
length: reader.read_u8()?,
div: reader.read_u8()?,
cs: reader.read_u8()?
},
/* 0x91: was Packet::SpiSetXferRequest */
0x92 => Packet::SpiWriteRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
data: reader.read_u32()?
},
0x93 => Packet::SpiReadRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?
},
0x94 => Packet::SpiReadReply {
succeeded: reader.read_bool()?,
data: reader.read_u32()?
},
0x95 => Packet::SpiBasicReply {
succeeded: reader.read_bool()?
},
ty => return Err(Error::UnknownPacket(ty))
})
}
pub fn write_to<W>(&self, writer: &mut W) -> Result<(), IoError>
where W: Write + ?Sized
{
match *self {
Packet::EchoRequest =>
writer.write_u8(0x00)?,
Packet::EchoReply =>
writer.write_u8(0x01)?,
Packet::ResetRequest =>
writer.write_u8(0x02)?,
Packet::ResetAck =>
writer.write_u8(0x03)?,
Packet::TSCAck =>
writer.write_u8(0x04)?,
Packet::DestinationStatusRequest { destination } => {
writer.write_u8(0x20)?;
writer.write_u8(destination)?;
},
Packet::DestinationDownReply =>
writer.write_u8(0x21)?,
Packet::DestinationOkReply =>
writer.write_u8(0x22)?,
Packet::DestinationSequenceErrorReply { channel } => {
writer.write_u8(0x23)?;
writer.write_u16(channel)?;
},
Packet::DestinationCollisionReply { channel } => {
writer.write_u8(0x24)?;
writer.write_u16(channel)?;
},
Packet::DestinationBusyReply { channel } => {
writer.write_u8(0x25)?;
writer.write_u16(channel)?;
},
Packet::RoutingSetPath { destination, hops } => {
writer.write_u8(0x30)?;
writer.write_u8(destination)?;
writer.write_all(&hops)?;
},
Packet::RoutingSetRank { rank } => {
writer.write_u8(0x31)?;
writer.write_u8(rank)?;
},
Packet::RoutingAck =>
writer.write_u8(0x32)?,
Packet::MonitorRequest { destination, channel, probe } => {
writer.write_u8(0x40)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u8(probe)?;
},
Packet::MonitorReply { value } => {
writer.write_u8(0x41)?;
writer.write_u64(value)?;
},
Packet::InjectionRequest { destination, channel, overrd, value } => {
writer.write_u8(0x50)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u8(overrd)?;
writer.write_u8(value)?;
},
Packet::InjectionStatusRequest { destination, channel, overrd } => {
writer.write_u8(0x51)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u8(overrd)?;
},
Packet::InjectionStatusReply { value } => {
writer.write_u8(0x52)?;
writer.write_u8(value)?;
},
Packet::I2cStartRequest { destination, busno } => {
writer.write_u8(0x80)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
},
Packet::I2cRestartRequest { destination, busno } => {
writer.write_u8(0x81)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
},
Packet::I2cStopRequest { destination, busno } => {
writer.write_u8(0x82)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
},
Packet::I2cWriteRequest { destination, busno, data } => {
writer.write_u8(0x83)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u8(data)?;
},
Packet::I2cWriteReply { succeeded, ack } => {
writer.write_u8(0x84)?;
writer.write_bool(succeeded)?;
writer.write_bool(ack)?;
},
Packet::I2cReadRequest { destination, busno, ack } => {
writer.write_u8(0x85)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_bool(ack)?;
},
Packet::I2cReadReply { succeeded, data } => {
writer.write_u8(0x86)?;
writer.write_bool(succeeded)?;
writer.write_u8(data)?;
},
Packet::I2cBasicReply { succeeded } => {
writer.write_u8(0x87)?;
writer.write_bool(succeeded)?;
},
Packet::I2cSwitchSelectRequest { destination, busno, address, mask } => {
writer.write_u8(0x88)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u8(address)?;
writer.write_u8(mask)?;
},
Packet::SpiSetConfigRequest { destination, busno, flags, length, div, cs } => {
writer.write_u8(0x90)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u8(flags)?;
writer.write_u8(length)?;
writer.write_u8(div)?;
writer.write_u8(cs)?;
},
Packet::SpiWriteRequest { destination, busno, data } => {
writer.write_u8(0x92)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u32(data)?;
},
Packet::SpiReadRequest { destination, busno } => {
writer.write_u8(0x93)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
},
Packet::SpiReadReply { succeeded, data } => {
writer.write_u8(0x94)?;
writer.write_bool(succeeded)?;
writer.write_u32(data)?;
},
Packet::SpiBasicReply { succeeded } => {
writer.write_u8(0x95)?;
writer.write_bool(succeeded)?;
},
}
Ok(())
}
}

View File

@ -1,69 +0,0 @@
#![no_std]
#![feature(never_type)]
extern crate log;
extern crate crc;
extern crate embedded_hal;
extern crate core_io;
extern crate io;
extern crate libboard_zynq;
extern crate libregister;
extern crate libconfig;
extern crate libcortex_a9;
extern crate libasync;
extern crate log_buffer;
#[path = "../../../build/pl.rs"]
pub mod pl;
pub mod drtioaux_proto;
pub mod drtio_routing;
pub mod logger;
#[cfg(has_si5324)]
pub mod si5324;
#[cfg(has_drtio)]
pub mod drtioaux;
#[cfg(has_drtio)]
pub mod drtioaux_async;
#[cfg(has_drtio)]
#[path = "../../../build/mem.rs"]
pub mod mem;
use core::{cmp, str};
use libboard_zynq::slcr;
use libregister::RegisterW;
pub fn identifier_read(buf: &mut [u8]) -> &str {
unsafe {
pl::csr::identifier::address_write(0);
let len = pl::csr::identifier::data_read();
let len = cmp::min(len, buf.len() as u8);
for i in 0..len {
pl::csr::identifier::address_write(1 + i);
buf[i as usize] = pl::csr::identifier::data_read();
}
str::from_utf8_unchecked(&buf[..len as usize])
}
}
pub fn init_gateware() {
// Set up PS->PL clocks
slcr::RegisterBlock::unlocked(|slcr| {
// As we are touching the mux, the clock may glitch, so reset the PL.
slcr.fpga_rst_ctrl.write(
slcr::FpgaRstCtrl::zeroed()
.fpga0_out_rst(true)
.fpga1_out_rst(true)
.fpga2_out_rst(true)
.fpga3_out_rst(true)
);
slcr.fpga0_clk_ctrl.write(
slcr::Fpga0ClkCtrl::zeroed()
.src_sel(slcr::PllSource::IoPll)
.divisor0(8)
.divisor1(1)
);
slcr.fpga_rst_ctrl.write(
slcr::FpgaRstCtrl::zeroed()
);
});
}

View File

@ -1,353 +0,0 @@
use core::result;
use log::info;
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds};
use embedded_hal::blocking::delay::DelayUs;
#[cfg(not(si5324_soft_reset))]
use crate::pl::csr;
type Result<T> = result::Result<T, &'static str>;
const ADDRESS: u8 = 0x68;
#[cfg(not(si5324_soft_reset))]
fn hard_reset(timer: &mut GlobalTimer) {
unsafe { csr::si5324_rst_n::out_write(0); }
timer.delay_us(1_000);
unsafe { csr::si5324_rst_n::out_write(1); }
timer.delay_us(10_000);
}
// NOTE: the logical parameters DO NOT MAP to physical values written
// into registers. They have to be mapped; see the datasheet.
// DSPLLsim reports the logical parameters in the design summary, not
// the physical register values.
pub struct FrequencySettings {
pub n1_hs: u8,
pub nc1_ls: u32,
pub n2_hs: u8,
pub n2_ls: u32,
pub n31: u32,
pub n32: u32,
pub bwsel: u8,
pub crystal_ref: bool
}
pub enum Input {
Ckin1,
Ckin2,
}
fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySettings> {
if settings.nc1_ls != 0 && (settings.nc1_ls % 2) == 1 {
return Err("NC1_LS must be 0 or even")
}
if settings.nc1_ls > (1 << 20) {
return Err("NC1_LS is too high")
}
if (settings.n2_ls % 2) == 1 {
return Err("N2_LS must be even")
}
if settings.n2_ls > (1 << 20) {
return Err("N2_LS is too high")
}
if settings.n31 > (1 << 19) {
return Err("N31 is too high")
}
if settings.n32 > (1 << 19) {
return Err("N32 is too high")
}
let r = FrequencySettings {
n1_hs: match settings.n1_hs {
4 => 0b000,
5 => 0b001,
6 => 0b010,
7 => 0b011,
8 => 0b100,
9 => 0b101,
10 => 0b110,
11 => 0b111,
_ => return Err("N1_HS has an invalid value")
},
nc1_ls: settings.nc1_ls - 1,
n2_hs: match settings.n2_hs {
4 => 0b000,
5 => 0b001,
6 => 0b010,
7 => 0b011,
8 => 0b100,
9 => 0b101,
10 => 0b110,
11 => 0b111,
_ => return Err("N2_HS has an invalid value")
},
n2_ls: settings.n2_ls - 1,
n31: settings.n31 - 1,
n32: settings.n32 - 1,
bwsel: settings.bwsel,
crystal_ref: settings.crystal_ref
};
Ok(r)
}
fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address")
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register")
}
if !i2c.write(val).unwrap() {
return Err("Si5324 failed to ack value")
}
i2c.stop().unwrap();
Ok(())
}
#[allow(dead_code)]
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address")
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register")
}
i2c.write(val).unwrap();
i2c.stop().unwrap();
Ok(())
}
fn read(i2c: &mut I2c, reg: u8) -> Result<u8> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address")
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register")
}
i2c.restart().unwrap();
if !i2c.write((ADDRESS << 1) | 1).unwrap() {
return Err("Si5324 failed to ack read address")
}
let val = i2c.read(false).unwrap();
i2c.stop().unwrap();
Ok(val)
}
fn rmw<F>(i2c: &mut I2c, reg: u8, f: F) -> Result<()> where
F: Fn(u8) -> u8 {
let value = read(i2c, reg)?;
write(i2c, reg, f(value))?;
Ok(())
}
fn ident(i2c: &mut I2c) -> Result<u16> {
Ok(((read(i2c, 134)? as u16) << 8) | (read(i2c, 135)? as u16))
}
#[cfg(si5324_soft_reset)]
fn soft_reset(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
let val = read(i2c, 136)?;
write_no_ack_value(i2c, 136, val | 0x80)?;
timer.delay_us(10_000);
Ok(())
}
fn has_xtal(i2c: &mut I2c) -> Result<bool> {
Ok((read(i2c, 129)? & 0x01) == 0) // LOSX_INT=0
}
fn has_ckin(i2c: &mut I2c, input: Input) -> Result<bool> {
match input {
Input::Ckin1 => Ok((read(i2c, 129)? & 0x02) == 0), // LOS1_INT=0
Input::Ckin2 => Ok((read(i2c, 129)? & 0x04) == 0), // LOS2_INT=0
}
}
fn locked(i2c: &mut I2c) -> Result<bool> {
Ok((read(i2c, 130)? & 0x01) == 0) // LOL_INT=0
}
fn monitor_lock(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
info!("waiting for Si5324 lock...");
let timeout = timer.get_time() + Milliseconds(20_000);
while !locked(i2c)? {
// Yes, lock can be really slow.
if timer.get_time() > timeout {
return Err("Si5324 lock timeout");
}
}
info!(" ...locked");
Ok(())
}
fn init(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
#[cfg(not(si5324_soft_reset))]
hard_reset(timer);
#[cfg(feature = "target_kasli_soc")]
{
i2c.pca954x_select(0x70, None)?;
i2c.pca954x_select(0x71, Some(3))?;
}
#[cfg(feature = "target_zc706")]
{
i2c.pca954x_select(0x74, Some(4))?;
}
if ident(i2c)? != 0x0182 {
return Err("Si5324 does not have expected product number");
}
#[cfg(si5324_soft_reset)]
soft_reset(i2c, timer)?;
Ok(())
}
pub fn bypass(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
let cksel_reg = match input {
Input::Ckin1 => 0b00,
Input::Ckin2 => 0b01,
};
init(i2c, timer)?;
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?; // CKSEL_REG
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
rmw(i2c, 0, |v| (v & 0xfd) | 0x02)?; // BYPASS_REG=1
Ok(())
}
pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input, timer: &mut GlobalTimer) -> Result<()> {
let s = map_frequency_settings(settings)?;
let cksel_reg = match input {
Input::Ckin1 => 0b00,
Input::Ckin2 => 0b01,
};
init(i2c, timer)?;
if settings.crystal_ref {
rmw(i2c, 0, |v| v | 0x40)?; // FREE_RUN=1
}
rmw(i2c, 2, |v| (v & 0x0f) | (s.bwsel << 4))?;
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
rmw(i2c, 3, |v| (v & 0x2f) | (cksel_reg << 6) | 0x10)?; // CKSEL_REG, SQ_ICAL=1
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
write(i2c, 25, (s.n1_hs << 5 ) as u8)?;
write(i2c, 31, (s.nc1_ls >> 16) as u8)?;
write(i2c, 32, (s.nc1_ls >> 8 ) as u8)?;
write(i2c, 33, (s.nc1_ls) as u8)?;
write(i2c, 34, (s.nc1_ls >> 16) as u8)?; // write to NC2_LS as well
write(i2c, 35, (s.nc1_ls >> 8 ) as u8)?;
write(i2c, 36, (s.nc1_ls) as u8)?;
write(i2c, 40, (s.n2_hs << 5 ) as u8 | (s.n2_ls >> 16) as u8)?;
write(i2c, 41, (s.n2_ls >> 8 ) as u8)?;
write(i2c, 42, (s.n2_ls) as u8)?;
write(i2c, 43, (s.n31 >> 16) as u8)?;
write(i2c, 44, (s.n31 >> 8) as u8)?;
write(i2c, 45, (s.n31) as u8)?;
write(i2c, 46, (s.n32 >> 16) as u8)?;
write(i2c, 47, (s.n32 >> 8) as u8)?;
write(i2c, 48, (s.n32) as u8)?;
rmw(i2c, 137, |v| v | 0x01)?; // FASTLOCK=1
rmw(i2c, 136, |v| v | 0x40)?; // ICAL=1
if !has_xtal(i2c)? {
return Err("Si5324 misses XA/XB signal");
}
if !has_ckin(i2c, input)? {
return Err("Si5324 misses clock input signal");
}
monitor_lock(i2c, timer)?;
Ok(())
}
pub fn select_input(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
let cksel_reg = match input {
Input::Ckin1 => 0b00,
Input::Ckin2 => 0b01,
};
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?;
if !has_ckin(i2c, input)? {
return Err("Si5324 misses clock input signal");
}
monitor_lock(i2c, timer)?;
Ok(())
}
#[cfg(has_siphaser)]
pub mod siphaser {
use super::*;
use crate::pl::csr;
pub fn select_recovered_clock(i2c: &mut I2c, rc: bool, timer: &mut GlobalTimer) -> Result<()> {
let val = read(i2c, 3)?;
write(i2c, 3, (val & 0xdf) | (1 << 5))?; // DHOLD=1
unsafe {
csr::siphaser::switch_clocks_write(if rc { 1 } else { 0 });
}
let val = read(i2c, 3)?;
write(i2c, 3, (val & 0xdf) | (0 << 5))?; // DHOLD=0
monitor_lock(i2c, timer)?;
Ok(())
}
fn phase_shift(direction: u8, timer: &mut GlobalTimer) {
unsafe {
csr::siphaser::phase_shift_write(direction);
while csr::siphaser::phase_shift_done_read() == 0 {}
}
// wait for the Si5324 loop to stabilize
timer.delay_us(500);
}
fn has_error(timer: &mut GlobalTimer) -> bool {
unsafe {
csr::siphaser::error_write(1);
}
timer.delay_us(5_000);
unsafe {
csr::siphaser::error_read() != 0
}
}
fn find_edge(target: bool, timer: &mut GlobalTimer) -> Result<u32> {
let mut nshifts = 0;
let mut previous = has_error(timer);
loop {
phase_shift(1, timer);
nshifts += 1;
let current = has_error(timer);
if previous != target && current == target {
return Ok(nshifts);
}
if nshifts > 5000 {
return Err("failed to find timing error edge");
}
previous = current;
}
}
pub fn calibrate_skew(timer: &mut GlobalTimer) -> Result<()> {
let jitter_margin = 32;
let lead = find_edge(false, timer)?;
for _ in 0..jitter_margin {
phase_shift(1, timer);
}
let width = find_edge(true, timer)? + jitter_margin;
// width is 360 degrees (one full rotation of the phase between s/h limits) minus jitter
info!("calibration successful, lead: {}, width: {} ({}deg)", lead, width, width*360/(56*8));
// Apply reverse phase shift for half the width to get into the
// middle of the working region.
for _ in 0..width/2 {
phase_shift(0, timer);
}
Ok(())
}
}

View File

@ -1,8 +0,0 @@
[package]
authors = ["M-Labs"]
name = "build_zynq"
version = "0.0.0"
[lib]
name = "build_zynq"
path = "lib.rs"

View File

@ -1,30 +0,0 @@
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;
pub fn add_linker_script() {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("link.x"))
.unwrap()
.write_all(include_bytes!("link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// Only re-run the build script when link.x is changed,
// instead of when any part of the source code changes.
println!("cargo:rerun-if-changed=link.x");
}
pub fn cfg() {
// Handle rustc-cfg file
let cfg_path = "../../build/rustc-cfg";
println!("cargo:rerun-if-changed={}", cfg_path);
let f = BufReader::new(File::open(cfg_path).unwrap());
for line in f.lines() {
println!("cargo:rustc-cfg={}", line.unwrap());
}
}

View File

@ -15,4 +15,3 @@ libc = { path = "../libc" }
unwind = { path = "../libunwind" } unwind = { path = "../libunwind" }
compiler_builtins = "0.1.0" compiler_builtins = "0.1.0"
cfg-if = "0.1.8" cfg-if = "0.1.8"
cslice = "0.3"

View File

@ -13,7 +13,6 @@
use crate::DwarfReader; use crate::DwarfReader;
use core::mem; use core::mem;
use cslice::CSlice;
pub const DW_EH_PE_omit: u8 = 0xFF; pub const DW_EH_PE_omit: u8 = 0xFF;
pub const DW_EH_PE_absptr: u8 = 0x00; pub const DW_EH_PE_absptr: u8 = 0x00;
@ -52,48 +51,10 @@ pub enum EHAction {
pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
fn size_of_encoded_value(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
0
} else {
let encoding = encoding & 0x07;
match encoding {
DW_EH_PE_absptr => core::mem::size_of::<*const ()>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
_ => unreachable!(),
}
}
}
unsafe fn get_ttype_entry(
offset: usize,
encoding: u8,
ttype_base: usize,
ttype: *const u8,
) -> Result<Option<*const u8>, ()> {
let i = (offset * size_of_encoded_value(encoding)) as isize;
read_encoded_pointer_with_base(
&mut DwarfReader::new(ttype.offset(-i)),
// the DW_EH_PE_pcrel is a hack.
// It seems that the default encoding is absolute, but we have to take reallocation into
// account. Unsure if we can fix this in the compiler setting or if this would be affected
// by updating the compiler
encoding | DW_EH_PE_pcrel,
ttype_base,
)
.map(|v| match v {
ttype_base => None,
ttype_entry => Some(ttype_entry as *const u8),
})
}
pub unsafe fn find_eh_action( pub unsafe fn find_eh_action(
lsda: *const u8, lsda: *const u8,
context: &EHContext<'_>, context: &EHContext<'_>,
foreign_exception: bool, foreign_exception: bool,
id: u32,
) -> Result<EHAction, ()> { ) -> Result<EHAction, ()> {
if lsda.is_null() { if lsda.is_null() {
return Ok(EHAction::None); return Ok(EHAction::None);
@ -111,17 +72,10 @@ pub unsafe fn find_eh_action(
}; };
let ttype_encoding = reader.read::<u8>(); let ttype_encoding = reader.read::<u8>();
// we do care about the type table if ttype_encoding != DW_EH_PE_omit {
let ttype_offset = if ttype_encoding != DW_EH_PE_omit { // Rust doesn't analyze exception types, so we don't care about the type table
reader.read_uleb128() reader.read_uleb128();
} else { }
0
};
// for rust functions, it seems that there is no type table, so I just put whatever value here.
// we should not return an error, otherwise we would abort unwinding and cannot unwind through
// rust functions
let ttype_base = get_base(ttype_encoding, context).unwrap_or(1);
let ttype_table = reader.ptr.offset(ttype_offset as isize);
let call_site_encoding = reader.read::<u8>(); let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128(); let call_site_table_length = reader.read_uleb128();
@ -140,53 +94,11 @@ pub unsafe fn find_eh_action(
break; break;
} }
if ip < func_start + cs_start + cs_len { if ip < func_start + cs_start + cs_len {
// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/eh_personality.cc#L528
let lpad = lpad_base + cs_lpad;
if cs_lpad == 0 { if cs_lpad == 0 {
// no cleanups/handler
return Ok(EHAction::None);
} else if cs_action == 0 {
return Ok(EHAction::Cleanup(lpad));
} else if foreign_exception {
return Ok(EHAction::None); return Ok(EHAction::None);
} else { } else {
let mut saw_cleanup = false; let lpad = lpad_base + cs_lpad;
let mut action_record = action_table.offset(cs_action as isize - 1); return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
loop {
let mut reader = DwarfReader::new(action_record);
let ar_filter = reader.read_sleb128();
action_record = reader.ptr;
let ar_disp = reader.read_sleb128();
if ar_filter == 0 {
saw_cleanup = true;
} else if ar_filter > 0 {
let catch_type = get_ttype_entry(
ar_filter as usize,
ttype_encoding,
ttype_base,
ttype_table,
)?;
match catch_type {
Some(clause_ptr) if *(clause_ptr as *const u32) == id => {
return Ok(EHAction::Catch(lpad))
}
None => return Ok(EHAction::Catch(lpad)),
_ => {}
}
} else if ar_filter < 0 {
// FIXME: how to handle this?
break;
}
if ar_disp == 0 {
break;
}
action_record = action_record.offset((ar_disp as usize) as isize);
}
if saw_cleanup {
return Ok(EHAction::Cleanup(lpad));
} else {
return Ok(EHAction::None);
}
} }
} }
} }
@ -194,7 +106,7 @@ pub unsafe fn find_eh_action(
// So rather than returning EHAction::Terminate, we do this. // So rather than returning EHAction::Terminate, we do this.
Ok(EHAction::None) Ok(EHAction::None)
} else { } else {
// SjLj version: (not yet modified) // SjLj version:
// The "IP" is an index into the call-site table, with two exceptions: // The "IP" is an index into the call-site table, with two exceptions:
// -1 means 'no-action', and 0 means 'terminate'. // -1 means 'no-action', and 0 means 'terminate'.
match ip as isize { match ip as isize {
@ -234,41 +146,18 @@ fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) ->
#[inline] #[inline]
fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> { fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
if align.is_power_of_two() { if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) }
Ok((unrounded + align - 1) & !(align - 1))
} else {
Err(())
}
}
fn get_base(encoding: u8, context: &EHContext<'_>) -> Result<usize, ()> {
match encoding & 0x70 {
DW_EH_PE_absptr | DW_EH_PE_pcrel | DW_EH_PE_aligned => Ok(0),
DW_EH_PE_textrel => Ok((*context.get_text_start)()),
DW_EH_PE_datarel => Ok((*context.get_data_start)()),
DW_EH_PE_funcrel if context.func_start != 0 => Ok(context.func_start),
_ => return Err(()),
}
} }
unsafe fn read_encoded_pointer( unsafe fn read_encoded_pointer(
reader: &mut DwarfReader, reader: &mut DwarfReader,
context: &EHContext<'_>, context: &EHContext<'_>,
encoding: u8, encoding: u8,
) -> Result<usize, ()> {
read_encoded_pointer_with_base(reader, encoding, get_base(encoding, context)?)
}
unsafe fn read_encoded_pointer_with_base(
reader: &mut DwarfReader,
encoding: u8,
base: usize,
) -> Result<usize, ()> { ) -> Result<usize, ()> {
if encoding == DW_EH_PE_omit { if encoding == DW_EH_PE_omit {
return Err(()); return Err(());
} }
let original_ptr = reader.ptr;
// DW_EH_PE_aligned implies it's an absolute pointer value // DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned { if encoding == DW_EH_PE_aligned {
reader.ptr = round_up(reader.ptr as usize, mem::size_of::<usize>())? as *const u8; reader.ptr = round_up(reader.ptr as usize, mem::size_of::<usize>())? as *const u8;
@ -288,10 +177,19 @@ unsafe fn read_encoded_pointer_with_base(
_ => return Err(()), _ => return Err(()),
}; };
result += if (encoding & 0x70) == DW_EH_PE_pcrel { result += match encoding & 0x70 {
original_ptr as usize DW_EH_PE_absptr => 0,
} else { // relative to address of the encoded value, despite the name
base DW_EH_PE_pcrel => reader.ptr as usize,
DW_EH_PE_funcrel => {
if context.func_start == 0 {
return Err(());
}
context.func_start
}
DW_EH_PE_textrel => (*context.get_text_start)(),
DW_EH_PE_datarel => (*context.get_data_start)(),
_ => return Err(()),
}; };
if encoding & DW_EH_PE_indirect != 0 { if encoding & DW_EH_PE_indirect != 0 {

View File

@ -26,10 +26,6 @@ impl DwarfReader {
DwarfReader { ptr } DwarfReader { ptr }
} }
pub unsafe fn offset(&mut self, offset: isize) {
self.ptr = self.ptr.offset(offset);
}
// DWARF streams are packed, so e.g., a u32 would not necessarily be aligned // DWARF streams are packed, so e.g., a u32 would not necessarily be aligned
// on a 4-byte boundary. This may cause problems on platforms with strict // on a 4-byte boundary. This may cause problems on platforms with strict
// alignment requirements. By wrapping data in a "packed" struct, we are // alignment requirements. By wrapping data in a "packed" struct, we are

View File

@ -2726,9 +2726,6 @@ impl Clone for Elf64_Lib {
fn clone(&self) -> Self { *self } fn clone(&self) -> Self { *self }
} }
pub type Elf32_Conflict = Elf32_Addr; pub type Elf32_Conflict = Elf32_Addr;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EXIDX_Entry(u32, u32);
pub fn ELF32_R_SYM(info: Elf32_Word) -> Elf32_Word { info >> 8 } pub fn ELF32_R_SYM(info: Elf32_Word) -> Elf32_Word { info >> 8 }
pub fn ELF32_R_TYPE(info: Elf32_Word) -> u8 { info as u8 } pub fn ELF32_R_TYPE(info: Elf32_Word) -> u8 { info as u8 }

View File

@ -138,7 +138,7 @@ impl Library {
reloc::rebind(self.arch, self, name, addr as Elf32_Word) reloc::rebind(self.arch, self, name, addr as Elf32_Word)
} }
pub fn exidx(&self) -> &[EXIDX_Entry] { pub fn exidx(&self) -> &[u32] {
self.image.get_ref_slice_unchecked(&self.exidx) self.image.get_ref_slice_unchecked(&self.exidx)
} }
} }

View File

@ -59,8 +59,7 @@ impl Relocatable for Elf32_Rela {
enum RelType { enum RelType {
None, None,
Relative, Relative,
LookupAbs, Lookup,
LookupRel,
} }
impl RelType { impl RelType {
@ -77,11 +76,9 @@ impl RelType {
Some(RelType::Relative), Some(RelType::Relative),
R_OR1K_32 | R_OR1K_GLOB_DAT | R_OR1K_JMP_SLOT R_OR1K_32 | R_OR1K_GLOB_DAT | R_OR1K_JMP_SLOT
if arch == Arch::OpenRisc => Some(RelType::LookupAbs), if arch == Arch::OpenRisc => Some(RelType::Lookup),
R_ARM_GLOB_DAT | R_ARM_JUMP_SLOT | R_ARM_ABS32 R_ARM_GLOB_DAT | R_ARM_JUMP_SLOT
if arch == Arch::Arm => Some(RelType::LookupAbs), if arch == Arch::Arm => Some(RelType::Lookup),
R_ARM_PREL31 if arch == Arch::Arm => Some(RelType::LookupRel),
_ => _ =>
None None
@ -109,86 +106,58 @@ pub fn relocate<R: Relocatable>(
let rel_type = RelType::new(arch, rel.type_info()) let rel_type = RelType::new(arch, rel.type_info())
.ok_or("unsupported relocation type")?; .ok_or("unsupported relocation type")?;
let value = match rel_type { let value;
match rel_type {
RelType::None => RelType::None =>
return Ok(()), return Ok(()),
RelType::Relative => { RelType::Relative => {
let addend = rel.addend(&lib.image); let addend = rel.addend(&lib.image);
lib.image.ptr().wrapping_offset(addend as isize) as Elf32_Word value = lib.image.ptr().wrapping_offset(addend as isize) as Elf32_Word;
} }
RelType::LookupAbs | RelType::LookupRel => { RelType::Lookup => {
let sym = sym.ok_or("relocation requires an associated symbol")?; let sym = sym.ok_or("relocation requires an associated symbol")?;
let sym_name = lib.name_starting_at(sym.st_name as usize)?; let sym_name = lib.name_starting_at(sym.st_name as usize)?;
let sym_addr = if let Some(addr) = lib.lookup(sym_name) { if let Some(addr) = lib.lookup(sym_name) {
// First, try to resolve against itself. // First, try to resolve against itself.
trace!("looked up symbol {} in image", format_sym_name(sym_name)); trace!("looked up symbol {} in image", format_sym_name(sym_name));
addr value = addr;
} else if let Some(addr) = resolve(sym_name) { } else if let Some(addr) = resolve(sym_name) {
// Second, call the user-provided function. // Second, call the user-provided function.
trace!("resolved symbol {:?}", format_sym_name(sym_name)); trace!("resolved symbol {:?}", format_sym_name(sym_name));
addr value = addr;
} else { } else {
// We couldn't find it anywhere. // We couldn't find it anywhere.
return Err(Error::Lookup(format_sym_name(sym_name))) return Err(Error::Lookup(format_sym_name(sym_name)))
};
match rel_type {
RelType::LookupAbs => sym_addr,
RelType::LookupRel =>
sym_addr.wrapping_sub(
lib.image.ptr().wrapping_offset(rel.offset() as isize) as Elf32_Addr),
_ => unreachable!()
} }
} }
};
match rel.type_info() {
R_ARM_PREL31 => {
let reloc_word = lib.image.get_ref::<Elf32_Word>(rel.offset())
.ok_or("relocation offset cannot be read")?;
lib.image.write(rel.offset(), (reloc_word & 0x80000000) | (value & 0x7FFFFFFF))
},
_ => lib.image.write(rel.offset(), value),
} }
lib.image.write(rel.offset(), value)
} }
pub fn rebind( pub fn rebind(
arch: Arch, lib: &Library, name: &[u8], value: Elf32_Word arch: Arch, lib: &Library, name: &[u8], value: Elf32_Word
) -> Result<(), Error> { ) -> Result<(), Error> {
fn rebind_symbol_to_value<R: Relocatable>( for rela in lib.pltrel() {
arch: Arch, lib: &Library,name: &[u8], value: Elf32_Word, relocs: &[R] let rel_type = RelType::new(arch, rela.type_info())
) -> Result<(), Error> {
for reloc in relocs {
let rel_type = RelType::new(arch, reloc.type_info())
.ok_or("unsupported relocation type")?; .ok_or("unsupported relocation type")?;
match rel_type { match rel_type {
RelType::LookupAbs => { RelType::Lookup => {
let sym = lib.symtab().get(reloc.sym_info() as usize) let sym = lib.symtab().get(ELF32_R_SYM(rela.r_info) as usize)
.ok_or("symbol out of bounds of symbol table")?; .ok_or("symbol out of bounds of symbol table")?;
let sym_name = lib.name_starting_at(sym.st_name as usize)?; let sym_name = lib.name_starting_at(sym.st_name as usize)?;
if sym_name == name { if sym_name == name {
lib.image.write(reloc.offset(), value)? lib.image.write(rela.offset(), value)?
} }
} }
// No associated symbols for other relocation types. // No associated symbols for other relocation types.
_ => {} _ => {}
} }
} }
Ok(())
}
if lib.pltrel().is_empty() {
rebind_symbol_to_value(arch, lib, name, value, lib.rela())?;
} else {
rebind_symbol_to_value(arch, lib, name, value, lib.pltrel())?;
}
// FIXME: the cache maintainance operations may be more than enough, // FIXME: the cache maintainance operations may be more than enough,
// may cause performance degradation. // may cause performance degradation.
dcci_slice(lib.image.data); dcci_slice(lib.image.data);

View File

@ -1,17 +0,0 @@
[package]
authors = ["M-Labs"]
name = "io"
version = "0.0.0"
[lib]
name = "io"
path = "lib.rs"
[dependencies]
core_io = { version = "0.1", features = ["collections"] }
byteorder = { version = "1.0", default-features = false, optional = true }
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
[features]
alloc = []

View File

@ -1,81 +0,0 @@
use core_io::{Read, Write, Error as IoError};
#[derive(Debug, Clone)]
pub struct Cursor<T> {
inner: T,
pos: usize
}
impl<T> Cursor<T> {
#[inline]
pub fn new(inner: T) -> Cursor<T> {
Cursor { inner, pos: 0 }
}
#[inline]
pub fn into_inner(self) -> T {
self.inner
}
#[inline]
pub fn get_ref(&self) -> &T {
&self.inner
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
#[inline]
pub fn position(&self) -> usize {
self.pos
}
#[inline]
pub fn set_position(&mut self, pos: usize) {
self.pos = pos
}
}
impl<T: AsRef<[u8]>> Read for Cursor<T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
let data = &self.inner.as_ref()[self.pos..];
let len = buf.len().min(data.len());
buf[..len].copy_from_slice(&data[..len]);
self.pos += len;
Ok(len)
}
}
impl Write for Cursor<&mut [u8]> {
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
let data = &mut self.inner[self.pos..];
let len = buf.len().min(data.len());
data[..len].copy_from_slice(&buf[..len]);
self.pos += len;
Ok(len)
}
#[inline]
fn flush(&mut self) -> Result<(), IoError> {
Ok(())
}
}
#[cfg(feature = "alloc")]
impl Write for Cursor<::alloc::Vec<u8>> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
self.inner.extend_from_slice(buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> Result<(), IoError> {
Ok(())
}
}

View File

@ -1,22 +0,0 @@
#![no_std]
#![feature(never_type)]
#![cfg_attr(feature = "alloc", feature(alloc))]
extern crate alloc;
extern crate core_io;
#[cfg(feature = "alloc")]
#[macro_use]
use alloc;
#[cfg(feature = "byteorder")]
extern crate byteorder;
pub mod cursor;
#[cfg(feature = "byteorder")]
pub mod proto;
pub use cursor::Cursor;
#[cfg(feature = "byteorder")]
pub use proto::{ProtoRead, ProtoWrite};
#[cfg(all(feature = "byteorder", feature = "alloc"))]
pub use proto::ReadStringError;

View File

@ -23,7 +23,6 @@ mod llvm_libunwind {
cfg.flag("-fno-stack-protector"); cfg.flag("-fno-stack-protector");
cfg.flag("--target=armv7-none-eabihf"); cfg.flag("--target=armv7-none-eabihf");
cfg.flag("-O2"); cfg.flag("-O2");
cfg.flag("-flto");
cfg.flag("-std=c99"); cfg.flag("-std=c99");
cfg.flag("-fstrict-aliasing"); cfg.flag("-fstrict-aliasing");

View File

@ -107,12 +107,9 @@ struct _Unwind_Control_Block {
} __attribute__((__aligned__(8))); } __attribute__((__aligned__(8)));
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
(int version, (_Unwind_State state,
_Unwind_Action actions,
uint64_t exceptionClass,
_Unwind_Exception* exceptionObject, _Unwind_Exception* exceptionObject,
struct _Unwind_Context* context, struct _Unwind_Context* context);
void* stop_parameter);
typedef _Unwind_Reason_Code (*__personality_routine) typedef _Unwind_Reason_Code (*__personality_routine)
(_Unwind_State state, (_Unwind_State state,

View File

@ -95,11 +95,9 @@ _Unwind_Reason_Code ProcessDescriptors(
case Descriptor::LU32: case Descriptor::LU32:
descriptor = getNextWord(descriptor, &length); descriptor = getNextWord(descriptor, &length);
descriptor = getNextWord(descriptor, &offset); descriptor = getNextWord(descriptor, &offset);
break;
case Descriptor::LU16: case Descriptor::LU16:
descriptor = getNextNibble(descriptor, &length); descriptor = getNextNibble(descriptor, &length);
descriptor = getNextNibble(descriptor, &offset); descriptor = getNextNibble(descriptor, &offset);
break;
default: default:
assert(false); assert(false);
return _URC_FAILURE; return _URC_FAILURE;
@ -185,14 +183,8 @@ static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state,
if (result != _URC_CONTINUE_UNWIND) if (result != _URC_CONTINUE_UNWIND)
return result; return result;
switch (__unw_step(reinterpret_cast<unw_cursor_t *>(context))) { if (__unw_step(reinterpret_cast<unw_cursor_t *>(context)) != UNW_STEP_SUCCESS)
case UNW_STEP_SUCCESS:
return _URC_CONTINUE_UNWIND;
case UNW_STEP_END:
return _URC_END_OF_STACK;
default:
return _URC_FAILURE; return _URC_FAILURE;
}
return _URC_CONTINUE_UNWIND; return _URC_CONTINUE_UNWIND;
} }
@ -685,128 +677,6 @@ static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor
return _URC_FATAL_PHASE2_ERROR; return _URC_FATAL_PHASE2_ERROR;
} }
static _Unwind_Reason_Code
unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
_Unwind_Exception *exception_object, _Unwind_Stop_Fn stop,
void *stop_parameter) {
// See comment at the start of unwind_phase1 regarding VRS integrity.
__unw_init_local(cursor, uc);
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p)",
static_cast<void *>(exception_object));
// Walk each frame until we reach where search phase said to stop.
bool end_of_stack = false;
// TODO: why can't libunwind handle end of stack properly?
// We should fix this kind of hack.
unw_word_t forced_phase2_prev_sp = 0x0;
while (!end_of_stack) {
// Get info about this frame.
unw_word_t sp;
unw_proc_info_t frameInfo;
__unw_get_reg(cursor, UNW_REG_SP, &sp);
if (sp == forced_phase2_prev_sp) {
break;
}
forced_phase2_prev_sp = sp;
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): __unw_get_proc_info "
"failed => _URC_FATAL_PHASE2_ERROR",
static_cast<void *>(exception_object));
return _URC_FATAL_PHASE2_ERROR;
}
// When tracing, print state information.
if (_LIBUNWIND_TRACING_UNWINDING) {
char functionBuf[512];
const char *functionName = functionBuf;
unw_word_t offset;
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
&offset) != UNW_ESUCCESS) ||
(frameInfo.start_ip + offset > frameInfo.end_ip))
functionName = ".anonymous.";
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR ", func=%s, sp=0x%" PRIxPTR ", "
"lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "",
static_cast<void *>(exception_object), frameInfo.start_ip,
functionName, sp, frameInfo.lsda,
frameInfo.handler);
}
_Unwind_Action action =
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
_Unwind_Reason_Code stopResult =
(*stop)(1, action, exception_object->exception_class, exception_object,
(_Unwind_Context *)(cursor), stop_parameter);
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): stop function returned %d",
(void *)exception_object, stopResult);
if (stopResult != _URC_NO_REASON) {
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): stopped by stop function",
(void *)exception_object);
return _URC_FATAL_PHASE2_ERROR;
}
// If there is a personality routine, tell it we are unwinding.
if (frameInfo.handler != 0) {
__personality_routine p =
(__personality_routine)(long)(frameInfo.handler);
struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor);
// EHABI #7.2
exception_object->pr_cache.fnstart = frameInfo.start_ip;
exception_object->pr_cache.ehtp =
(_Unwind_EHT_Header *)frameInfo.unwind_info;
exception_object->pr_cache.additional = frameInfo.flags;
_Unwind_Reason_Code personalityResult =
(*p)(_US_FORCE_UNWIND | _US_UNWIND_FRAME_STARTING, exception_object,
context);
switch (personalityResult) {
case _URC_CONTINUE_UNWIND:
// Continue unwinding
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): _URC_CONTINUE_UNWIND",
static_cast<void *>(exception_object));
break;
case _URC_INSTALL_CONTEXT:
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): _URC_INSTALL_CONTEXT",
static_cast<void *>(exception_object));
{
// EHABI #7.4.1 says we need to preserve pc for when _Unwind_Resume
// is called back, to find this same frame.
unw_word_t pc;
__unw_get_reg(cursor, UNW_REG_IP, &pc);
exception_object->unwinder_cache.reserved2 = (uint32_t)pc;
}
// We may get control back if landing pad calls _Unwind_Resume().
__unw_resume(cursor);
break;
case _URC_END_OF_STACK:
end_of_stack = true;
break;
default:
// Personality routine returned an unknown result code.
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
personalityResult);
return _URC_FATAL_PHASE2_ERROR;
}
}
}
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
"function with _UA_END_OF_STACK",
(void *)exception_object);
_Unwind_Action lastAction =
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
(struct _Unwind_Context *)(cursor), stop_parameter);
return _URC_FATAL_PHASE2_ERROR;
}
/// Called by __cxa_throw. Only returns if there is a fatal error. /// Called by __cxa_throw. Only returns if there is a fatal error.
_LIBUNWIND_EXPORT _Unwind_Reason_Code _LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_RaiseException(_Unwind_Exception *exception_object) { _Unwind_RaiseException(_Unwind_Exception *exception_object) {
@ -854,36 +724,15 @@ _Unwind_Resume(_Unwind_Exception *exception_object) {
unw_cursor_t cursor; unw_cursor_t cursor;
__unw_getcontext(&uc); __unw_getcontext(&uc);
if (exception_object->unwinder_cache.reserved1) // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0,
unwind_phase2_forced( // which is in the same position as private_1 below.
&uc, &cursor, exception_object, // TODO(ajwong): Who wronte the above? Why is it true?
(_Unwind_Stop_Fn)exception_object->unwinder_cache.reserved1,
(void *)exception_object->unwinder_cache.reserved3);
else
unwind_phase2(&uc, &cursor, exception_object, true); unwind_phase2(&uc, &cursor, exception_object, true);
// Clients assume _Unwind_Resume() does not return, so all we can do is abort. // Clients assume _Unwind_Resume() does not return, so all we can do is abort.
_LIBUNWIND_ABORT("_Unwind_Resume() can't return"); _LIBUNWIND_ABORT("_Unwind_Resume() can't return");
} }
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, _Unwind_Stop_Fn stop,
void *stop_parameter) {
_LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)",
(void *)exception_object, (void *)(uintptr_t)stop);
unw_context_t uc;
unw_cursor_t cursor;
__unw_getcontext(&uc);
// Mark that this is a forced unwind, so _Unwind_Resume() can do
// the right thing.
exception_object->unwinder_cache.reserved1 = (uintptr_t)stop;
exception_object->unwinder_cache.reserved3 = (uintptr_t)stop_parameter;
return unwind_phase2_forced(&uc, &cursor, exception_object, stop,
stop_parameter);
}
/// Called by personality handler during phase 2 to get LSDA for current frame. /// Called by personality handler during phase 2 to get LSDA for current frame.
_LIBUNWIND_EXPORT uintptr_t _LIBUNWIND_EXPORT uintptr_t
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
@ -1153,14 +1002,9 @@ extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code
__gnu_unwind_frame(_Unwind_Exception *exception_object, __gnu_unwind_frame(_Unwind_Exception *exception_object,
struct _Unwind_Context *context) { struct _Unwind_Context *context) {
unw_cursor_t *cursor = (unw_cursor_t *)context; unw_cursor_t *cursor = (unw_cursor_t *)context;
switch (__unw_step(cursor)) { if (__unw_step(cursor) != UNW_STEP_SUCCESS)
case UNW_STEP_SUCCESS:
return _URC_OK;
case UNW_STEP_END:
return _URC_END_OF_STACK;
default:
return _URC_FAILURE; return _URC_FAILURE;
} return _URC_OK;
} }
#endif // defined(_LIBUNWIND_ARM_EHABI) #endif // defined(_LIBUNWIND_ARM_EHABI)

View File

@ -6,13 +6,12 @@ authors = ["M-Labs"]
edition = "2018" edition = "2018"
[features] [features]
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"] target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"] target_coraz7 = ["libboard_zynq/target_coraz7", "libsupport_zynq/target_coraz7", "libconfig/target_coraz7"]
target_redpitaya = ["libboard_zynq/target_redpitaya", "libsupport_zynq/target_redpitaya", "libconfig/target_redpitaya"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
default = ["target_zc706"] default = ["target_zc706"]
[build-dependencies]
build_zynq = { path = "../libbuild_zynq" }
[dependencies] [dependencies]
num-traits = { version = "0.2", default-features = false } num-traits = { version = "0.2", default-features = false }
num-derive = "0.3" num-derive = "0.3"
@ -34,11 +33,9 @@ libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" } libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" } libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" } libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn", "ipv6"] } libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["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" }
libboard_artiq = { path = "../libboard_artiq" }

View File

@ -1,6 +1,28 @@
extern crate build_zynq; use std::env;
use std::fs::File;
use std::io::Write;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
fn main() { fn main() {
build_zynq::add_linker_script(); // Put the linker script somewhere the linker can find it
build_zynq::cfg(); let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("link.x"))
.unwrap()
.write_all(include_bytes!("link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// Only re-run the build script when link.x is changed,
// instead of when any part of the source code changes.
println!("cargo:rerun-if-changed=link.x");
// Handle rustc-cfg file
let cfg_path = "../../build/rustc-cfg";
println!("cargo:rerun-if-changed={}", cfg_path);
let f = BufReader::new(File::open(cfg_path).unwrap());
for line in f.lines() {
println!("cargo:rustc-cfg={}", line.unwrap());
}
} }

View File

@ -1,8 +1,8 @@
use core::fmt; use core::fmt;
use core::cell::RefCell; use core::cell::RefCell;
use core::str::Utf8Error;
use alloc::{vec, vec::Vec, string::String, collections::BTreeMap, rc::Rc}; use alloc::{vec, vec::Vec, string::String, collections::BTreeMap, rc::Rc};
use log::{info, warn, error}; use log::{info, warn, error};
use cslice::CSlice;
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive};
@ -21,7 +21,6 @@ use libcortex_a9::{semaphore::Semaphore, mutex::Mutex, sync_channel::{Sender, Re
use futures::{select_biased, future::FutureExt}; use futures::{select_biased, future::FutureExt};
use libasync::{smoltcp::{Sockets, TcpStream}, task}; use libasync::{smoltcp::{Sockets, TcpStream}, task};
use libconfig::{Config, net_settings}; use libconfig::{Config, net_settings};
use libboard_artiq::drtio_routing;
use crate::proto_async::*; use crate::proto_async::*;
use crate::kernel; use crate::kernel;
@ -29,9 +28,7 @@ use crate::rpc;
use crate::moninj; use crate::moninj;
use crate::mgmt; use crate::mgmt;
use crate::analyzer; use crate::analyzer;
use crate::rtio_mgt;
#[cfg(has_drtio)]
use crate::pl;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error { pub enum Error {
@ -39,6 +36,7 @@ pub enum Error {
UnexpectedPattern, UnexpectedPattern,
UnrecognizedPacket, UnrecognizedPacket,
BufferExhausted, BufferExhausted,
Utf8Error(Utf8Error),
} }
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
@ -50,6 +48,7 @@ impl fmt::Display for 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"),
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
} }
} }
} }
@ -95,13 +94,13 @@ async fn read_request(stream: &TcpStream, allow_close: bool) -> Result<Option<Re
Ok(true) => {} Ok(true) => {}
Ok(false) => Ok(false) =>
return Err(Error::UnexpectedPattern), return Err(Error::UnexpectedPattern),
Err(smoltcp::Error::Finished) => { Err(smoltcp::Error::Illegal) => {
if allow_close { if allow_close {
info!("peer closed connection"); info!("peer closed connection");
return Ok(None); return Ok(None);
} else { } else {
error!("peer unexpectedly closed connection"); error!("peer unexpectedly closed connection");
return Err(smoltcp::Error::Finished)?; return Err(smoltcp::Error::Illegal)?;
} }
}, },
Err(e) => Err(e) =>
@ -120,6 +119,11 @@ async fn read_bytes(stream: &TcpStream, max_length: usize) -> Result<Vec<u8>> {
Ok(buffer) Ok(buffer)
} }
async fn read_string(stream: &TcpStream, max_length: usize) -> Result<String> {
let bytes = read_bytes(stream, max_length).await?;
Ok(String::from_utf8(bytes).map_err(|err| Error::Utf8Error(err.utf8_error()))?)
}
const RETRY_LIMIT: usize = 100; const RETRY_LIMIT: usize = 100;
async fn fast_send(sender: &mut Sender<'_, kernel::Message>, content: kernel::Message) { async fn fast_send(sender: &mut Sender<'_, kernel::Message>, content: kernel::Message) {
@ -149,17 +153,7 @@ async fn fast_recv(receiver: &mut Receiver<'_, kernel::Message>) -> kernel::Mess
receiver.async_recv().await receiver.async_recv().await
} }
async fn write_exception_string(stream: &TcpStream, s: CSlice<'static, u8>) -> Result<()> { async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kernel::Control>>) -> Result<()> {
if s.len() == usize::MAX {
write_i32(stream, -1).await?;
write_i32(stream, s.as_ptr() as i32).await?
} else {
write_chunk(stream, s.as_ref()).await?;
};
Ok(())
}
async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kernel::Control>>, _up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) -> Result<()> {
control.borrow_mut().tx.async_send(kernel::Message::StartRequest).await; control.borrow_mut().tx.async_send(kernel::Message::StartRequest).await;
loop { loop {
let reply = control.borrow_mut().rx.async_recv().await; let reply = control.borrow_mut().rx.async_recv().await;
@ -207,17 +201,17 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
kernel::Message::RpcRecvRequest(_) => (), kernel::Message::RpcRecvRequest(_) => (),
other => panic!("expected (ignored) root value slot from kernel CPU, not {:?}", other), other => panic!("expected (ignored) root value slot from kernel CPU, not {:?}", other),
} }
let id = read_i32(stream).await? as u32; let name = read_string(stream, 16384).await?;
let message = read_i32(stream).await? as u32; let message = read_string(stream, 16384).await?;
let param = [read_i64(stream).await?, let param = [read_i64(stream).await?,
read_i64(stream).await?, read_i64(stream).await?,
read_i64(stream).await?]; read_i64(stream).await?];
let file = read_i32(stream).await? as u32; let file = read_string(stream, 16384).await?;
let line = read_i32(stream).await?; let line = read_i32(stream).await?;
let column = read_i32(stream).await?; let column = read_i32(stream).await?;
let function = read_i32(stream).await? as u32; let function = read_string(stream, 16384).await?;
control.tx.async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException { control.tx.async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException {
id, message, param, file, line, column, function name, message, param, file, line, column, function
}))).await; }))).await;
}, },
_ => { _ => {
@ -227,46 +221,34 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
} }
} }
}, },
kernel::Message::KernelFinished(async_errors) => { kernel::Message::KernelFinished => {
if let Some(stream) = stream { if let Some(stream) = stream {
write_header(stream, Reply::KernelFinished).await?; write_header(stream, Reply::KernelFinished).await?;
write_i8(stream, async_errors as i8).await?;
} }
break; break;
}, },
kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors) => { kernel::Message::KernelException(exception, backtrace) => {
match stream { match stream {
Some(stream) => { Some(stream) => {
// only send the exception data to host if there is host, // only send the exception data to host if there is host,
// i.e. not idle/startup kernel. // i.e. not idle/startup kernel.
write_header(stream, Reply::KernelException).await?; write_header(stream, Reply::KernelException).await?;
write_i32(stream, exceptions.len() as i32).await?; write_chunk(stream, exception.name.as_ref()).await?;
for exception in exceptions.iter() { write_chunk(stream, exception.message.as_ref()).await?;
let exception = exception.as_ref().unwrap();
write_i32(stream, exception.id as i32).await?;
write_exception_string(stream, exception.message).await?;
write_i64(stream, exception.param[0] as i64).await?; write_i64(stream, exception.param[0] as i64).await?;
write_i64(stream, exception.param[1] as i64).await?; write_i64(stream, exception.param[1] as i64).await?;
write_i64(stream, exception.param[2] as i64).await?; write_i64(stream, exception.param[2] as i64).await?;
write_exception_string(stream, exception.file).await?; write_chunk(stream, exception.file.as_ref()).await?;
write_i32(stream, exception.line as i32).await?; write_i32(stream, exception.line as i32).await?;
write_i32(stream, exception.column as i32).await?; write_i32(stream, exception.column as i32).await?;
write_exception_string(stream, exception.function).await?; write_chunk(stream, exception.function.as_ref()).await?;
}
for sp in stack_pointers.iter() {
write_i32(stream, sp.stack_pointer as i32).await?;
write_i32(stream, sp.initial_backtrace_size as i32).await?;
write_i32(stream, sp.current_backtrace_size as i32).await?;
}
write_i32(stream, backtrace.len() as i32).await?; write_i32(stream, backtrace.len() as i32).await?;
for &(addr, sp) in backtrace { for &addr in backtrace {
write_i32(stream, addr as i32).await?; write_i32(stream, addr as i32).await?;
write_i32(stream, sp as i32).await?;
} }
write_i8(stream, async_errors as i8).await?;
}, },
None => { None => {
error!("Uncaught kernel exceptions: {:?}", exceptions); error!("Uncaught kernel exception: {:?}", exception);
} }
} }
break; break;
@ -290,11 +272,6 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
let result = DMA_RECORD_STORE.lock().get(&name).map(|v| v.clone()); let result = DMA_RECORD_STORE.lock().get(&name).map(|v| v.clone());
control.borrow_mut().tx.async_send(kernel::Message::DmaGetReply(result)).await; control.borrow_mut().tx.async_send(kernel::Message::DmaGetReply(result)).await;
}, },
#[cfg(has_drtio)]
kernel::Message::UpDestinationsRequest(destination) => {
let result = _up_destinations.borrow()[destination as usize];
control.borrow_mut().tx.async_send(kernel::Message::UpDestinationsReply(result)).await;
}
_ => { _ => {
panic!("unexpected message from core1 while kernel was running: {:?}", reply); panic!("unexpected message from core1 while kernel was running: {:?}", reply);
} }
@ -336,9 +313,7 @@ async fn load_kernel(buffer: &Vec<u8>, control: &Rc<RefCell<kernel::Control>>, s
} }
} }
async fn handle_connection(stream: &mut TcpStream, control: Rc<RefCell<kernel::Control>>, up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) -> Result<()> { async fn handle_connection(stream: &TcpStream, control: Rc<RefCell<kernel::Control>>) -> Result<()> {
stream.set_ack_delay(None);
if !expect(stream, b"ARTIQ coredev\n").await? { if !expect(stream, b"ARTIQ coredev\n").await? {
return Err(Error::UnexpectedPattern); return Err(Error::UnexpectedPattern);
} }
@ -359,7 +334,7 @@ async fn handle_connection(stream: &mut TcpStream, control: Rc<RefCell<kernel::C
load_kernel(&buffer, &control, Some(stream)).await?; load_kernel(&buffer, &control, Some(stream)).await?;
}, },
Request::RunKernel => { Request::RunKernel => {
handle_run_kernel(Some(stream), &control, &up_destinations).await?; handle_run_kernel(Some(stream), &control).await?;
}, },
_ => { _ => {
error!("unexpected request from host: {:?}", request); error!("unexpected request from host: {:?}", request);
@ -370,7 +345,7 @@ async fn handle_connection(stream: &mut TcpStream, control: Rc<RefCell<kernel::C
} }
pub fn main(timer: GlobalTimer, cfg: Config) { pub fn main(timer: GlobalTimer, cfg: Config) {
let net_addresses = net_settings::get_addresses(&cfg); let net_addresses = net_settings::get_adresses(&cfg);
info!("network addresses: {}", net_addresses); info!("network addresses: {}", net_addresses);
let eth = zynq::eth::Eth::eth0(net_addresses.hardware_addr.0.clone()); let eth = zynq::eth::Eth::eth0(net_addresses.hardware_addr.0.clone());
@ -410,21 +385,8 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
Sockets::init(32); Sockets::init(32);
// before, mutex was on io, but now that io isn't used...?
let aux_mutex: Rc<Mutex<bool>> = Rc::new(Mutex::new(false));
#[cfg(has_drtio)]
let drtio_routing_table = Rc::new(RefCell::new(
drtio_routing::config_routing_table(pl::csr::DRTIO.len(), &cfg)));
#[cfg(not(has_drtio))]
let drtio_routing_table = Rc::new(RefCell::new(drtio_routing::RoutingTable::default_empty()));
let up_destinations = Rc::new(RefCell::new([false; drtio_routing::DEST_COUNT]));
#[cfg(has_drtio_routing)]
drtio_routing::interconnect_disable_all();
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer);
analyzer::start(); analyzer::start();
moninj::start(timer, aux_mutex, drtio_routing_table); moninj::start(timer);
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start())); let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
let idle_kernel = Rc::new(cfg.read("idle").ok()); let idle_kernel = Rc::new(cfg.read("idle").ok());
@ -432,7 +394,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
info!("Loading startup kernel..."); info!("Loading startup kernel...");
if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) { if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) {
info!("Starting startup kernel..."); info!("Starting startup kernel...");
let _ = task::block_on(handle_run_kernel(None, &control, &up_destinations)); let _ = task::block_on(handle_run_kernel(None, &control));
info!("Startup kernel finished!"); info!("Startup kernel finished!");
} else { } else {
error!("Error loading startup kernel!"); error!("Error loading startup kernel!");
@ -445,7 +407,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
let connection = Rc::new(Semaphore::new(1, 1)); let connection = Rc::new(Semaphore::new(1, 1));
let terminate = Rc::new(Semaphore::new(0, 1)); let terminate = Rc::new(Semaphore::new(0, 1));
loop { loop {
let mut stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap(); let stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
if connection.try_wait().is_none() { if connection.try_wait().is_none() {
// there is an existing connection // there is an existing connection
@ -457,14 +419,13 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
let idle_kernel = idle_kernel.clone(); let idle_kernel = idle_kernel.clone();
let connection = connection.clone(); let connection = connection.clone();
let terminate = terminate.clone(); let terminate = terminate.clone();
let up_destinations = up_destinations.clone();
// we make sure the value of terminate is 0 before we start // we make sure the value of terminate is 0 before we start
let _ = terminate.try_wait(); let _ = terminate.try_wait();
task::spawn(async move { task::spawn(async move {
select_biased! { select_biased! {
_ = (async { _ = (async {
let _ = handle_connection(&mut stream, control.clone(), &up_destinations) let _ = handle_connection(&stream, control.clone())
.await .await
.map_err(|e| warn!("connection terminated: {}", e)); .map_err(|e| warn!("connection terminated: {}", e));
if let Some(buffer) = &*idle_kernel { if let Some(buffer) = &*idle_kernel {
@ -472,7 +433,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
let _ = load_kernel(&buffer, &control, None) let _ = load_kernel(&buffer, &control, None)
.await.map_err(|_| warn!("error loading idle kernel")); .await.map_err(|_| warn!("error loading idle kernel"));
info!("Running idle kernel"); info!("Running idle kernel");
let _ = handle_run_kernel(None, &control, &up_destinations) let _ = handle_run_kernel(None, &control)
.await.map_err(|_| warn!("error running idle kernel")); .await.map_err(|_| warn!("error running idle kernel"));
info!("Idle kernel terminated"); info!("Idle kernel terminated");
} }

View File

@ -15,9 +15,8 @@
use core::mem; use core::mem;
use cslice::CSlice; use cslice::CSlice;
use unwind as uw; use unwind as uw;
use libc::{c_int, c_void, uintptr_t}; use libc::{c_int, uintptr_t};
use log::{trace, error}; use log::trace;
use crate::kernel::KERNEL_IMAGE;
use dwarf::eh::{self, EHAction, EHContext}; use dwarf::eh::{self, EHAction, EHContext};
@ -27,12 +26,10 @@ const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51;
#[cfg(target_arch = "arm")] #[cfg(target_arch = "arm")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
// Note: CSlice within an exception may not be actual cslice, they may be strings that exist only
// in the host. If the length == usize:MAX, the pointer is actually a string key in the host.
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Exception<'a> { pub struct Exception<'a> {
pub id: u32, pub name: CSlice<'a, u8>,
pub file: CSlice<'a, u8>, pub file: CSlice<'a, u8>,
pub line: u32, pub line: u32,
pub column: u32, pub column: u32,
@ -45,100 +42,31 @@ fn str_err(_: core::str::Utf8Error) -> core::fmt::Error {
core::fmt::Error core::fmt::Error
} }
fn exception_str<'a>(s: &'a CSlice<'a, u8>) -> Result<&'a str, core::str::Utf8Error> {
if s.len() == usize::MAX {
Ok("<host string>")
} else {
core::str::from_utf8(s.as_ref())
}
}
impl<'a> core::fmt::Debug for Exception<'a> { impl<'a> core::fmt::Debug for Exception<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Exception {} from {} in {}:{}:{}, message: {}", write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
self.id, core::str::from_utf8(self.name.as_ref()).map_err(str_err)?,
exception_str(&self.function).map_err(str_err)?, core::str::from_utf8(self.function.as_ref()).map_err(str_err)?,
exception_str(&self.file).map_err(str_err)?, core::str::from_utf8(self.file.as_ref()).map_err(str_err)?,
self.line, self.column, self.line, self.column,
exception_str(&self.message).map_err(str_err)?) core::str::from_utf8(self.message.as_ref()).map_err(str_err)?)
} }
} }
const MAX_INFLIGHT_EXCEPTIONS: usize = 10;
const MAX_BACKTRACE_SIZE: usize = 128; const MAX_BACKTRACE_SIZE: usize = 128;
#[derive(Debug, Default)] #[repr(C)]
pub struct StackPointerBacktrace { struct ExceptionInfo {
pub stack_pointer: usize, uw_exception: uw::_Unwind_Exception,
pub initial_backtrace_size: usize, exception: Option<Exception<'static>>,
pub current_backtrace_size: usize, handled: bool,
} backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
struct ExceptionBuffer {
// we need n _Unwind_Exception, because each will have their own private data
uw_exceptions: [uw::_Unwind_Exception; MAX_INFLIGHT_EXCEPTIONS],
exceptions: [Option<Exception<'static>>; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_stack: [isize; MAX_INFLIGHT_EXCEPTIONS + 1],
// nested exceptions will share the backtrace buffer, treated as a tree
// backtrace contains a tuple of IP and SP
backtrace: [(usize, usize); MAX_BACKTRACE_SIZE],
backtrace_size: usize,
// stack pointers are stored to reconstruct backtrace for each exception
stack_pointers: [StackPointerBacktrace; MAX_INFLIGHT_EXCEPTIONS + 1],
// current allocated nested exceptions
exception_count: usize,
}
static mut EXCEPTION_BUFFER: ExceptionBuffer = ExceptionBuffer {
uw_exceptions: [uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
}; MAX_INFLIGHT_EXCEPTIONS],
exceptions: [None; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_stack: [-1; MAX_INFLIGHT_EXCEPTIONS + 1],
backtrace: [(0, 0); MAX_BACKTRACE_SIZE],
backtrace_size: 0,
stack_pointers: [StackPointerBacktrace {
stack_pointer: 0,
initial_backtrace_size: 0,
current_backtrace_size: 0
}; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_count: 0
};
pub unsafe extern fn reset_exception_buffer() {
trace!("reset exception buffer");
EXCEPTION_BUFFER.uw_exceptions = [uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
}; MAX_INFLIGHT_EXCEPTIONS];
EXCEPTION_BUFFER.exceptions = [None; MAX_INFLIGHT_EXCEPTIONS + 1];
EXCEPTION_BUFFER.exception_stack = [-1; MAX_INFLIGHT_EXCEPTIONS + 1];
EXCEPTION_BUFFER.backtrace_size = 0;
EXCEPTION_BUFFER.exception_count = 0;
}
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: i32,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;
extern {
// not defined in EHABI, but LLVM added it and is useful to us
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
} }
unsafe fn find_eh_action( unsafe fn find_eh_action(
context: *mut uw::_Unwind_Context, context: *mut uw::_Unwind_Context,
foreign_exception: bool, foreign_exception: bool,
id: u32,
) -> Result<EHAction, ()> { ) -> Result<EHAction, ()> {
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let mut ip_before_instr: c_int = 0; let mut ip_before_instr: c_int = 0;
@ -151,14 +79,32 @@ unsafe fn find_eh_action(
get_text_start: &|| uw::_Unwind_GetTextRelBase(context), get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
get_data_start: &|| uw::_Unwind_GetDataRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
}; };
eh::find_eh_action(lsda, &eh_context, foreign_exception, id) eh::find_eh_action(lsda, &eh_context, foreign_exception)
} }
pub unsafe fn artiq_personality(_state: uw::_Unwind_State, pub unsafe fn artiq_personality(state: uw::_Unwind_State,
exception_object: *mut uw::_Unwind_Exception, exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context) context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code { -> uw::_Unwind_Reason_Code {
// we will only do phase 2 forced unwinding now let state = state as c_int;
let action = state & uw::_US_ACTION_MASK as c_int;
let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int {
// Backtraces on ARM will call the personality routine with
// state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
// we want to continue unwinding the stack, otherwise all our backtraces
// would end at __rust_try
if state & uw::_US_FORCE_UNWIND as c_int != 0 {
return continue_unwind(exception_object, context);
}
true
} else if action == uw::_US_UNWIND_FRAME_STARTING as c_int {
false
} else if action == uw::_US_UNWIND_FRAME_RESUME as c_int {
return continue_unwind(exception_object, context);
} else {
return uw::_URC_FAILURE;
};
// The DWARF unwinder assumes that _Unwind_Context holds things like the function // The DWARF unwinder assumes that _Unwind_Context holds things like the function
// and LSDA pointers, however ARM EHABI places them into the exception object. // and LSDA pointers, however ARM EHABI places them into the exception object.
// To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
@ -174,16 +120,33 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
let exception_class = (*exception_object).exception_class; let exception_class = (*exception_object).exception_class;
let foreign_exception = exception_class != EXCEPTION_CLASS; let foreign_exception = exception_class != EXCEPTION_CLASS;
assert!(!foreign_exception, "we do not expect foreign exceptions"); let eh_action = match find_eh_action(context, foreign_exception) {
let index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
assert!(index != -1);
let exception = EXCEPTION_BUFFER.exceptions[index as usize].as_ref().unwrap();
let id = exception.id;
let eh_action = match find_eh_action(context, foreign_exception, id) {
Ok(action) => action, Ok(action) => action,
Err(_) => return uw::_URC_FAILURE, Err(_) => return uw::_URC_FAILURE,
}; };
let exception_info = &mut *(exception_object as *mut ExceptionInfo);
let exception = &exception_info.exception.unwrap();
if search_phase {
match eh_action {
EHAction::None => return continue_unwind(exception_object, context),
// Actually, cleanup should not return handler found, this is to workaround
// the issue of terminating directly when no catch cause is found while
// having some cleanup routines defined by finally.
// The best way to handle this is to force unwind the stack in the raise
// function when end of stack is reached, and call terminate at the end of
// the unwind. Unfortunately, there is no forced unwind function defined
// for EHABI, and I have no idea how to implement that, so this is a hack.
EHAction::Cleanup(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Catch(_) => {
// EHABI requires the personality routine to update the
// SP value in the barrier cache of the exception object.
(*exception_object).private[5] =
uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
return uw::_URC_HANDLER_FOUND;
}
EHAction::Terminate => return uw::_URC_FAILURE,
}
} else {
match eh_action { match eh_action {
EHAction::None => return continue_unwind(exception_object, context), EHAction::None => return continue_unwind(exception_object, context),
EHAction::Cleanup(lpad) | EHAction::Cleanup(lpad) |
@ -196,17 +159,17 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
} }
EHAction::Terminate => return uw::_URC_FAILURE, EHAction::Terminate => return uw::_URC_FAILURE,
} }
}
// On ARM EHABI the personality routine is responsible for actually // On ARM EHABI the personality routine is responsible for actually
// unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context) context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code { -> uw::_Unwind_Reason_Code {
let reason = __gnu_unwind_frame(exception_object, context); if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
if reason == uw::_URC_NO_REASON {
uw::_URC_CONTINUE_UNWIND uw::_URC_CONTINUE_UNWIND
} else { } else {
reason uw::_URC_FAILURE
} }
} }
// defined in libgcc // defined in libgcc
@ -217,225 +180,74 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
} }
} }
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
uw_exception: *mut uw::_Unwind_Exception) {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
exception_info.exception = None;
}
}
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
uw_exception: uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
},
exception: None,
handled: true,
backtrace: [0; MAX_BACKTRACE_SIZE],
backtrace_size: 0
};
pub unsafe extern fn raise(exception: *const Exception) -> ! { pub unsafe extern fn raise(exception: *const Exception) -> ! {
trace!("Trying to raise exception");
// FIXME: unsound transmute
// This would cause stack memory corruption.
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
INFLIGHT.handled = false;
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
assert!(result == uw::_URC_END_OF_STACK);
INFLIGHT.backtrace_size = 0;
// read backtrace
let _ = uw::backtrace(|ip| {
if INFLIGHT.backtrace_size < MAX_BACKTRACE_SIZE {
INFLIGHT.backtrace[INFLIGHT.backtrace_size] = ip;
INFLIGHT.backtrace_size += 1;
}
});
crate::kernel::core1::terminate(INFLIGHT.exception.as_ref().unwrap(), INFLIGHT.backtrace[..INFLIGHT.backtrace_size].as_mut());
}
pub unsafe extern fn reraise() -> ! {
use cslice::AsCSlice; use cslice::AsCSlice;
let count = EXCEPTION_BUFFER.exception_count; // Reraise is basically cxa_rethrow, which calls _Unwind_Resume_or_Rethrow,
let stack = &mut EXCEPTION_BUFFER.exception_stack; // which for EHABI would always call _Unwind_RaiseException.
let diff = exception as isize - EXCEPTION_BUFFER.exceptions.as_ptr() as isize; match INFLIGHT.exception {
if 0 <= diff && diff <= (mem::size_of::<Option<Exception>>() * MAX_INFLIGHT_EXCEPTIONS) as isize { Some(ref exception) => raise(exception),
let index = diff / (mem::size_of::<Option<Exception>>() as isize); None => raise(&Exception {
trace!("reraise at {}", index); name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
let mut found = false;
for i in 0..=MAX_INFLIGHT_EXCEPTIONS + 1 {
if found {
if stack[i] == -1 {
stack[i - 1] = index;
assert!(i == count);
break;
} else {
stack[i - 1] = stack[i];
}
} else {
if stack[i] == index {
found = true;
}
}
}
assert!(found);
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[stack[count - 1] as usize],
stop_fn, core::ptr::null_mut());
} else {
if count < MAX_INFLIGHT_EXCEPTIONS {
trace!("raising exception at level {}", count);
let exception = &*exception;
for (i, slot) in EXCEPTION_BUFFER.exceptions.iter_mut().enumerate() {
// we should always be able to find a slot
if slot.is_none() {
*slot = Some(
*mem::transmute::<*const Exception, *const Exception<'static>>
(exception));
EXCEPTION_BUFFER.exception_stack[count] = i as isize;
EXCEPTION_BUFFER.uw_exceptions[i].private =
[0; uw::unwinder_private_data_size];
EXCEPTION_BUFFER.stack_pointers[i] = StackPointerBacktrace {
stack_pointer: 0,
initial_backtrace_size: EXCEPTION_BUFFER.backtrace_size,
current_backtrace_size: 0,
};
EXCEPTION_BUFFER.exception_count += 1;
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i],
stop_fn, core::ptr::null_mut());
}
}
} else {
error!("too many nested exceptions");
// TODO: better reporting?
let exception = Exception {
id: get_exception_id("RuntimeError"),
file: file!().as_c_slice(), file: file!().as_c_slice(),
line: line!(), line: line!(),
column: column!(), column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719 // https://github.com/rust-lang/rfcs/pull/1719
function: "__artiq_raise".as_c_slice(), function: "__artiq_reraise".as_c_slice(),
message: "too many nested exceptions".as_c_slice(), message: "No active exception to reraise".as_c_slice(),
param: [0, 0, 0] param: [0, 0, 0]
}; })
EXCEPTION_BUFFER.exceptions[MAX_INFLIGHT_EXCEPTIONS] = Some(mem::transmute(exception));
EXCEPTION_BUFFER.stack_pointers[MAX_INFLIGHT_EXCEPTIONS] = Default::default();
EXCEPTION_BUFFER.exception_count += 1;
uncaught_exception()
} }
}
unreachable!();
}
pub unsafe extern fn resume() -> ! {
trace!("resume");
assert!(EXCEPTION_BUFFER.exception_count != 0);
let i = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
assert!(i != -1);
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i as usize],
stop_fn, core::ptr::null_mut());
unreachable!()
}
pub unsafe extern fn end_catch() {
let mut count = EXCEPTION_BUFFER.exception_count;
assert!(count != 0);
// we remove all exceptions with SP <= current exception SP
// i.e. the outer exception escapes the finally block
let index = EXCEPTION_BUFFER.exception_stack[count - 1] as usize;
EXCEPTION_BUFFER.exception_stack[count - 1] = -1;
EXCEPTION_BUFFER.exceptions[index] = None;
let outer_sp = EXCEPTION_BUFFER.stack_pointers
[index].stack_pointer;
count -= 1;
for i in (0..count).rev() {
let index = EXCEPTION_BUFFER.exception_stack[i];
assert!(index != -1);
let index = index as usize;
let sp = EXCEPTION_BUFFER.stack_pointers[index].stack_pointer;
if sp >= outer_sp {
break;
}
EXCEPTION_BUFFER.exceptions[index] = None;
EXCEPTION_BUFFER.exception_stack[i] = -1;
count -= 1;
}
EXCEPTION_BUFFER.exception_count = count;
EXCEPTION_BUFFER.backtrace_size = if count > 0 {
let index = EXCEPTION_BUFFER.exception_stack[count - 1];
assert!(index != -1);
EXCEPTION_BUFFER.stack_pointers[index as usize].current_backtrace_size
} else {
0
};
}
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
_uw_exception: *mut uw::_Unwind_Exception) {
unimplemented!()
}
fn uncaught_exception() -> ! {
unsafe {
// dump way to reorder the stack
for i in 0..EXCEPTION_BUFFER.exception_count {
if EXCEPTION_BUFFER.exception_stack[i] != i as isize {
// find the correct index
let index = EXCEPTION_BUFFER.exception_stack
.iter()
.position(|v| *v == i as isize).unwrap();
let a = EXCEPTION_BUFFER.exception_stack[index];
let b = EXCEPTION_BUFFER.exception_stack[i];
assert!(a != -1 && b != -1);
core::mem::swap(&mut EXCEPTION_BUFFER.exception_stack[index],
&mut EXCEPTION_BUFFER.exception_stack[i]);
core::mem::swap(&mut EXCEPTION_BUFFER.exceptions[a as usize],
&mut EXCEPTION_BUFFER.exceptions[b as usize]);
core::mem::swap(&mut EXCEPTION_BUFFER.stack_pointers[a as usize],
&mut EXCEPTION_BUFFER.stack_pointers[b as usize]);
}
}
}
unsafe {
crate::kernel::core1::terminate(
EXCEPTION_BUFFER.exceptions[..EXCEPTION_BUFFER.exception_count].as_ref(),
EXCEPTION_BUFFER.stack_pointers[..EXCEPTION_BUFFER.exception_count].as_ref(),
EXCEPTION_BUFFER.backtrace[..EXCEPTION_BUFFER.backtrace_size].as_mut())
}
}
// stop function which would be executed when we unwind each frame
extern fn stop_fn(_version: c_int,
actions: i32,
_uw_exception_class: uw::_Unwind_Exception_Class,
_uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code {
unsafe {
let load_addr = KERNEL_IMAGE.as_ref().unwrap().get_load_addr();
let backtrace_size = EXCEPTION_BUFFER.backtrace_size;
// we try to remove unrelated backtrace here to save some buffer size
if backtrace_size < MAX_BACKTRACE_SIZE {
let ip = uw::_Unwind_GetIP(context);
if ip >= load_addr {
let ip = ip - load_addr;
let sp = uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
trace!("SP: {:X}, backtrace_size: {}", sp, backtrace_size);
EXCEPTION_BUFFER.backtrace[backtrace_size] = (ip, sp);
EXCEPTION_BUFFER.backtrace_size += 1;
let last_index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
assert!(last_index != -1);
let sp_info = &mut EXCEPTION_BUFFER.stack_pointers[last_index as usize];
sp_info.stack_pointer = sp;
sp_info.current_backtrace_size = backtrace_size + 1;
}
} else {
trace!("backtrace size exceeded");
}
if actions as u32 & uw::_US_END_OF_STACK as u32 != 0 {
uncaught_exception()
} else {
uw::_URC_NO_REASON
}
}
}
// Must be kept in sync with preallocate_runtime_exception_names() in artiq/language/embedding_map.py
static EXCEPTION_ID_LOOKUP: [(&str, u32); 11] = [
("RuntimeError", 0),
("RTIOUnderflow", 1),
("RTIOOverflow", 2),
("RTIODestinationUnreachable", 3),
("DMAError", 4),
("I2CError", 5),
("CacheError", 6),
("SPIError", 7),
("ZeroDivisionError", 8),
("IndexError", 9),
("UnwrapNoneError", 10),
];
pub fn get_exception_id(name: &str) -> u32 {
for (n, id) in EXCEPTION_ID_LOOKUP.iter() {
if *n == name {
return *id
}
}
unimplemented!("unallocated internal exception id")
} }
#[macro_export] #[macro_export]
macro_rules! artiq_raise { macro_rules! artiq_raise {
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({ ($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
use cslice::AsCSlice; use cslice::AsCSlice;
let name_id = $crate::eh_artiq::get_exception_id($name);
let exn = $crate::eh_artiq::Exception { let exn = $crate::eh_artiq::Exception {
id: name_id, name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
file: file!().as_c_slice(), file: file!().as_c_slice(),
line: line!(), line: line!(),
column: column!(), column: column!(),
@ -445,9 +257,7 @@ macro_rules! artiq_raise {
param: [$param0, $param1, $param2] param: [$param0, $param1, $param2]
}; };
#[allow(unused_unsafe)] #[allow(unused_unsafe)]
unsafe { unsafe { $crate::eh_artiq::raise(&exn) }
$crate::eh_artiq::raise(&exn)
}
}); });
($name:expr, $message:expr) => ({ ($name:expr, $message:expr) => ({
artiq_raise!($name, $message, 0, 0, 0) artiq_raise!($name, $message, 0, 0, 0)

View File

@ -1,9 +1,11 @@
use libboard_zynq; #[cfg(feature = "target_zc706")]
use crate::artiq_raise; mod i2c {
use libboard_zynq;
use crate::artiq_raise;
pub static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None; static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
pub extern fn start(busno: i32) { pub extern fn start(busno: i32) {
if busno > 0 { if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed"); artiq_raise!("I2CError", "I2C bus could not be accessed");
} }
@ -12,9 +14,9 @@ pub extern fn start(busno: i32) {
artiq_raise!("I2CError", "I2C start failed"); artiq_raise!("I2CError", "I2C start failed");
} }
} }
} }
pub extern fn restart(busno: i32) { pub extern fn restart(busno: i32) {
if busno > 0 { if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed"); artiq_raise!("I2CError", "I2C bus could not be accessed");
} }
@ -23,9 +25,9 @@ pub extern fn restart(busno: i32) {
artiq_raise!("I2CError", "I2C restart failed"); artiq_raise!("I2CError", "I2C restart failed");
} }
} }
} }
pub extern fn stop(busno: i32) { pub extern fn stop(busno: i32) {
if busno > 0 { if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed"); artiq_raise!("I2CError", "I2C bus could not be accessed");
} }
@ -34,9 +36,9 @@ pub extern fn stop(busno: i32) {
artiq_raise!("I2CError", "I2C stop failed"); artiq_raise!("I2CError", "I2C stop failed");
} }
} }
} }
pub extern fn write(busno: i32, data: i32) -> bool { pub extern fn write(busno: i32, data: i32) -> bool {
if busno > 0 { if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed"); artiq_raise!("I2CError", "I2C bus could not be accessed");
} }
@ -46,9 +48,9 @@ pub extern fn write(busno: i32, data: i32) -> bool {
Err(_) => artiq_raise!("I2CError", "I2C write failed"), Err(_) => artiq_raise!("I2CError", "I2C write failed"),
} }
} }
} }
pub extern fn read(busno: i32, ack: bool) -> i32 { pub extern fn read(busno: i32, ack: bool) -> i32 {
if busno > 0 { if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed"); artiq_raise!("I2CError", "I2C bus could not be accessed");
} }
@ -58,33 +60,41 @@ pub extern fn read(busno: i32, ack: bool) -> i32 {
Err(_) => artiq_raise!("I2CError", "I2C read failed"), Err(_) => artiq_raise!("I2CError", "I2C read failed"),
} }
} }
} }
pub extern fn switch_select(busno: i32, address: i32, mask: i32) { pub fn init() {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
let ch = match mask { //decode from mainline, PCA9548-centric API
0x00 => None,
0x01 => Some(0),
0x02 => Some(1),
0x04 => Some(2),
0x08 => Some(3),
0x10 => Some(4),
0x20 => Some(5),
0x40 => Some(6),
0x80 => Some(7),
_ => artiq_raise!("I2CError", "switch select supports only one channel")
};
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().pca954x_select(address as u8, ch).is_err() {
artiq_raise!("I2CError", "switch select failed");
}
}
}
pub fn init() {
let mut i2c = libboard_zynq::i2c::I2c::i2c0(); let mut i2c = libboard_zynq::i2c::I2c::i2c0();
i2c.init().expect("I2C bus initialization failed"); i2c.init().expect("I2C bus initialization failed");
unsafe { I2C_BUS = Some(i2c) }; unsafe { I2C_BUS = Some(i2c) };
}
} }
#[cfg(not(feature = "target_zc706"))]
mod i2c {
use crate::artiq_raise;
pub extern fn start(_busno: i32) {
artiq_raise!("I2CError", "No I2C bus");
}
pub extern fn restart(_busno: i32) {
artiq_raise!("I2CError", "No I2C bus");
}
pub extern fn stop(_busno: i32) {
artiq_raise!("I2CError", "No I2C bus");
}
pub extern fn write(_busno: i32, _data: i32) -> bool {
artiq_raise!("I2CError", "No I2C bus");
}
pub extern fn read(_busno: i32, _ack: bool) -> i32 {
artiq_raise!("I2CError", "No I2C bus");
}
pub fn init() {
}
}
pub use i2c::*;

View File

@ -13,7 +13,7 @@ use crate::i2c;
use super::rpc::{rpc_send, rpc_send_async, rpc_recv}; use super::rpc::{rpc_send, rpc_send_async, rpc_recv};
use super::dma; use super::dma;
use super::cache; use super::cache;
use super::core1::rtio_get_destination_status;
extern "C" { extern "C" {
fn vsnprintf_(buffer: *mut c_char, count: size_t, format: *const c_char, va: VaList) -> c_int; fn vsnprintf_(buffer: *mut c_char, count: size_t, format: *const c_char, va: VaList) -> c_int;
@ -87,7 +87,7 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
// rtio // rtio
api!(rtio_init = rtio::init), api!(rtio_init = rtio::init),
api!(rtio_get_destination_status = rtio_get_destination_status), api!(rtio_get_destination_status = rtio::get_destination_status),
api!(rtio_get_counter = rtio::get_counter), api!(rtio_get_counter = rtio::get_counter),
api!(rtio_output = rtio::output), api!(rtio_output = rtio::output),
api!(rtio_output_wide = rtio::output_wide), api!(rtio_output_wide = rtio::output_wide),
@ -116,7 +116,6 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
api!(i2c_stop = i2c::stop), api!(i2c_stop = i2c::stop),
api!(i2c_write = i2c::write), api!(i2c_write = i2c::write),
api!(i2c_read = i2c::read), api!(i2c_read = i2c::read),
api!(i2c_switch_select = i2c::switch_select),
// 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
@ -199,22 +198,13 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
api!(__aeabi_memclr), api!(__aeabi_memclr),
// libc // libc
api!(memcpy, extern { fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; }), api!(memcmp, extern { fn memcmp(a: *const u8, b: *mut u8, size: usize); }),
api!(memmove, extern { fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; }),
api!(memset, extern { fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8; }),
api!(memcmp, extern { fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; }),
// exceptions // exceptions
api!(_Unwind_Resume = unwind::_Unwind_Resume), api!(_Unwind_Resume = unwind::_Unwind_Resume),
api!(__nac3_personality = eh_artiq::artiq_personality),
api!(__nac3_raise = eh_artiq::raise),
api!(__nac3_resume = eh_artiq::resume),
api!(__nac3_end_catch = eh_artiq::end_catch),
// legacy exception symbols
api!(__artiq_personality = eh_artiq::artiq_personality), api!(__artiq_personality = eh_artiq::artiq_personality),
api!(__artiq_raise = eh_artiq::raise), api!(__artiq_raise = eh_artiq::raise),
api!(__artiq_resume = eh_artiq::resume), api!(__artiq_reraise = eh_artiq::reraise),
api!(__artiq_end_catch = eh_artiq::end_catch),
// Implementations for LLVM math intrinsics // Implementations for LLVM math intrinsics
api!(__powidf2), api!(__powidf2),

View File

@ -1,26 +1,26 @@
use alloc::{string::String, boxed::Box}; use alloc::string::String;
use cslice::{CSlice, AsCSlice}; use cslice::{CSlice, AsCSlice};
use core::mem::{transmute, forget}; use core::mem::{transmute, forget};
use super::{KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, Message}; use super::{KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, Message};
pub extern fn get(key: CSlice<u8>) -> &CSlice<'static, i32> { pub extern fn get(key: CSlice<u8>) -> CSlice<'static, i32> {
let key = String::from_utf8(key.as_ref().to_vec()).unwrap(); let key = String::from_utf8(key.as_ref().to_vec()).unwrap();
unsafe { unsafe {
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::CacheGetRequest(key)); KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::CacheGetRequest(key));
let msg = KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv(); let msg = KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv();
if let Message::CacheGetReply(v) = msg { if let Message::CacheGetReply(v) = msg {
let leaked = Box::new(v.as_c_slice()); let slice = transmute(v.as_c_slice());
let reference = transmute(leaked.as_ref()); // we intentionally leak the memory here,
forget(leaked); // which does not matter as core1 would restart
forget(v); forget(v);
reference slice
} else { } else {
panic!("Expected CacheGetReply for CacheGetRequest"); panic!("Expected CacheGetReply for CacheGetRequest");
} }
} }
} }
pub extern fn put(key: CSlice<u8>, list: &CSlice<i32>) { pub extern fn put(key: CSlice<u8>, list: CSlice<i32>) {
let key = String::from_utf8(key.as_ref().to_vec()).unwrap(); let key = String::from_utf8(key.as_ref().to_vec()).unwrap();
let value = list.as_ref().to_vec(); let value = list.as_ref().to_vec();
unsafe { unsafe {

View File

@ -13,8 +13,8 @@ use libcortex_a9::{
}; };
use libboard_zynq::{mpcore, gic}; use libboard_zynq::{mpcore, gic};
use libsupport_zynq::ram; use libsupport_zynq::ram;
use dyld::{self, Library, elf::EXIDX_Entry}; use dyld::{self, Library};
use crate::{eh_artiq, get_async_errors, rtio}; use crate::{eh_artiq, rtio};
use super::{ use super::{
api::resolve, api::resolve,
rpc::rpc_send_async, rpc::rpc_send_async,
@ -31,8 +31,8 @@ use super::{
extern "C" { extern "C" {
static __text_start: u32; static __text_start: u32;
static __text_end: u32; static __text_end: u32;
static __exidx_start: EXIDX_Entry; static __exidx_start: u32;
static __exidx_end: EXIDX_Entry; static __exidx_end: u32;
} }
unsafe fn attribute_writeback(typeinfo: *const ()) { unsafe fn attribute_writeback(typeinfo: *const ()) {
@ -182,7 +182,6 @@ pub extern "C" fn main_core1() {
info!("kernel starting"); info!("kernel starting");
if let Some(kernel) = loaded_kernel.take() { if let Some(kernel) = loaded_kernel.take() {
unsafe { unsafe {
eh_artiq::reset_exception_buffer();
KERNEL_CHANNEL_0TO1 = Some(core1_rx); KERNEL_CHANNEL_0TO1 = Some(core1_rx);
KERNEL_CHANNEL_1TO0 = Some(core1_tx); KERNEL_CHANNEL_1TO0 = Some(core1_tx);
KERNEL_IMAGE = &kernel as *const KernelImage; KERNEL_IMAGE = &kernel as *const KernelImage;
@ -193,8 +192,7 @@ pub extern "C" fn main_core1() {
} }
} }
info!("kernel finished"); info!("kernel finished");
let async_errors = unsafe { get_async_errors() }; core1_tx.send(Message::KernelFinished);
core1_tx.send(Message::KernelFinished(async_errors));
} }
_ => error!("Core1 received unexpected message: {:?}", message), _ => error!("Core1 received unexpected message: {:?}", message),
} }
@ -202,13 +200,23 @@ pub extern "C" fn main_core1() {
} }
/// Called by eh_artiq /// Called by eh_artiq
pub fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>], pub fn terminate(exception: &'static eh_artiq::Exception<'static>, backtrace: &'static mut [usize]) -> ! {
stack_pointers: &'static [eh_artiq::StackPointerBacktrace], let load_addr = unsafe {
backtrace: &'static mut [(usize, usize)]) -> ! { KERNEL_IMAGE.as_ref().unwrap().get_load_addr()
};
let mut cursor = 0;
// The address in the backtrace is relocated, so we have to convert it back to the address in
// the original python script, and remove those Rust function backtrace.
for i in 0..backtrace.len() {
if backtrace[i] >= load_addr {
backtrace[cursor] = backtrace[i] - load_addr;
cursor += 1;
}
}
{ {
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() }; let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
let errors = unsafe { get_async_errors() }; core1_tx.send(Message::KernelException(exception, &backtrace[..cursor]));
core1_tx.send(Message::KernelException(exceptions, stack_pointers, backtrace, errors));
} }
loop {} loop {}
} }
@ -217,10 +225,10 @@ pub fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
#[no_mangle] #[no_mangle]
extern fn dl_unwind_find_exidx(pc: *const u32, len_ptr: *mut u32) -> *const u32 { extern fn dl_unwind_find_exidx(pc: *const u32, len_ptr: *mut u32) -> *const u32 {
let length; let length;
let start: *const EXIDX_Entry; let start: *const u32;
unsafe { unsafe {
if &__text_start as *const u32 <= pc && pc < &__text_end as *const u32 { if &__text_start as *const u32 <= pc && pc < &__text_end as *const u32 {
length = (&__exidx_end as *const EXIDX_Entry).offset_from(&__exidx_start) as u32; length = (&__exidx_end as *const u32).offset_from(&__exidx_start) as u32;
start = &__exidx_start; start = &__exidx_start;
} else { } else {
let exidx = KERNEL_IMAGE.as_ref() let exidx = KERNEL_IMAGE.as_ref()
@ -231,23 +239,5 @@ extern fn dl_unwind_find_exidx(pc: *const u32, len_ptr: *mut u32) -> *const u32
} }
*len_ptr = length; *len_ptr = length;
} }
start as *const u32 start
}
pub extern fn rtio_get_destination_status(destination: i32) -> bool {
#[cfg(has_drtio)]
if destination > 0 && destination < 255 {
let reply = unsafe {
let core1_rx = KERNEL_CHANNEL_0TO1.as_mut().unwrap();
let core1_tx = KERNEL_CHANNEL_1TO0.as_mut().unwrap();
core1_tx.send(Message::UpDestinationsRequest(destination));
core1_rx.recv()
};
return match reply {
Message::UpDestinationsReply(x) => x,
_ => panic!("received unexpected reply to UpDestinationsRequest: {:?}", reply)
};
}
destination == 0
} }

View File

@ -15,13 +15,13 @@ mod cache;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RPCException { pub struct RPCException {
pub id: u32, pub name: String,
pub message: u32, pub message: String,
pub param: [i64; 3], pub param: [i64; 3],
pub file: u32, pub file: String,
pub line: i32, pub line: i32,
pub column: i32, pub column: i32,
pub function: u32 pub function: String
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -30,11 +30,8 @@ pub enum Message {
LoadCompleted, LoadCompleted,
LoadFailed, LoadFailed,
StartRequest, StartRequest,
KernelFinished(u8), KernelFinished,
KernelException(&'static [Option<eh_artiq::Exception<'static>>], KernelException(&'static eh_artiq::Exception<'static>, &'static [usize]),
&'static [eh_artiq::StackPointerBacktrace],
&'static [(usize, usize)],
u8),
RpcSend { is_async: bool, data: Vec<u8> }, RpcSend { is_async: bool, data: Vec<u8> },
RpcRecvRequest(*mut ()), RpcRecvRequest(*mut ()),
RpcRecvReply(Result<usize, RPCException>), RpcRecvReply(Result<usize, RPCException>),
@ -47,11 +44,6 @@ pub enum Message {
DmaEraseRequest(String), DmaEraseRequest(String),
DmaGetRequest(String), DmaGetRequest(String),
DmaGetReply(Option<(Vec<u8>, i64)>), DmaGetReply(Option<(Vec<u8>, i64)>),
#[cfg(has_drtio)]
UpDestinationsRequest(i32),
#[cfg(has_drtio)]
UpDestinationsReply(bool),
} }
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);
@ -61,7 +53,7 @@ static CHANNEL_SEM: Semaphore = Semaphore::new(0, 1);
static mut KERNEL_CHANNEL_0TO1: Option<sync_channel::Receiver<'static, Message>> = None; static mut KERNEL_CHANNEL_0TO1: Option<sync_channel::Receiver<'static, Message>> = None;
static mut KERNEL_CHANNEL_1TO0: Option<sync_channel::Sender<'static, Message>> = None; static mut KERNEL_CHANNEL_1TO0: Option<sync_channel::Sender<'static, Message>> = None;
pub static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null(); static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null();
static INIT_LOCK: Mutex<()> = Mutex::new(()); static INIT_LOCK: Mutex<()> = Mutex::new(());

View File

@ -1,7 +1,7 @@
//! Kernel-side RPC API //! Kernel-side RPC API
use alloc::vec::Vec; use alloc::vec::Vec;
use cslice::CSlice; use cslice::{CSlice, AsCSlice};
use crate::eh_artiq; use crate::eh_artiq;
use crate::rpc::send_args; use crate::rpc::send_args;
@ -36,12 +36,12 @@ pub extern fn rpc_recv(slot: *mut ()) -> usize {
Message::RpcRecvReply(Ok(alloc_size)) => alloc_size, Message::RpcRecvReply(Ok(alloc_size)) => alloc_size,
Message::RpcRecvReply(Err(exception)) => unsafe { Message::RpcRecvReply(Err(exception)) => unsafe {
eh_artiq::raise(&eh_artiq::Exception { eh_artiq::raise(&eh_artiq::Exception {
id: exception.id, name: exception.name.as_bytes().as_c_slice(),
file: CSlice::new(exception.file as *const u8, usize::MAX), file: exception.file.as_bytes().as_c_slice(),
line: exception.line as u32, line: exception.line as u32,
column: exception.column as u32, column: exception.column as u32,
function: CSlice::new(exception.function as *const u8, usize::MAX), function: exception.function.as_bytes().as_c_slice(),
message: CSlice::new(exception.message as *const u8, usize::MAX), message: exception.message.as_bytes().as_c_slice(),
param: exception.param param: exception.param
}) })
}, },

View File

@ -11,43 +11,120 @@
extern crate alloc; extern crate alloc;
use core::{cmp, str};
use log::{info, warn, error}; use log::{info, warn, error};
use libboard_zynq::{timer::GlobalTimer, mpcore, gic}; use libboard_zynq::{timer::GlobalTimer, mpcore, gic, slcr};
use libasync::{task, block_async}; use libasync::{task, block_async};
use libsupport_zynq::ram; use libsupport_zynq::ram;
use nb; use nb;
use void::Void; use void::Void;
use embedded_hal::blocking::delay::DelayMs;
use libconfig::Config; use libconfig::Config;
use libregister::RegisterW;
use libcortex_a9::l2c::enable_l2_cache; use libcortex_a9::l2c::enable_l2_cache;
use libboard_artiq::{logger, identifier_read, init_gateware, pl};
mod proto_core_io;
mod proto_async; mod proto_async;
mod comms; mod comms;
mod rpc; mod rpc;
#[path = "../../../build/pl.rs"]
mod pl;
#[cfg(ki_impl = "csr")] #[cfg(ki_impl = "csr")]
#[path = "rtio_csr.rs"] #[path = "rtio_csr.rs"]
mod rtio; mod rtio;
#[cfg(ki_impl = "acp")] #[cfg(ki_impl = "acp")]
#[path = "rtio_acp.rs"] #[path = "rtio_acp.rs"]
mod rtio; mod rtio;
mod rtio_mgt;
mod rtio_clocking;
mod kernel; mod kernel;
mod moninj; mod moninj;
mod eh_artiq; mod eh_artiq;
mod panic; mod panic;
mod logger;
mod mgmt; mod mgmt;
mod analyzer; mod analyzer;
mod irq; mod irq;
mod i2c; mod i2c;
static mut SEEN_ASYNC_ERRORS: u8 = 0; fn init_gateware() {
// Set up PS->PL clocks
slcr::RegisterBlock::unlocked(|slcr| {
// As we are touching the mux, the clock may glitch, so reset the PL.
slcr.fpga_rst_ctrl.write(
slcr::FpgaRstCtrl::zeroed()
.fpga0_out_rst(true)
.fpga1_out_rst(true)
.fpga2_out_rst(true)
.fpga3_out_rst(true)
);
slcr.fpga0_clk_ctrl.write(
slcr::Fpga0ClkCtrl::zeroed()
.src_sel(slcr::PllSource::IoPll)
.divisor0(8)
.divisor1(1)
);
slcr.fpga_rst_ctrl.write(
slcr::FpgaRstCtrl::zeroed()
);
});
}
pub unsafe fn get_async_errors() -> u8 { fn identifier_read(buf: &mut [u8]) -> &str {
let errors = SEEN_ASYNC_ERRORS; unsafe {
SEEN_ASYNC_ERRORS = 0; pl::csr::identifier::address_write(0);
errors let len = pl::csr::identifier::data_read();
let len = cmp::min(len, buf.len() as u8);
for i in 0..len {
pl::csr::identifier::address_write(1 + i);
buf[i as usize] = pl::csr::identifier::data_read();
}
str::from_utf8_unchecked(&buf[..len as usize])
}
}
fn init_rtio(timer: &mut GlobalTimer, cfg: &Config) {
let clock_sel =
if let Ok(rtioclk) = cfg.read_str("rtioclk") {
match rtioclk.as_ref() {
"internal" => {
info!("using internal RTIO clock");
0
},
"external" => {
info!("using external RTIO clock");
1
},
other => {
warn!("RTIO clock specification '{}' not recognized", other);
info!("using internal RTIO clock");
0
},
}
} else {
info!("using internal RTIO clock (default)");
0
};
loop {
unsafe {
pl::csr::rtio_crg::pll_reset_write(1);
pl::csr::rtio_crg::clock_sel_write(clock_sel);
pl::csr::rtio_crg::pll_reset_write(0);
}
timer.delay_ms(1);
let locked = unsafe { pl::csr::rtio_crg::pll_locked_read() != 0 };
if locked {
info!("RTIO PLL locked");
break;
} else {
warn!("RTIO PLL failed to lock, retrying...");
timer.delay_ms(500);
}
}
unsafe {
pl::csr::rtio_core::reset_phy_write(1);
}
} }
fn wait_for_async_rtio_error() -> nb::Result<(), Void> { fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
@ -77,14 +154,11 @@ async fn report_async_rtio_errors() {
error!("RTIO sequence error involving channel {}", error!("RTIO sequence error involving channel {}",
pl::csr::rtio_core::sequence_error_channel_read()); pl::csr::rtio_core::sequence_error_channel_read());
} }
SEEN_ASYNC_ERRORS = errors;
pl::csr::rtio_core::async_error_write(errors); pl::csr::rtio_core::async_error_write(errors);
} }
} }
} }
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17]; static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
#[no_mangle] #[no_mangle]
@ -117,8 +191,7 @@ pub fn main_core0() {
} }
}; };
rtio_clocking::init(&mut timer, &cfg); init_rtio(&mut timer, &cfg);
task::spawn(report_async_rtio_errors()); task::spawn(report_async_rtio_errors());
comms::main(timer, cfg); comms::main(timer, cfg);

View File

@ -6,7 +6,7 @@ use core::cell::RefCell;
use alloc::{rc::Rc, vec::Vec, string::String}; use alloc::{rc::Rc, vec::Vec, string::String};
use log::{self, info, debug, warn, error, LevelFilter}; use log::{self, info, debug, warn, error, LevelFilter};
use libboard_artiq::logger::{BufferLogger, LogBufferRef}; use crate::logger::{BufferLogger, LogBufferRef};
use crate::proto_async::*; use crate::proto_async::*;
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
@ -121,7 +121,7 @@ async fn handle_connection(
loop { loop {
let msg = read_i8(stream).await; let msg = read_i8(stream).await;
if let Err(smoltcp::Error::Finished) = msg { if let Err(smoltcp::Error::Illegal) = msg {
return Ok(()); return Ok(());
} }
let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?; let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?;

View File

@ -1,26 +1,25 @@
use core::{fmt, cell::RefCell}; use core::fmt;
use alloc::{collections::BTreeMap, rc::Rc}; use alloc::collections::BTreeMap;
use log::{debug, info, warn}; use log::{debug, info, warn};
use void::Void; use void::Void;
use libboard_artiq::drtio_routing;
use libboard_zynq::{smoltcp, timer::GlobalTimer, time::Milliseconds}; use libboard_zynq::{smoltcp, timer::GlobalTimer, time::Milliseconds};
use libasync::{task, smoltcp::TcpStream, block_async, nb}; use libasync::{task, smoltcp::TcpStream, block_async, nb};
use libcortex_a9::mutex::Mutex;
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive};
use futures::{pin_mut, select_biased, FutureExt}; use futures::{pin_mut, select_biased, FutureExt};
use crate::proto_async::*; use crate::proto_async::*;
use crate::pl::csr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error { pub enum Error {
NetworkError(smoltcp::Error), NetworkError(smoltcp::Error),
UnexpectedPattern, UnexpectedPattern,
UnrecognizedPacket UnrecognizedPacket,
} }
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
@ -55,131 +54,53 @@ enum DeviceMessage {
InjectionStatus = 1 InjectionStatus = 1
} }
#[cfg(has_drtio)] fn read_probe(channel: i32, probe: i8) -> i32 {
mod remote_moninj {
use super::*;
use libboard_artiq::drtioaux_async;
use crate::rtio_mgt::drtio;
use log::error;
pub async fn read_probe(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, probe: i8) -> i64 {
let reply = drtio::aux_transact(aux_mutex, linkno, &drtioaux_async::Packet::MonitorRequest {
destination: destination,
channel: channel as _,
probe: probe as _},
timer).await;
match reply {
Ok(drtioaux_async::Packet::MonitorReply { value }) => return value as i64,
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
Err("link went down") => { debug!("link is down"); },
Err(e) => error!("aux packet error ({})", e)
}
0
}
pub async fn inject(aux_mutex: &Rc<Mutex<bool>>, _timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8, value: i8) {
let _lock = aux_mutex.lock();
drtioaux_async::send(linkno, &drtioaux_async::Packet::InjectionRequest {
destination: destination,
channel: channel as _,
overrd: overrd as _,
value: value as _
}).await.unwrap();
}
pub async fn read_injection_status(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8) -> i8 {
let reply = drtio::aux_transact(aux_mutex,
linkno,
&drtioaux_async::Packet::InjectionStatusRequest {
destination: destination,
channel: channel as _,
overrd: overrd as _},
timer).await;
match reply {
Ok(drtioaux_async::Packet::InjectionStatusReply { value }) => return value as i8,
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
Err("link went down") => { debug!("link is down"); },
Err(e) => error!("aux packet error ({})", e)
}
0
}
}
mod local_moninj {
use libboard_artiq::pl::csr;
pub fn read_probe(channel: i32, probe: i8) -> i64 {
unsafe { unsafe {
csr::rtio_moninj::mon_chan_sel_write(channel as _); csr::rtio_moninj::mon_chan_sel_write(channel as _);
csr::rtio_moninj::mon_probe_sel_write(probe as _); csr::rtio_moninj::mon_probe_sel_write(probe as _);
csr::rtio_moninj::mon_value_update_write(1); csr::rtio_moninj::mon_value_update_write(1);
csr::rtio_moninj::mon_value_read() as i64 csr::rtio_moninj::mon_value_read() as i32
}
} }
}
pub fn inject(channel: i32, overrd: i8, value: i8) { fn inject(channel: i32, overrd: i8, value: i8) {
unsafe { unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _); csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd as _); csr::rtio_moninj::inj_override_sel_write(overrd as _);
csr::rtio_moninj::inj_value_write(value as _); csr::rtio_moninj::inj_value_write(value as _);
} }
} }
pub fn read_injection_status(channel: i32, overrd: i8) -> i8 { fn read_injection_status(channel: i32, overrd: i8) -> i8 {
unsafe { unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _); csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd as _); csr::rtio_moninj::inj_override_sel_write(overrd as _);
csr::rtio_moninj::inj_value_read() as i8 csr::rtio_moninj::inj_value_read() as i8
} }
}
} }
#[cfg(has_drtio)] async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()> {
macro_rules! dispatch {
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
let destination = ($channel >> 16) as u8;
let channel = $channel;
let hop = $routing_table.0[destination as usize][0];
if hop == 0 {
local_moninj::$func(channel.into(), $($param, )*)
} else {
let linkno = hop - 1 as u8;
remote_moninj::$func($aux_mutex, $timer, linkno, destination, channel, $($param, )*).await
}
}}
}
#[cfg(not(has_drtio))]
macro_rules! dispatch {
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
let channel = $channel as u16;
local_moninj::$func(channel.into(), $($param, )*)
}}
}
async fn handle_connection(stream: &TcpStream, timer: GlobalTimer,
_aux_mutex: &Rc<Mutex<bool>>, _routing_table: &drtio_routing::RoutingTable) -> Result<()> {
if !expect(&stream, b"ARTIQ moninj\n").await? { if !expect(&stream, b"ARTIQ moninj\n").await? {
return Err(Error::UnexpectedPattern); return Err(Error::UnexpectedPattern);
} }
stream.send_slice("e".as_bytes()).await?;
let mut probe_watch_list: BTreeMap<(i32, i8), Option<i64>> = BTreeMap::new(); let mut probe_watch_list: BTreeMap<(i32, i8), Option<i32>> = BTreeMap::new();
let mut inject_watch_list: BTreeMap<(i32, i8), Option<i8>> = BTreeMap::new(); let mut inject_watch_list: BTreeMap<(i32, i8), Option<i8>> = BTreeMap::new();
let mut next_check = timer.get_time(); let mut next_check = Milliseconds(0);
let timeout = |next_check: Milliseconds| -> nb::Result<(), Void> {
if timer.get_time() < next_check {
Err(nb::Error::WouldBlock)
} else {
Ok(())
}
};
loop { loop {
// TODO: we don't need fuse() here. // TODO: we don't need fuse() here.
// remove after https://github.com/rust-lang/futures-rs/issues/1989 lands // remove after https://github.com/rust-lang/futures-rs/issues/1989 lands
let read_message_f = read_i8(&stream).fuse(); let read_message_f = read_i8(&stream).fuse();
let next_check_c = next_check.clone(); let next_check_c = next_check.clone();
let timeout = || -> nb::Result<(), Void> {
let timeout_f = block_async!(timeout(next_check_c)).fuse(); if timer.get_time() < next_check_c {
Err(nb::Error::WouldBlock)
} else {
Ok(())
}
};
let timeout_f = block_async!(timeout()).fuse();
pin_mut!(read_message_f, timeout_f); pin_mut!(read_message_f, timeout_f);
select_biased! { select_biased! {
message = read_message_f => { message = read_message_f => {
@ -214,13 +135,13 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer,
let channel = read_i32(&stream).await?; let channel = read_i32(&stream).await?;
let overrd = read_i8(&stream).await?; let overrd = read_i8(&stream).await?;
let value = read_i8(&stream).await?; let value = read_i8(&stream).await?;
dispatch!(timer, _aux_mutex, _routing_table, channel, inject, overrd, value); inject(channel, overrd, value);
debug!("INJECT channel {}, overrd {}, value {}", channel, overrd, value); debug!("INJECT channel {}, overrd {}, value {}", channel, overrd, value);
}, },
HostMessage::GetInjectionStatus => { HostMessage::GetInjectionStatus => {
let channel = read_i32(&stream).await?; let channel = read_i32(&stream).await?;
let overrd = read_i8(&stream).await?; let overrd = read_i8(&stream).await?;
let value = dispatch!(timer, _aux_mutex, _routing_table, channel, read_injection_status, overrd); let value = read_injection_status(channel, overrd);
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?; write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
write_i32(&stream, channel).await?; write_i32(&stream, channel).await?;
write_i8(&stream, overrd).await?; write_i8(&stream, overrd).await?;
@ -230,17 +151,17 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer,
}, },
_ = timeout_f => { _ = timeout_f => {
for (&(channel, probe), previous) in probe_watch_list.iter_mut() { for (&(channel, probe), previous) in probe_watch_list.iter_mut() {
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_probe, probe); let current = read_probe(channel, probe);
if previous.is_none() || previous.unwrap() != current { if previous.is_none() || previous.unwrap() != current {
write_i8(&stream, DeviceMessage::MonitorStatus.to_i8().unwrap()).await?; write_i8(&stream, DeviceMessage::MonitorStatus.to_i8().unwrap()).await?;
write_i32(&stream, channel).await?; write_i32(&stream, channel).await?;
write_i8(&stream, probe).await?; write_i8(&stream, probe).await?;
write_i64(&stream, current).await?; write_i32(&stream, current).await?;
*previous = Some(current); *previous = Some(current);
} }
} }
for (&(channel, overrd), previous) in inject_watch_list.iter_mut() { for (&(channel, overrd), previous) in inject_watch_list.iter_mut() {
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_injection_status, overrd); let current = read_injection_status(channel, overrd);
if previous.is_none() || previous.unwrap() != current { if previous.is_none() || previous.unwrap() != current {
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?; write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
write_i32(&stream, channel).await?; write_i32(&stream, channel).await?;
@ -249,24 +170,21 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer,
*previous = Some(current); *previous = Some(current);
} }
} }
next_check = timer.get_time() + Milliseconds(200); next_check = next_check + Milliseconds(200);
} }
} }
} }
} }
pub fn start(timer: GlobalTimer, aux_mutex: Rc<Mutex<bool>>, routing_table: Rc<RefCell<drtio_routing::RoutingTable>>) { pub fn start(timer: GlobalTimer) {
task::spawn(async move { task::spawn(async move {
loop { loop {
let aux_mutex = aux_mutex.clone();
let routing_table = routing_table.clone();
let stream = TcpStream::accept(1383, 2048, 2048).await.unwrap(); let stream = TcpStream::accept(1383, 2048, 2048).await.unwrap();
task::spawn(async move { task::spawn(async move {
info!("received connection"); info!("received connection");
let routing_table = routing_table.borrow(); let result = handle_connection(&stream, timer).await;
let result = handle_connection(&stream, timer, &aux_mutex, &routing_table).await;
match result { match result {
Err(Error::NetworkError(smoltcp::Error::Finished)) => info!("peer closed connection"), Err(Error::NetworkError(smoltcp::Error::Illegal)) => info!("peer closed connection"),
Err(error) => warn!("connection terminated: {}", error), Err(error) => warn!("connection terminated: {}", error),
_ => (), _ => (),
} }

View File

@ -10,7 +10,7 @@ use libasync::smoltcp::TcpStream;
use alloc::boxed::Box; use alloc::boxed::Box;
use async_recursion::async_recursion; use async_recursion::async_recursion;
use io::proto::ProtoWrite; use crate::proto_core_io::ProtoWrite;
use crate::proto_async; use crate::proto_async;
use self::tag::{Tag, TagIterator, split_tag}; use self::tag::{Tag, TagIterator, split_tag};
@ -82,22 +82,19 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
Tag::List(it) => { Tag::List(it) => {
#[repr(C)] #[repr(C)]
struct List { elements: *mut (), length: u32 } struct List { elements: *mut (), length: u32 }
consume_value!(*mut List, |ptr| { consume_value!(List, |ptr| {
let length = proto_async::read_i32(stream).await? as usize; let length = proto_async::read_i32(stream).await? as usize;
(*ptr).length = length as u32;
let tag = it.clone().next().expect("truncated tag"); let tag = it.clone().next().expect("truncated tag");
let data_size = tag.size() * length as usize + let data_size = tag.size() * length as usize +
match tag { match tag {
Tag::Int64 | Tag::Float64 => 4, Tag::Int64 | Tag::Float64 => 4,
_ => 0 _ => 0
}; };
let data = alloc(data_size + 8).await as *mut u8; let mut data = alloc(data_size).await;
*ptr = data as *mut List;
let ptr = data as *mut List;
let data = data.offset(8);
let alignment = tag.alignment(); let alignment = tag.alignment();
let mut data = data.offset(alignment_offset(alignment as isize, data as isize)) as *mut (); data = data.offset(alignment_offset(alignment as isize, data as isize));
(*ptr).length = length as u32;
(*ptr).elements = data; (*ptr).elements = data;
match tag { match tag {
Tag::Bool => { Tag::Bool => {
@ -253,11 +250,11 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
Tag::List(it) => { Tag::List(it) => {
#[repr(C)] #[repr(C)]
struct List { elements: *const (), length: u32 } struct List { elements: *const (), length: u32 }
consume_value!(&List, |ptr| { consume_value!(List, |ptr| {
let length = (**ptr).length as isize; let length = (*ptr).length as isize;
writer.write_u32((*ptr).length)?; writer.write_u32((*ptr).length)?;
let tag = it.clone().next().expect("truncated tag"); let tag = it.clone().next().expect("truncated tag");
let mut data = (**ptr).elements; let mut data = (*ptr).elements;
writer.write_u8(tag.as_u8())?; writer.write_u8(tag.as_u8())?;
match tag { match tag {
Tag::Bool => { Tag::Bool => {
@ -449,10 +446,10 @@ mod tag {
it.take(3).map(|t| t.alignment()).max().unwrap() it.take(3).map(|t| t.alignment()).max().unwrap()
} }
// CSlice basically // CSlice basically
Tag::Bytes | Tag::String | Tag::ByteArray => Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) =>
core::mem::align_of::<CSlice<()>>(), core::mem::align_of::<CSlice<()>>(),
// array buffer is allocated, so no need for alignment first // array buffer is allocated, so no need for alignment first
Tag::List(_) | Tag::Array(_, _) => 1, Tag::Array(_, _) => 1,
// will not be sent from the host // will not be sent from the host
_ => unreachable!("unexpected tag from host") _ => unreachable!("unexpected tag from host")
} }
@ -480,7 +477,7 @@ mod tag {
} }
size size
} }
Tag::List(_) => 4, Tag::List(_) => 8,
Tag::Array(_, num_dims) => 4 * (1 + num_dims as usize), Tag::Array(_, num_dims) => 4 * (1 + num_dims as usize),
Tag::Range(it) => { Tag::Range(it) => {
let tag = it.clone().next().expect("truncated tag"); let tag = it.clone().next().expect("truncated tag");

View File

@ -57,6 +57,11 @@ pub extern fn init() {
} }
} }
pub extern fn get_destination_status(destination: i32) -> bool {
// TODO
destination == 0
}
pub extern fn get_counter() -> i64 { pub extern fn get_counter() -> i64 {
unsafe { unsafe {
csr::rtio::counter_update_write(1); csr::rtio::counter_update_write(1);

View File

@ -1,254 +0,0 @@
use log::{info, warn, error};
use libboard_zynq::timer::GlobalTimer;
use embedded_hal::blocking::delay::DelayMs;
use libconfig::Config;
use libboard_artiq::pl;
#[cfg(has_si5324)]
use libboard_zynq::i2c::I2c;
#[cfg(has_si5324)]
use crate::i2c;
#[cfg(has_si5324)]
use libboard_artiq::si5324;
#[derive(Debug, PartialEq, Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum RtioClock {
Default,
Int_125,
Int_100,
Int_150,
Ext0_Bypass,
Ext0_Synth0_10to125,
Ext0_Synth0_100to125,
Ext0_Synth0_125to125,
}
#[allow(unreachable_code)]
fn get_rtio_clock_cfg(cfg: &Config) -> RtioClock {
let mut res = RtioClock::Default;
if let Ok(clk) = cfg.read_str("rtio_clock") {
res = match clk.as_ref() {
"int_125" => RtioClock::Int_125,
"int_100" => RtioClock::Int_100,
"int_150" => RtioClock::Int_150,
"ext0_bypass" => RtioClock::Ext0_Bypass,
"ext0_bypass_125" => RtioClock::Ext0_Bypass,
"ext0_bypass_100" => RtioClock::Ext0_Bypass,
"ext0_synth0_10to125" => RtioClock::Ext0_Synth0_10to125,
"ext0_synth0_100to125" => RtioClock::Ext0_Synth0_100to125,
"ext0_synth0_125to125" => RtioClock::Ext0_Synth0_125to125,
_ => {
warn!("Unrecognised rtio_clock setting. Falling back to default.");
RtioClock::Default
}
};
}
else {
warn!("error reading configuration. Falling back to default.");
}
if res == RtioClock::Default {
#[cfg(rtio_frequency="100.0")]
{
warn!("Using default configuration - internal 100MHz RTIO clock.");
return RtioClock::Int_100;
}
#[cfg(rtio_frequency="125.0")]
{
warn!("Using default configuration - internal 125MHz RTIO clock.");
return RtioClock::Int_125;
}
// anything else
{
warn!("Using default configuration - internal 125MHz RTIO clock.");
return RtioClock::Int_125;
}
}
res
}
fn init_rtio(timer: &mut GlobalTimer, _clk: RtioClock) {
#[cfg(has_rtio_crg_clock_sel)]
let clock_sel = match _clk {
RtioClock::Ext0_Bypass => {
info!("Using bypassed external clock");
1
},
RtioClock::Int_125 => {
info!("Using internal RTIO clock");
0
},
_ => {
warn!("rtio_clock setting '{:?}' is not supported. Using default internal RTIO clock instead", _clk);
0
}
};
unsafe {
pl::csr::rtio_crg::pll_reset_write(1);
#[cfg(has_rtio_crg_clock_sel)]
pl::csr::rtio_crg::clock_sel_write(clock_sel);
pl::csr::rtio_crg::pll_reset_write(0);
}
timer.delay_ms(1);
let locked = unsafe { pl::csr::rtio_crg::pll_locked_read() != 0 };
if locked {
info!("RTIO PLL locked");
} else {
error!("RTIO PLL failed to lock");
}
unsafe {
pl::csr::rtio_core::reset_phy_write(1);
}
}
#[cfg(has_drtio)]
fn init_drtio(timer: &mut GlobalTimer)
{
unsafe {
pl::csr::drtio_transceiver::stable_clkin_write(1);
}
timer.delay_ms(2); // wait for CPLL/QPLL lock
unsafe {
pl::csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
}
}
#[cfg(has_si5324)]
fn setup_si5324(i2c: &mut I2c, timer: &mut GlobalTimer, clk: RtioClock) {
let (si5324_settings, si5324_ref_input) = match clk {
RtioClock::Ext0_Synth0_10to125 => { // 125 MHz output from 10 MHz CLKINx reference, 504 Hz BW
info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
(
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 300,
n31 : 6,
n32 : 6,
bwsel : 4,
crystal_ref: false
},
si5324::Input::Ckin1
)
},
RtioClock::Ext0_Synth0_100to125 => { // 125MHz output, from 100MHz CLKINx reference, 586 Hz loop bandwidth
info!("using 100MHz reference to make 125MHz RTIO clock with PLL");
(
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 260,
n31 : 52,
n32 : 52,
bwsel : 4,
crystal_ref: false
},
si5324::Input::Ckin1
)
},
RtioClock::Ext0_Synth0_125to125 => { // 125MHz output, from 125MHz CLKINx reference, 606 Hz loop bandwidth
info!("using 125MHz reference to make 125MHz RTIO clock with PLL");
(
si5324::FrequencySettings {
n1_hs : 5,
nc1_ls : 8,
n2_hs : 7,
n2_ls : 360,
n31 : 63,
n32 : 63,
bwsel : 4,
crystal_ref: false
},
si5324::Input::Ckin1
)
},
RtioClock::Int_150 => { // 150MHz output, from crystal
info!("using internal 150MHz RTIO clock");
(
si5324::FrequencySettings {
n1_hs : 9,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 33732,
n31 : 7139,
n32 : 7139,
bwsel : 3,
crystal_ref: true
},
si5324::Input::Ckin2
)
},
RtioClock::Int_100 => { // 100MHz output, from crystal.
info!("using internal 100MHz RTIO clock");
(
si5324::FrequencySettings {
n1_hs : 9,
nc1_ls : 6,
n2_hs : 10,
n2_ls : 33732,
n31 : 7139,
n32 : 7139,
bwsel : 3,
crystal_ref: true
},
si5324::Input::Ckin2
)
},
RtioClock::Int_125 => { // 125MHz output, from crystal, 7 Hz
info!("using internal 125MHz RTIO clock");
(
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 19972,
n31 : 4565,
n32 : 4565,
bwsel : 4,
crystal_ref: true
},
si5324::Input::Ckin2
)
},
_ => { // same setting as Int_125, but fallback to default
warn!("rtio_clock setting '{:?}' is unsupported. Falling back to default internal 125MHz RTIO clock.", clk);
(
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 19972,
n31 : 4565,
n32 : 4565,
bwsel : 4,
crystal_ref: true
},
si5324::Input::Ckin2
)
}
};
si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324");
}
pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
let clk = get_rtio_clock_cfg(cfg);
#[cfg(has_si5324)]
{
let i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
let si5324_ext_input = si5324::Input::Ckin1;
match clk {
RtioClock::Ext0_Bypass => si5324::bypass(i2c, si5324_ext_input, timer).expect("cannot bypass Si5324"),
_ => setup_si5324(i2c, timer, clk),
}
}
#[cfg(has_drtio)]
init_drtio(timer);
init_rtio(timer, clk);
}

View File

@ -25,6 +25,11 @@ pub extern fn init() {
} }
} }
pub extern fn get_destination_status(destination: i32) -> bool {
// TODO
destination == 0
}
pub extern fn get_counter() -> i64 { pub extern fn get_counter() -> i64 {
unsafe { unsafe {
csr::rtio::counter_update_write(1); csr::rtio::counter_update_write(1);

View File

@ -1,355 +0,0 @@
use core::cell::RefCell;
use alloc::rc::Rc;
use libboard_zynq::timer::GlobalTimer;
use libboard_artiq::{pl::csr, drtio_routing};
use libcortex_a9::mutex::Mutex;
#[cfg(has_drtio)]
pub mod drtio {
use super::*;
use libboard_artiq::drtioaux_async;
use libboard_artiq::drtioaux_async::Packet;
use libboard_artiq::drtioaux::Error;
use log::{warn, error, info};
use embedded_hal::blocking::delay::DelayMs;
use libasync::{task, delay};
use libboard_zynq::time::Milliseconds;
pub fn startup(aux_mutex: &Rc<Mutex<bool>>,
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer) {
let aux_mutex = aux_mutex.clone();
let routing_table = routing_table.clone();
let up_destinations = up_destinations.clone();
task::spawn(async move {
let routing_table = routing_table.borrow();
link_task(&aux_mutex, &routing_table, &up_destinations, timer).await;
});
}
async fn link_rx_up(linkno: u8) -> bool {
let linkno = linkno as usize;
unsafe {
(csr::DRTIO[linkno].rx_up_read)() == 1
}
}
async fn recv_aux_timeout(linkno: u8, timeout: u64, timer: GlobalTimer) -> Result<Packet, &'static str> {
if !link_rx_up(linkno).await {
return Err("link went down");
}
match drtioaux_async::recv_timeout(linkno, Some(timeout), timer).await {
Ok(packet) => return Ok(packet),
Err(Error::TimedOut) => return Err("timed out"),
Err(_) => return Err("aux packet error"),
}
}
pub async fn aux_transact(aux_mutex: &Mutex<bool>, linkno: u8, request: &Packet,
timer: GlobalTimer) -> Result<Packet, &'static str> {
if !link_rx_up(linkno).await {
return Err("link went down");
}
let _lock = aux_mutex.async_lock().await;
drtioaux_async::send(linkno, request).await.unwrap();
recv_aux_timeout(linkno, 200, timer).await
}
async fn drain_buffer(linkno: u8, draining_time: Milliseconds, timer: GlobalTimer) {
let max_time = timer.get_time() + draining_time;
loop {
if timer.get_time() > max_time {
return;
} //could this be cut short?
let _ = drtioaux_async::recv(linkno).await;
}
}
async fn ping_remote(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, timer: GlobalTimer) -> u32 {
let mut count = 0;
loop {
if !link_rx_up(linkno).await {
return 0
}
count += 1;
if count > 100 {
return 0;
}
let reply = aux_transact(aux_mutex, linkno, &Packet::EchoRequest, timer).await;
match reply {
Ok(Packet::EchoReply) => {
// make sure receive buffer is drained
let draining_time = Milliseconds(200);
drain_buffer(linkno, draining_time, timer).await;
return count;
}
_ => {}
}
}
}
async fn sync_tsc(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, timer: GlobalTimer) -> Result<(), &'static str> {
let _lock = aux_mutex.async_lock().await;
unsafe {
(csr::DRTIO[linkno as usize].set_time_write)(1);
while (csr::DRTIO[linkno as usize].set_time_read)() == 1 {}
}
// TSCAck is the only aux packet that is sent spontaneously
// by the satellite, in response to a TSC set on the RT link.
let reply = recv_aux_timeout(linkno, 10000, timer).await?;
if reply == Packet::TSCAck {
return Ok(());
} else {
return Err("unexpected reply");
}
}
async fn load_routing_table(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer) -> Result<(), &'static str> {
for i in 0..drtio_routing::DEST_COUNT {
let reply = aux_transact(aux_mutex, linkno, &Packet::RoutingSetPath {
destination: i as u8,
hops: routing_table.0[i]
}, timer).await?;
if reply != Packet::RoutingAck {
return Err("unexpected reply");
}
}
Ok(())
}
async fn set_rank(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, rank: u8, timer: GlobalTimer) -> Result<(), &'static str> {
let reply = aux_transact(aux_mutex, linkno, &Packet::RoutingSetRank {
rank: rank
}, timer).await?;
if reply != Packet::RoutingAck {
return Err("unexpected reply");
}
Ok(())
}
async fn init_buffer_space(destination: u8, linkno: u8) {
let linkno = linkno as usize;
unsafe {
(csr::DRTIO[linkno].destination_write)(destination);
(csr::DRTIO[linkno].force_destination_write)(1);
(csr::DRTIO[linkno].o_get_buffer_space_write)(1);
while (csr::DRTIO[linkno].o_wait_read)() == 1 {}
info!("[DEST#{}] buffer space is {}",
destination, (csr::DRTIO[linkno].o_dbg_buffer_space_read)());
(csr::DRTIO[linkno].force_destination_write)(0);
}
}
async fn process_unsolicited_aux(aux_mutex: &Rc<Mutex<bool>>, linkno: u8) {
let _lock = aux_mutex.async_lock().await;
match drtioaux_async::recv(linkno).await {
Ok(Some(packet)) => warn!("[LINK#{}] unsolicited aux packet: {:?}", linkno, packet),
Ok(None) => (),
Err(_) => warn!("[LINK#{}] aux packet error", linkno)
}
}
async fn process_local_errors(linkno: u8) {
let errors;
let linkidx = linkno as usize;
unsafe {
errors = (csr::DRTIO[linkidx].protocol_error_read)();
(csr::DRTIO[linkidx].protocol_error_write)(errors);
}
if errors != 0 {
error!("[LINK#{}] error(s) found (0x{:02x}):", linkno, errors);
if errors & 1 != 0 {
error!("[LINK#{}] received packet of an unknown type", linkno);
}
if errors & 2 != 0 {
error!("[LINK#{}] received truncated packet", linkno);
}
if errors & 4 != 0 {
error!("[LINK#{}] timeout attempting to get remote buffer space", linkno);
}
}
}
async fn destination_set_up(routing_table: &drtio_routing::RoutingTable,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
destination: u8, up: bool) {
let mut up_destinations = up_destinations.borrow_mut();
up_destinations[destination as usize] = up;
if up {
drtio_routing::interconnect_enable(routing_table, 0, destination);
info!("[DEST#{}] destination is up", destination);
} else {
drtio_routing::interconnect_disable(destination);
info!("[DEST#{}] destination is down", destination);
}
}
async fn destination_up(up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, destination: u8) -> bool {
let up_destinations = up_destinations.borrow();
up_destinations[destination as usize]
}
async fn destination_survey(aux_mutex: &Rc<Mutex<bool>>, routing_table: &drtio_routing::RoutingTable,
up_links: &[bool],
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer) {
for destination in 0..drtio_routing::DEST_COUNT {
let hop = routing_table.0[destination][0];
let destination = destination as u8;
if hop == 0 {
/* local RTIO */
if !destination_up(up_destinations, destination).await {
destination_set_up(routing_table, up_destinations, destination, true).await;
}
} else if hop as usize <= csr::DRTIO.len() {
let linkno = hop - 1;
if destination_up(up_destinations, destination).await {
if up_links[linkno as usize] {
let reply = aux_transact(aux_mutex, linkno, &Packet::DestinationStatusRequest {
destination: destination
}, timer).await;
match reply {
Ok(Packet::DestinationDownReply) =>
destination_set_up(routing_table, up_destinations, destination, false).await,
Ok(Packet::DestinationOkReply) => (),
Ok(Packet::DestinationSequenceErrorReply { channel }) =>
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}", destination, channel),
Ok(Packet::DestinationCollisionReply { channel }) =>
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}", destination, channel),
Ok(Packet::DestinationBusyReply { channel }) =>
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}", destination, channel),
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
}
} else {
destination_set_up(routing_table, up_destinations, destination, false).await;
}
} else {
if up_links[linkno as usize] {
let reply = aux_transact(aux_mutex, linkno, &Packet::DestinationStatusRequest {
destination: destination
}, timer).await;
match reply {
Ok(Packet::DestinationDownReply) => (),
Ok(Packet::DestinationOkReply) => {
destination_set_up(routing_table, up_destinations, destination, true).await;
init_buffer_space(destination as u8, linkno).await;
},
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
}
}
}
}
}
}
pub async fn link_task(aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer) {
let mut up_links = [false; csr::DRTIO.len()];
loop {
for linkno in 0..csr::DRTIO.len() {
let linkno = linkno as u8;
if up_links[linkno as usize] {
/* link was previously up */
if link_rx_up(linkno).await {
process_unsolicited_aux(aux_mutex, linkno).await;
process_local_errors(linkno).await;
} else {
info!("[LINK#{}] link is down", linkno);
up_links[linkno as usize] = false;
}
} else {
/* link was previously down */
if link_rx_up(linkno).await {
info!("[LINK#{}] link RX became up, pinging", linkno);
let ping_count = ping_remote(aux_mutex, linkno, timer).await;
if ping_count > 0 {
info!("[LINK#{}] remote replied after {} packets", linkno, ping_count);
up_links[linkno as usize] = true;
if let Err(e) = sync_tsc(aux_mutex, linkno, timer).await {
error!("[LINK#{}] failed to sync TSC ({})", linkno, e);
}
if let Err(e) = load_routing_table(aux_mutex, linkno, routing_table, timer).await {
error!("[LINK#{}] failed to load routing table ({})", linkno, e);
}
if let Err(e) = set_rank(aux_mutex, linkno, 1 as u8, timer).await {
error!("[LINK#{}] failed to set rank ({})", linkno, e);
}
info!("[LINK#{}] link initialization completed", linkno);
} else {
error!("[LINK#{}] ping failed", linkno);
}
}
}
}
destination_survey(aux_mutex, routing_table, &up_links, up_destinations, timer).await;
let mut countdown = timer.countdown();
delay(&mut countdown, Milliseconds(200)).await;
}
}
#[allow(dead_code)]
pub fn reset(aux_mutex: Rc<Mutex<bool>>, mut timer: GlobalTimer) {
for linkno in 0..csr::DRTIO.len() {
unsafe {
(csr::DRTIO[linkno].reset_write)(1);
}
}
timer.delay_ms(1);
for linkno in 0..csr::DRTIO.len() {
unsafe {
(csr::DRTIO[linkno].reset_write)(0);
}
}
for linkno in 0..csr::DRTIO.len() {
let linkno = linkno as u8;
if task::block_on(link_rx_up(linkno)) {
let reply = task::block_on(aux_transact(&aux_mutex, linkno,
&Packet::ResetRequest, timer));
match reply {
Ok(Packet::ResetAck) => (),
Ok(_) => error!("[LINK#{}] reset failed, received unexpected aux packet", linkno),
Err(e) => error!("[LINK#{}] reset failed, aux packet error ({})", linkno, e)
}
}
}
}
}
#[cfg(not(has_drtio))]
pub mod drtio {
use super::*;
pub fn startup(_aux_mutex: &Rc<Mutex<bool>>, _routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
_up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, _timer: GlobalTimer) {}
#[allow(dead_code)]
pub fn reset(_aux_mutex: Rc<Mutex<bool>>, mut _timer: GlobalTimer) {}
}
pub fn startup(aux_mutex: &Rc<Mutex<bool>>,
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer) {
drtio::startup(aux_mutex, routing_table, up_destinations, timer);
unsafe {
csr::rtio_core::reset_phy_write(1);
}
}
#[allow(dead_code)]
pub fn reset(aux_mutex: Rc<Mutex<bool>>, timer: GlobalTimer) {
unsafe {
csr::rtio_core::reset_write(1);
}
drtio::reset(aux_mutex, timer)
}

View File

@ -1,28 +0,0 @@
[package]
authors = ["M-Labs"]
name = "satman"
version = "0.0.0"
build = "build.rs"
[features]
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
default = ["target_zc706", ]
[build-dependencies]
build_zynq = { path = "../libbuild_zynq" }
[dependencies]
log = { version = "0.4", default-features = false }
embedded-hal = "0.2"
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["ipv6"]}
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn", "ipv6"] }
libboard_artiq = { path = "../libboard_artiq" }
unwind = { path = "../libunwind" }
libc = { path = "../libc" }

View File

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

View File

@ -1,659 +0,0 @@
#![no_std]
#![no_main]
#![feature(never_type, panic_info_message, asm, naked_functions)]
#![feature(alloc_error_handler)]
#[macro_use]
extern crate log;
extern crate embedded_hal;
extern crate libboard_zynq;
extern crate libboard_artiq;
extern crate libsupport_zynq;
extern crate libcortex_a9;
extern crate libregister;
extern crate unwind;
extern crate alloc;
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds, print, println, mpcore, gic, stdio};
use libsupport_zynq::ram;
#[cfg(has_si5324)]
use libboard_artiq::si5324;
use libboard_artiq::{pl::csr, drtio_routing, drtioaux, logger, identifier_read, init_gateware};
use libcortex_a9::{spin_lock_yield, interrupt_handler, regs::{MPIDR, SP}, notify_spin_lock, asm, l2c::enable_l2_cache};
use libregister::{RegisterW, RegisterR};
use embedded_hal::blocking::delay::DelayUs;
use core::sync::atomic::{AtomicBool, Ordering};
mod repeater;
fn drtiosat_reset(reset: bool) {
unsafe {
csr::drtiosat::reset_write(if reset { 1 } else { 0 });
}
}
fn drtiosat_reset_phy(reset: bool) {
unsafe {
csr::drtiosat::reset_phy_write(if reset { 1 } else { 0 });
}
}
fn drtiosat_link_rx_up() -> bool {
unsafe {
csr::drtiosat::rx_up_read() == 1
}
}
fn drtiosat_tsc_loaded() -> bool {
unsafe {
let tsc_loaded = csr::drtiosat::tsc_loaded_read() == 1;
if tsc_loaded {
csr::drtiosat::tsc_loaded_write(1);
}
tsc_loaded
}
}
#[cfg(has_drtio_routing)]
macro_rules! forward {
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr, $timer:expr) => {{
let hop = $routing_table.0[$destination as usize][$rank as usize];
if hop != 0 {
let repno = (hop - 1) as usize;
if repno < $repeaters.len() {
return $repeaters[repno].aux_forward($packet, $timer);
} else {
return Err(drtioaux::Error::RoutingError);
}
}
}}
}
#[cfg(not(has_drtio_routing))]
macro_rules! forward {
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr, $timer:expr) => {}
}
fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
_routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
packet: drtioaux::Packet, timer: &mut GlobalTimer, i2c: &mut I2c) -> Result<(), drtioaux::Error> {
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
// and u16 otherwise; hence the `as _` conversion.
match packet {
drtioaux::Packet::EchoRequest =>
drtioaux::send(0, &drtioaux::Packet::EchoReply),
drtioaux::Packet::ResetRequest => {
info!("resetting RTIO");
drtiosat_reset(true);
timer.delay_us(100);
drtiosat_reset(false);
for rep in _repeaters.iter() {
if let Err(e) = rep.rtio_reset(timer) {
error!("failed to issue RTIO reset ({:?})", e);
}
}
drtioaux::send(0, &drtioaux::Packet::ResetAck)
},
drtioaux::Packet::DestinationStatusRequest { destination: _destination } => {
#[cfg(has_drtio_routing)]
let hop = _routing_table.0[_destination as usize][*_rank as usize];
#[cfg(not(has_drtio_routing))]
let hop = 0;
if hop == 0 {
let errors;
unsafe {
errors = csr::drtiosat::rtio_error_read();
}
if errors & 1 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::sequence_error_channel_read();
csr::drtiosat::rtio_error_write(1);
}
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)?;
}
}
#[cfg(has_drtio_routing)]
{
if hop != 0 {
let hop = hop as usize;
if hop <= csr::DRTIOREP.len() {
let repno = hop - 1;
match _repeaters[repno].aux_forward(&drtioaux::Packet::DestinationStatusRequest {
destination: _destination
}, timer) {
Ok(()) => (),
Err(drtioaux::Error::LinkDown) => drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?,
Err(e) => {
drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?;
error!("aux error when handling destination status request: {:?}", e);
},
}
} else {
drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?;
}
}
}
Ok(())
}
#[cfg(has_drtio_routing)]
drtioaux::Packet::RoutingSetPath { destination, hops } => {
_routing_table.0[destination as usize] = hops;
for rep in _repeaters.iter() {
if let Err(e) = rep.set_path(destination, &hops, timer) {
error!("failed to set path ({:?})", e);
}
}
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
}
#[cfg(has_drtio_routing)]
drtioaux::Packet::RoutingSetRank { rank } => {
*_rank = rank;
drtio_routing::interconnect_enable_all(_routing_table, rank);
let rep_rank = rank + 1;
for rep in _repeaters.iter() {
if let Err(e) = rep.set_rank(rep_rank, timer) {
error!("failed to set rank ({:?})", e);
}
}
info!("rank: {}", rank);
info!("routing table: {}", _routing_table);
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
}
#[cfg(not(has_drtio_routing))]
drtioaux::Packet::RoutingSetPath { destination: _, hops: _ } => {
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
}
#[cfg(not(has_drtio_routing))]
drtioaux::Packet::RoutingSetRank { rank: _ } => {
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
}
drtioaux::Packet::MonitorRequest { destination: _destination, channel: _channel, probe: _probe } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let value;
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::mon_chan_sel_write(channel as _);
csr::rtio_moninj::mon_probe_sel_write(probe);
csr::rtio_moninj::mon_value_update_write(1);
value = csr::rtio_moninj::mon_value_read() as u64;
}
#[cfg(not(has_rtio_moninj))]
{
value = 0;
}
let reply = drtioaux::Packet::MonitorReply { value: value };
drtioaux::send(0, &reply)
},
drtioaux::Packet::InjectionRequest { destination: _destination, channel: _channel,
overrd: _overrd, value: _value } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd);
csr::rtio_moninj::inj_value_write(value);
}
Ok(())
},
drtioaux::Packet::InjectionStatusRequest { destination: _destination,
channel: _channel, overrd: _overrd } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let value;
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd);
value = csr::rtio_moninj::inj_value_read();
}
#[cfg(not(has_rtio_moninj))]
{
value = 0;
}
drtioaux::send(0, &drtioaux::Packet::InjectionStatusReply { value: value })
},
drtioaux::Packet::I2cStartRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.start().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cRestartRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.restart().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cStopRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.stop().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cWriteRequest { destination: _destination, busno: _busno, data } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
match i2c.write(data) {
Ok(ack) => drtioaux::send(0,
&drtioaux::Packet::I2cWriteReply { succeeded: true, ack: ack }),
Err(_) => drtioaux::send(0,
&drtioaux::Packet::I2cWriteReply { succeeded: false, ack: false })
}
}
drtioaux::Packet::I2cReadRequest { destination: _destination, busno: _busno, ack } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
match i2c.read(ack) {
Ok(data) => drtioaux::send(0,
&drtioaux::Packet::I2cReadReply { succeeded: true, data: data }),
Err(_) => drtioaux::send(0,
&drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff })
}
}
drtioaux::Packet::I2cSwitchSelectRequest { destination: _destination, busno: _busno, address, mask } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let ch = match mask { //decode from mainline, PCA9548-centric API
0x00 => None,
0x01 => Some(0),
0x02 => Some(1),
0x04 => Some(2),
0x08 => Some(3),
0x10 => Some(4),
0x20 => Some(5),
0x40 => Some(6),
0x80 => Some(7),
_ => { return drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: false }); }
};
let succeeded = i2c.pca954x_select(address, ch).is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::SpiSetConfigRequest { destination: _destination, busno: _busno,
flags: _flags, length: _length, div: _div, cs: _cs } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
// todo: reimplement when/if SPI is available
//let succeeded = spi::set_config(busno, flags, length, div, cs).is_ok();
drtioaux::send(0,
&drtioaux::Packet::SpiBasicReply { succeeded: false })
},
drtioaux::Packet::SpiWriteRequest { destination: _destination, busno: _busno, data: _data } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
// todo: reimplement when/if SPI is available
//let succeeded = spi::write(busno, data).is_ok();
drtioaux::send(0,
&drtioaux::Packet::SpiBasicReply { succeeded: false })
}
drtioaux::Packet::SpiReadRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
// todo: reimplement when/if SPI is available
// match spi::read(busno) {
// Ok(data) => drtioaux::send(0,
// &drtioaux::Packet::SpiReadReply { succeeded: true, data: data }),
// Err(_) => drtioaux::send(0,
// &drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
// }
drtioaux::send(0,
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
}
_ => {
warn!("received unexpected aux packet");
Ok(())
}
}
}
fn process_aux_packets(repeaters: &mut [repeater::Repeater],
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8,
timer: &mut GlobalTimer, i2c: &mut I2c) {
let result =
drtioaux::recv(0).and_then(|packet| {
if let Some(packet) = packet {
process_aux_packet(repeaters, routing_table, rank, packet, timer, i2c)
} else {
Ok(())
}
});
match result {
Ok(()) => (),
Err(e) => warn!("aux packet error ({:?})", e)
}
}
fn drtiosat_process_errors() {
let errors;
unsafe {
errors = csr::drtiosat::protocol_error_read();
}
if errors & 1 != 0 {
error!("received packet of an unknown type");
}
if errors & 2 != 0 {
error!("received truncated packet");
}
if errors & 4 != 0 {
let destination;
unsafe {
destination = csr::drtiosat::buffer_space_timeout_dest_read();
}
error!("timeout attempting to get buffer space from CRI, destination=0x{:02x}", destination)
}
if errors & 8 != 0 {
let channel;
let timestamp_event;
let timestamp_counter;
unsafe {
channel = csr::drtiosat::underflow_channel_read();
timestamp_event = csr::drtiosat::underflow_timestamp_event_read() as i64;
timestamp_counter = csr::drtiosat::underflow_timestamp_counter_read() as i64;
}
error!("write underflow, channel={}, timestamp={}, counter={}, slack={}",
channel, timestamp_event, timestamp_counter, timestamp_event-timestamp_counter);
}
if errors & 16 != 0 {
error!("write overflow");
}
unsafe {
csr::drtiosat::protocol_error_write(errors);
}
}
#[cfg(has_rtio_crg)]
fn init_rtio_crg(timer: &mut GlobalTimer) {
unsafe {
csr::rtio_crg::pll_reset_write(0);
}
timer.delay_us(150);
let locked = unsafe { csr::rtio_crg::pll_locked_read() != 0 };
if !locked {
error!("RTIO clock failed");
}
else {
info!("RTIO PLL locked");
}
}
#[cfg(not(has_rtio_crg))]
fn init_rtio_crg(_timer: &mut GlobalTimer) { }
fn hardware_tick(ts: &mut u64, timer: &mut GlobalTimer) {
let now = timer.get_time();
let mut ts_ms = Milliseconds(*ts);
if now > ts_ms {
ts_ms = now + Milliseconds(200);
*ts = ts_ms.0;
}
}
#[cfg(all(has_si5324, rtio_frequency = "125.0"))]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
n1_hs : 5,
nc1_ls : 8,
n2_hs : 7,
n2_ls : 360,
n31 : 63,
n32 : 63,
bwsel : 4,
crystal_ref: true
};
#[cfg(all(has_si5324, rtio_frequency = "100.0"))]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
n1_hs : 5,
nc1_ls : 10,
n2_hs : 10,
n2_ls : 250,
n31 : 50,
n32 : 50,
bwsel : 4,
crystal_ref: true
};
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
#[no_mangle]
pub extern fn main_core0() -> i32 {
enable_l2_cache(0x8);
let mut timer = GlobalTimer::start();
let buffer_logger = unsafe {
logger::BufferLogger::new(&mut LOG_BUFFER[..])
};
buffer_logger.set_uart_log_level(log::LevelFilter::Info);
buffer_logger.register();
log::set_max_level(log::LevelFilter::Info);
init_gateware();
info!("ARTIQ satellite manager starting...");
info!("gateware ident {}", identifier_read(&mut [0; 64]));
ram::init_alloc_core0();
let mut i2c = I2c::i2c0();
i2c.init().expect("I2C initialization failed");
#[cfg(has_si5324)]
si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324");
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
timer.delay_us(1500); // wait for CPLL/QPLL lock
unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
}
init_rtio_crg(&mut timer);
#[cfg(has_drtio_routing)]
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
#[cfg(not(has_drtio_routing))]
let mut repeaters = [repeater::Repeater::default(); 0];
for i in 0..repeaters.len() {
repeaters[i] = repeater::Repeater::new(i as u8);
}
let mut routing_table = drtio_routing::RoutingTable::default_empty();
let mut rank = 1;
let mut hardware_tick_ts = 0;
loop {
while !drtiosat_link_rx_up() {
drtiosat_process_errors();
#[allow(unused_mut)]
for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer);
}
hardware_tick(&mut hardware_tick_ts, &mut timer);
}
info!("uplink is up, switching to recovered clock");
#[cfg(has_siphaser)]
{
si5324::siphaser::select_recovered_clock(&mut i2c, true, &mut timer).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew(&mut timer).expect("failed to calibrate skew");
}
drtioaux::reset(0);
drtiosat_reset(false);
drtiosat_reset_phy(false);
while drtiosat_link_rx_up() {
drtiosat_process_errors();
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank, &mut timer, &mut i2c);
#[allow(unused_mut)]
for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer);
}
hardware_tick(&mut hardware_tick_ts, &mut timer);
if drtiosat_tsc_loaded() {
info!("TSC loaded from uplink");
for rep in repeaters.iter() {
if let Err(e) = rep.sync_tsc(&mut timer) {
error!("failed to sync TSC ({:?})", e);
}
}
if let Err(e) = drtioaux::send(0, &drtioaux::Packet::TSCAck) {
error!("aux packet error: {:?}", e);
}
}
}
drtiosat_reset_phy(true);
drtiosat_reset(true);
drtiosat_tsc_loaded();
info!("uplink is down, switching to local oscillator clock");
#[cfg(has_siphaser)]
si5324::siphaser::select_recovered_clock(&mut i2c, false, &mut timer).expect("failed to switch clocks");
}
}
extern "C" {
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 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]
pub extern fn exception(_vect: u32, _regs: *const u32, pc: u32, ea: u32) {
fn hexdump(addr: u32) {
let addr = (addr - addr % 4) as *const u32;
let mut ptr = addr;
println!("@ {:08p}", ptr);
for _ in 0..4 {
print!("+{:04x}: ", ptr as usize - addr as usize);
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
print!("{:08x}\n", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
}
}
hexdump(pc);
hexdump(ea);
panic!("exception at PC 0x{:x}, EA 0x{:x}", pc, ea)
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_handler]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
let id = MPIDR.read().cpu_id() as usize;
print!("Core {} ", id);
unsafe {
if PANICKED[id] {
println!("nested panic!");
loop {}
}
PANICKED[id] = true;
}
print!("panic at ");
if let Some(location) = info.location() {
print!("{}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("unknown location");
}
if let Some(message) = info.message() {
println!(": {}", message);
} else {
println!("");
}
loop {}
}
// linker symbols
extern "C" {
static __text_start: u32;
static __text_end: u32;
static __exidx_start: u32;
static __exidx_end: u32;
}
#[no_mangle]
extern 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,290 +0,0 @@
use libboard_artiq::{drtioaux, drtio_routing};
use libboard_zynq::timer::GlobalTimer;
#[cfg(has_drtio_routing)]
use libboard_artiq::{pl::csr};
#[cfg(has_drtio_routing)]
use libboard_zynq::time::Milliseconds;
#[cfg(has_drtio_routing)]
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
#[cfg(has_drtio_routing)]
fn rep_link_rx_up(repno: u8) -> bool {
let repno = repno as usize;
unsafe {
(csr::DRTIOREP[repno].rx_up_read)() == 1
}
}
#[cfg(has_drtio_routing)]
#[derive(Clone, Copy, PartialEq)]
enum RepeaterState {
Down,
SendPing { ping_count: u16 },
WaitPingReply { ping_count: u16, timeout: Milliseconds },
Up,
Failed
}
#[cfg(has_drtio_routing)]
impl Default for RepeaterState {
fn default() -> RepeaterState { RepeaterState::Down }
}
#[cfg(has_drtio_routing)]
#[derive(Clone, Copy, Default)]
pub struct Repeater {
repno: u8,
auxno: u8,
state: RepeaterState
}
#[cfg(has_drtio_routing)]
impl Repeater {
pub fn new(repno: u8) -> Repeater {
Repeater {
repno: repno,
auxno: repno + 1,
state: RepeaterState::Down
}
}
#[allow(dead_code)]
pub fn is_up(&self) -> bool {
self.state == RepeaterState::Up
}
pub fn service(&mut self, routing_table: &drtio_routing::RoutingTable, rank: u8,
timer: &mut GlobalTimer) {
self.process_local_errors();
match self.state {
RepeaterState::Down => {
if rep_link_rx_up(self.repno) {
info!("[REP#{}] link RX became up, pinging", self.repno);
self.state = RepeaterState::SendPing { ping_count: 0 };
}
}
RepeaterState::SendPing { ping_count } => {
if rep_link_rx_up(self.repno) {
drtioaux::send(self.auxno, &drtioaux::Packet::EchoRequest).unwrap();
self.state = RepeaterState::WaitPingReply {
ping_count: ping_count + 1,
timeout: timer.get_time() + Milliseconds(100)
}
} else {
error!("[REP#{}] link RX went down during ping", self.repno);
self.state = RepeaterState::Down;
}
}
RepeaterState::WaitPingReply { ping_count, timeout } => {
if rep_link_rx_up(self.repno) {
if let Ok(Some(drtioaux::Packet::EchoReply)) = drtioaux::recv(self.auxno) {
info!("[REP#{}] remote replied after {} packets", self.repno, ping_count);
self.state = RepeaterState::Up;
if let Err(e) = self.sync_tsc(timer) {
error!("[REP#{}] failed to sync TSC ({:?})", self.repno, e);
self.state = RepeaterState::Failed;
return;
}
if let Err(e) = self.load_routing_table(routing_table, timer) {
error!("[REP#{}] failed to load routing table ({:?})", self.repno, e);
self.state = RepeaterState::Failed;
return;
}
if let Err(e) = self.set_rank(rank + 1, timer) {
error!("[REP#{}] failed to set rank ({:?})", self.repno, e);
self.state = RepeaterState::Failed;
return;
}
} else {
if timer.get_time() > timeout {
if ping_count > 200 {
error!("[REP#{}] ping failed", self.repno);
self.state = RepeaterState::Failed;
} else {
self.state = RepeaterState::SendPing { ping_count: ping_count };
}
}
}
} else {
error!("[REP#{}] link RX went down during ping", self.repno);
self.state = RepeaterState::Down;
}
}
RepeaterState::Up => {
self.process_unsolicited_aux();
if !rep_link_rx_up(self.repno) {
info!("[REP#{}] link is down", self.repno);
self.state = RepeaterState::Down;
}
}
RepeaterState::Failed => {
if !rep_link_rx_up(self.repno) {
info!("[REP#{}] link is down", self.repno);
self.state = RepeaterState::Down;
}
}
}
}
fn process_unsolicited_aux(&self) {
match drtioaux::recv(self.auxno) {
Ok(Some(packet)) => warn!("[REP#{}] unsolicited aux packet: {:?}", self.repno, packet),
Ok(None) => (),
Err(_) => warn!("[REP#{}] aux packet error", self.repno)
}
}
fn process_local_errors(&self) {
let repno = self.repno as usize;
let errors;
unsafe {
errors = (csr::DRTIOREP[repno].protocol_error_read)();
}
if errors & 1 != 0 {
error!("[REP#{}] received packet of an unknown type", repno);
}
if errors & 2 != 0 {
error!("[REP#{}] received truncated packet", repno);
}
if errors & 4 != 0 {
let cmd;
let chan_sel;
unsafe {
cmd = (csr::DRTIOREP[repno].command_missed_cmd_read)();
chan_sel = (csr::DRTIOREP[repno].command_missed_chan_sel_read)();
}
error!("[REP#{}] CRI command missed, cmd={}, chan_sel=0x{:06x}", repno, cmd, chan_sel)
}
if errors & 8 != 0 {
let destination;
unsafe {
destination = (csr::DRTIOREP[repno].buffer_space_timeout_dest_read)();
}
error!("[REP#{}] timeout attempting to get remote buffer space, destination=0x{:02x}", repno, destination);
}
unsafe {
(csr::DRTIOREP[repno].protocol_error_write)(errors);
}
}
fn recv_aux_timeout(&self, timeout: u32, timer: &mut GlobalTimer) -> Result<drtioaux::Packet, drtioaux::Error> {
let max_time = timer.get_time() + Milliseconds(timeout.into());
loop {
if !rep_link_rx_up(self.repno) {
return Err(drtioaux::Error::LinkDown);
}
if timer.get_time() > max_time {
return Err(drtioaux::Error::TimedOut);
}
match drtioaux::recv(self.auxno) {
Ok(Some(packet)) => return Ok(packet),
Ok(None) => (),
Err(e) => return Err(e)
}
}
}
pub fn aux_forward(&self, request: &drtioaux::Packet, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
if self.state != RepeaterState::Up {
return Err(drtioaux::Error::LinkDown);
}
drtioaux::send(self.auxno, request).unwrap();
let reply = self.recv_aux_timeout(200, timer)?;
drtioaux::send(0, &reply).unwrap();
Ok(())
}
pub fn sync_tsc(&self, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
if self.state != RepeaterState::Up {
return Ok(());
}
let repno = self.repno as usize;
unsafe {
(csr::DRTIOREP[repno].set_time_write)(1);
while (csr::DRTIOREP[repno].set_time_read)() == 1 {}
}
// TSCAck is the only aux packet that is sent spontaneously
// by the satellite, in response to a TSC set on the RT link.
let reply = self.recv_aux_timeout(10000, timer)?;
if reply == drtioaux::Packet::TSCAck {
return Ok(());
} else {
return Err(drtioaux::Error::UnexpectedReply);
}
}
pub fn set_path(&self, destination: u8, hops: &[u8; drtio_routing::MAX_HOPS], timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
if self.state != RepeaterState::Up {
return Ok(());
}
drtioaux::send(self.auxno, &drtioaux::Packet::RoutingSetPath {
destination: destination,
hops: *hops
}).unwrap();
let reply = self.recv_aux_timeout(200, timer)?;
if reply != drtioaux::Packet::RoutingAck {
return Err(drtioaux::Error::UnexpectedReply);
}
Ok(())
}
pub fn load_routing_table(&self, routing_table: &drtio_routing::RoutingTable, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
for i in 0..drtio_routing::DEST_COUNT {
self.set_path(i as u8, &routing_table.0[i], timer)?;
}
Ok(())
}
pub fn set_rank(&self, rank: u8, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
if self.state != RepeaterState::Up {
return Ok(());
}
drtioaux::send(self.auxno, &drtioaux::Packet::RoutingSetRank {
rank: rank
}).unwrap();
let reply = self.recv_aux_timeout(200, timer)?;
if reply != drtioaux::Packet::RoutingAck {
return Err(drtioaux::Error::UnexpectedReply);
}
Ok(())
}
pub fn rtio_reset(&self, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
let repno = self.repno as usize;
unsafe { (csr::DRTIOREP[repno].reset_write)(1); }
timer.delay_us(100);
unsafe { (csr::DRTIOREP[repno].reset_write)(0); }
if self.state != RepeaterState::Up {
return Ok(());
}
drtioaux::send(self.auxno, &drtioaux::Packet::ResetRequest).unwrap();
let reply = self.recv_aux_timeout(200, timer)?;
if reply != drtioaux::Packet::ResetAck {
return Err(drtioaux::Error::UnexpectedReply);
}
Ok(())
}
}
#[cfg(not(has_drtio_routing))]
#[derive(Clone, Copy, Default)]
pub struct Repeater {
}
#[cfg(not(has_drtio_routing))]
impl Repeater {
pub fn new(_repno: u8) -> Repeater { Repeater::default() }
pub fn service(&self, _routing_table: &drtio_routing::RoutingTable, _rank: u8, _timer: &mut GlobalTimer) { }
pub fn sync_tsc(&self, _timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> { Ok(()) }
pub fn rtio_reset(&self, _timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> { Ok(()) }
}

8
zynq-rs.nix Normal file
View File

@ -0,0 +1,8 @@
let
pkgs = import <nixpkgs> {};
in
pkgs.fetchgit {
url = "https://git.m-labs.hk/M-Labs/zynq-rs.git";
rev = "2c161720fa12f8b7abecaf60f77b062b08ac9bc1";
sha256 = "0zpazkicqzps86r7lgqf09y9ary94mjvxw6gc41z9kjjyxar5fhr";
}