forked from M-Labs/artiq-zynq
Compare commits
No commits in common. "si5324-unify-riscv" and "master" have entirely different histories.
si5324-uni
...
master
31
README.md
31
README.md
|
@ -4,7 +4,7 @@ ARTIQ on Zynq
|
||||||
How to use
|
How to use
|
||||||
----------
|
----------
|
||||||
|
|
||||||
1. Install ARTIQ-7 or newer.
|
1. Install ARTIQ-6 or newer.
|
||||||
2. Select the latest successful build on Hydra: https://nixbld.m-labs.hk/jobset/artiq/zynq
|
2. Select the latest successful build on Hydra: https://nixbld.m-labs.hk/jobset/artiq/zynq
|
||||||
3. Search for the job named ``<board>-<variant>-sd`` (for example: ``zc706-nist_clock-sd`` or ``zc706-nist_qc2-sd``).
|
3. Search for the job named ``<board>-<variant>-sd`` (for example: ``zc706-nist_clock-sd`` or ``zc706-nist_qc2-sd``).
|
||||||
4. Download the ``boot.bin`` "binary distribution" and place it at the root of a FAT-formatted SD card.
|
4. Download the ``boot.bin`` "binary distribution" and place it at the root of a FAT-formatted SD card.
|
||||||
|
@ -24,47 +24,48 @@ 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.
|
|
||||||
|
|
||||||
Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is
|
|
||||||
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.8+ 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 known to work with Nixpkgs 20.03 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.
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Copyright (C) 2019-2022 M-Labs Limited.
|
Copyright (C) 2019-2020 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
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,99 @@
|
||||||
|
{
|
||||||
|
mozillaOverlay ? import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz),
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
|
||||||
|
rustPlatform = (import ./rustPlatform.nix { inherit pkgs; });
|
||||||
|
artiqpkgs = import <artiq-fast/default.nix> { inherit pkgs; };
|
||||||
|
vivado = import <artiq-fast/vivado.nix> { inherit pkgs; };
|
||||||
|
mkbootimage = (import ./mkbootimage.nix { inherit pkgs; });
|
||||||
|
build-zc706 = { variant }: let
|
||||||
|
firmware = rustPlatform.buildRustPackage rec {
|
||||||
|
name = "zc706-${variant}-firmware";
|
||||||
|
version = "0.1.0";
|
||||||
|
|
||||||
|
src = ./src;
|
||||||
|
cargoSha256 = "0w1d9z6k1ag5ylaz961xr3q957nj1ask07rmkmarb1kw933hr3lp";
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.gnumake
|
||||||
|
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
|
||||||
|
pkgs.cargo-xbuild
|
||||||
|
pkgs.llvmPackages_9.llvm
|
||||||
|
pkgs.llvmPackages_9.clang-unwrapped
|
||||||
|
];
|
||||||
|
buildPhase = ''
|
||||||
|
export XARGO_RUST_SRC="${rustPlatform.rust.rustc.src}/src"
|
||||||
|
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
|
||||||
|
make VARIANT=${variant}
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out $out/nix-support
|
||||||
|
cp ../build/firmware/armv7-none-eabihf/release/runtime $out/runtime.elf
|
||||||
|
cp ../build/firmware/armv7-none-eabihf/release/szl $out/szl.elf
|
||||||
|
echo file binary-dist $out/runtime.elf >> $out/nix-support/hydra-build-products
|
||||||
|
echo file binary-dist $out/szl.elf >> $out/nix-support/hydra-build-products
|
||||||
|
'';
|
||||||
|
|
||||||
|
doCheck = false;
|
||||||
|
dontFixup = true;
|
||||||
|
};
|
||||||
|
gateware = pkgs.runCommand "zc706-${variant}-gateware"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [
|
||||||
|
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
|
||||||
|
vivado
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
python ${./src/gateware}/zc706.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
|
||||||
|
'';
|
||||||
|
|
||||||
|
jtag = pkgs.runCommand "zc706-${variant}-jtag" {}
|
||||||
|
''
|
||||||
|
mkdir $out
|
||||||
|
ln -s ${firmware}/szl.elf $out
|
||||||
|
ln -s ${gateware}/top.bit $out
|
||||||
|
'';
|
||||||
|
sd = pkgs.runCommand "zc706-${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 ${firmware}/szl.elf szl.elf
|
||||||
|
ln -s ${gateware}/top.bit top.bit
|
||||||
|
cat > boot.bif << EOF
|
||||||
|
the_ROM_image:
|
||||||
|
{
|
||||||
|
[bootloader]szl.elf
|
||||||
|
top.bit
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
"zc706-${variant}-firmware" = firmware;
|
||||||
|
"zc706-${variant}-gateware" = gateware;
|
||||||
|
"zc706-${variant}-jtag" = jtag;
|
||||||
|
"zc706-${variant}-sd" = sd;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
(
|
||||||
|
(build-zc706 { variant = "simple"; }) //
|
||||||
|
(build-zc706 { variant = "nist_clock"; }) //
|
||||||
|
(build-zc706 { variant = "nist_qc2"; }) //
|
||||||
|
(build-zc706 { variant = "acpki_simple"; }) //
|
||||||
|
(build-zc706 { variant = "acpki_nist_clock"; }) //
|
||||||
|
(build-zc706 { variant = "acpki_nist_qc2"; })
|
||||||
|
)
|
60
demo.json
60
demo.json
|
@ -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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ device_db = {
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"host": "192.168.1.52",
|
"host": "192.168.1.52",
|
||||||
"ref_period": 1e-9,
|
"ref_period": 1e-9,
|
||||||
"ref_multiplier": 8,
|
"ref_multiplier": 1,
|
||||||
"target": "cortexa9"
|
"target": "cortexa9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -22,18 +22,30 @@ device_db = {
|
||||||
"module": "artiq.coredevice.dma",
|
"module": "artiq.coredevice.dma",
|
||||||
"class": "CoreDMA"
|
"class": "CoreDMA"
|
||||||
},
|
},
|
||||||
|
# led? are common to all variants
|
||||||
"i2c_switch": {
|
|
||||||
"type": "local",
|
|
||||||
"module": "artiq.coredevice.i2c",
|
|
||||||
"class": "PCA9548"
|
|
||||||
},
|
|
||||||
|
|
||||||
"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,22 +55,9 @@ 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"] = {
|
|
||||||
"type": "local",
|
|
||||||
"module": "artiq.coredevice.ad9914",
|
|
||||||
"class": "AD9914",
|
|
||||||
"arguments": {"sysclk": 3e9, "bus_channel": 50, "channel": 0},
|
|
||||||
}
|
|
||||||
device_db["ad9914dds1"] = {
|
|
||||||
"type": "local",
|
|
||||||
"module": "artiq.coredevice.ad9914",
|
|
||||||
"class": "AD9914",
|
|
||||||
"arguments": {"sysclk": 3e9, "bus_channel": 50, "channel": 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
# for ARTIQ test suite
|
# for ARTIQ test suite
|
||||||
device_db.update(
|
device_db.update(
|
||||||
loop_out="ttl0",
|
loop_out="ttl0",
|
||||||
|
|
235
flake.lock
235
flake.lock
|
@ -1,235 +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": 1669817967,
|
|
||||||
"narHash": "sha256-0TlH+RE3N8JqhrZpMsHPO68/IoF4t64V1cky2GFztCw=",
|
|
||||||
"ref": "master",
|
|
||||||
"rev": "3735b7ea9d428fc124dff0b5000c362790227a50",
|
|
||||||
"revCount": 8227,
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/m-labs/artiq.git"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/m-labs/artiq.git"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"artiq-comtools": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": [
|
|
||||||
"artiq",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"sipyco": [
|
|
||||||
"artiq",
|
|
||||||
"sipyco"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1664405593,
|
|
||||||
"narHash": "sha256-yP441NerlLGig7n+9xHsx8yCtZ+Ggd0VqfBSzc20E04=",
|
|
||||||
"owner": "m-labs",
|
|
||||||
"repo": "artiq-comtools",
|
|
||||||
"rev": "15ddac62813ef623a076ccf982b3bc63d314e651",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "m-labs",
|
|
||||||
"repo": "artiq-comtools",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1659877975,
|
|
||||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mozilla-overlay": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1664789696,
|
|
||||||
"narHash": "sha256-UGWJHQShiwLCr4/DysMVFrYdYYHcOqAOVsWNUu+l6YU=",
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"rev": "80627b282705101e7b38e19ca6e8df105031b072",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mozilla-overlay_2": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1664789696,
|
|
||||||
"narHash": "sha256-UGWJHQShiwLCr4/DysMVFrYdYYHcOqAOVsWNUu+l6YU=",
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"rev": "80627b282705101e7b38e19ca6e8df105031b072",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mozilla-overlay_3": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1664789696,
|
|
||||||
"narHash": "sha256-UGWJHQShiwLCr4/DysMVFrYdYYHcOqAOVsWNUu+l6YU=",
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"rev": "80627b282705101e7b38e19ca6e8df105031b072",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "mozilla",
|
|
||||||
"repo": "nixpkgs-mozilla",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1669735802,
|
|
||||||
"narHash": "sha256-qtG/o/i5ZWZLmXw108N2aPiVsxOcidpHJYNkT45ry9Q=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "731cc710aeebecbf45a258e977e8b68350549522",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-22.11",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"artiq": "artiq",
|
|
||||||
"mozilla-overlay": "mozilla-overlay_2",
|
|
||||||
"zynq-rs": "zynq-rs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sipyco": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"artiq",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1669369686,
|
|
||||||
"narHash": "sha256-YHez+S3PTUgtuliUNB5WM+RXcj8RKLbHVRvOgELSkwU=",
|
|
||||||
"owner": "m-labs",
|
|
||||||
"repo": "sipyco",
|
|
||||||
"rev": "98db6eacb084c2c5280fb653bee3d313e3ca6df8",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "m-labs",
|
|
||||||
"repo": "sipyco",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src-migen": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1662111470,
|
|
||||||
"narHash": "sha256-IPyhoFZLhY8d3jHB8jyvGdbey7V+X5eCzBZYSrJ18ec=",
|
|
||||||
"owner": "m-labs",
|
|
||||||
"repo": "migen",
|
|
||||||
"rev": "639e66f4f453438e83d86dc13491b9403bbd8ec6",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "m-labs",
|
|
||||||
"repo": "migen",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src-misoc": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1669779825,
|
|
||||||
"narHash": "sha256-l3lyy6dmbivo9Tppb08KHSyU89ZZG1CCcSjPlNRD210=",
|
|
||||||
"ref": "master",
|
|
||||||
"rev": "2c255775f732a41ba1a512ab3d2547af4e25f674",
|
|
||||||
"revCount": 2435,
|
|
||||||
"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": 1669819016,
|
|
||||||
"narHash": "sha256-WvNMUekL4Elc55RdqX8XP43QPnBrK8Rbd0bsoI61E5U=",
|
|
||||||
"ref": "master",
|
|
||||||
"rev": "67dbb5932fa8ff5f143983476f741f945871d286",
|
|
||||||
"revCount": 624,
|
|
||||||
"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
|
|
||||||
}
|
|
385
flake.nix
385
flake.nix
|
@ -1,385 +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-WvNMUekL4Elc55RdqX8XP43QPnBrK8Rbd0bsoI61E5U=";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
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 =
|
|
||||||
{
|
|
||||||
inherit fastnumbers artiq-netboot ramda migen-axi binutils-arm;
|
|
||||||
} //
|
|
||||||
(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;
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
41
local_run.sh
41
local_run.sh
|
@ -2,21 +2,10 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -z "$OPENOCD_ZYNQ" ]; then
|
|
||||||
echo "OPENOCD_ZYNQ environment variable must be set"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "$SZL" ]; then
|
|
||||||
echo "SZL environment variable must be set"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
impure=0
|
impure=0
|
||||||
load_bitstream=1
|
load_bitstream=1
|
||||||
board_type="kasli_soc"
|
|
||||||
fw_type="runtime"
|
|
||||||
|
|
||||||
while getopts "ilb:t:f:" opt; do
|
while getopts "h:il" opt; do
|
||||||
case "$opt" in
|
case "$opt" in
|
||||||
\?) exit 1
|
\?) exit 1
|
||||||
;;
|
;;
|
||||||
|
@ -24,38 +13,20 @@ while getopts "ilb:t:f:" opt; do
|
||||||
;;
|
;;
|
||||||
l) load_bitstream=0
|
l) load_bitstream=0
|
||||||
;;
|
;;
|
||||||
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
|
cd openocd
|
||||||
result_dir=`pwd`/result
|
|
||||||
cd $OPENOCD_ZYNQ
|
|
||||||
openocd -f $board_type.cfg -c "load_image $SZL/szl-$board_type.elf; resume 0; exit"
|
|
||||||
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="pld load 0 ../build/gateware/top.bit;"
|
||||||
fi
|
fi
|
||||||
artiq_netboot $load_bitstream_cmd -f $build_dir/$fw_type.bin -b $board_host
|
openocd -f zc706.cfg -c "$load_bitstream_cmd load_image ../build/firmware/armv7-none-eabihf/release/szl; resume 0; exit"
|
||||||
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="pld load 0 ../result/top.bit;"
|
||||||
fi
|
fi
|
||||||
artiq_netboot $load_bitstream_cmd -f $result_dir/$fw_type.bin -b $board_host
|
openocd -f zc706.cfg -c "$load_bitstream_cmd load_image ../result/szl.elf; resume 0; exit"
|
||||||
fi
|
fi
|
|
@ -0,0 +1,24 @@
|
||||||
|
{ pkgs }:
|
||||||
|
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
pname = "mkbootimage";
|
||||||
|
version = "2.2";
|
||||||
|
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "antmicro";
|
||||||
|
repo = "zynq-mkbootimage";
|
||||||
|
rev = "4ee42d782a9ba65725ed165a4916853224a8edf7";
|
||||||
|
sha256 = "1k1mbsngqadqihzjgvwvsrkvryxy5ladpxd9yh9iqn2s7fxqwqa9";
|
||||||
|
};
|
||||||
|
|
||||||
|
propagatedBuildInputs = [ pkgs.libelf pkgs.pcre ];
|
||||||
|
patchPhase =
|
||||||
|
''
|
||||||
|
substituteInPlace Makefile --replace "git rev-parse --short HEAD" "echo nix"
|
||||||
|
'';
|
||||||
|
installPhase =
|
||||||
|
''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp mkbootimage $out/bin
|
||||||
|
'';
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# Digilent JTAG-SMT2-NC
|
||||||
|
#
|
||||||
|
# http://store.digilentinc.com/jtag-smt2-nc-surface-mount-programming-module/
|
||||||
|
# https://reference.digilentinc.com/_media/jtag_smt2nc/jtag-smt2-nc_rm.pdf
|
||||||
|
#
|
||||||
|
# Based on reference sheet (above) and Xilinx KCU105 schematics
|
||||||
|
# https://www.xilinx.com/products/boards-and-kits/kcu105.html#documentation
|
||||||
|
#
|
||||||
|
# Note that the digilent_jtag_smt2 layout does not work and hangs while
|
||||||
|
# the ftdi_device_desc from digilent_hs2 is wrong.
|
||||||
|
|
||||||
|
interface ftdi
|
||||||
|
ftdi_device_desc "Digilent USB Device"
|
||||||
|
ftdi_vid_pid 0x0403 0x6014
|
||||||
|
ftdi_channel 0
|
||||||
|
ftdi_layout_init 0x00e8 0x60eb
|
||||||
|
ftdi_layout_signal nSRST -data 0x2000
|
|
@ -0,0 +1,40 @@
|
||||||
|
source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg]
|
||||||
|
adapter_khz 1000
|
||||||
|
|
||||||
|
set PL_TAPID 0x23731093
|
||||||
|
set SMP 1
|
||||||
|
|
||||||
|
source ./zynq-7000.cfg
|
||||||
|
|
||||||
|
reset_config srst_only srst_open_drain
|
||||||
|
adapter_nsrst_assert_width 250
|
||||||
|
adapter_nsrst_delay 400
|
||||||
|
|
||||||
|
set XC7_JSHUTDOWN 0x0d
|
||||||
|
set XC7_JPROGRAM 0x0b
|
||||||
|
set XC7_JSTART 0x0c
|
||||||
|
set XC7_BYPASS 0x3f
|
||||||
|
|
||||||
|
proc xc7_program {tap} {
|
||||||
|
global XC7_JSHUTDOWN XC7_JPROGRAM XC7_JSTART XC7_BYPASS
|
||||||
|
irscan $tap $XC7_JSHUTDOWN
|
||||||
|
irscan $tap $XC7_JPROGRAM
|
||||||
|
runtest 60000
|
||||||
|
#JSTART prevents this from working...
|
||||||
|
#irscan $tap $XC7_JSTART
|
||||||
|
runtest 2000
|
||||||
|
irscan $tap $XC7_BYPASS
|
||||||
|
runtest 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
pld device virtex2 zynq.tap 1
|
||||||
|
init
|
||||||
|
xc7_program zynq.tap
|
||||||
|
|
||||||
|
reset halt
|
||||||
|
|
||||||
|
# Disable MMU
|
||||||
|
targets $_TARGETNAME_1
|
||||||
|
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]
|
||||||
|
targets $_TARGETNAME_0
|
||||||
|
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]
|
|
@ -0,0 +1,95 @@
|
||||||
|
#
|
||||||
|
# Xilinx Zynq 7000 SoC
|
||||||
|
#
|
||||||
|
# Chris Johns <chrisj@rtems.org>
|
||||||
|
#
|
||||||
|
# Setup
|
||||||
|
# -----
|
||||||
|
#
|
||||||
|
# Create a user configuration following the "Configuration Basics" in the user
|
||||||
|
# documentation. In the file have:
|
||||||
|
#
|
||||||
|
# source [find interface/ftdi/flyswatter2.cfg]
|
||||||
|
# source [find board/zynq-zc706-eval.cfg]
|
||||||
|
# adapter_khz 2000
|
||||||
|
# init
|
||||||
|
#
|
||||||
|
|
||||||
|
if { [info exists CHIPNAME] } {
|
||||||
|
global _CHIPNAME
|
||||||
|
set _CHIPNAME $CHIPNAME
|
||||||
|
} else {
|
||||||
|
global _CHIPNAME
|
||||||
|
set _CHIPNAME zynq
|
||||||
|
}
|
||||||
|
|
||||||
|
if { [info exists ENDIAN] } {
|
||||||
|
set _ENDIAN $ENDIAN
|
||||||
|
} else {
|
||||||
|
# this defaults to a bigendian
|
||||||
|
set _ENDIAN little
|
||||||
|
}
|
||||||
|
|
||||||
|
if { [info exists SMP] } {
|
||||||
|
global _SMP
|
||||||
|
set _SMP 1
|
||||||
|
} else {
|
||||||
|
global _SMP
|
||||||
|
set _SMP 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# PL Tap.
|
||||||
|
#
|
||||||
|
# See ug585 ZYNQ-7000 TRM PSS_IDCODE for how this number is constructed.
|
||||||
|
# 0x03731093 - ZC706 Eval board 1.1
|
||||||
|
# 0x23731093 - ??
|
||||||
|
# 0x23727093 - Zedboard Rev. C and D
|
||||||
|
#
|
||||||
|
# Set in your configuration file or board specific file.
|
||||||
|
#
|
||||||
|
if { [info exists PL_TAPID] } {
|
||||||
|
set _PL_TAPID $PL_TAPID
|
||||||
|
} else {
|
||||||
|
set _PL_TAPID 0x03731093
|
||||||
|
}
|
||||||
|
|
||||||
|
jtag newtap $_CHIPNAME tap -irlen 6 -ircapture 0x001 -irmask 0x003 \
|
||||||
|
-expected-id $_PL_TAPID
|
||||||
|
|
||||||
|
#
|
||||||
|
# CoreSight Debug Access Port
|
||||||
|
#
|
||||||
|
if { [info exists DAP_TAPID] } {
|
||||||
|
set _DAP_TAPID $DAP_TAPID
|
||||||
|
} else {
|
||||||
|
set _DAP_TAPID 0x4ba00477
|
||||||
|
}
|
||||||
|
|
||||||
|
jtag newtap $_CHIPNAME dap -irlen 4 -ircapture 0x01 -irmask 0x03 \
|
||||||
|
-expected-id $_DAP_TAPID
|
||||||
|
|
||||||
|
#
|
||||||
|
# GDB target: Cortex-A9, using DAP, configuring only one core
|
||||||
|
# Base addresses of cores:
|
||||||
|
# core 0 - 0xF8890000
|
||||||
|
# core 1 - 0xF8892000
|
||||||
|
#
|
||||||
|
# Read from the ROM table with the patch to read the nested table.
|
||||||
|
#
|
||||||
|
|
||||||
|
set _TARGETNAME_0 $_CHIPNAME.cpu.0
|
||||||
|
set _TARGETNAME_1 $_CHIPNAME.cpu.1
|
||||||
|
|
||||||
|
target create $_TARGETNAME_0 cortex_a -coreid 0 \
|
||||||
|
-endian $_ENDIAN \
|
||||||
|
-chain-position $_CHIPNAME.dap \
|
||||||
|
-dbgbase 0x80090000
|
||||||
|
if { $_SMP } {
|
||||||
|
echo "Zynq CPU1."
|
||||||
|
target create $_TARGETNAME_1 cortex_a -coreid 1 \
|
||||||
|
-endian $_ENDIAN \
|
||||||
|
-chain-position $_CHIPNAME.dap \
|
||||||
|
-dbgbase 0x80092000
|
||||||
|
target smp $_TARGETNAME_0 $_TARGETNAME_1
|
||||||
|
}
|
|
@ -1,28 +1,15 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Only ZC706 supported for now.
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -z "$OPENOCD_ZYNQ" ]; then
|
|
||||||
echo "OPENOCD_ZYNQ environment variable must be set"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "$SZL" ]; then
|
|
||||||
echo "SZL environment variable must be set"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
target_host="rpi-4.m-labs.hk"
|
target_host="rpi-4.m-labs.hk"
|
||||||
impure=0
|
impure=0
|
||||||
pure_dir="result"
|
pure_dir="result"
|
||||||
impure_dir="build"
|
impure_dir="build"
|
||||||
sshopts=""
|
sshopts=""
|
||||||
load_bitstream=1
|
load_bitstream=1
|
||||||
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
|
||||||
;;
|
;;
|
||||||
|
@ -37,33 +24,29 @@ while getopts "h:id:o:lt:" opt; do
|
||||||
;;
|
;;
|
||||||
l) load_bitstream=0
|
l) load_bitstream=0
|
||||||
;;
|
;;
|
||||||
b) board_host=$OPTARG
|
|
||||||
;;
|
|
||||||
t) fw_type=$OPTARG
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
target_folder="/tmp/zynq-$USER"
|
target_folder="/tmp/zynq-$USER"
|
||||||
load_bitstream_cmd=""
|
load_bitstream_cmd=""
|
||||||
|
if [ $load_bitstream -eq 1 ]; then
|
||||||
|
load_bitstream_cmd="pld load 0 top.bit;"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Creating $target_folder..."
|
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" openocd/* $target_host:$target_folder
|
||||||
rsync -e "ssh $sshopts" -Lc $SZL/szl-zc706.elf $target_host:$target_folder/szl.elf
|
|
||||||
if [ $impure -eq 1 ]; then
|
if [ $impure -eq 1 ]; then
|
||||||
|
rsync -e "ssh $sshopts" $impure_dir/firmware/armv7-none-eabihf/release/szl $target_host:$target_folder/szl.elf
|
||||||
if [ $load_bitstream -eq 1 ]; then
|
if [ $load_bitstream -eq 1 ]; then
|
||||||
load_bitstream_cmd="-g build/gateware/top.bit"
|
rsync -e "ssh $sshopts" $impure_dir/gateware/top.bit $target_host:$target_folder
|
||||||
fi
|
fi
|
||||||
firmware="build/$fw_type.bin"
|
|
||||||
else
|
else
|
||||||
|
rsync -e "ssh $sshopts" -Lc $pure_dir/szl.elf $target_host:$target_folder
|
||||||
if [ $load_bitstream -eq 1 ]; then
|
if [ $load_bitstream -eq 1 ]; then
|
||||||
load_bitstream_cmd="-g $pure_dir/top.bit"
|
rsync -e "ssh $sshopts" -Lc $pure_dir/top.bit $target_host:$target_folder
|
||||||
fi
|
fi
|
||||||
firmware="$pure_dir/$fw_type.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_bitstream_cmd load_image szl.elf; resume 0; exit'"
|
||||||
sleep 5
|
|
||||||
artiq_netboot $load_bitstream_cmd -f $firmware -b $board_host
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{ pkgs }:
|
||||||
|
|
||||||
|
let
|
||||||
|
rustcSrc = pkgs.fetchgit {
|
||||||
|
url = "https://github.com/rust-lang/rust.git";
|
||||||
|
# sync with git_commit_hash from pkg.rust in channel-rust-nightly.toml
|
||||||
|
rev = "5ef299eb9805b4c86b227b718b39084e8bf24454";
|
||||||
|
sha256 = "0gc9hmb1sfkaf3ba8fsynl1n6bs8nk65hbhhx7ss89dfkrsxrn0x";
|
||||||
|
fetchSubmodules = true;
|
||||||
|
};
|
||||||
|
rustManifest = ./channel-rust-nightly.toml;
|
||||||
|
|
||||||
|
targets = [];
|
||||||
|
rustChannelOfTargets = _channel: _date: targets:
|
||||||
|
(pkgs.lib.rustLib.fromManifestFile rustManifest {
|
||||||
|
inherit (pkgs) stdenv fetchurl patchelf;
|
||||||
|
}).rust.override { inherit targets; };
|
||||||
|
rust =
|
||||||
|
rustChannelOfTargets "nightly" null targets;
|
||||||
|
in
|
||||||
|
pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
|
||||||
|
rustc = rust // { src = rustcSrc; };
|
||||||
|
cargo = rust;
|
||||||
|
})
|
|
@ -0,0 +1,31 @@
|
||||||
|
let
|
||||||
|
mozillaOverlay = import (builtins.fetchTarball "https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz");
|
||||||
|
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
|
||||||
|
artiq-fast = <artiq-fast>;
|
||||||
|
rustPlatform = (import ./rustPlatform.nix { inherit pkgs; });
|
||||||
|
artiqpkgs = import "${artiq-fast}/default.nix" { inherit pkgs; };
|
||||||
|
vivado = import "${artiq-fast}/vivado.nix" { inherit pkgs; };
|
||||||
|
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
|
||||||
|
pkgs.cargo-xbuild
|
||||||
|
|
||||||
|
pkgs.openocd
|
||||||
|
pkgs.openssh pkgs.rsync
|
||||||
|
|
||||||
|
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
|
||||||
|
vivado
|
||||||
|
artiqpkgs.binutils-arm
|
||||||
|
|
||||||
|
(import ./mkbootimage.nix { inherit pkgs; })
|
||||||
|
];
|
||||||
|
|
||||||
|
XARGO_RUST_SRC = "${rustPlatform.rust.rustc.src}/src";
|
||||||
|
}
|
|
@ -1,12 +1,10 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-recursion"
|
name = "async-recursion"
|
||||||
version = "0.3.2"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
|
checksum = "e5444eec77a9ec2bfe4524139e09195862e981400c4358d3b760cae634e4c4ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -15,43 +13,33 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit_field"
|
name = "bit_field"
|
||||||
version = "0.10.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
|
checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
|
||||||
|
|
||||||
[[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"
|
||||||
version = "1.4.3"
|
version = "1.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.77"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -59,34 +47,17 @@ version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compiler_builtins"
|
name = "compiler_builtins"
|
||||||
version = "0.1.39"
|
version = "0.1.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b"
|
checksum = "7bc4ac2c824d2bfc612cba57708198547e9a26943af0632aff033e0693074d5c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core_io"
|
name = "core_io"
|
||||||
version = "0.1.20210325"
|
version = "0.1.20200410"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "97f8932064288cc79feb4d343a399d353a6f6f001e586ece47fe518a9e8507df"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc_version",
|
"memchr",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc"
|
|
||||||
version = "1.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
|
|
||||||
dependencies = [
|
|
||||||
"build_const",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -95,13 +66,28 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a"
|
checksum = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cstr_core"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8758514b5f03968703f1db1f1e196e031d5268f5295ff99a5bf345008790ba85"
|
||||||
|
dependencies = [
|
||||||
|
"cty",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cty"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7313c0d620d0cb4dbd9d019e461a4beb501071ff46ec0ab933efb4daa76d73e3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dwarf"
|
name = "dwarf"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.10",
|
"cfg-if",
|
||||||
"compiler_builtins",
|
"compiler_builtins",
|
||||||
"cslice",
|
|
||||||
"libc",
|
"libc",
|
||||||
"unwind",
|
"unwind",
|
||||||
]
|
]
|
||||||
|
@ -116,9 +102,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-hal"
|
name = "embedded-hal"
|
||||||
version = "0.2.7"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nb 0.1.3",
|
"nb 0.1.3",
|
||||||
"void",
|
"void",
|
||||||
|
@ -126,9 +112,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fatfs"
|
name = "fatfs"
|
||||||
version = "0.3.5"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e18f80a87439240dac45d927fd8f8081b6f1e34c03e97271189fa8a8c2e96c8f"
|
checksum = "93079df23039e52059e1f03b4c29fb0c72da2c792aad91bb2236c9fb81d3592e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -138,9 +124,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.25"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -152,9 +138,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.25"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
|
@ -162,22 +148,23 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.25"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.25"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.25"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"proc-macro-hack",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
@ -185,79 +172,51 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.25"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.25"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.25"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"pin-project-lite",
|
"pin-project",
|
||||||
"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#67dbb5932fa8ff5f143983476f741f945871d286"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"libcortex_a9",
|
"libcortex_a9",
|
||||||
"nb 1.0.0",
|
"nb 0.1.3",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"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#67dbb5932fa8ff5f143983476f741f945871d286"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"libasync",
|
|
||||||
"libcortex_a9",
|
"libcortex_a9",
|
||||||
"libregister",
|
"libregister",
|
||||||
"log",
|
"log",
|
||||||
|
@ -275,37 +234,25 @@ dependencies = [
|
||||||
"libboard_zynq",
|
"libboard_zynq",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libconfig"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#67dbb5932fa8ff5f143983476f741f945871d286"
|
|
||||||
dependencies = [
|
|
||||||
"core_io",
|
|
||||||
"fatfs",
|
|
||||||
"libboard_zynq",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libcortex_a9"
|
name = "libcortex_a9"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#67dbb5932fa8ff5f143983476f741f945871d286"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"libregister",
|
"libregister",
|
||||||
"volatile-register",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
version = "0.2.6"
|
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 = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
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#67dbb5932fa8ff5f143983476f741f945871d286"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"vcell",
|
"vcell",
|
||||||
|
@ -315,9 +262,8 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsupport_zynq"
|
name = "libsupport_zynq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#67dbb5932fa8ff5f143983476f741f945871d286"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
|
||||||
"compiler_builtins",
|
"compiler_builtins",
|
||||||
"libboard_zynq",
|
"libboard_zynq",
|
||||||
"libcortex_a9",
|
"libcortex_a9",
|
||||||
|
@ -328,17 +274,17 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked_list_allocator"
|
name = "linked_list_allocator"
|
||||||
version = "0.8.11"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "822add9edb1860698b79522510da17bef885171f75aa395cff099d770c609c24"
|
checksum = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -353,6 +299,12 @@ version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
|
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb"
|
name = "nb"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -370,9 +322,9 @@ checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-derive"
|
name = "num-derive"
|
||||||
version = "0.3.3"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
checksum = "e0396233fb2d5b0ae3f05ff6aba9a09185f7f6e70f87fb01147d545f85364665"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -381,18 +333,32 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project"
|
||||||
version = "0.2.9"
|
version = "0.4.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-internal"
|
||||||
|
version = "0.4.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
|
@ -401,19 +367,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.43"
|
version = "0.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-nested"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.21"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -429,20 +407,17 @@ 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",
|
||||||
"dwarf",
|
"dwarf",
|
||||||
"dyld",
|
"dyld",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
|
"fatfs",
|
||||||
"futures",
|
"futures",
|
||||||
"io",
|
|
||||||
"libasync",
|
"libasync",
|
||||||
"libboard_artiq",
|
|
||||||
"libboard_zynq",
|
"libboard_zynq",
|
||||||
"libc",
|
"libc",
|
||||||
"libconfig",
|
|
||||||
"libcortex_a9",
|
"libcortex_a9",
|
||||||
"libm",
|
"libm",
|
||||||
"libregister",
|
"libregister",
|
||||||
|
@ -457,44 +432,11 @@ dependencies = [
|
||||||
"void",
|
"void",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc_version"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
|
||||||
dependencies = [
|
|
||||||
"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]]
|
|
||||||
name = "semver"
|
|
||||||
version = "0.1.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smoltcp"
|
name = "smoltcp"
|
||||||
version = "0.7.5"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e4a069bef843d170df47e7c0a8bf8d037f217d9f5b325865acc3e466ffe40d3"
|
checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -503,36 +445,48 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.101"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-ident",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "szl"
|
||||||
version = "1.0.5"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cstr_core",
|
||||||
|
"libboard_zynq",
|
||||||
|
"libcortex_a9",
|
||||||
|
"libsupport_zynq",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
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 = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unwind"
|
name = "unwind"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if 0.1.10",
|
"cfg-if",
|
||||||
"compiler_builtins",
|
"compiler_builtins",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcell"
|
name = "vcell"
|
||||||
version = "0.1.3"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "void"
|
name = "void"
|
||||||
|
@ -542,9 +496,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",
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,16 +2,19 @@
|
||||||
members = [
|
members = [
|
||||||
"libc",
|
"libc",
|
||||||
"libdyld",
|
"libdyld",
|
||||||
|
"libcoreio",
|
||||||
"libdwarf",
|
"libdwarf",
|
||||||
"libio",
|
|
||||||
"libunwind",
|
"libunwind",
|
||||||
"runtime",
|
"runtime",
|
||||||
"satman"
|
"szl"
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
debug = true
|
debug = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
opt-level = 2
|
opt-level = 'z'
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
core_io = { path = "./libcoreio" }
|
||||||
|
|
38
src/Makefile
38
src/Makefile
|
@ -1,34 +1,20 @@
|
||||||
TARGET := zc706
|
VARIANT := simple
|
||||||
GWARGS := -V nist_clock
|
|
||||||
|
|
||||||
all: runtime
|
all: ../build/firmware/armv7-none-eabihf/release/szl
|
||||||
|
|
||||||
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/zc706.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 . -path ./szl -prune -o -print)
|
||||||
cd runtime && \
|
XBUILD_SYSROOT_PATH=`pwd`/../build/sysroot cargo xbuild --release -p runtime --target-dir ../build/firmware
|
||||||
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
|
||||||
cargo xbuild --release \
|
|
||||||
--target-dir ../../build/firmware \
|
|
||||||
--no-default-features --features=target_$(TARGET)
|
|
||||||
|
|
||||||
../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime
|
../build/szl-payload.bin.lzma: ../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/szl-payload.bin
|
||||||
|
lzma --keep -f ../build/szl-payload.bin
|
||||||
|
|
||||||
../build/firmware/armv7-none-eabihf/release/satman: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
|
../build/firmware/armv7-none-eabihf/release/szl: .cargo/* armv7-none-eabihf.json Cargo.lock Cargo.toml szl/* szl/src/* ../build/szl-payload.bin.lzma
|
||||||
cd satman && \
|
XBUILD_SYSROOT_PATH=`pwd`/../build/sysroot cargo xbuild --release -p szl --target-dir ../build/firmware
|
||||||
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
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ from misoc.interconnect.csr import *
|
||||||
|
|
||||||
from artiq.gateware import rtio
|
from artiq.gateware import rtio
|
||||||
|
|
||||||
OUT_BURST_LEN = 10
|
|
||||||
|
OUT_BURST_LEN = 4
|
||||||
IN_BURST_LEN = 4
|
IN_BURST_LEN = 4
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ class Engine(Module, AutoCSR):
|
||||||
### Write
|
### Write
|
||||||
self.comb += [
|
self.comb += [
|
||||||
w.data.eq(self.din),
|
w.data.eq(self.din),
|
||||||
aw.addr.eq(self.addr_base.storage+96),
|
aw.addr.eq(self.addr_base.storage+32), # Write to next cache line
|
||||||
w.strb.eq(0xff),
|
w.strb.eq(0xff),
|
||||||
aw.burst.eq(axi.Burst.incr.value),
|
aw.burst.eq(axi.Burst.incr.value),
|
||||||
aw.len.eq(IN_BURST_LEN-1), # Number of transfers in burst minus 1
|
aw.len.eq(IN_BURST_LEN-1), # Number of transfers in burst minus 1
|
||||||
|
@ -190,31 +191,22 @@ class KernelInitiator(Module, AutoCSR):
|
||||||
cmd_read.eq(cmd == 1)
|
cmd_read.eq(cmd == 1)
|
||||||
]
|
]
|
||||||
|
|
||||||
out_len = Signal(8)
|
|
||||||
dout_cases = {}
|
dout_cases = {}
|
||||||
dout_cases[0] = [
|
dout_cases[0] = [
|
||||||
cmd.eq(self.engine.dout[:8]),
|
cmd.eq(self.engine.dout[:8]),
|
||||||
out_len.eq(self.engine.dout[8:16]),
|
|
||||||
cri.chan_sel.eq(self.engine.dout[40:]),
|
cri.chan_sel.eq(self.engine.dout[40:]),
|
||||||
cri.o_address.eq(self.engine.dout[32:40])
|
cri.o_address.eq(self.engine.dout[32:40])
|
||||||
]
|
]
|
||||||
for i in range(8):
|
|
||||||
target = cri.o_data[i*64:(i+1)*64]
|
|
||||||
dout_cases[0] += [If(i >= self.engine.dout[8:16], target.eq(0))]
|
|
||||||
|
|
||||||
dout_cases[1] = [
|
dout_cases[1] = [
|
||||||
cri.o_timestamp.eq(self.engine.dout),
|
cri.o_timestamp.eq(self.engine.dout)
|
||||||
cri.i_timeout.eq(self.engine.dout)
|
|
||||||
]
|
]
|
||||||
for i in range(8):
|
dout_cases[2] = [cri.o_data.eq(self.engine.dout)] # only lowest 64 bits
|
||||||
target = cri.o_data[i*64:(i+1)*64]
|
|
||||||
dout_cases[i+2] = [target.eq(self.engine.dout)]
|
|
||||||
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
cri.cmd.eq(rtio.cri.commands["nop"]),
|
cri.cmd.eq(rtio.cri.commands["nop"]),
|
||||||
If(self.engine.dout_stb,
|
If(self.engine.dout_stb,
|
||||||
Case(self.engine.dout_index, dout_cases),
|
Case(self.engine.dout_index, dout_cases),
|
||||||
If(self.engine.dout_index == out_len + 2,
|
If(self.engine.dout_index == 2,
|
||||||
If(cmd_write, cri.cmd.eq(rtio.cri.commands["write"])),
|
If(cmd_write, cri.cmd.eq(rtio.cri.commands["write"])),
|
||||||
If(cmd_read, cri.cmd.eq(rtio.cri.commands["read"]))
|
If(cmd_read, cri.cmd.eq(rtio.cri.commands["read"]))
|
||||||
)
|
)
|
||||||
|
@ -234,11 +226,7 @@ class KernelInitiator(Module, AutoCSR):
|
||||||
)
|
)
|
||||||
fsm.act("WAIT_OUT_CYCLE",
|
fsm.act("WAIT_OUT_CYCLE",
|
||||||
self.engine.din_ready.eq(0),
|
self.engine.din_ready.eq(0),
|
||||||
If(self.engine.dout_stb & cmd_write & (self.engine.dout_index == out_len + 2),
|
If(self.engine.dout_stb & (self.engine.dout_index == 3),
|
||||||
NextState("WAIT_READY")
|
|
||||||
),
|
|
||||||
# for some reason read requires some delay until the next state
|
|
||||||
If(self.engine.dout_stb & cmd_read & (self.engine.dout_index == out_len + 3),
|
|
||||||
NextState("WAIT_READY")
|
NextState("WAIT_READY")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
|
|
@ -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 = description["variant"]
|
|
||||||
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 = description["variant"]
|
|
||||||
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 = description["variant"]
|
|
||||||
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()
|
|
|
@ -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()
|
||||||
|
|
|
@ -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" }
|
|
|
@ -1,5 +0,0 @@
|
||||||
extern crate build_zynq;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
build_zynq::cfg();
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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())
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,356 +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, ext_input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
|
||||||
let s = map_frequency_settings(settings)?;
|
|
||||||
|
|
||||||
// FREE_RUN=1 routes XA/XB to CKIN2.
|
|
||||||
let input = if settings.crystal_ref { Input::Ckin2 } else { ext_input };
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
[package]
|
|
||||||
authors = ["M-Labs"]
|
|
||||||
name = "build_zynq"
|
|
||||||
version = "0.0.0"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "build_zynq"
|
|
||||||
path = "lib.rs"
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,8 +5,6 @@ fn main() {
|
||||||
|
|
||||||
mod libc {
|
mod libc {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::env;
|
|
||||||
|
|
||||||
pub fn compile() {
|
pub fn compile() {
|
||||||
let cfg = &mut cc::Build::new();
|
let cfg = &mut cc::Build::new();
|
||||||
cfg.no_default_flags(true);
|
cfg.no_default_flags(true);
|
||||||
|
@ -18,9 +16,6 @@ mod libc {
|
||||||
cfg.flag("-ffreestanding");
|
cfg.flag("-ffreestanding");
|
||||||
cfg.flag("-fno-PIC");
|
cfg.flag("-fno-PIC");
|
||||||
cfg.flag("-isystem../include");
|
cfg.flag("-isystem../include");
|
||||||
if let Ok(extra_include) = env::var("CLANG_EXTRA_INCLUDE_DIR") {
|
|
||||||
cfg.flag(&("-isystem".to_owned() + &extra_include));
|
|
||||||
}
|
|
||||||
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");
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "core_io"
|
||||||
|
version = "0.1.20200410"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "core_io"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
memchr = { version = "2", default-features = false, optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = []
|
||||||
|
collections = ["alloc", "memchr"]
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,896 @@
|
||||||
|
use crate::io::prelude::*;
|
||||||
|
|
||||||
|
use core::cmp;
|
||||||
|
use crate::io::{self, Error, ErrorKind, Initializer, SeekFrom};
|
||||||
|
|
||||||
|
#[cfg(feature = "collections")]
|
||||||
|
use core::convert::TryInto;
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
use collections::vec::Vec;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
|
/// A `Cursor` wraps an in-memory buffer and provides it with a
|
||||||
|
/// [`Seek`] implementation.
|
||||||
|
///
|
||||||
|
/// `Cursor`s are used with in-memory buffers, anything implementing
|
||||||
|
/// `AsRef<[u8]>`, to allow them to implement [`Read`] and/or [`Write`],
|
||||||
|
/// allowing these buffers to be used anywhere you might use a reader or writer
|
||||||
|
/// that does actual I/O.
|
||||||
|
///
|
||||||
|
/// The standard library implements some I/O traits on various types which
|
||||||
|
/// are commonly used as a buffer, like `Cursor<`[`Vec`]`<u8>>` and
|
||||||
|
/// `Cursor<`[`&[u8]`][bytes]`>`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// We may want to write bytes to a [`File`] in our production
|
||||||
|
/// code, but use an in-memory buffer in our tests. We can do this with
|
||||||
|
/// `Cursor`:
|
||||||
|
///
|
||||||
|
/// [`Seek`]: trait.Seek.html
|
||||||
|
/// [`Read`]: ../../std/io/trait.Read.html
|
||||||
|
/// [`Write`]: ../../std/io/trait.Write.html
|
||||||
|
/// [`Vec`]: ../../std/vec/struct.Vec.html
|
||||||
|
/// [bytes]: ../../std/primitive.slice.html
|
||||||
|
/// [`File`]: ../fs/struct.File.html
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::{self, SeekFrom};
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// // a library function we've written
|
||||||
|
/// fn write_ten_bytes_at_end<W: Write + Seek>(writer: &mut W) -> io::Result<()> {
|
||||||
|
/// writer.seek(SeekFrom::End(-10))?;
|
||||||
|
///
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// writer.write(&[i])?;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // all went well
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn foo() -> io::Result<()> {
|
||||||
|
/// // Here's some code that uses this library function.
|
||||||
|
/// //
|
||||||
|
/// // We might want to use a BufReader here for efficiency, but let's
|
||||||
|
/// // keep this example focused.
|
||||||
|
/// let mut file = File::create("foo.txt")?;
|
||||||
|
///
|
||||||
|
/// write_ten_bytes_at_end(&mut file)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
/// // now let's write a test
|
||||||
|
/// #[test]
|
||||||
|
/// fn test_writes_bytes() {
|
||||||
|
/// // setting up a real File is much slower than an in-memory buffer,
|
||||||
|
/// // let's use a cursor instead
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
/// let mut buff = Cursor::new(vec![0; 15]);
|
||||||
|
///
|
||||||
|
/// write_ten_bytes_at_end(&mut buff).unwrap();
|
||||||
|
///
|
||||||
|
/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct Cursor<T> {
|
||||||
|
inner: T,
|
||||||
|
pos: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cursor<T> {
|
||||||
|
/// Creates a new cursor wrapping the provided underlying in-memory buffer.
|
||||||
|
///
|
||||||
|
/// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`)
|
||||||
|
/// is not empty. So writing to cursor starts with overwriting `Vec`
|
||||||
|
/// content, not with appending to it.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
/// ```
|
||||||
|
pub fn new(inner: T) -> Cursor<T> {
|
||||||
|
Cursor { pos: 0, inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes this cursor, returning the underlying value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let vec = buff.into_inner();
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a reference to the underlying value in this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let reference = buff.get_ref();
|
||||||
|
/// ```
|
||||||
|
pub fn get_ref(&self) -> &T {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a mutable reference to the underlying value in this cursor.
|
||||||
|
///
|
||||||
|
/// Care should be taken to avoid modifying the internal I/O state of the
|
||||||
|
/// underlying value as it may corrupt this cursor's position.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let reference = buff.get_mut();
|
||||||
|
/// ```
|
||||||
|
pub fn get_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current position of this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::SeekFrom;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(buff.position(), 0);
|
||||||
|
///
|
||||||
|
/// buff.seek(SeekFrom::Current(2)).unwrap();
|
||||||
|
/// assert_eq!(buff.position(), 2);
|
||||||
|
///
|
||||||
|
/// buff.seek(SeekFrom::Current(-1)).unwrap();
|
||||||
|
/// assert_eq!(buff.position(), 1);
|
||||||
|
/// ```
|
||||||
|
pub fn position(&self) -> u64 {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the position of this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(buff.position(), 0);
|
||||||
|
///
|
||||||
|
/// buff.set_position(2);
|
||||||
|
/// assert_eq!(buff.position(), 2);
|
||||||
|
///
|
||||||
|
/// buff.set_position(4);
|
||||||
|
/// assert_eq!(buff.position(), 4);
|
||||||
|
/// ```
|
||||||
|
pub fn set_position(&mut self, pos: u64) {
|
||||||
|
self.pos = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> io::Seek for Cursor<T>
|
||||||
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
|
||||||
|
let (base_pos, offset) = match style {
|
||||||
|
SeekFrom::Start(n) => {
|
||||||
|
self.pos = n;
|
||||||
|
return Ok(n);
|
||||||
|
}
|
||||||
|
SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n),
|
||||||
|
SeekFrom::Current(n) => (self.pos, n),
|
||||||
|
};
|
||||||
|
let new_pos = if offset >= 0 {
|
||||||
|
base_pos.checked_add(offset as u64)
|
||||||
|
} else {
|
||||||
|
base_pos.checked_sub((offset.wrapping_neg()) as u64)
|
||||||
|
};
|
||||||
|
match new_pos {
|
||||||
|
Some(n) => {
|
||||||
|
self.pos = n;
|
||||||
|
Ok(self.pos)
|
||||||
|
}
|
||||||
|
None => Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"invalid seek to a negative or overflowing position",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_len(&mut self) -> io::Result<u64> {
|
||||||
|
Ok(self.inner.as_ref().len() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_position(&mut self) -> io::Result<u64> {
|
||||||
|
Ok(self.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Read for Cursor<T>
|
||||||
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
let n = Read::read(&mut self.get_ref().as_ref(), buf)?;
|
||||||
|
self.pos += n as u64;
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
let n = buf.len();
|
||||||
|
Read::read_exact(&mut self.get_ref().as_ref(), buf)?;
|
||||||
|
self.pos += n as u64;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
Initializer::nop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "collections")]
|
||||||
|
impl<T> BufRead for Cursor<T>
|
||||||
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64);
|
||||||
|
Ok(&self.inner.as_ref()[(amt as usize)..])
|
||||||
|
}
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
self.pos += amt as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-resizing write implementation
|
||||||
|
#[inline]
|
||||||
|
fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<usize> {
|
||||||
|
let pos = cmp::min(*pos_mut, slice.len() as u64);
|
||||||
|
let amt = (&mut slice[(pos as usize)..]).write(buf)?;
|
||||||
|
*pos_mut += amt as u64;
|
||||||
|
Ok(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resizing write implementation
|
||||||
|
#[cfg(feature = "collections")]
|
||||||
|
fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
let pos: usize = (*pos_mut).try_into().map_err(|_| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"cursor position exceeds maximum possible vector length",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
// Make sure the internal buffer is as least as big as where we
|
||||||
|
// currently are
|
||||||
|
let len = vec.len();
|
||||||
|
if len < pos {
|
||||||
|
// use `resize` so that the zero filling is as efficient as possible
|
||||||
|
vec.resize(pos, 0);
|
||||||
|
}
|
||||||
|
// Figure out what bytes will be used to overwrite what's currently
|
||||||
|
// there (left), and what will be appended on the end (right)
|
||||||
|
{
|
||||||
|
let space = vec.len() - pos;
|
||||||
|
let (left, right) = buf.split_at(cmp::min(space, buf.len()));
|
||||||
|
vec[pos..pos + left.len()].copy_from_slice(left);
|
||||||
|
vec.extend_from_slice(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bump us forward
|
||||||
|
*pos_mut = (pos + buf.len()) as u64;
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Cursor<&mut [u8]> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
slice_write(&mut self.pos, self.inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "collections")]
|
||||||
|
impl Write for Cursor<&mut Vec<u8>> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
vec_write(&mut self.pos, self.inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "collections")]
|
||||||
|
impl Write for Cursor<Vec<u8>> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
vec_write(&mut self.pos, &mut self.inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl Write for Cursor<Box<[u8]>> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
slice_write(&mut self.pos, &mut self.inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::io::prelude::*;
|
||||||
|
use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vec_writer() {
|
||||||
|
let mut writer = Vec::new();
|
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||||
|
.unwrap(),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
assert_eq!(writer, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mem_writer() {
|
||||||
|
let mut writer = Cursor::new(Vec::new());
|
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||||
|
.unwrap(),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mem_mut_writer() {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
let mut writer = Cursor::new(&mut vec);
|
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||||
|
.unwrap(),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_box_slice_writer() {
|
||||||
|
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
assert_eq!(writer.write(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
|
||||||
|
assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write(&[10]).unwrap(), 0);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
assert_eq!(&**writer.get_ref(), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_box_slice_writer_vectored() {
|
||||||
|
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),])
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
assert_eq!(&**writer.get_ref(), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buf_writer() {
|
||||||
|
let mut buf = [0 as u8; 9];
|
||||||
|
{
|
||||||
|
let mut writer = Cursor::new(&mut buf[..]);
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
assert_eq!(writer.write(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
|
||||||
|
assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write(&[10]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buf_writer_vectored() {
|
||||||
|
let mut buf = [0 as u8; 9];
|
||||||
|
{
|
||||||
|
let mut writer = Cursor::new(&mut buf[..]);
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7])],)
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buf_writer_seek() {
|
||||||
|
let mut buf = [0 as u8; 8];
|
||||||
|
{
|
||||||
|
let mut writer = Cursor::new(&mut buf[..]);
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write(&[1]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2);
|
||||||
|
assert_eq!(writer.position(), 2);
|
||||||
|
assert_eq!(writer.write(&[2]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 3);
|
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
assert_eq!(writer.write(&[3]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 2);
|
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
|
||||||
|
assert_eq!(writer.position(), 7);
|
||||||
|
assert_eq!(writer.write(&[4]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
}
|
||||||
|
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buf_writer_error() {
|
||||||
|
let mut buf = [0 as u8; 2];
|
||||||
|
let mut writer = Cursor::new(&mut buf[..]);
|
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write(&[0, 0]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write(&[0, 0]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mem_reader() {
|
||||||
|
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
assert_eq!(reader.position(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||||
|
assert_eq!(reader.position(), 1);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||||
|
assert_eq!(reader.position(), 5);
|
||||||
|
let b: &[_] = &[1, 2, 3, 4];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||||
|
let b: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(&buf[..3], b);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mem_reader_vectored() {
|
||||||
|
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||||
|
assert_eq!(reader.position(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
|
||||||
|
.unwrap(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
assert_eq!(reader.position(), 1);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf1 = [0; 4];
|
||||||
|
let mut buf2 = [0; 4];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),])
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
let b1: &[_] = &[1, 2, 3, 4];
|
||||||
|
let b2: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(buf1, b1);
|
||||||
|
assert_eq!(&buf2[..3], b2);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boxed_slice_reader() {
|
||||||
|
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
assert_eq!(reader.position(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||||
|
assert_eq!(reader.position(), 1);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||||
|
assert_eq!(reader.position(), 5);
|
||||||
|
let b: &[_] = &[1, 2, 3, 4];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||||
|
let b: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(&buf[..3], b);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boxed_slice_reader_vectored() {
|
||||||
|
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||||
|
assert_eq!(reader.position(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
|
||||||
|
.unwrap(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
assert_eq!(reader.position(), 1);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf1 = [0; 4];
|
||||||
|
let mut buf2 = [0; 4];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
let b1: &[_] = &[1, 2, 3, 4];
|
||||||
|
let b2: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(buf1, b1);
|
||||||
|
assert_eq!(&buf2[..3], b2);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_to_end() {
|
||||||
|
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
let mut v = Vec::new();
|
||||||
|
reader.read_to_end(&mut v).unwrap();
|
||||||
|
assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_reader() {
|
||||||
|
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
let reader = &mut &in_buf[..];
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||||
|
assert_eq!(reader.len(), 7);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(&buf[..], b);
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||||
|
assert_eq!(reader.len(), 3);
|
||||||
|
let b: &[_] = &[1, 2, 3, 4];
|
||||||
|
assert_eq!(&buf[..], b);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||||
|
let b: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(&buf[..3], b);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_reader_vectored() {
|
||||||
|
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
let reader = &mut &in_buf[..];
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
|
||||||
|
.unwrap(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
assert_eq!(reader.len(), 7);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf1 = [0; 4];
|
||||||
|
let mut buf2 = [0; 4];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
let b1: &[_] = &[1, 2, 3, 4];
|
||||||
|
let b2: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(buf1, b1);
|
||||||
|
assert_eq!(&buf2[..3], b2);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_exact() {
|
||||||
|
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
let reader = &mut &in_buf[..];
|
||||||
|
let mut buf = [];
|
||||||
|
assert!(reader.read_exact(&mut buf).is_ok());
|
||||||
|
let mut buf = [8];
|
||||||
|
assert!(reader.read_exact(&mut buf).is_ok());
|
||||||
|
assert_eq!(buf[0], 0);
|
||||||
|
assert_eq!(reader.len(), 7);
|
||||||
|
let mut buf = [0, 0, 0, 0, 0, 0, 0];
|
||||||
|
assert!(reader.read_exact(&mut buf).is_ok());
|
||||||
|
assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
assert_eq!(reader.len(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert!(reader.read_exact(&mut buf).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buf_reader() {
|
||||||
|
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
let mut reader = Cursor::new(&in_buf[..]);
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
assert_eq!(reader.position(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||||
|
assert_eq!(reader.position(), 1);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||||
|
assert_eq!(reader.position(), 5);
|
||||||
|
let b: &[_] = &[1, 2, 3, 4];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||||
|
let b: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(&buf[..3], b);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn seek_past_end() {
|
||||||
|
let buf = [0xff];
|
||||||
|
let mut r = Cursor::new(&buf[..]);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||||
|
assert_eq!(r.read(&mut [0]).unwrap(), 0);
|
||||||
|
|
||||||
|
let mut r = Cursor::new(vec![10]);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||||
|
assert_eq!(r.read(&mut [0]).unwrap(), 0);
|
||||||
|
|
||||||
|
let mut buf = [0];
|
||||||
|
let mut r = Cursor::new(&mut buf[..]);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||||
|
assert_eq!(r.write(&[3]).unwrap(), 0);
|
||||||
|
|
||||||
|
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||||
|
assert_eq!(r.write(&[3]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn seek_past_i64() {
|
||||||
|
let buf = [0xff];
|
||||||
|
let mut r = Cursor::new(&buf[..]);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||||
|
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||||
|
|
||||||
|
let mut r = Cursor::new(vec![10]);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||||
|
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||||
|
|
||||||
|
let mut buf = [0];
|
||||||
|
let mut r = Cursor::new(&mut buf[..]);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||||
|
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||||
|
|
||||||
|
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||||
|
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||||
|
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn seek_before_0() {
|
||||||
|
let buf = [0xff];
|
||||||
|
let mut r = Cursor::new(&buf[..]);
|
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||||
|
|
||||||
|
let mut r = Cursor::new(vec![10]);
|
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||||
|
|
||||||
|
let mut buf = [0];
|
||||||
|
let mut r = Cursor::new(&mut buf[..]);
|
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||||
|
|
||||||
|
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_seekable_mem_writer() {
|
||||||
|
let mut writer = Cursor::new(Vec::<u8>::new());
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0);
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write(&[3, 4]).unwrap(), 2);
|
||||||
|
let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3);
|
||||||
|
assert_eq!(writer.write(&[0, 1]).unwrap(), 2);
|
||||||
|
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
|
||||||
|
assert_eq!(writer.write(&[1, 2]).unwrap(), 2);
|
||||||
|
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10);
|
||||||
|
assert_eq!(writer.write(&[1]).unwrap(), 1);
|
||||||
|
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_seek_past_end() {
|
||||||
|
let mut r = Cursor::new(Vec::new());
|
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||||
|
assert_eq!(r.write(&[3]).unwrap(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_seek_before_0() {
|
||||||
|
let mut r = Cursor::new(Vec::new());
|
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
fn vec_seek_and_write_past_usize_max() {
|
||||||
|
let mut c = Cursor::new(Vec::new());
|
||||||
|
c.set_position(<usize>::max_value() as u64 + 1);
|
||||||
|
assert!(c.write_all(&[1, 2, 3]).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_partial_eq() {
|
||||||
|
assert_eq!(Cursor::new(Vec::<u8>::new()), Cursor::new(Vec::<u8>::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eq() {
|
||||||
|
struct AssertEq<T: Eq>(pub T);
|
||||||
|
|
||||||
|
let _: AssertEq<Cursor<Vec<u8>>> = AssertEq(Cursor::new(Vec::new()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,551 @@
|
||||||
|
#[cfg(feature="alloc")] use alloc::boxed::Box;
|
||||||
|
#[cfg(not(feature="alloc"))] use ::FakeBox as Box;
|
||||||
|
use core::convert::Into;
|
||||||
|
use core::fmt;
|
||||||
|
use core::marker::{Send, Sync};
|
||||||
|
use core::option::Option::{self, Some, None};
|
||||||
|
use core::result;
|
||||||
|
#[cfg(feature="collections")] use collections::string::String;
|
||||||
|
#[cfg(not(feature="collections"))] use ::ErrorString as String;
|
||||||
|
use core::convert::From;
|
||||||
|
|
||||||
|
/// A specialized [`Result`](../result/enum.Result.html) type for I/O
|
||||||
|
/// operations.
|
||||||
|
///
|
||||||
|
/// This type is broadly used across [`std::io`] for any operation which may
|
||||||
|
/// produce an error.
|
||||||
|
///
|
||||||
|
/// This typedef is generally used to avoid writing out [`io::Error`] directly and
|
||||||
|
/// is otherwise a direct mapping to [`Result`].
|
||||||
|
///
|
||||||
|
/// While usual Rust style is to import types directly, aliases of [`Result`]
|
||||||
|
/// often are not, to make it easier to distinguish between them. [`Result`] is
|
||||||
|
/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias
|
||||||
|
/// will generally use `io::Result` instead of shadowing the prelude's import
|
||||||
|
/// of [`std::result::Result`][`Result`].
|
||||||
|
///
|
||||||
|
/// [`std::io`]: ../io/index.html
|
||||||
|
/// [`io::Error`]: ../io/struct.Error.html
|
||||||
|
/// [`Result`]: ../result/enum.Result.html
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A convenience function that bubbles an `io::Result` to its caller:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// fn get_string() -> io::Result<String> {
|
||||||
|
/// let mut buffer = String::new();
|
||||||
|
///
|
||||||
|
/// io::stdin().read_line(&mut buffer)?;
|
||||||
|
///
|
||||||
|
/// Ok(buffer)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and
|
||||||
|
/// associated traits.
|
||||||
|
///
|
||||||
|
/// Errors mostly originate from the underlying OS, but custom instances of
|
||||||
|
/// `Error` can be created with crafted error messages and a particular value of
|
||||||
|
/// [`ErrorKind`].
|
||||||
|
///
|
||||||
|
/// [`Read`]: ../io/trait.Read.html
|
||||||
|
/// [`Write`]: ../io/trait.Write.html
|
||||||
|
/// [`Seek`]: ../io/trait.Seek.html
|
||||||
|
/// [`ErrorKind`]: enum.ErrorKind.html
|
||||||
|
pub struct Error {
|
||||||
|
repr: Repr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.repr, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Repr {
|
||||||
|
Os(i32),
|
||||||
|
Simple(ErrorKind),
|
||||||
|
#[cfg(feature="alloc")]
|
||||||
|
Custom(Box<Custom>),
|
||||||
|
#[cfg(not(feature="alloc"))]
|
||||||
|
Custom(Custom),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Custom {
|
||||||
|
kind: ErrorKind,
|
||||||
|
error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list specifying general categories of I/O error.
|
||||||
|
///
|
||||||
|
/// This list is intended to grow over time and it is not recommended to
|
||||||
|
/// exhaustively match against it.
|
||||||
|
///
|
||||||
|
/// It is used with the [`io::Error`] type.
|
||||||
|
///
|
||||||
|
/// [`io::Error`]: struct.Error.html
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
#[allow(deprecated)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
/// An entity was not found, often a file.
|
||||||
|
NotFound,
|
||||||
|
/// The operation lacked the necessary privileges to complete.
|
||||||
|
PermissionDenied,
|
||||||
|
/// The connection was refused by the remote server.
|
||||||
|
ConnectionRefused,
|
||||||
|
/// The connection was reset by the remote server.
|
||||||
|
ConnectionReset,
|
||||||
|
/// The connection was aborted (terminated) by the remote server.
|
||||||
|
ConnectionAborted,
|
||||||
|
/// The network operation failed because it was not connected yet.
|
||||||
|
NotConnected,
|
||||||
|
/// A socket address could not be bound because the address is already in
|
||||||
|
/// use elsewhere.
|
||||||
|
AddrInUse,
|
||||||
|
/// A nonexistent interface was requested or the requested address was not
|
||||||
|
/// local.
|
||||||
|
AddrNotAvailable,
|
||||||
|
/// The operation failed because a pipe was closed.
|
||||||
|
BrokenPipe,
|
||||||
|
/// An entity already exists, often a file.
|
||||||
|
AlreadyExists,
|
||||||
|
/// The operation needs to block to complete, but the blocking operation was
|
||||||
|
/// requested to not occur.
|
||||||
|
WouldBlock,
|
||||||
|
/// A parameter was incorrect.
|
||||||
|
InvalidInput,
|
||||||
|
/// Data not valid for the operation were encountered.
|
||||||
|
///
|
||||||
|
/// Unlike [`InvalidInput`], this typically means that the operation
|
||||||
|
/// parameters were valid, however the error was caused by malformed
|
||||||
|
/// input data.
|
||||||
|
///
|
||||||
|
/// For example, a function that reads a file into a string will error with
|
||||||
|
/// `InvalidData` if the file's contents are not valid UTF-8.
|
||||||
|
///
|
||||||
|
/// [`InvalidInput`]: #variant.InvalidInput
|
||||||
|
InvalidData,
|
||||||
|
/// The I/O operation's timeout expired, causing it to be canceled.
|
||||||
|
TimedOut,
|
||||||
|
/// An error returned when an operation could not be completed because a
|
||||||
|
/// call to [`write`] returned [`Ok(0)`].
|
||||||
|
///
|
||||||
|
/// This typically means that an operation could only succeed if it wrote a
|
||||||
|
/// particular number of bytes but only a smaller number of bytes could be
|
||||||
|
/// written.
|
||||||
|
///
|
||||||
|
/// [`write`]: ../../std/io/trait.Write.html#tymethod.write
|
||||||
|
/// [`Ok(0)`]: ../../std/io/type.Result.html
|
||||||
|
WriteZero,
|
||||||
|
/// This operation was interrupted.
|
||||||
|
///
|
||||||
|
/// Interrupted operations can typically be retried.
|
||||||
|
Interrupted,
|
||||||
|
/// Any I/O error not part of this list.
|
||||||
|
Other,
|
||||||
|
|
||||||
|
/// An error returned when an operation could not be completed because an
|
||||||
|
/// "end of file" was reached prematurely.
|
||||||
|
///
|
||||||
|
/// This typically means that an operation could only succeed if it read a
|
||||||
|
/// particular number of bytes but only a smaller number of bytes could be
|
||||||
|
/// read.
|
||||||
|
UnexpectedEof,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorKind {
|
||||||
|
pub(crate) fn as_str(&self) -> &'static str {
|
||||||
|
match *self {
|
||||||
|
ErrorKind::NotFound => "entity not found",
|
||||||
|
ErrorKind::PermissionDenied => "permission denied",
|
||||||
|
ErrorKind::ConnectionRefused => "connection refused",
|
||||||
|
ErrorKind::ConnectionReset => "connection reset",
|
||||||
|
ErrorKind::ConnectionAborted => "connection aborted",
|
||||||
|
ErrorKind::NotConnected => "not connected",
|
||||||
|
ErrorKind::AddrInUse => "address in use",
|
||||||
|
ErrorKind::AddrNotAvailable => "address not available",
|
||||||
|
ErrorKind::BrokenPipe => "broken pipe",
|
||||||
|
ErrorKind::AlreadyExists => "entity already exists",
|
||||||
|
ErrorKind::WouldBlock => "operation would block",
|
||||||
|
ErrorKind::InvalidInput => "invalid input parameter",
|
||||||
|
ErrorKind::InvalidData => "invalid data",
|
||||||
|
ErrorKind::TimedOut => "timed out",
|
||||||
|
ErrorKind::WriteZero => "write zero",
|
||||||
|
ErrorKind::Interrupted => "operation interrupted",
|
||||||
|
ErrorKind::Other => "other os error",
|
||||||
|
ErrorKind::UnexpectedEof => "unexpected end of file",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intended for use for errors not exposed to the user, where allocating onto
|
||||||
|
/// the heap (for normal construction via Error::new) is too costly.
|
||||||
|
impl From<ErrorKind> for Error {
|
||||||
|
/// Converts an [`ErrorKind`] into an [`Error`].
|
||||||
|
///
|
||||||
|
/// This conversion allocates a new error with a simple representation of error kind.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// let not_found = ErrorKind::NotFound;
|
||||||
|
/// let error = Error::from(not_found);
|
||||||
|
/// assert_eq!("entity not found", format!("{}", error));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`ErrorKind`]: ../../std/io/enum.ErrorKind.html
|
||||||
|
/// [`Error`]: ../../std/io/struct.Error.html
|
||||||
|
#[inline]
|
||||||
|
fn from(kind: ErrorKind) -> Error {
|
||||||
|
Error { repr: Repr::Simple(kind) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
/// Creates a new I/O error from a known kind of error as well as an
|
||||||
|
/// arbitrary error payload.
|
||||||
|
///
|
||||||
|
/// This function is used to generically create I/O errors which do not
|
||||||
|
/// originate from the OS itself. The `error` argument is an arbitrary
|
||||||
|
/// payload which will be contained in this `Error`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// // errors can be created from strings
|
||||||
|
/// let custom_error = Error::new(ErrorKind::Other, "oh no!");
|
||||||
|
///
|
||||||
|
/// // errors can also be created from other errors
|
||||||
|
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
|
||||||
|
/// ```
|
||||||
|
pub fn new<E>(kind: ErrorKind, error: E) -> Error
|
||||||
|
where
|
||||||
|
E: Into<String>,
|
||||||
|
{
|
||||||
|
Self::_new(kind, error.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _new(kind: ErrorKind, error: String) -> Error {
|
||||||
|
Error { repr: Repr::Custom(Box::new(Custom { kind, error })) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new instance of an `Error` from a particular OS error code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// On Linux:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # if cfg!(target_os = "linux") {
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// let error = io::Error::from_raw_os_error(22);
|
||||||
|
/// assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// On Windows:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # if cfg!(windows) {
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// let error = io::Error::from_raw_os_error(10022);
|
||||||
|
/// assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn from_raw_os_error(code: i32) -> Error {
|
||||||
|
Error { repr: Repr::Os(code) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the OS error that this error represents (if any).
|
||||||
|
///
|
||||||
|
/// If this `Error` was constructed via `last_os_error` or
|
||||||
|
/// `from_raw_os_error`, then this function will return `Some`, otherwise
|
||||||
|
/// it will return `None`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_os_error(err: &Error) {
|
||||||
|
/// if let Some(raw_os_err) = err.raw_os_error() {
|
||||||
|
/// println!("raw OS error: {:?}", raw_os_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("Not an OS error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "raw OS error: ...".
|
||||||
|
/// print_os_error(&Error::last_os_error());
|
||||||
|
/// // Will print "Not an OS error".
|
||||||
|
/// print_os_error(&Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn raw_os_error(&self) -> Option<i32> {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(i) => Some(i),
|
||||||
|
Repr::Custom(..) => None,
|
||||||
|
Repr::Simple(..) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the inner error wrapped by this error (if any).
|
||||||
|
///
|
||||||
|
/// If this `Error` was constructed via `new` then this function will
|
||||||
|
/// return `Some`, otherwise it will return `None`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: &Error) {
|
||||||
|
/// if let Some(inner_err) = err.get_ref() {
|
||||||
|
/// println!("Inner error: {:?}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(&Error::last_os_error());
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(&Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn get_ref(&self) -> Option<&String> {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(..) => None,
|
||||||
|
Repr::Simple(..) => None,
|
||||||
|
Repr::Custom(ref c) => Some(&c.error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the inner error wrapped by this error
|
||||||
|
/// (if any).
|
||||||
|
///
|
||||||
|
/// If this `Error` was constructed via `new` then this function will
|
||||||
|
/// return `Some`, otherwise it will return `None`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
/// use std::{error, fmt};
|
||||||
|
/// use std::fmt::Display;
|
||||||
|
///
|
||||||
|
/// #[derive(Debug)]
|
||||||
|
/// struct MyError {
|
||||||
|
/// v: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl MyError {
|
||||||
|
/// fn new() -> MyError {
|
||||||
|
/// MyError {
|
||||||
|
/// v: "oh no!".to_string()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn change_message(&mut self, new_message: &str) {
|
||||||
|
/// self.v = new_message.to_string();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl error::Error for MyError {}
|
||||||
|
///
|
||||||
|
/// impl Display for MyError {
|
||||||
|
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
/// write!(f, "MyError: {}", &self.v)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn change_error(mut err: Error) -> Error {
|
||||||
|
/// if let Some(inner_err) = err.get_mut() {
|
||||||
|
/// inner_err.downcast_mut::<MyError>().unwrap().change_message("I've been changed!");
|
||||||
|
/// }
|
||||||
|
/// err
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn print_error(err: &Error) {
|
||||||
|
/// if let Some(inner_err) = err.get_ref() {
|
||||||
|
/// println!("Inner error: {}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(&change_error(Error::last_os_error()));
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new())));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn get_mut(&mut self) -> Option<&mut String> {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(..) => None,
|
||||||
|
Repr::Simple(..) => None,
|
||||||
|
Repr::Custom(ref mut c) => Some(&mut c.error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the `Error`, returning its inner error (if any).
|
||||||
|
///
|
||||||
|
/// If this `Error` was constructed via `new` then this function will
|
||||||
|
/// return `Some`, otherwise it will return `None`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: Error) {
|
||||||
|
/// if let Some(inner_err) = err.into_inner() {
|
||||||
|
/// println!("Inner error: {}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(Error::last_os_error());
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(self) -> Option<String> {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(..) => None,
|
||||||
|
Repr::Simple(..) => None,
|
||||||
|
Repr::Custom(c) => Some(c.error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the corresponding `ErrorKind` for this error.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: Error) {
|
||||||
|
/// println!("{:?}", err.kind());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(Error::last_os_error());
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn kind(&self) -> ErrorKind {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(_code) => ErrorKind::Other,
|
||||||
|
Repr::Custom(ref c) => c.kind,
|
||||||
|
Repr::Simple(kind) => kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Repr {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Repr::Os(code) => fmt
|
||||||
|
.debug_struct("Os")
|
||||||
|
.field("code", &code)
|
||||||
|
.finish(),
|
||||||
|
Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt),
|
||||||
|
Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(code) => {
|
||||||
|
write!(fmt, "os error {}", code)
|
||||||
|
}
|
||||||
|
Repr::Custom(ref c) => c.error.fmt(fmt),
|
||||||
|
Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _assert_error_is_sync_send() {
|
||||||
|
fn _is_sync_send<T: Sync + Send>() {}
|
||||||
|
_is_sync_send::<Error>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{Custom, Error, ErrorKind, Repr};
|
||||||
|
use crate::error;
|
||||||
|
use crate::fmt;
|
||||||
|
use crate::sys::decode_error_kind;
|
||||||
|
use crate::sys::os::error_string;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_debug_error() {
|
||||||
|
let code = 6;
|
||||||
|
let msg = error_string(code);
|
||||||
|
let kind = decode_error_kind(code);
|
||||||
|
let err = Error {
|
||||||
|
repr: Repr::Custom(box Custom {
|
||||||
|
kind: ErrorKind::InvalidInput,
|
||||||
|
error: box Error { repr: super::Repr::Os(code) },
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let expected = format!(
|
||||||
|
"Custom {{ \
|
||||||
|
kind: InvalidInput, \
|
||||||
|
error: Os {{ \
|
||||||
|
code: {:?}, \
|
||||||
|
kind: {:?}, \
|
||||||
|
message: {:?} \
|
||||||
|
}} \
|
||||||
|
}}",
|
||||||
|
code, kind, msg
|
||||||
|
);
|
||||||
|
assert_eq!(format!("{:?}", err), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_downcasting() {
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TestError;
|
||||||
|
|
||||||
|
impl fmt::Display for TestError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("asdf")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for TestError {}
|
||||||
|
|
||||||
|
// we have to call all of these UFCS style right now since method
|
||||||
|
// resolution won't implicitly drop the Send+Sync bounds
|
||||||
|
let mut err = Error::new(ErrorKind::Other, TestError);
|
||||||
|
assert!(err.get_ref().unwrap().is::<TestError>());
|
||||||
|
assert_eq!("asdf", err.get_ref().unwrap().to_string());
|
||||||
|
assert!(err.get_mut().unwrap().is::<TestError>());
|
||||||
|
let extracted = err.into_inner().unwrap();
|
||||||
|
extracted.downcast::<TestError>().unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,378 @@
|
||||||
|
use core::cmp;
|
||||||
|
use core::fmt;
|
||||||
|
use crate::io::{
|
||||||
|
self, Error, ErrorKind, Initializer, Read, Seek, SeekFrom, Write,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "collections")] use crate::io::BufRead;
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
use collections::{
|
||||||
|
vec::Vec,
|
||||||
|
string::String,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Forwarding implementations
|
||||||
|
|
||||||
|
impl<R: Read + ?Sized> Read for &mut R {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
(**self).read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
(**self).initializer()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
(**self).read_to_end(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||||
|
(**self).read_to_string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
(**self).read_exact(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<W: Write + ?Sized> Write for &mut W {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
(**self).write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
(**self).flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||||
|
(**self).write_all(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||||
|
(**self).write_fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<S: Seek + ?Sized> Seek for &mut S {
|
||||||
|
#[inline]
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
(**self).seek(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "collections")]
|
||||||
|
impl<B: BufRead + ?Sized> BufRead for &mut B {
|
||||||
|
#[inline]
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
(**self).fill_buf()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
(**self).consume(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
(**self).read_until(byte, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||||
|
(**self).read_line(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="alloc")]
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl<R: Read + ?Sized> Read for Box<R> {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
(**self).read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
(**self).read_to_end(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||||
|
(**self).read_to_string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
(**self).read_exact(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature="alloc")]
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl<W: Write + ?Sized> Write for Box<W> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
(**self).write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
(**self).flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||||
|
(**self).write_all(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||||
|
(**self).write_fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl<S: Seek + ?Sized> Seek for Box<S> {
|
||||||
|
#[inline]
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
(**self).seek(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl<B: BufRead + ?Sized> BufRead for Box<B> {
|
||||||
|
#[inline]
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
(**self).fill_buf()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
(**self).consume(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
(**self).read_until(byte, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||||
|
(**self).read_line(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by panicking::default_hook
|
||||||
|
#[cfg(test)]
|
||||||
|
/// This impl is only used by printing logic, so any error returned is always
|
||||||
|
/// of kind `Other`, and should be ignored.
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl Write for Box<dyn (::realstd::io::Write) + Send> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
(**self).write(buf).map_err(|_| ErrorKind::Other.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
(**self).flush().map_err(|_| ErrorKind::Other.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// In-memory buffer implementations
|
||||||
|
|
||||||
|
/// Read is implemented for `&[u8]` by copying from the slice.
|
||||||
|
///
|
||||||
|
/// Note that reading updates the slice to point to the yet unread part.
|
||||||
|
/// The slice will be empty when EOF is reached.
|
||||||
|
impl Read for &[u8] {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
let amt = cmp::min(buf.len(), self.len());
|
||||||
|
let (a, b) = self.split_at(amt);
|
||||||
|
|
||||||
|
// First check if the amount of bytes we want to read is small:
|
||||||
|
// `copy_from_slice` will generally expand to a call to `memcpy`, and
|
||||||
|
// for a single byte the overhead is significant.
|
||||||
|
if amt == 1 {
|
||||||
|
buf[0] = a[0];
|
||||||
|
} else {
|
||||||
|
buf[..amt].copy_from_slice(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
*self = b;
|
||||||
|
Ok(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
Initializer::nop()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
if buf.len() > self.len() {
|
||||||
|
return Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"));
|
||||||
|
}
|
||||||
|
let (a, b) = self.split_at(buf.len());
|
||||||
|
|
||||||
|
// First check if the amount of bytes we want to read is small:
|
||||||
|
// `copy_from_slice` will generally expand to a call to `memcpy`, and
|
||||||
|
// for a single byte the overhead is significant.
|
||||||
|
if buf.len() == 1 {
|
||||||
|
buf[0] = a[0];
|
||||||
|
} else {
|
||||||
|
buf.copy_from_slice(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
*self = b;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
buf.extend_from_slice(*self);
|
||||||
|
let len = self.len();
|
||||||
|
*self = &self[len..];
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl BufRead for &[u8] {
|
||||||
|
#[inline]
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
Ok(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
*self = &self[amt..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting
|
||||||
|
/// its data.
|
||||||
|
///
|
||||||
|
/// Note that writing updates the slice to point to the yet unwritten part.
|
||||||
|
/// The slice will be empty when it has been completely overwritten.
|
||||||
|
impl Write for &mut [u8] {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||||
|
let amt = cmp::min(data.len(), self.len());
|
||||||
|
let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
|
||||||
|
a.copy_from_slice(&data[..amt]);
|
||||||
|
*self = b;
|
||||||
|
Ok(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
|
||||||
|
if self.write(data)? == data.len() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write is implemented for `Vec<u8>` by appending to the vector.
|
||||||
|
/// The vector will grow as needed.
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl Write for Vec<u8> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||||
|
self.extend_from_slice(buf);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::io::prelude::*;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_read_slice(b: &mut test::Bencher) {
|
||||||
|
let buf = [5; 1024];
|
||||||
|
let mut dst = [0; 128];
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut rd = &buf[..];
|
||||||
|
for _ in 0..8 {
|
||||||
|
let _ = rd.read(&mut dst);
|
||||||
|
test::black_box(&dst);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_write_slice(b: &mut test::Bencher) {
|
||||||
|
let mut buf = [0; 1024];
|
||||||
|
let src = [5; 128];
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut wr = &mut buf[..];
|
||||||
|
for _ in 0..8 {
|
||||||
|
let _ = wr.write_all(&src);
|
||||||
|
test::black_box(&wr);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_read_vec(b: &mut test::Bencher) {
|
||||||
|
let buf = vec![5; 1024];
|
||||||
|
let mut dst = [0; 128];
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut rd = &buf[..];
|
||||||
|
for _ in 0..8 {
|
||||||
|
let _ = rd.read(&mut dst);
|
||||||
|
test::black_box(&dst);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_write_vec(b: &mut test::Bencher) {
|
||||||
|
let mut buf = Vec::with_capacity(1024);
|
||||||
|
let src = [5; 128];
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut wr = &mut buf[..];
|
||||||
|
for _ in 0..8 {
|
||||||
|
let _ = wr.write_all(&src);
|
||||||
|
test::black_box(&wr);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,13 @@
|
||||||
|
//! The I/O Prelude
|
||||||
|
//!
|
||||||
|
//! The purpose of this module is to alleviate imports of many common I/O traits
|
||||||
|
//! by adding a glob import to the top of I/O heavy modules:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # #![allow(unused_imports)]
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
|
||||||
|
pub use super::{Read, Seek, Write};
|
||||||
|
#[cfg(feature = "collections")] pub use super::BufRead;
|
|
@ -0,0 +1,269 @@
|
||||||
|
#![allow(missing_copy_implementations)]
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
use core::mem;
|
||||||
|
use crate::io::{self, ErrorKind, Initializer, Read, Write};
|
||||||
|
#[cfg(feature = "collections")] use crate::io::BufRead;
|
||||||
|
|
||||||
|
/// Copies the entire contents of a reader into a writer.
|
||||||
|
///
|
||||||
|
/// This function will continuously read data from `reader` and then
|
||||||
|
/// write it into `writer` in a streaming fashion until `reader`
|
||||||
|
/// returns EOF.
|
||||||
|
///
|
||||||
|
/// On success, the total number of bytes that were copied from
|
||||||
|
/// `reader` to `writer` is returned.
|
||||||
|
///
|
||||||
|
/// If you’re wanting to copy the contents of one file to another and you’re
|
||||||
|
/// working with filesystem paths, see the [`fs::copy`] function.
|
||||||
|
///
|
||||||
|
/// [`fs::copy`]: ../fs/fn.copy.html
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error immediately if any call to `read` or
|
||||||
|
/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
|
||||||
|
/// handled by this function and the underlying operation is retried.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// fn main() -> io::Result<()> {
|
||||||
|
/// let mut reader: &[u8] = b"hello";
|
||||||
|
/// let mut writer: Vec<u8> = vec![];
|
||||||
|
///
|
||||||
|
/// io::copy(&mut reader, &mut writer)?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(&b"hello"[..], &writer[..]);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
|
||||||
|
where
|
||||||
|
R: Read,
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
let mut buf = unsafe {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let mut buf: [u8; super::DEFAULT_BUF_SIZE] = mem::uninitialized();
|
||||||
|
reader.initializer().initialize(&mut buf);
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut written = 0;
|
||||||
|
loop {
|
||||||
|
let len = match reader.read(&mut buf) {
|
||||||
|
Ok(0) => return Ok(written),
|
||||||
|
Ok(len) => len,
|
||||||
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
writer.write_all(&buf[..len])?;
|
||||||
|
written += len as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A reader which is always at EOF.
|
||||||
|
///
|
||||||
|
/// This struct is generally created by calling [`empty`]. Please see
|
||||||
|
/// the documentation of [`empty()`][`empty`] for more details.
|
||||||
|
///
|
||||||
|
/// [`empty`]: fn.empty.html
|
||||||
|
pub struct Empty {
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new handle to an empty reader.
|
||||||
|
///
|
||||||
|
/// All reads from the returned reader will return [`Ok`]`(0)`.
|
||||||
|
///
|
||||||
|
/// [`Ok`]: ../result/enum.Result.html#variant.Ok
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A slightly sad example of not reading anything into a buffer:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{self, Read};
|
||||||
|
///
|
||||||
|
/// let mut buffer = String::new();
|
||||||
|
/// io::empty().read_to_string(&mut buffer).unwrap();
|
||||||
|
/// assert!(buffer.is_empty());
|
||||||
|
/// ```
|
||||||
|
pub fn empty() -> Empty {
|
||||||
|
Empty { _priv: () }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Empty {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
Initializer::nop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl BufRead for Empty {
|
||||||
|
#[inline]
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
Ok(&[])
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn consume(&mut self, _n: usize) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Empty {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.pad("Empty { .. }")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A reader which yields one byte over and over and over and over and over and...
|
||||||
|
///
|
||||||
|
/// This struct is generally created by calling [`repeat`][repeat]. Please
|
||||||
|
/// see the documentation of `repeat()` for more details.
|
||||||
|
///
|
||||||
|
/// [repeat]: fn.repeat.html
|
||||||
|
pub struct Repeat {
|
||||||
|
byte: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an instance of a reader that infinitely repeats one byte.
|
||||||
|
///
|
||||||
|
/// All reads from this reader will succeed by filling the specified buffer with
|
||||||
|
/// the given byte.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{self, Read};
|
||||||
|
///
|
||||||
|
/// let mut buffer = [0; 3];
|
||||||
|
/// io::repeat(0b101).read_exact(&mut buffer).unwrap();
|
||||||
|
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
|
||||||
|
/// ```
|
||||||
|
pub fn repeat(byte: u8) -> Repeat {
|
||||||
|
Repeat { byte }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Repeat {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
for slot in &mut *buf {
|
||||||
|
*slot = self.byte;
|
||||||
|
}
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
Initializer::nop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Repeat {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.pad("Repeat { .. }")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A writer which will move data into the void.
|
||||||
|
///
|
||||||
|
/// This struct is generally created by calling [`sink`][sink]. Please
|
||||||
|
/// see the documentation of `sink()` for more details.
|
||||||
|
///
|
||||||
|
/// [sink]: fn.sink.html
|
||||||
|
pub struct Sink {
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an instance of a writer which will successfully consume all data.
|
||||||
|
///
|
||||||
|
/// All calls to `write` on the returned instance will return `Ok(buf.len())`
|
||||||
|
/// and the contents of the buffer will not be inspected.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use std::io::{self, Write};
|
||||||
|
///
|
||||||
|
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||||
|
/// let num_bytes = io::sink().write(&buffer).unwrap();
|
||||||
|
/// assert_eq!(num_bytes, 5);
|
||||||
|
/// ```
|
||||||
|
pub fn sink() -> Sink {
|
||||||
|
Sink { _priv: () }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Sink {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Sink {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.pad("Sink { .. }")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::io::prelude::*;
|
||||||
|
use crate::io::{copy, empty, repeat, sink};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn copy_copies() {
|
||||||
|
let mut r = repeat(0).take(4);
|
||||||
|
let mut w = sink();
|
||||||
|
assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
|
||||||
|
|
||||||
|
let mut r = repeat(0).take(1 << 17);
|
||||||
|
assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sink_sinks() {
|
||||||
|
let mut s = sink();
|
||||||
|
assert_eq!(s.write(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(s.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(s.write(&[0; 1024]).unwrap(), 1024);
|
||||||
|
assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_reads() {
|
||||||
|
let mut e = empty();
|
||||||
|
assert_eq!(e.read(&mut []).unwrap(), 0);
|
||||||
|
assert_eq!(e.read(&mut [0]).unwrap(), 0);
|
||||||
|
assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
|
||||||
|
assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn repeat_repeats() {
|
||||||
|
let mut r = repeat(4);
|
||||||
|
let mut b = [0; 1024];
|
||||||
|
assert_eq!(r.read(&mut b).unwrap(), 1024);
|
||||||
|
assert!(b.iter().all(|b| *b == 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn take_some_bytes() {
|
||||||
|
assert_eq!(repeat(4).take(100).bytes().count(), 100);
|
||||||
|
assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4);
|
||||||
|
assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
//! <p id="core_io-show-docblock"></p>
|
||||||
|
//! This is just a listing of the functionality available in this crate. See
|
||||||
|
//! the [std documentation](https://doc.rust-lang.org/nightly/std/io/index.html)
|
||||||
|
//! for a full description of the functionality.
|
||||||
|
#![allow(stable_features,unused_features)]
|
||||||
|
#![feature(question_mark,const_fn,copy_from_slice,try_from,str_internals,align_offset,slice_internals)]
|
||||||
|
#![cfg_attr(any(feature="alloc",feature="collections"),feature(alloc))]
|
||||||
|
#![cfg_attr(pattern_guards,feature(bind_by_move_pattern_guards,nll))]
|
||||||
|
#![cfg_attr(non_exhaustive,feature(non_exhaustive))]
|
||||||
|
#![cfg_attr(unicode,feature(str_char))]
|
||||||
|
#![cfg_attr(unicode,feature(unicode))]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[cfg_attr(feature="collections",macro_use)]
|
||||||
|
#[cfg_attr(feature="collections",allow(unused_imports))]
|
||||||
|
#[cfg(feature="collections")] extern crate alloc as collections;
|
||||||
|
#[cfg(feature="alloc")] extern crate alloc;
|
||||||
|
#[cfg(rustc_unicode)]
|
||||||
|
extern crate rustc_unicode;
|
||||||
|
#[cfg(std_unicode)]
|
||||||
|
extern crate std_unicode;
|
||||||
|
|
||||||
|
#[cfg(not(feature="collections"))]
|
||||||
|
pub type ErrorString = &'static str;
|
||||||
|
|
||||||
|
// Provide Box::new wrapper
|
||||||
|
#[cfg(not(feature="alloc"))]
|
||||||
|
struct FakeBox<T>(core::marker::PhantomData<T>);
|
||||||
|
#[cfg(not(feature="alloc"))]
|
||||||
|
impl<T> FakeBox<T> {
|
||||||
|
fn new(val: T) -> T {
|
||||||
|
val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed for older compilers, to ignore vec!/format! macros in tests
|
||||||
|
#[cfg(not(feature="collections"))]
|
||||||
|
#[allow(unused)]
|
||||||
|
macro_rules! vec (
|
||||||
|
( $ elem : expr ; $ n : expr ) => { () };
|
||||||
|
( $ ( $ x : expr ) , * ) => { () };
|
||||||
|
( $ ( $ x : expr , ) * ) => { () };
|
||||||
|
);
|
||||||
|
#[cfg(not(feature="collections"))]
|
||||||
|
#[allow(unused)]
|
||||||
|
macro_rules! format {
|
||||||
|
( $ ( $ arg : tt ) * ) => { () };
|
||||||
|
}
|
||||||
|
|
||||||
|
mod io;
|
||||||
|
pub use io::*;
|
|
@ -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"
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -3,7 +3,7 @@ use core::{
|
||||||
mem,
|
mem,
|
||||||
slice,
|
slice,
|
||||||
};
|
};
|
||||||
use alloc::alloc::{alloc_zeroed, dealloc, Layout, LayoutError};
|
use alloc::alloc::{alloc_zeroed, dealloc, Layout, LayoutErr};
|
||||||
use super::{
|
use super::{
|
||||||
elf::*,
|
elf::*,
|
||||||
Error,
|
Error,
|
||||||
|
@ -27,7 +27,7 @@ pub struct Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
pub fn new(size: usize, align: usize) -> Result<Self, LayoutError> {
|
pub fn new(size: usize, align: usize) -> Result<Self, LayoutErr> {
|
||||||
let layout = Layout::from_size_align(size, align)?;
|
let layout = Layout::from_size_align(size, align)?;
|
||||||
let data = unsafe {
|
let data = unsafe {
|
||||||
let ptr = alloc_zeroed(layout);
|
let ptr = alloc_zeroed(layout);
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = lib.image.ptr() as u32 + 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> {
|
.ok_or("unsupported relocation type")?;
|
||||||
for reloc in relocs {
|
match rel_type {
|
||||||
let rel_type = RelType::new(arch, reloc.type_info())
|
RelType::Lookup => {
|
||||||
.ok_or("unsupported relocation type")?;
|
let sym = lib.symtab().get(ELF32_R_SYM(rela.r_info) as usize)
|
||||||
match rel_type {
|
.ok_or("symbol out of bounds of symbol table")?;
|
||||||
RelType::LookupAbs => {
|
let sym_name = lib.name_starting_at(sym.st_name as usize)?;
|
||||||
let sym = lib.symtab().get(reloc.sym_info() as usize)
|
|
||||||
.ok_or("symbol out of bounds of symbol table")?;
|
|
||||||
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);
|
||||||
|
|
|
@ -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 = []
|
|
|
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -6,7 +6,6 @@ fn main() {
|
||||||
|
|
||||||
mod llvm_libunwind {
|
mod llvm_libunwind {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::env;
|
|
||||||
|
|
||||||
fn setup_options(cfg: &mut cc::Build) {
|
fn setup_options(cfg: &mut cc::Build) {
|
||||||
cfg.no_default_flags(true);
|
cfg.no_default_flags(true);
|
||||||
|
@ -17,13 +16,9 @@ mod llvm_libunwind {
|
||||||
cfg.flag("-fno-PIC");
|
cfg.flag("-fno-PIC");
|
||||||
cfg.flag("-Isrc");
|
cfg.flag("-Isrc");
|
||||||
cfg.flag("-isystem../include");
|
cfg.flag("-isystem../include");
|
||||||
if let Ok(extra_include) = env::var("CLANG_EXTRA_INCLUDE_DIR") {
|
|
||||||
cfg.flag(&("-isystem".to_owned() + &extra_include));
|
|
||||||
}
|
|
||||||
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");
|
||||||
|
|
|
@ -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,
|
_Unwind_Exception* exceptionObject,
|
||||||
uint64_t exceptionClass,
|
struct _Unwind_Context* context);
|
||||||
_Unwind_Exception* exceptionObject,
|
|
||||||
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,
|
||||||
|
|
|
@ -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,
|
unwind_phase2(&uc, &cursor, exception_object, true);
|
||||||
(void *)exception_object->unwinder_cache.reserved3);
|
|
||||||
else
|
|
||||||
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)
|
||||||
|
|
|
@ -6,13 +6,9 @@ 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"]
|
||||||
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"]
|
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"
|
||||||
|
@ -25,20 +21,18 @@ byteorder = { version = "1.3", default-features = false }
|
||||||
void = { version = "1", default-features = false }
|
void = { version = "1", default-features = false }
|
||||||
futures = { version = "0.3", default-features = false, features = ["async-await"] }
|
futures = { version = "0.3", default-features = false, features = ["async-await"] }
|
||||||
async-recursion = "0.3"
|
async-recursion = "0.3"
|
||||||
|
fatfs = { version = "0.3", features = ["core_io"], default-features = false }
|
||||||
log_buffer = { version = "1.2" }
|
log_buffer = { version = "1.2" }
|
||||||
libm = { version = "0.2", features = ["unstable"] }
|
libm = { version = "0.2", features = ["unstable"] }
|
||||||
vcell = "0.1"
|
vcell = "0.1"
|
||||||
|
|
||||||
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["ipv6"]}
|
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
||||||
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
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" }
|
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"] }
|
|
||||||
|
|
||||||
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" }
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,10 @@ SECTIONS
|
||||||
.heap (NOLOAD) : ALIGN(8)
|
.heap (NOLOAD) : ALIGN(8)
|
||||||
{
|
{
|
||||||
__heap0_start = .;
|
__heap0_start = .;
|
||||||
. += 0x8000000;
|
. += 0x800000;
|
||||||
__heap0_end = .;
|
__heap0_end = .;
|
||||||
__heap1_start = .;
|
__heap1_start = .;
|
||||||
. += 0x8000000;
|
. += 0x800000;
|
||||||
__heap1_end = .;
|
__heap1_end = .;
|
||||||
} > SDRAM
|
} > SDRAM
|
||||||
|
|
||||||
|
@ -69,18 +69,4 @@ SECTIONS
|
||||||
. += 0x20000;
|
. += 0x20000;
|
||||||
__stack0_start = .;
|
__stack0_start = .;
|
||||||
} > SDRAM
|
} > SDRAM
|
||||||
|
|
||||||
.irq_stack1 (NOLOAD) : ALIGN(8)
|
|
||||||
{
|
|
||||||
__irq_stack1_end = .;
|
|
||||||
. += 0x100;
|
|
||||||
__irq_stack1_start = .;
|
|
||||||
} > SDRAM
|
|
||||||
|
|
||||||
.irq_stack0 (NOLOAD) : ALIGN(8)
|
|
||||||
{
|
|
||||||
__irq_stack0_end = .;
|
|
||||||
. += 0x100;
|
|
||||||
__irq_stack0_start = .;
|
|
||||||
} > SDRAM
|
|
||||||
}
|
}
|
|
@ -50,7 +50,6 @@ struct Header {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_header(stream: &mut TcpStream, header: &Header) -> Result<(), Error> {
|
async fn write_header(stream: &mut TcpStream, header: &Header) -> Result<(), Error> {
|
||||||
stream.send_slice("e".as_bytes()).await?;
|
|
||||||
write_i32(stream, header.sent_bytes as i32).await?;
|
write_i32(stream, header.sent_bytes as i32).await?;
|
||||||
write_i64(stream, header.total_byte_count as i64).await?;
|
write_i64(stream, header.total_byte_count as i64).await?;
|
||||||
write_i8(stream, header.error_occurred as i8).await?;
|
write_i8(stream, header.error_occurred as i8).await?;
|
||||||
|
|
|
@ -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};
|
||||||
|
@ -17,23 +17,19 @@ use libboard_zynq::{
|
||||||
},
|
},
|
||||||
timer::GlobalTimer,
|
timer::GlobalTimer,
|
||||||
};
|
};
|
||||||
use libcortex_a9::{semaphore::Semaphore, mutex::Mutex, sync_channel::{Sender, Receiver}};
|
use libcortex_a9::{semaphore::Semaphore, mutex::Mutex};
|
||||||
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 libboard_artiq::drtio_routing;
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
|
||||||
use libboard_zynq::error_led::ErrorLED;
|
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
use crate::net_settings;
|
||||||
use crate::proto_async::*;
|
use crate::proto_async::*;
|
||||||
use crate::kernel;
|
use crate::kernel;
|
||||||
use crate::rpc;
|
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 {
|
||||||
|
@ -41,6 +37,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>;
|
||||||
|
@ -52,6 +49,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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +86,7 @@ static CACHE_STORE: Mutex<BTreeMap<String, Vec<i32>>> = Mutex::new(BTreeMap::new
|
||||||
static DMA_RECORD_STORE: Mutex<BTreeMap<String, (Vec<u8>, i64)>> = Mutex::new(BTreeMap::new());
|
static DMA_RECORD_STORE: Mutex<BTreeMap<String, (Vec<u8>, i64)>> = Mutex::new(BTreeMap::new());
|
||||||
|
|
||||||
async fn write_header(stream: &TcpStream, reply: Reply) -> Result<()> {
|
async fn write_header(stream: &TcpStream, reply: Reply) -> Result<()> {
|
||||||
stream.send_slice(&[0x5a, 0x5a, 0x5a, 0x5a, reply.to_u8().unwrap()]).await?;
|
stream.send([0x5a, 0x5a, 0x5a, 0x5a, reply.to_u8().unwrap()].iter().copied()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,13 +95,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) =>
|
||||||
|
@ -122,46 +120,12 @@ async fn read_bytes(stream: &TcpStream, max_length: usize) -> Result<Vec<u8>> {
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
const RETRY_LIMIT: usize = 100;
|
async fn read_string(stream: &TcpStream, max_length: usize) -> Result<String> {
|
||||||
|
let bytes = read_bytes(stream, max_length).await?;
|
||||||
async fn fast_send(sender: &mut Sender<'_, kernel::Message>, content: kernel::Message) {
|
Ok(String::from_utf8(bytes).map_err(|err| Error::Utf8Error(err.utf8_error()))?)
|
||||||
let mut content = content;
|
|
||||||
for _ in 0..RETRY_LIMIT {
|
|
||||||
match sender.try_send(content) {
|
|
||||||
Ok(()) => {
|
|
||||||
return
|
|
||||||
},
|
|
||||||
Err(v) => {
|
|
||||||
content = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sender.async_send(content).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fast_recv(receiver: &mut Receiver<'_, kernel::Message>) -> kernel::Message {
|
async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kernel::Control>>) -> Result<()> {
|
||||||
for _ in 0..RETRY_LIMIT {
|
|
||||||
match receiver.try_recv() {
|
|
||||||
Ok(v) => {
|
|
||||||
return v;
|
|
||||||
},
|
|
||||||
Err(()) => ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
receiver.async_recv().await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn write_exception_string(stream: &TcpStream, s: CSlice<'static, u8>) -> 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;
|
||||||
|
@ -174,13 +138,13 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||||
let stream = stream.unwrap();
|
let stream = stream.unwrap();
|
||||||
write_header(stream, Reply::RPCRequest).await?;
|
write_header(stream, Reply::RPCRequest).await?;
|
||||||
write_bool(stream, is_async).await?;
|
write_bool(stream, is_async).await?;
|
||||||
stream.send_slice(&data).await?;
|
stream.send(data.iter().copied()).await?;
|
||||||
if !is_async {
|
if !is_async {
|
||||||
let host_request = read_request(stream, false).await?.unwrap();
|
let host_request = read_request(stream, false).await?.unwrap();
|
||||||
match host_request {
|
match host_request {
|
||||||
Request::RPCReply => {
|
Request::RPCReply => {
|
||||||
let tag = read_bytes(stream, 512).await?;
|
let tag = read_bytes(stream, 512).await?;
|
||||||
let slot = match fast_recv(&mut control.borrow_mut().rx).await {
|
let slot = match control.borrow_mut().rx.async_recv().await {
|
||||||
kernel::Message::RpcRecvRequest(slot) => slot,
|
kernel::Message::RpcRecvRequest(slot) => slot,
|
||||||
other => panic!("expected root value slot from core1, not {:?}", other),
|
other => panic!("expected root value slot from core1, not {:?}", other),
|
||||||
};
|
};
|
||||||
|
@ -193,8 +157,8 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||||
0 as *mut ()
|
0 as *mut ()
|
||||||
} else {
|
} else {
|
||||||
let mut control = control.borrow_mut();
|
let mut control = control.borrow_mut();
|
||||||
fast_send(&mut control.tx, kernel::Message::RpcRecvReply(Ok(size))).await;
|
control.tx.async_send(kernel::Message::RpcRecvReply(Ok(size))).await;
|
||||||
match fast_recv(&mut control.rx).await {
|
match control.rx.async_recv().await {
|
||||||
kernel::Message::RpcRecvRequest(slot) => slot,
|
kernel::Message::RpcRecvRequest(slot) => slot,
|
||||||
other => panic!("expected nested value slot from kernel CPU, not {:?}", other),
|
other => panic!("expected nested value slot from kernel CPU, not {:?}", other),
|
||||||
}
|
}
|
||||||
|
@ -209,17 +173,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;
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -229,46 +193,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_i64(stream, exception.param[0] as i64).await?;
|
||||||
write_i32(stream, exception.id as i32).await?;
|
write_i64(stream, exception.param[1] as i64).await?;
|
||||||
write_exception_string(stream, exception.message).await?;
|
write_i64(stream, exception.param[2] as i64).await?;
|
||||||
write_i64(stream, exception.param[0] as i64).await?;
|
write_chunk(stream, exception.file.as_ref()).await?;
|
||||||
write_i64(stream, exception.param[1] as i64).await?;
|
write_i32(stream, exception.line as i32).await?;
|
||||||
write_i64(stream, exception.param[2] as i64).await?;
|
write_i32(stream, exception.column as i32).await?;
|
||||||
write_exception_string(stream, exception.file).await?;
|
write_chunk(stream, exception.function.as_ref()).await?;
|
||||||
write_i32(stream, exception.line as i32).await?;
|
|
||||||
write_i32(stream, exception.column as i32).await?;
|
|
||||||
write_exception_string(stream, exception.function).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;
|
||||||
|
@ -292,11 +244,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);
|
||||||
}
|
}
|
||||||
|
@ -338,13 +285,10 @@ 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);
|
||||||
}
|
}
|
||||||
stream.send_slice("e".as_bytes()).await?;
|
|
||||||
loop {
|
loop {
|
||||||
let request = read_request(stream, true).await?;
|
let request = read_request(stream, true).await?;
|
||||||
if request.is_none() {
|
if request.is_none() {
|
||||||
|
@ -354,14 +298,14 @@ async fn handle_connection(stream: &mut TcpStream, control: Rc<RefCell<kernel::C
|
||||||
match request {
|
match request {
|
||||||
Request::SystemInfo => {
|
Request::SystemInfo => {
|
||||||
write_header(stream, Reply::SystemInfo).await?;
|
write_header(stream, Reply::SystemInfo).await?;
|
||||||
stream.send_slice("ARZQ".as_bytes()).await?;
|
stream.send("ARZQ".bytes()).await?;
|
||||||
},
|
},
|
||||||
Request::LoadKernel => {
|
Request::LoadKernel => {
|
||||||
let buffer = read_bytes(stream, 1024*1024).await?;
|
let buffer = read_bytes(stream, 1024*1024).await?;
|
||||||
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);
|
||||||
|
@ -371,15 +315,15 @@ 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::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::default(net_addresses.hardware_addr.0.clone());
|
||||||
const RX_LEN: usize = 64;
|
const RX_LEN: usize = 8;
|
||||||
// Number of transmission buffers (minimum is two because with
|
// Number of transmission buffers (minimum is two because with
|
||||||
// one, duplicate packet transmission occurs)
|
// one, duplicate packet transmission occurs)
|
||||||
const TX_LEN: usize = 64;
|
const TX_LEN: usize = 8;
|
||||||
let eth = eth.start_rx(RX_LEN);
|
let eth = eth.start_rx(RX_LEN);
|
||||||
let mut eth = eth.start_tx(TX_LEN);
|
let mut eth = eth.start_tx(TX_LEN);
|
||||||
|
|
||||||
|
@ -412,21 +356,9 @@ 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...?
|
mgmt::start();
|
||||||
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());
|
||||||
|
@ -434,20 +366,18 @@ 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!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mgmt::start(cfg);
|
|
||||||
|
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
let connection = Rc::new(Semaphore::new(1, 1));
|
let connection = Rc::new(Semaphore::new(1, 1));
|
||||||
let terminate = Rc::new(Semaphore::new(0, 1));
|
let terminate = Rc::new(Semaphore::new(0, 1));
|
||||||
loop {
|
loop {
|
||||||
let mut stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
|
let stream = TcpStream::accept(1381, 2048, 2048).await.unwrap();
|
||||||
|
|
||||||
if connection.try_wait().is_none() {
|
if connection.try_wait().is_none() {
|
||||||
// there is an existing connection
|
// there is an existing connection
|
||||||
|
@ -459,23 +389,22 @@ 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 {
|
||||||
info!("Loading idle kernel");
|
info!("Loading idle kernel");
|
||||||
let _ = load_kernel(&buffer, &control, None)
|
let _ = load_kernel(&buffer, &control, None)
|
||||||
.await.map_err(|_| warn!("error loading idle kernel"));
|
.await.map_err(|e| 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(|e| warn!("error running idle kernel"));
|
||||||
info!("Idle kernel terminated");
|
info!("Idle kernel terminated");
|
||||||
}
|
}
|
||||||
}).fuse() => (),
|
}).fuse() => (),
|
||||||
|
@ -492,61 +421,3 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
Instant::from_millis(timer.get_time().0 as i32)
|
Instant::from_millis(timer.get_time().0 as i32)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn soft_panic_main(timer: GlobalTimer, cfg: Config) -> ! {
|
|
||||||
|
|
||||||
let net_addresses = net_settings::get_addresses(&cfg);
|
|
||||||
info!("network addresses: {}", net_addresses);
|
|
||||||
|
|
||||||
let eth = zynq::eth::Eth::eth0(net_addresses.hardware_addr.0.clone());
|
|
||||||
const RX_LEN: usize = 64;
|
|
||||||
// Number of transmission buffers (minimum is two because with
|
|
||||||
// one, duplicate packet transmission occurs)
|
|
||||||
const TX_LEN: usize = 64;
|
|
||||||
let eth = eth.start_rx(RX_LEN);
|
|
||||||
let mut eth = eth.start_tx(TX_LEN);
|
|
||||||
|
|
||||||
let neighbor_cache = NeighborCache::new(alloc::collections::BTreeMap::new());
|
|
||||||
let mut iface = match net_addresses.ipv6_addr {
|
|
||||||
Some(addr) => {
|
|
||||||
let ip_addrs = [
|
|
||||||
IpCidr::new(net_addresses.ipv4_addr, 0),
|
|
||||||
IpCidr::new(net_addresses.ipv6_ll_addr, 0),
|
|
||||||
IpCidr::new(addr, 0)
|
|
||||||
];
|
|
||||||
EthernetInterfaceBuilder::new(&mut eth)
|
|
||||||
.ethernet_addr(net_addresses.hardware_addr)
|
|
||||||
.ip_addrs(ip_addrs)
|
|
||||||
.neighbor_cache(neighbor_cache)
|
|
||||||
.finalize()
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let ip_addrs = [
|
|
||||||
IpCidr::new(net_addresses.ipv4_addr, 0),
|
|
||||||
IpCidr::new(net_addresses.ipv6_ll_addr, 0)
|
|
||||||
];
|
|
||||||
EthernetInterfaceBuilder::new(&mut eth)
|
|
||||||
.ethernet_addr(net_addresses.hardware_addr)
|
|
||||||
.ip_addrs(ip_addrs)
|
|
||||||
.neighbor_cache(neighbor_cache)
|
|
||||||
.finalize()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Sockets::init(32);
|
|
||||||
|
|
||||||
mgmt::start(cfg);
|
|
||||||
|
|
||||||
// getting eth settings disables the LED as it resets GPIO
|
|
||||||
// need to re-enable it here
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
|
||||||
{
|
|
||||||
let mut err_led = ErrorLED::error_led();
|
|
||||||
err_led.toggle(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Sockets::run(&mut iface, || {
|
|
||||||
Instant::from_millis(timer.get_time().0 as i32)
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
use crate::sd_reader;
|
||||||
|
use core::fmt;
|
||||||
|
use alloc::{string::FromUtf8Error, string::String, vec::Vec};
|
||||||
|
use core_io::{self as io, BufRead, BufReader, Read};
|
||||||
|
|
||||||
|
use libboard_zynq::sdio;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error<'a> {
|
||||||
|
SdError(sdio::sd_card::CardInitializationError),
|
||||||
|
IoError(io::Error),
|
||||||
|
Utf8Error(FromUtf8Error),
|
||||||
|
KeyNotFoundError(&'a str),
|
||||||
|
NoConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<'a, T> = core::result::Result<T, Error<'a>>;
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for Error<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::SdError(error) => write!(f, "SD error: {}", error),
|
||||||
|
Error::IoError(error) => write!(f, "I/O error: {}", error),
|
||||||
|
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
|
||||||
|
Error::KeyNotFoundError(name) => write!(f, "Configuration key `{}` not found", name),
|
||||||
|
Error::NoConfig => write!(f, "Configuration not present"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<sdio::sd_card::CardInitializationError> for Error<'a> {
|
||||||
|
fn from(error: sdio::sd_card::CardInitializationError) -> Self {
|
||||||
|
Error::SdError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<io::Error> for Error<'a> {
|
||||||
|
fn from(error: io::Error) -> Self {
|
||||||
|
Error::IoError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<FromUtf8Error> for Error<'a> {
|
||||||
|
fn from(error: FromUtf8Error) -> Self {
|
||||||
|
Error::Utf8Error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_config<'a>(
|
||||||
|
key: &'a str,
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
file: fatfs::File<sd_reader::SdReader>,
|
||||||
|
) -> Result<'a, ()> {
|
||||||
|
let prefix = [key, "="].concat();
|
||||||
|
for line in BufReader::new(file).lines() {
|
||||||
|
let line = line?;
|
||||||
|
if line.starts_with(&prefix) {
|
||||||
|
buffer.extend(line[prefix.len()..].as_bytes());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::KeyNotFoundError(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
fs: Option<fatfs::FileSystem<sd_reader::SdReader>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new() -> Result<'static, Self> {
|
||||||
|
let sdio = sdio::SDIO::sdio0(true);
|
||||||
|
if !sdio.is_card_inserted() {
|
||||||
|
Err(sdio::sd_card::CardInitializationError::NoCardInserted)?;
|
||||||
|
}
|
||||||
|
let sd = sdio::sd_card::SdCard::from_sdio(sdio)?;
|
||||||
|
let reader = sd_reader::SdReader::new(sd);
|
||||||
|
|
||||||
|
let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?;
|
||||||
|
Ok(Config { fs: Some(fs) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_dummy() -> Self {
|
||||||
|
Config { fs: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<'b>(&self, key: &'b str) -> Result<'b, Vec<u8>> {
|
||||||
|
if let Some(fs) = &self.fs {
|
||||||
|
let root_dir = fs.root_dir();
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
match root_dir.open_file(&["/CONFIG/", key, ".BIN"].concat()) {
|
||||||
|
Ok(mut f) => f.read_to_end(&mut buffer).map(|_| ())?,
|
||||||
|
Err(_) => match root_dir.open_file("/CONFIG.TXT") {
|
||||||
|
Ok(f) => parse_config(key, &mut buffer, f)?,
|
||||||
|
Err(_) => return Err(Error::KeyNotFoundError(key)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(buffer)
|
||||||
|
} else {
|
||||||
|
Err(Error::NoConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_str<'b>(&self, key: &'b str) -> Result<'b, String> {
|
||||||
|
Ok(String::from_utf8(self.read(key)?)?)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,27 +120,45 @@ 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,
|
||||||
};
|
};
|
||||||
match eh_action {
|
let exception_info = &mut *(exception_object as *mut ExceptionInfo);
|
||||||
EHAction::None => return continue_unwind(exception_object, context),
|
let exception = &exception_info.exception.unwrap();
|
||||||
EHAction::Cleanup(lpad) |
|
if search_phase {
|
||||||
EHAction::Catch(lpad) => {
|
match eh_action {
|
||||||
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
|
EHAction::None => return continue_unwind(exception_object, context),
|
||||||
exception_object as uintptr_t);
|
// Actually, cleanup should not return handler found, this is to workaround
|
||||||
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, exception as *const _ as uw::_Unwind_Word);
|
// the issue of terminating directly when no catch cause is found while
|
||||||
uw::_Unwind_SetIP(context, lpad);
|
// having some cleanup routines defined by finally.
|
||||||
return uw::_URC_INSTALL_CONTEXT;
|
// 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 {
|
||||||
|
EHAction::None => return continue_unwind(exception_object, context),
|
||||||
|
EHAction::Cleanup(lpad) |
|
||||||
|
EHAction::Catch(lpad) => {
|
||||||
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
|
||||||
|
exception_object as uintptr_t);
|
||||||
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, exception as *const _ as uw::_Unwind_Word);
|
||||||
|
uw::_Unwind_SetIP(context, lpad);
|
||||||
|
return uw::_URC_INSTALL_CONTEXT;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
@ -202,11 +166,10 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
|
||||||
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(),
|
||||||
|
file: file!().as_c_slice(),
|
||||||
let mut found = false;
|
line: line!(),
|
||||||
for i in 0..=MAX_INFLIGHT_EXCEPTIONS + 1 {
|
column: column!(),
|
||||||
if found {
|
// https://github.com/rust-lang/rfcs/pull/1719
|
||||||
if stack[i] == -1 {
|
function: "__artiq_reraise".as_c_slice(),
|
||||||
stack[i - 1] = index;
|
message: "No active exception to reraise".as_c_slice(),
|
||||||
assert!(i == count);
|
param: [0, 0, 0]
|
||||||
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(),
|
|
||||||
line: line!(),
|
|
||||||
column: column!(),
|
|
||||||
// https://github.com/rust-lang/rfcs/pull/1719
|
|
||||||
function: "__artiq_raise".as_c_slice(),
|
|
||||||
message: "too many nested exceptions".as_c_slice(),
|
|
||||||
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)
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
use libboard_zynq;
|
|
||||||
use crate::artiq_raise;
|
|
||||||
|
|
||||||
pub static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
|
|
||||||
|
|
||||||
pub extern fn start(busno: i32) {
|
|
||||||
if busno > 0 {
|
|
||||||
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
if (&mut I2C_BUS).as_mut().unwrap().start().is_err() {
|
|
||||||
artiq_raise!("I2CError", "I2C start failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn restart(busno: i32) {
|
|
||||||
if busno > 0 {
|
|
||||||
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() {
|
|
||||||
artiq_raise!("I2CError", "I2C restart failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn stop(busno: i32) {
|
|
||||||
if busno > 0 {
|
|
||||||
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
if (&mut I2C_BUS).as_mut().unwrap().stop().is_err() {
|
|
||||||
artiq_raise!("I2CError", "I2C stop failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn write(busno: i32, data: i32) -> bool {
|
|
||||||
if busno > 0 {
|
|
||||||
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
match (&mut I2C_BUS).as_mut().unwrap().write(data as u8) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(_) => artiq_raise!("I2CError", "I2C write failed"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn read(busno: i32, ack: bool) -> i32 {
|
|
||||||
if busno > 0 {
|
|
||||||
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
match (&mut I2C_BUS).as_mut().unwrap().read(ack) {
|
|
||||||
Ok(r) => r as i32,
|
|
||||||
Err(_) => artiq_raise!("I2CError", "I2C read failed"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn switch_select(busno: i32, address: i32, mask: i32) {
|
|
||||||
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();
|
|
||||||
i2c.init().expect("I2C bus initialization failed");
|
|
||||||
unsafe { I2C_BUS = Some(i2c) };
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
use libboard_zynq::{gic, mpcore, println, stdio};
|
use libboard_zynq::{gic, mpcore, println, stdio};
|
||||||
use libcortex_a9::{
|
use libcortex_a9::{
|
||||||
asm, interrupt_handler,
|
asm,
|
||||||
regs::MPIDR,
|
regs::{MPIDR, SP},
|
||||||
spin_lock_yield, notify_spin_lock
|
spin_lock_yield, notify_spin_lock
|
||||||
};
|
};
|
||||||
use libregister::RegisterR;
|
use libregister::{RegisterR, RegisterW};
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -14,34 +14,31 @@ extern "C" {
|
||||||
|
|
||||||
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
|
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
|
#[link_section = ".text.boot"]
|
||||||
|
#[no_mangle]
|
||||||
|
#[naked]
|
||||||
|
pub unsafe extern "C" fn IRQ() {
|
||||||
if MPIDR.read().cpu_id() == 1 {
|
if MPIDR.read().cpu_id() == 1 {
|
||||||
let mpcore = mpcore::RegisterBlock::mpcore();
|
let mpcore = mpcore::RegisterBlock::new();
|
||||||
let mut gic = gic::InterruptController::gic(mpcore);
|
let mut gic = gic::InterruptController::new(mpcore);
|
||||||
let id = gic.get_interrupt_id();
|
let id = gic.get_interrupt_id();
|
||||||
if id.0 == 0 {
|
if id.0 == 0 {
|
||||||
gic.end_interrupt(id);
|
gic.end_interrupt(id);
|
||||||
asm::exit_irq();
|
asm::exit_irq();
|
||||||
asm!("b core1_restart");
|
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();
|
stdio::drop_uart();
|
||||||
println!("IRQ");
|
println!("IRQ");
|
||||||
loop {}
|
loop {}
|
||||||
});
|
}
|
||||||
|
|
||||||
// This is actually not an interrupt handler, just use the macro for convenience.
|
|
||||||
// This function would be called in normal mode (instead of interrupt mode), the outer naked
|
|
||||||
// function wrapper is to tell libunwind to stop when it reaches here.
|
|
||||||
interrupt_handler!(core1_restart, core1_restart_impl, __stack0_start, __stack1_start, {
|
|
||||||
asm::enable_irq();
|
|
||||||
CORE1_RESTART.store(false, Ordering::Relaxed);
|
|
||||||
notify_spin_lock();
|
|
||||||
main_core1();
|
|
||||||
});
|
|
||||||
|
|
||||||
pub fn restart_core1() {
|
pub fn restart_core1() {
|
||||||
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
|
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
|
||||||
CORE1_RESTART.store(true, Ordering::Relaxed);
|
CORE1_RESTART.store(true, Ordering::Relaxed);
|
||||||
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core1.into());
|
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core1.into());
|
||||||
while CORE1_RESTART.load(Ordering::Relaxed) {
|
while CORE1_RESTART.load(Ordering::Relaxed) {
|
||||||
|
|
|
@ -9,11 +9,10 @@ use alloc::vec;
|
||||||
|
|
||||||
use crate::eh_artiq;
|
use crate::eh_artiq;
|
||||||
use crate::rtio;
|
use crate::rtio;
|
||||||
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;
|
||||||
|
@ -64,15 +63,6 @@ macro_rules! api_libm_f64f64 {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! api_libm_f64f64f64 {
|
|
||||||
($i:ident) => ({
|
|
||||||
extern fn $i(x: f64, y: f64) -> f64 {
|
|
||||||
libm::$i(x, y)
|
|
||||||
}
|
|
||||||
api!($i = $i)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve(required: &[u8]) -> Option<u32> {
|
pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
let api = &[
|
let api = &[
|
||||||
// timing
|
// timing
|
||||||
|
@ -87,7 +77,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),
|
||||||
|
@ -110,14 +100,6 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
api!(cache_get = cache::get),
|
api!(cache_get = cache::get),
|
||||||
api!(cache_put = cache::put),
|
api!(cache_put = cache::put),
|
||||||
|
|
||||||
// i2c
|
|
||||||
api!(i2c_start = i2c::start),
|
|
||||||
api!(i2c_restart = i2c::restart),
|
|
||||||
api!(i2c_stop = i2c::stop),
|
|
||||||
api!(i2c_write = i2c::write),
|
|
||||||
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
|
||||||
api!(__aeabi_dadd),
|
api!(__aeabi_dadd),
|
||||||
|
@ -199,25 +181,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
|
|
||||||
api!(__powidf2),
|
|
||||||
|
|
||||||
// libm
|
// libm
|
||||||
api_libm_f64f64!(acos),
|
api_libm_f64f64!(acos),
|
||||||
|
@ -225,11 +195,14 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
api_libm_f64f64!(asin),
|
api_libm_f64f64!(asin),
|
||||||
api_libm_f64f64!(asinh),
|
api_libm_f64f64!(asinh),
|
||||||
api_libm_f64f64!(atan),
|
api_libm_f64f64!(atan),
|
||||||
api_libm_f64f64f64!(atan2),
|
{
|
||||||
api_libm_f64f64!(atanh),
|
extern fn atan2(y: f64, x: f64) -> f64 {
|
||||||
|
libm::atan2(y, x)
|
||||||
|
}
|
||||||
|
api!(atan2 = atan2)
|
||||||
|
},
|
||||||
api_libm_f64f64!(cbrt),
|
api_libm_f64f64!(cbrt),
|
||||||
api_libm_f64f64!(ceil),
|
api_libm_f64f64!(ceil),
|
||||||
api_libm_f64f64f64!(copysign),
|
|
||||||
api_libm_f64f64!(cos),
|
api_libm_f64f64!(cos),
|
||||||
api_libm_f64f64!(cosh),
|
api_libm_f64f64!(cosh),
|
||||||
api_libm_f64f64!(erf),
|
api_libm_f64f64!(erf),
|
||||||
|
@ -246,10 +219,18 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
}
|
}
|
||||||
api!(fma = fma)
|
api!(fma = fma)
|
||||||
},
|
},
|
||||||
api_libm_f64f64f64!(fmax),
|
{
|
||||||
api_libm_f64f64f64!(fmin),
|
extern fn fmod(x: f64, y: f64) -> f64 {
|
||||||
api_libm_f64f64f64!(fmod),
|
libm::fmod(x, y)
|
||||||
api_libm_f64f64f64!(hypot),
|
}
|
||||||
|
api!(fmod = fmod)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extern fn hypot(x: f64, y: f64) -> f64 {
|
||||||
|
libm::hypot(x, y)
|
||||||
|
}
|
||||||
|
api!(hypot = hypot)
|
||||||
|
},
|
||||||
api_libm_f64f64!(j0),
|
api_libm_f64f64!(j0),
|
||||||
api_libm_f64f64!(j1),
|
api_libm_f64f64!(j1),
|
||||||
{
|
{
|
||||||
|
@ -262,8 +243,12 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
api_libm_f64f64!(log),
|
api_libm_f64f64!(log),
|
||||||
api_libm_f64f64!(log2),
|
api_libm_f64f64!(log2),
|
||||||
api_libm_f64f64!(log10),
|
api_libm_f64f64!(log10),
|
||||||
api_libm_f64f64f64!(nextafter),
|
{
|
||||||
api_libm_f64f64f64!(pow),
|
extern fn pow(x: f64, y: f64) -> f64 {
|
||||||
|
libm::pow(x, y)
|
||||||
|
}
|
||||||
|
api!(pow = pow)
|
||||||
|
},
|
||||||
api_libm_f64f64!(round),
|
api_libm_f64f64!(round),
|
||||||
api_libm_f64f64!(sin),
|
api_libm_f64f64!(sin),
|
||||||
api_libm_f64f64!(sinh),
|
api_libm_f64f64!(sinh),
|
||||||
|
|
|
@ -1,30 +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 {
|
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::CacheGetRequest(key));
|
||||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::CacheGetRequest(key));
|
let msg = KERNEL_CHANNEL_0TO1.lock().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 slice = unsafe { transmute(v.as_c_slice()) };
|
||||||
let leaked = Box::new(v.as_c_slice());
|
// we intentionally leak the memory here,
|
||||||
let reference = transmute(leaked.as_ref());
|
// which does not matter as core1 would restart
|
||||||
forget(leaked);
|
forget(v);
|
||||||
forget(v);
|
slice
|
||||||
reference
|
} 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 {
|
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::CachePutRequest(key, value));
|
||||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::CachePutRequest(key, value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use libcortex_a9::sync_channel::{Sender, Receiver};
|
use libcortex_a9::sync_channel::{Sender, Receiver};
|
||||||
use libsupport_zynq::boot::Core1;
|
use libsupport_zynq::boot::Core1;
|
||||||
|
|
||||||
use super::{CHANNEL_0TO1, CHANNEL_1TO0, CHANNEL_SEM, INIT_LOCK, Message};
|
use super::{CHANNEL_0TO1, CHANNEL_1TO0, INIT_LOCK, Message};
|
||||||
use crate::irq::restart_core1;
|
use crate::irq::restart_core1;
|
||||||
|
|
||||||
use core::mem::{forget, replace};
|
use core::mem::{forget, replace};
|
||||||
|
@ -12,7 +12,6 @@ pub struct Control {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_channels() -> (Sender<'static, Message>, Receiver<'static, Message>) {
|
fn get_channels() -> (Sender<'static, Message>, Receiver<'static, Message>) {
|
||||||
CHANNEL_SEM.wait();
|
|
||||||
let mut core0_tx = None;
|
let mut core0_tx = None;
|
||||||
while core0_tx.is_none() {
|
while core0_tx.is_none() {
|
||||||
core0_tx = CHANNEL_0TO1.lock().take();
|
core0_tx = CHANNEL_0TO1.lock().take();
|
||||||
|
@ -41,7 +40,7 @@ impl Control {
|
||||||
|
|
||||||
pub fn restart(&mut self) {
|
pub fn restart(&mut self) {
|
||||||
{
|
{
|
||||||
let _lock = INIT_LOCK.lock();
|
INIT_LOCK.lock();
|
||||||
restart_core1();
|
restart_core1();
|
||||||
unsafe {
|
unsafe {
|
||||||
self.tx.drop_elements();
|
self.tx.drop_elements();
|
||||||
|
|
|
@ -13,14 +13,13 @@ 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;
|
||||||
use super::{
|
use super::{
|
||||||
api::resolve,
|
api::resolve,
|
||||||
rpc::rpc_send_async,
|
rpc::rpc_send_async,
|
||||||
INIT_LOCK,
|
INIT_LOCK,
|
||||||
CHANNEL_0TO1, CHANNEL_1TO0,
|
CHANNEL_0TO1, CHANNEL_1TO0,
|
||||||
CHANNEL_SEM,
|
|
||||||
KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0,
|
KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0,
|
||||||
KERNEL_IMAGE,
|
KERNEL_IMAGE,
|
||||||
Message,
|
Message,
|
||||||
|
@ -29,10 +28,14 @@ use super::{
|
||||||
|
|
||||||
// linker symbols
|
// linker symbols
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#[no_mangle]
|
||||||
static __text_start: u32;
|
static __text_start: u32;
|
||||||
|
#[no_mangle]
|
||||||
static __text_end: u32;
|
static __text_end: u32;
|
||||||
static __exidx_start: EXIDX_Entry;
|
#[no_mangle]
|
||||||
static __exidx_end: EXIDX_Entry;
|
static __exidx_start: u32;
|
||||||
|
#[no_mangle]
|
||||||
|
static __exidx_end: u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn attribute_writeback(typeinfo: *const ()) {
|
unsafe fn attribute_writeback(typeinfo: *const ()) {
|
||||||
|
@ -118,7 +121,7 @@ impl KernelImage {
|
||||||
dsb();
|
dsb();
|
||||||
isb();
|
isb();
|
||||||
|
|
||||||
(mem::transmute::<u32, extern "C" fn()>(self.__modinit__))();
|
(mem::transmute::<u32, fn()>(self.__modinit__))();
|
||||||
|
|
||||||
if let Some(typeinfo) = self.typeinfo {
|
if let Some(typeinfo) = self.typeinfo {
|
||||||
attribute_writeback(typeinfo as *const ());
|
attribute_writeback(typeinfo as *const ());
|
||||||
|
@ -133,12 +136,14 @@ impl KernelImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn main_core1() {
|
pub fn main_core1() {
|
||||||
enable_fpu();
|
|
||||||
debug!("Core1 started");
|
debug!("Core1 started");
|
||||||
|
|
||||||
|
enable_fpu();
|
||||||
|
debug!("FPU enabled on Core1");
|
||||||
|
|
||||||
ram::init_alloc_core1();
|
ram::init_alloc_core1();
|
||||||
gic::InterruptController::gic(mpcore::RegisterBlock::mpcore()).enable_interrupts();
|
gic::InterruptController::new(mpcore::RegisterBlock::new()).enable_interrupts();
|
||||||
|
|
||||||
let (mut core0_tx, mut core1_rx) = sync_channel!(Message, 4);
|
let (mut core0_tx, mut core1_rx) = sync_channel!(Message, 4);
|
||||||
let (mut core1_tx, core0_rx) = sync_channel!(Message, 4);
|
let (mut core1_tx, core0_rx) = sync_channel!(Message, 4);
|
||||||
|
@ -146,17 +151,10 @@ pub extern "C" fn main_core1() {
|
||||||
INIT_LOCK.lock();
|
INIT_LOCK.lock();
|
||||||
core0_tx.reset();
|
core0_tx.reset();
|
||||||
core1_tx.reset();
|
core1_tx.reset();
|
||||||
if !KERNEL_IMAGE.is_null() {
|
|
||||||
// indicates forceful termination of previous kernel
|
|
||||||
KERNEL_IMAGE = core::ptr::null();
|
|
||||||
debug!("rtio init");
|
|
||||||
rtio::init();
|
|
||||||
}
|
|
||||||
dma::init_dma_recorder();
|
dma::init_dma_recorder();
|
||||||
}
|
}
|
||||||
*CHANNEL_0TO1.lock() = Some(core0_tx);
|
*CHANNEL_0TO1.lock() = Some(core0_tx);
|
||||||
*CHANNEL_1TO0.lock() = Some(core0_rx);
|
*CHANNEL_1TO0.lock() = Some(core0_rx);
|
||||||
CHANNEL_SEM.signal();
|
|
||||||
|
|
||||||
// set on load, cleared on start
|
// set on load, cleared on start
|
||||||
let mut loaded_kernel = None;
|
let mut loaded_kernel = None;
|
||||||
|
@ -181,20 +179,18 @@ pub extern "C" fn main_core1() {
|
||||||
Message::StartRequest => {
|
Message::StartRequest => {
|
||||||
info!("kernel starting");
|
info!("kernel starting");
|
||||||
if let Some(kernel) = loaded_kernel.take() {
|
if let Some(kernel) = loaded_kernel.take() {
|
||||||
|
*KERNEL_CHANNEL_0TO1.lock() = Some(core1_rx);
|
||||||
|
*KERNEL_CHANNEL_1TO0.lock() = Some(core1_tx);
|
||||||
unsafe {
|
unsafe {
|
||||||
eh_artiq::reset_exception_buffer();
|
|
||||||
KERNEL_CHANNEL_0TO1 = Some(core1_rx);
|
|
||||||
KERNEL_CHANNEL_1TO0 = Some(core1_tx);
|
|
||||||
KERNEL_IMAGE = &kernel as *const KernelImage;
|
KERNEL_IMAGE = &kernel as *const KernelImage;
|
||||||
kernel.exec();
|
kernel.exec();
|
||||||
KERNEL_IMAGE = ptr::null();
|
KERNEL_IMAGE = ptr::null();
|
||||||
core1_rx = KERNEL_CHANNEL_0TO1.take().unwrap();
|
|
||||||
core1_tx = KERNEL_CHANNEL_1TO0.take().unwrap();
|
|
||||||
}
|
}
|
||||||
|
core1_rx = core::mem::replace(&mut *KERNEL_CHANNEL_0TO1.lock(), None).unwrap();
|
||||||
|
core1_tx = core::mem::replace(&mut *KERNEL_CHANNEL_1TO0.lock(), None).unwrap();
|
||||||
}
|
}
|
||||||
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 +198,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 mut core1_tx = KERNEL_CHANNEL_1TO0.lock();
|
||||||
let errors = unsafe { get_async_errors() };
|
core1_tx.as_mut().unwrap().send(Message::KernelException(exception, &backtrace[..cursor]));
|
||||||
core1_tx.send(Message::KernelException(exceptions, stack_pointers, backtrace, errors));
|
|
||||||
}
|
}
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
@ -216,41 +222,23 @@ pub fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
|
||||||
/// Called by llvm_libunwind
|
/// Called by llvm_libunwind
|
||||||
#[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 exidx = unsafe {
|
||||||
|
KERNEL_IMAGE.as_ref()
|
||||||
|
.expect("dl_unwind_find_exidx kernel image")
|
||||||
|
.library.get().as_ref().unwrap().exidx()
|
||||||
|
};
|
||||||
|
|
||||||
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 if KERNEL_IMAGE != ptr::null() {
|
} else {
|
||||||
let exidx = KERNEL_IMAGE.as_ref()
|
|
||||||
.expect("dl_unwind_find_exidx kernel image")
|
|
||||||
.library.get().as_ref().unwrap().exidx();
|
|
||||||
length = exidx.len() as u32;
|
length = exidx.len() as u32;
|
||||||
start = exidx.as_ptr();
|
start = exidx.as_ptr();
|
||||||
} else {
|
|
||||||
length = 0;
|
|
||||||
start = ptr::null();
|
|
||||||
}
|
}
|
||||||
*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
|
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ use alloc::{vec::Vec, string::String, boxed::Box};
|
||||||
use cslice::CSlice;
|
use cslice::CSlice;
|
||||||
use super::{KERNEL_IMAGE, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, Message};
|
use super::{KERNEL_IMAGE, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, Message};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
use libcortex_a9::cache::dcci_slice;
|
use libcortex_a9::cache::dcci_slice;
|
||||||
|
|
||||||
|
@ -35,9 +36,7 @@ pub unsafe fn init_dma_recorder() {
|
||||||
|
|
||||||
pub extern fn dma_record_start(name: CSlice<u8>) {
|
pub extern fn dma_record_start(name: CSlice<u8>) {
|
||||||
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
||||||
unsafe {
|
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::DmaEraseRequest(name.clone()));
|
||||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::DmaEraseRequest(name.clone()));
|
|
||||||
}
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if RECORDER.is_some() {
|
if RECORDER.is_some() {
|
||||||
artiq_raise!("DMAError", "DMA is already recording")
|
artiq_raise!("DMAError", "DMA is already recording")
|
||||||
|
@ -71,7 +70,7 @@ pub extern fn dma_record_stop(duration: i64) {
|
||||||
|
|
||||||
let mut recorder = RECORDER.take().unwrap();
|
let mut recorder = RECORDER.take().unwrap();
|
||||||
recorder.duration = duration;
|
recorder.duration = duration;
|
||||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(
|
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(
|
||||||
Message::DmaPutRequest(recorder)
|
Message::DmaPutRequest(recorder)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -136,17 +135,13 @@ pub extern fn dma_record_output_wide(target: i32, words: CSlice<i32>) {
|
||||||
|
|
||||||
pub extern fn dma_erase(name: CSlice<u8>) {
|
pub extern fn dma_erase(name: CSlice<u8>) {
|
||||||
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
||||||
unsafe {
|
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::DmaEraseRequest(name));
|
||||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::DmaEraseRequest(name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
pub extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
||||||
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
||||||
unsafe {
|
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::DmaGetRequest(name));
|
||||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::DmaGetRequest(name));
|
match KERNEL_CHANNEL_0TO1.lock().as_mut().unwrap().recv() {
|
||||||
}
|
|
||||||
match unsafe {KERNEL_CHANNEL_0TO1.as_mut().unwrap()}.recv() {
|
|
||||||
Message::DmaGetReply(None) => (),
|
Message::DmaGetReply(None) => (),
|
||||||
Message::DmaGetReply(Some((mut v, duration))) => {
|
Message::DmaGetReply(Some((mut v, duration))) => {
|
||||||
v.reserve(ALIGNMENT - 1);
|
v.reserve(ALIGNMENT - 1);
|
||||||
|
@ -159,7 +154,6 @@ pub extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
||||||
// trailing zero to indicate end of buffer
|
// trailing zero to indicate end of buffer
|
||||||
v.push(0);
|
v.push(0);
|
||||||
v.copy_within(0..original_length, padding);
|
v.copy_within(0..original_length, padding);
|
||||||
dcci_slice(&v);
|
|
||||||
let v = Box::new(v);
|
let v = Box::new(v);
|
||||||
let address = Box::into_raw(v) as *mut Vec<u8> as i32;
|
let address = Box::into_raw(v) as *mut Vec<u8> as i32;
|
||||||
return DmaTrace {
|
return DmaTrace {
|
||||||
|
@ -174,10 +168,12 @@ pub extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern fn dma_playback(timestamp: i64, ptr: i32) {
|
pub extern fn dma_playback(timestamp: i64, ptr: i32) {
|
||||||
|
debug!("DMA playback started");
|
||||||
unsafe {
|
unsafe {
|
||||||
let v = Box::from_raw(ptr as *mut Vec<u8>);
|
let v = Box::from_raw(ptr as *mut Vec<u8>);
|
||||||
let padding = ALIGNMENT - v.as_ptr() as usize % ALIGNMENT;
|
let padding = ALIGNMENT - v.as_ptr() as usize % ALIGNMENT;
|
||||||
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
||||||
|
dcci_slice(&v[padding..]);
|
||||||
let ptr = v.as_ptr().add(padding) as i32;
|
let ptr = v.as_ptr().add(padding) as i32;
|
||||||
|
|
||||||
csr::rtio_dma::base_address_write(ptr as u32);
|
csr::rtio_dma::base_address_write(ptr as u32);
|
||||||
|
@ -188,8 +184,8 @@ pub extern fn dma_playback(timestamp: i64, ptr: i32) {
|
||||||
while csr::rtio_dma::enable_read() != 0 {}
|
while csr::rtio_dma::enable_read() != 0 {}
|
||||||
csr::cri_con::selected_write(0);
|
csr::cri_con::selected_write(0);
|
||||||
|
|
||||||
// leave the handle as we may try to do playback for another time.
|
|
||||||
mem::forget(v);
|
mem::forget(v);
|
||||||
|
debug!("DMA playback finished");
|
||||||
|
|
||||||
let error = csr::rtio_dma::error_read();
|
let error = csr::rtio_dma::error_read();
|
||||||
if error != 0 {
|
if error != 0 {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
use alloc::{vec::Vec, string::String};
|
use alloc::{vec::Vec, string::String};
|
||||||
|
|
||||||
use libcortex_a9::{mutex::Mutex, sync_channel, semaphore::Semaphore};
|
use libcortex_a9::{mutex::Mutex, sync_channel};
|
||||||
use crate::eh_artiq;
|
use crate::eh_artiq;
|
||||||
|
|
||||||
mod control;
|
mod control;
|
||||||
|
@ -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,21 +44,15 @@ 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);
|
||||||
static CHANNEL_1TO0: Mutex<Option<sync_channel::Receiver<'static, Message>>> = Mutex::new(None);
|
static CHANNEL_1TO0: Mutex<Option<sync_channel::Receiver<'static, Message>>> = Mutex::new(None);
|
||||||
static CHANNEL_SEM: Semaphore = Semaphore::new(0, 1);
|
|
||||||
|
|
||||||
static mut KERNEL_CHANNEL_0TO1: Option<sync_channel::Receiver<'static, Message>> = None;
|
static KERNEL_CHANNEL_0TO1: Mutex<Option<sync_channel::Receiver<'static, Message>>> = Mutex::new(None);
|
||||||
static mut KERNEL_CHANNEL_1TO0: Option<sync_channel::Sender<'static, Message>> = None;
|
static KERNEL_CHANNEL_1TO0: Mutex<Option<sync_channel::Sender<'static, Message>>> = Mutex::new(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(());
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -11,10 +11,10 @@ use super::{
|
||||||
};
|
};
|
||||||
|
|
||||||
fn rpc_send_common(is_async: bool, service: u32, tag: &CSlice<u8>, data: *const *const ()) {
|
fn rpc_send_common(is_async: bool, service: u32, tag: &CSlice<u8>, data: *const *const ()) {
|
||||||
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
let mut core1_tx = KERNEL_CHANNEL_1TO0.lock();
|
||||||
let mut buffer = Vec::<u8>::new();
|
let mut buffer = Vec::<u8>::new();
|
||||||
send_args(&mut buffer, service, tag.as_ref(), data).expect("RPC encoding failed");
|
send_args(&mut buffer, service, tag.as_ref(), data).expect("RPC encoding failed");
|
||||||
core1_tx.send(Message::RpcSend { is_async, data: buffer });
|
core1_tx.as_mut().unwrap().send(Message::RpcSend { is_async, data: buffer });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern fn rpc_send(service: u32, tag: &CSlice<u8>, data: *const *const ()) {
|
pub extern fn rpc_send(service: u32, tag: &CSlice<u8>, data: *const *const ()) {
|
||||||
|
@ -26,22 +26,22 @@ pub extern fn rpc_send_async(service: u32, tag: &CSlice<u8>, data: *const *const
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern fn rpc_recv(slot: *mut ()) -> usize {
|
pub extern fn rpc_recv(slot: *mut ()) -> usize {
|
||||||
let reply = unsafe {
|
let reply = {
|
||||||
let core1_rx = KERNEL_CHANNEL_0TO1.as_mut().unwrap();
|
let mut core1_rx = KERNEL_CHANNEL_0TO1.lock();
|
||||||
let core1_tx = KERNEL_CHANNEL_1TO0.as_mut().unwrap();
|
let mut core1_tx = KERNEL_CHANNEL_1TO0.lock();
|
||||||
core1_tx.send(Message::RpcRecvRequest(slot));
|
core1_tx.as_mut().unwrap().send(Message::RpcRecvRequest(slot));
|
||||||
core1_rx.recv()
|
core1_rx.as_mut().unwrap().recv()
|
||||||
};
|
};
|
||||||
match reply {
|
match reply {
|
||||||
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
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
use crate::sd_reader;
|
||||||
|
use core_io::{Error, Read, Seek, SeekFrom};
|
||||||
|
use libboard_zynq::{devc, sdio};
|
||||||
|
use log::{info, debug};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PlLoadingError {
|
||||||
|
BootImageNotFound,
|
||||||
|
InvalidBootImageHeader,
|
||||||
|
MissingBitstreamPartition,
|
||||||
|
EncryptedBitstream,
|
||||||
|
IoError(Error),
|
||||||
|
DevcError(devc::DevcError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for PlLoadingError {
|
||||||
|
fn from(error: Error) -> Self {
|
||||||
|
PlLoadingError::IoError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<devc::DevcError> for PlLoadingError {
|
||||||
|
fn from(error: devc::DevcError) -> Self {
|
||||||
|
PlLoadingError::DevcError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for PlLoadingError {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
use PlLoadingError::*;
|
||||||
|
match self {
|
||||||
|
BootImageNotFound => write!(
|
||||||
|
f,
|
||||||
|
"Boot image not found, make sure `boot.bin` exists and your SD card is plugged in."
|
||||||
|
),
|
||||||
|
InvalidBootImageHeader => write!(
|
||||||
|
f,
|
||||||
|
"Invalid boot image header. Check if the file is correct."
|
||||||
|
),
|
||||||
|
MissingBitstreamPartition => write!(
|
||||||
|
f,
|
||||||
|
"Bitstream partition not found. Check your compile configuration."
|
||||||
|
),
|
||||||
|
EncryptedBitstream => write!(f, "Encrypted bitstream is not supported."),
|
||||||
|
IoError(e) => write!(f, "Error while reading: {}", e),
|
||||||
|
DevcError(e) => write!(f, "PCAP interface error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct PartitionHeader {
|
||||||
|
pub encrypted_length: u32,
|
||||||
|
pub unencrypted_length: u32,
|
||||||
|
pub word_length: u32,
|
||||||
|
pub dest_load_addr: u32,
|
||||||
|
pub dest_exec_addr: u32,
|
||||||
|
pub data_offset: u32,
|
||||||
|
pub attribute_bits: u32,
|
||||||
|
pub section_count: u32,
|
||||||
|
pub checksum_offset: u32,
|
||||||
|
pub header_offset: u32,
|
||||||
|
pub cert_offset: u32,
|
||||||
|
pub reserved: [u32; 4],
|
||||||
|
pub checksum: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a u32 word from the reader.
|
||||||
|
fn read_u32<Reader: Read>(reader: &mut Reader) -> Result<u32, PlLoadingError> {
|
||||||
|
let mut buffer: [u8; 4] = [0; 4];
|
||||||
|
reader.read_exact(&mut buffer)?;
|
||||||
|
let mut result: u32 = 0;
|
||||||
|
for i in 0..4 {
|
||||||
|
result |= (buffer[i] as u32) << (i * 8);
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load PL partition header.
|
||||||
|
fn load_pl_header<File: Read + Seek>(
|
||||||
|
file: &mut File,
|
||||||
|
) -> Result<Option<PartitionHeader>, PlLoadingError> {
|
||||||
|
let mut buffer: [u8; 0x40] = [0; 0x40];
|
||||||
|
file.read_exact(&mut buffer)?;
|
||||||
|
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) };
|
||||||
|
if header.attribute_bits & (2 << 4) != 0 {
|
||||||
|
Ok(Some(header))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Locate the PL bitstream from the image, and return the size (in bytes) of the bitstream if successful.
|
||||||
|
/// This function would seek the file to the location of the bitstream.
|
||||||
|
fn locate_bitstream<File: Read + Seek>(file: &mut File) -> Result<usize, PlLoadingError> {
|
||||||
|
const BOOT_HEADER_SIGN: u32 = 0x584C4E58;
|
||||||
|
// read boot header signature
|
||||||
|
file.seek(SeekFrom::Start(0x24))?;
|
||||||
|
if read_u32(file)? != BOOT_HEADER_SIGN {
|
||||||
|
return Err(PlLoadingError::InvalidBootImageHeader);
|
||||||
|
}
|
||||||
|
// read partition header offset
|
||||||
|
file.seek(SeekFrom::Start(0x9C))?;
|
||||||
|
let ptr = read_u32(file)?;
|
||||||
|
debug!("Partition header pointer = {:0X}", ptr);
|
||||||
|
file.seek(SeekFrom::Start(ptr as u64))?;
|
||||||
|
|
||||||
|
let mut header_opt = None;
|
||||||
|
// at most 3 partition headers
|
||||||
|
for _ in 0..3 {
|
||||||
|
let result = load_pl_header(file)?;
|
||||||
|
if let Some(h) = result {
|
||||||
|
header_opt = Some(h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let header = match header_opt {
|
||||||
|
None => return Err(PlLoadingError::MissingBitstreamPartition),
|
||||||
|
Some(h) => h,
|
||||||
|
};
|
||||||
|
|
||||||
|
let encrypted_length = header.encrypted_length;
|
||||||
|
let unencrypted_length = header.unencrypted_length;
|
||||||
|
debug!("Unencrypted length = {:0X}", unencrypted_length);
|
||||||
|
if encrypted_length != unencrypted_length {
|
||||||
|
return Err(PlLoadingError::EncryptedBitstream);
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_addr = header.data_offset;
|
||||||
|
debug!("Partition start address: {:0X}", start_addr);
|
||||||
|
file.seek(SeekFrom::Start(start_addr as u64 * 4))?;
|
||||||
|
|
||||||
|
Ok(unencrypted_length as usize * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load bitstream from bootgen file.
|
||||||
|
/// This function parses the file, locate the bitstream and load it through the PCAP driver.
|
||||||
|
/// It requires a large buffer, please enable the DDR RAM before using it.
|
||||||
|
pub fn load_bitstream<File: Read + Seek>(
|
||||||
|
file: &mut File,
|
||||||
|
) -> Result<(), PlLoadingError> {
|
||||||
|
let size = locate_bitstream(file)?;
|
||||||
|
let mut buffer: alloc::vec::Vec<u8> = alloc::vec::Vec::with_capacity(size);
|
||||||
|
unsafe {
|
||||||
|
buffer.set_len(buffer.capacity());
|
||||||
|
}
|
||||||
|
file.read_exact(&mut buffer)?;
|
||||||
|
|
||||||
|
let mut devcfg = devc::DevC::new();
|
||||||
|
devcfg.enable();
|
||||||
|
devcfg.program(&buffer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_bitstream_from_sd() -> Result<(), PlLoadingError> {
|
||||||
|
let sdio0 = sdio::SDIO::sdio0(true);
|
||||||
|
if sdio0.is_card_inserted() {
|
||||||
|
info!("Card inserted. Mounting file system.");
|
||||||
|
let sd = sdio::sd_card::SdCard::from_sdio(sdio0).unwrap();
|
||||||
|
let reader = sd_reader::SdReader::new(sd);
|
||||||
|
|
||||||
|
let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?;
|
||||||
|
let root_dir = fs.root_dir();
|
||||||
|
let mut file = root_dir.open_file("/BOOT.BIN").map_err(|_| PlLoadingError::BootImageNotFound)?;
|
||||||
|
info!("Found boot image!");
|
||||||
|
load_bitstream(&mut file)
|
||||||
|
} else {
|
||||||
|
info!("SD card not inserted. Bitstream cannot be loaded.");
|
||||||
|
Err(PlLoadingError::BootImageNotFound)
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,53 +5,143 @@
|
||||||
#![feature(panic_info_message)]
|
#![feature(panic_info_message)]
|
||||||
#![feature(c_variadic)]
|
#![feature(c_variadic)]
|
||||||
#![feature(const_btree_new)]
|
#![feature(const_btree_new)]
|
||||||
|
#![feature(ptr_offset_from)]
|
||||||
#![feature(const_in_array_repeat_expressions)]
|
#![feature(const_in_array_repeat_expressions)]
|
||||||
#![feature(naked_functions)]
|
#![feature(naked_functions)]
|
||||||
#![feature(asm)]
|
|
||||||
|
|
||||||
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, devc, slcr, mpcore, gic};
|
||||||
use libasync::{task, block_async};
|
use libasync::{task, block_async};
|
||||||
use libsupport_zynq::ram;
|
use libsupport_zynq::ram;
|
||||||
|
use libregister::RegisterW;
|
||||||
use nb;
|
use nb;
|
||||||
use void::Void;
|
use void::Void;
|
||||||
use libconfig::Config;
|
use embedded_hal::blocking::delay::DelayMs;
|
||||||
use libcortex_a9::l2c::enable_l2_cache;
|
|
||||||
use libboard_artiq::{logger, identifier_read, init_gateware, pl};
|
|
||||||
|
|
||||||
const ASYNC_ERROR_COLLISION: u8 = 1 << 0;
|
|
||||||
const ASYNC_ERROR_BUSY: u8 = 1 << 1;
|
|
||||||
const ASYNC_ERROR_SEQUENCE_ERROR: u8 = 1 << 2;
|
|
||||||
|
|
||||||
|
mod sd_reader;
|
||||||
|
mod config;
|
||||||
|
mod net_settings;
|
||||||
|
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 load_pl;
|
||||||
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;
|
|
||||||
|
|
||||||
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()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if devc::DevC::new().is_done() {
|
||||||
|
info!("gateware already loaded");
|
||||||
|
// Do not load again: assume that the gateware already present is
|
||||||
|
// what we want (e.g. gateware configured via JTAG before PS
|
||||||
|
// startup, or by FSBL).
|
||||||
|
// Make sure that the PL/PS interface is enabled (e.g. OpenOCD does not enable it).
|
||||||
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
|
slcr.init_postload_fpga();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Load from SD card
|
||||||
|
match load_pl::load_bitstream_from_sd() {
|
||||||
|
Ok(_) => info!("Bitstream loaded successfully!"),
|
||||||
|
Err(e) => info!("Failure loading bitstream: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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::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> {
|
||||||
|
@ -69,61 +159,54 @@ async fn report_async_rtio_errors() {
|
||||||
let _ = block_async!(wait_for_async_rtio_error()).await;
|
let _ = block_async!(wait_for_async_rtio_error()).await;
|
||||||
unsafe {
|
unsafe {
|
||||||
let errors = pl::csr::rtio_core::async_error_read();
|
let errors = pl::csr::rtio_core::async_error_read();
|
||||||
if errors & ASYNC_ERROR_COLLISION != 0 {
|
if errors & 1 != 0 {
|
||||||
error!("RTIO collision involving channel {}",
|
error!("RTIO collision involving channel {}",
|
||||||
pl::csr::rtio_core::collision_channel_read());
|
pl::csr::rtio_core::collision_channel_read());
|
||||||
}
|
}
|
||||||
if errors & ASYNC_ERROR_BUSY != 0 {
|
if errors & 2 != 0 {
|
||||||
error!("RTIO busy error involving channel {}",
|
error!("RTIO busy error involving channel {}",
|
||||||
pl::csr::rtio_core::busy_channel_read());
|
pl::csr::rtio_core::busy_channel_read());
|
||||||
}
|
}
|
||||||
if errors & ASYNC_ERROR_SEQUENCE_ERROR != 0 {
|
if errors & 4 != 0 {
|
||||||
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]
|
||||||
pub fn main_core0() {
|
pub fn main_core0() {
|
||||||
enable_l2_cache(0x8);
|
|
||||||
let mut timer = GlobalTimer::start();
|
let mut timer = GlobalTimer::start();
|
||||||
|
|
||||||
let buffer_logger = unsafe {
|
let buffer_logger = unsafe {
|
||||||
logger::BufferLogger::new(&mut LOG_BUFFER[..])
|
logger::BufferLogger::new(&mut LOG_BUFFER[..])
|
||||||
};
|
};
|
||||||
buffer_logger.set_uart_log_level(log::LevelFilter::Info);
|
buffer_logger.set_uart_log_level(log::LevelFilter::Debug);
|
||||||
buffer_logger.register();
|
buffer_logger.register();
|
||||||
log::set_max_level(log::LevelFilter::Info);
|
log::set_max_level(log::LevelFilter::Debug);
|
||||||
|
|
||||||
info!("NAR3/Zynq7000 starting...");
|
info!("NAR3/Zynq7000 starting...");
|
||||||
|
|
||||||
ram::init_alloc_core0();
|
ram::init_alloc_core0();
|
||||||
gic::InterruptController::gic(mpcore::RegisterBlock::mpcore()).enable_interrupts();
|
gic::InterruptController::new(mpcore::RegisterBlock::new()).enable_interrupts();
|
||||||
|
|
||||||
init_gateware();
|
init_gateware();
|
||||||
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
|
info!("detected gateware: {}", identifier_read(&mut [0; 64]));
|
||||||
|
|
||||||
i2c::init();
|
let cfg = match config::Config::new() {
|
||||||
|
|
||||||
let cfg = match Config::new() {
|
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("config initialization failed: {}", err);
|
warn!("config initialization failed: {}", err);
|
||||||
Config::new_dummy()
|
config::Config::new_dummy()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use futures::{future::poll_fn, task::Poll};
|
use futures::{future::poll_fn, task::Poll};
|
||||||
use libasync::{smoltcp::TcpStream, task};
|
use libasync::{smoltcp::TcpStream, task};
|
||||||
use libboard_zynq::{smoltcp, slcr};
|
use libboard_zynq::smoltcp;
|
||||||
use libconfig::Config;
|
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use alloc::{rc::Rc, vec::Vec, string::String};
|
use alloc::rc::Rc;
|
||||||
use log::{self, info, debug, warn, error, LevelFilter};
|
use log::{self, info, warn, 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;
|
||||||
|
@ -44,21 +43,13 @@ pub enum Request {
|
||||||
ClearLog = 2,
|
ClearLog = 2,
|
||||||
PullLog = 7,
|
PullLog = 7,
|
||||||
SetLogFilter = 3,
|
SetLogFilter = 3,
|
||||||
Reboot = 5,
|
|
||||||
SetUartLogFilter = 6,
|
SetUartLogFilter = 6,
|
||||||
|
|
||||||
ConfigRead = 12,
|
|
||||||
ConfigWrite = 13,
|
|
||||||
ConfigRemove = 14,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(i8)]
|
#[repr(i8)]
|
||||||
pub enum Reply {
|
pub enum Reply {
|
||||||
Success = 1,
|
Success = 1,
|
||||||
LogContent = 2,
|
LogContent = 2,
|
||||||
RebootImminent = 3,
|
|
||||||
Error = 6,
|
|
||||||
ConfigData = 7,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_log_level_filter(stream: &mut TcpStream) -> Result<log::LevelFilter> {
|
async fn read_log_level_filter(stream: &mut TcpStream) -> Result<log::LevelFilter> {
|
||||||
|
@ -94,36 +85,14 @@ async fn get_logger_buffer() -> LogBufferRef<'static> {
|
||||||
get_logger_buffer_pred(|_| true).await
|
get_logger_buffer_pred(|_| true).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_key(stream: &mut TcpStream) -> Result<String> {
|
async fn handle_connection(stream: &mut TcpStream, pull_id: Rc<RefCell<u32>>) -> Result<()> {
|
||||||
let len = read_i32(stream).await?;
|
|
||||||
if len <= 0 {
|
|
||||||
write_i8(stream, Reply::Error as i8).await?;
|
|
||||||
return Err(Error::UnexpectedPattern);
|
|
||||||
}
|
|
||||||
let mut buffer = Vec::with_capacity(len as usize);
|
|
||||||
for _ in 0..len {
|
|
||||||
buffer.push(0);
|
|
||||||
}
|
|
||||||
read_chunk(stream, &mut buffer).await?;
|
|
||||||
if !buffer.is_ascii() {
|
|
||||||
write_i8(stream, Reply::Error as i8).await?;
|
|
||||||
return Err(Error::UnexpectedPattern);
|
|
||||||
}
|
|
||||||
Ok(String::from_utf8(buffer).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_connection(
|
|
||||||
stream: &mut TcpStream,
|
|
||||||
pull_id: Rc<RefCell<u32>>,
|
|
||||||
cfg: Rc<Config>) -> Result<()> {
|
|
||||||
if !expect(&stream, b"ARTIQ management\n").await? {
|
if !expect(&stream, b"ARTIQ management\n").await? {
|
||||||
return Err(Error::UnexpectedPattern);
|
return Err(Error::UnexpectedPattern);
|
||||||
}
|
}
|
||||||
stream.send_slice("e".as_bytes()).await?;
|
|
||||||
|
|
||||||
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)?;
|
||||||
|
@ -181,76 +150,19 @@ async fn handle_connection(
|
||||||
}
|
}
|
||||||
write_i8(stream, Reply::Success as i8).await?;
|
write_i8(stream, Reply::Success as i8).await?;
|
||||||
}
|
}
|
||||||
Request::ConfigRead => {
|
|
||||||
let key = read_key(stream).await?;
|
|
||||||
debug!("read key: {}", key);
|
|
||||||
let value = cfg.read(&key);
|
|
||||||
if let Ok(value) = value {
|
|
||||||
debug!("got value");
|
|
||||||
write_i8(stream, Reply::ConfigData as i8).await?;
|
|
||||||
write_chunk(stream, &value).await?;
|
|
||||||
} else {
|
|
||||||
warn!("read error: no such key");
|
|
||||||
write_i8(stream, Reply::Error as i8).await?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Request::ConfigWrite => {
|
|
||||||
let key = read_key(stream).await?;
|
|
||||||
debug!("write key: {}", key);
|
|
||||||
let len = read_i32(stream).await?;
|
|
||||||
let len = if len <= 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
len as usize
|
|
||||||
};
|
|
||||||
let mut buffer = Vec::with_capacity(len);
|
|
||||||
unsafe {
|
|
||||||
buffer.set_len(len);
|
|
||||||
}
|
|
||||||
read_chunk(stream, &mut buffer).await?;
|
|
||||||
let value = cfg.write(&key, buffer);
|
|
||||||
if value.is_ok() {
|
|
||||||
debug!("write success");
|
|
||||||
write_i8(stream, Reply::Success as i8).await?;
|
|
||||||
} else {
|
|
||||||
// this is an error because we do not expect write to fail
|
|
||||||
error!("failed to write: {:?}", value);
|
|
||||||
write_i8(stream, Reply::Error as i8).await?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Request::ConfigRemove => {
|
|
||||||
let key = read_key(stream).await?;
|
|
||||||
debug!("erase key: {}", key);
|
|
||||||
let value = cfg.remove(&key);
|
|
||||||
if value.is_ok() {
|
|
||||||
debug!("erase success");
|
|
||||||
write_i8(stream, Reply::Success as i8).await?;
|
|
||||||
} else {
|
|
||||||
warn!("erase failed");
|
|
||||||
write_i8(stream, Reply::Error as i8).await?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Request::Reboot => {
|
|
||||||
info!("rebooting");
|
|
||||||
write_i8(stream, Reply::RebootImminent as i8).await?;
|
|
||||||
stream.flush().await?;
|
|
||||||
slcr::reboot();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(cfg: Config) {
|
pub fn start() {
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
let pull_id = Rc::new(RefCell::new(0u32));
|
let pull_id = Rc::new(RefCell::new(0u32));
|
||||||
let cfg = Rc::new(cfg);
|
|
||||||
loop {
|
loop {
|
||||||
let mut stream = TcpStream::accept(1380, 2048, 2048).await.unwrap();
|
let mut stream = TcpStream::accept(1380, 2048, 2048).await.unwrap();
|
||||||
let pull_id = pull_id.clone();
|
let pull_id = pull_id.clone();
|
||||||
let cfg = cfg.clone();
|
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
info!("received connection");
|
info!("received connection");
|
||||||
let _ = handle_connection(&mut stream, pull_id, cfg)
|
let _ = handle_connection(&mut stream, pull_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| warn!("connection terminated: {:?}", e));
|
.map_err(|e| warn!("connection terminated: {:?}", e));
|
||||||
let _ = stream.flush().await;
|
let _ = stream.flush().await;
|
||||||
|
|
|
@ -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,52 @@ enum DeviceMessage {
|
||||||
InjectionStatus = 1
|
InjectionStatus = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(has_drtio)]
|
fn read_probe(channel: i32, probe: i8) -> i32 {
|
||||||
mod remote_moninj {
|
unsafe {
|
||||||
use super::*;
|
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
||||||
use libboard_artiq::drtioaux_async;
|
csr::rtio_moninj::mon_probe_sel_write(probe as _);
|
||||||
use crate::rtio_mgt::drtio;
|
csr::rtio_moninj::mon_value_update_write(1);
|
||||||
use log::error;
|
csr::rtio_moninj::mon_value_read() as i32
|
||||||
|
|
||||||
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 {
|
fn inject(channel: i32, overrd: i8, value: i8) {
|
||||||
use libboard_artiq::pl::csr;
|
unsafe {
|
||||||
|
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
||||||
pub fn read_probe(channel: i32, probe: i8) -> i64 {
|
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
||||||
unsafe {
|
csr::rtio_moninj::inj_value_write(value as _);
|
||||||
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
|
||||||
csr::rtio_moninj::mon_probe_sel_write(probe as _);
|
|
||||||
csr::rtio_moninj::mon_value_update_write(1);
|
|
||||||
csr::rtio_moninj::mon_value_read() as i64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inject(channel: i32, overrd: i8, value: i8) {
|
|
||||||
unsafe {
|
|
||||||
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
|
||||||
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
|
||||||
csr::rtio_moninj::inj_value_write(value as _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_injection_status(channel: i32, overrd: i8) -> i8 {
|
|
||||||
unsafe {
|
|
||||||
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
|
||||||
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
|
||||||
csr::rtio_moninj::inj_value_read() as i8
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(has_drtio)]
|
fn read_injection_status(channel: i32, overrd: i8) -> i8 {
|
||||||
macro_rules! dispatch {
|
unsafe {
|
||||||
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
||||||
let destination = ($channel >> 16) as u8;
|
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
||||||
let channel = $channel;
|
csr::rtio_moninj::inj_value_read() as i8
|
||||||
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))]
|
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 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +134,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 +150,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 +169,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),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use libboard_zynq::smoltcp::wire::{EthernetAddress, IpAddress};
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
|
||||||
|
pub struct NetAddresses {
|
||||||
|
pub hardware_addr: EthernetAddress,
|
||||||
|
pub ipv4_addr: IpAddress,
|
||||||
|
pub ipv6_ll_addr: IpAddress,
|
||||||
|
pub ipv6_addr: Option<IpAddress>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NetAddresses {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "MAC={} IPv4={} IPv6-LL={} IPv6=",
|
||||||
|
self.hardware_addr, self.ipv4_addr, self.ipv6_ll_addr)?;
|
||||||
|
match self.ipv6_addr {
|
||||||
|
Some(addr) => write!(f, "{}", addr)?,
|
||||||
|
None => write!(f, "no configured address")?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_adresses(cfg: &config::Config) -> NetAddresses {
|
||||||
|
let mut hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x52]);
|
||||||
|
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 52);
|
||||||
|
let mut ipv6_addr = None;
|
||||||
|
|
||||||
|
if let Ok(Ok(addr)) = cfg.read_str("mac").map(|s| s.parse()) {
|
||||||
|
hardware_addr = addr;
|
||||||
|
}
|
||||||
|
if let Ok(Ok(addr)) = cfg.read_str("ip").map(|s| s.parse()) {
|
||||||
|
ipv4_addr = addr;
|
||||||
|
}
|
||||||
|
if let Ok(Ok(addr)) = cfg.read_str("ip6").map(|s| s.parse()) {
|
||||||
|
ipv6_addr = Some(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ipv6_ll_addr = IpAddress::v6(
|
||||||
|
0xfe80, 0x0000, 0x0000, 0x0000,
|
||||||
|
(((hardware_addr.0[0] ^ 0x02) as u16) << 8) | (hardware_addr.0[1] as u16),
|
||||||
|
((hardware_addr.0[2] as u16) << 8) | 0x00ff,
|
||||||
|
0xfe00 | (hardware_addr.0[3] as u16),
|
||||||
|
((hardware_addr.0[4] as u16) << 8) | (hardware_addr.0[5] as u16));
|
||||||
|
|
||||||
|
NetAddresses {
|
||||||
|
hardware_addr: hardware_addr,
|
||||||
|
ipv4_addr: ipv4_addr,
|
||||||
|
ipv6_ll_addr: ipv6_ll_addr,
|
||||||
|
ipv6_addr: ipv6_addr
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,21 +3,20 @@ use libregister::RegisterR;
|
||||||
use libcortex_a9::regs::MPIDR;
|
use libcortex_a9::regs::MPIDR;
|
||||||
use unwind::backtrace;
|
use unwind::backtrace;
|
||||||
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
|
||||||
use libboard_zynq::error_led::ErrorLED;
|
|
||||||
use crate::comms::soft_panic_main;
|
|
||||||
use log::error;
|
|
||||||
use libboard_zynq::timer::GlobalTimer;
|
|
||||||
use libconfig::Config;
|
|
||||||
|
|
||||||
static mut PANICKED: [bool; 2] = [false; 2];
|
static mut PANICKED: [bool; 2] = [false; 2];
|
||||||
static mut SOFT_PANICKED: bool = false;
|
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
let id = MPIDR.read().cpu_id() as usize;
|
let id = MPIDR.read().cpu_id() as usize;
|
||||||
let soft_panicked = unsafe { SOFT_PANICKED };
|
print!("Core {} ", id);
|
||||||
print!("Core {} panic at ", id);
|
unsafe {
|
||||||
|
if PANICKED[id] {
|
||||||
|
println!("nested panic!");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
PANICKED[id] = true;
|
||||||
|
}
|
||||||
|
print!("panic at ");
|
||||||
if let Some(location) = info.location() {
|
if let Some(location) = info.location() {
|
||||||
print!("{}:{}:{}", location.file(), location.line(), location.column());
|
print!("{}:{}:{}", location.file(), location.line(), location.column());
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,20 +27,6 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
} else {
|
} else {
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
unsafe {
|
|
||||||
// soft panics only allowed for core 0
|
|
||||||
if PANICKED[id] && (SOFT_PANICKED || id == 1) {
|
|
||||||
println!("nested panic!");
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
SOFT_PANICKED = true;
|
|
||||||
PANICKED[id] = true;
|
|
||||||
}
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
|
||||||
{
|
|
||||||
let mut err_led = ErrorLED::error_led();
|
|
||||||
err_led.toggle(true);
|
|
||||||
}
|
|
||||||
println!("Backtrace: ");
|
println!("Backtrace: ");
|
||||||
let _ = backtrace(|ip| {
|
let _ = backtrace(|ip| {
|
||||||
// Backtrace gives us the return address, i.e. the address after the delay slot,
|
// Backtrace gives us the return address, i.e. the address after the delay slot,
|
||||||
|
@ -49,28 +34,6 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
print!("{:#08x} ", ip - 2 * 4);
|
print!("{:#08x} ", ip - 2 * 4);
|
||||||
});
|
});
|
||||||
println!("\nEnd backtrace");
|
println!("\nEnd backtrace");
|
||||||
if !soft_panicked && id == 0 {
|
|
||||||
soft_panic(info);
|
|
||||||
}
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn soft_panic(info: &core::panic::PanicInfo) -> ! {
|
|
||||||
// write panic info to log, so coremgmt can also read it
|
|
||||||
if let Some(location) = info.location() {
|
|
||||||
error!("panic at {}:{}:{}", location.file(), location.line(), location.column());
|
|
||||||
} else {
|
|
||||||
error!("panic at unknown location");
|
|
||||||
}
|
|
||||||
if let Some(message) = info.message() {
|
|
||||||
error!("panic message: {}", message);
|
|
||||||
}
|
|
||||||
let timer = GlobalTimer::start();
|
|
||||||
let cfg = match Config::new() {
|
|
||||||
Ok(cfg) => cfg,
|
|
||||||
Err(_) => {
|
|
||||||
Config::new_dummy()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
soft_panic_main(timer, cfg);
|
|
||||||
}
|
|
|
@ -52,15 +52,55 @@ pub async fn read_i8(stream: &TcpStream) -> Result<i8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_i32(stream: &TcpStream) -> Result<i32> {
|
pub async fn read_i32(stream: &TcpStream) -> Result<i32> {
|
||||||
let mut buffer: [u8; 4] = [0; 4];
|
let mut state = RecvState::NeedsMore(0, 0);
|
||||||
read_chunk(stream, &mut buffer).await?;
|
loop {
|
||||||
Ok(i32::from_le_bytes(buffer))
|
state = stream.recv(|buf| {
|
||||||
|
let mut consumed = 0;
|
||||||
|
if let RecvState::NeedsMore(mut cur_index, mut cur_value) = state {
|
||||||
|
for b in buf.iter() {
|
||||||
|
consumed += 1;
|
||||||
|
cur_index += 1;
|
||||||
|
cur_value <<= 8;
|
||||||
|
cur_value |= *b as i32;
|
||||||
|
if cur_index == 4 {
|
||||||
|
return (consumed, RecvState::Completed(cur_value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(consumed, RecvState::NeedsMore(cur_index, cur_value))
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}).await?;
|
||||||
|
if let RecvState::Completed(result) = state {
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_i64(stream: &TcpStream) -> Result<i64> {
|
pub async fn read_i64(stream: &TcpStream) -> Result<i64> {
|
||||||
let mut buffer: [u8; 8] = [0; 8];
|
let mut state = RecvState::NeedsMore(0, 0);
|
||||||
read_chunk(stream, &mut buffer).await?;
|
loop {
|
||||||
Ok(i64::from_le_bytes(buffer))
|
state = stream.recv(|buf| {
|
||||||
|
let mut consumed = 0;
|
||||||
|
if let RecvState::NeedsMore(mut cur_index, mut cur_value) = state {
|
||||||
|
for b in buf.iter() {
|
||||||
|
consumed += 1;
|
||||||
|
cur_index += 1;
|
||||||
|
cur_value <<= 8;
|
||||||
|
cur_value |= *b as i64;
|
||||||
|
if cur_index == 8 {
|
||||||
|
return (consumed, RecvState::Completed(cur_value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(consumed, RecvState::NeedsMore(cur_index, cur_value))
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}).await?;
|
||||||
|
if let RecvState::Completed(result) = state {
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_chunk(stream: &TcpStream, destination: &mut [u8]) -> Result<()> {
|
pub async fn read_chunk(stream: &TcpStream, destination: &mut [u8]) -> Result<()> {
|
||||||
|
@ -80,27 +120,39 @@ pub async fn read_chunk(stream: &TcpStream, destination: &mut [u8]) -> Result<()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_i8(stream: &TcpStream, value: i8) -> Result<()> {
|
pub async fn write_i8(stream: &TcpStream, value: i8) -> Result<()> {
|
||||||
stream.send_slice(&[value as u8]).await?;
|
stream.send([value as u8].iter().copied()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_bool(stream: &TcpStream, value: bool) -> Result<()> {
|
pub async fn write_bool(stream: &TcpStream, value: bool) -> Result<()> {
|
||||||
stream.send_slice(&[value as u8]).await?;
|
stream.send([value as u8].iter().copied()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_i32(stream: &TcpStream, value: i32) -> Result<()> {
|
pub async fn write_i32(stream: &TcpStream, value: i32) -> Result<()> {
|
||||||
stream.send_slice(&value.to_le_bytes()).await?;
|
stream.send([
|
||||||
|
(value >> 24) as u8,
|
||||||
|
(value >> 16) as u8,
|
||||||
|
(value >> 8) as u8,
|
||||||
|
value as u8].iter().copied()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_i64(stream: &TcpStream, value: i64) -> Result<()> {
|
pub async fn write_i64(stream: &TcpStream, value: i64) -> Result<()> {
|
||||||
stream.send_slice(&value.to_le_bytes()).await?;
|
stream.send([
|
||||||
|
(value >> 56) as u8,
|
||||||
|
(value >> 48) as u8,
|
||||||
|
(value >> 40) as u8,
|
||||||
|
(value >> 32) as u8,
|
||||||
|
(value >> 24) as u8,
|
||||||
|
(value >> 16) as u8,
|
||||||
|
(value >> 8) as u8,
|
||||||
|
value as u8].iter().copied()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_chunk(stream: &TcpStream, value: &[u8]) -> Result<()> {
|
pub async fn write_chunk(stream: &TcpStream, value: &[u8]) -> Result<()> {
|
||||||
write_i32(stream, value.len() as i32).await?;
|
write_i32(stream, value.len() as i32).await?;
|
||||||
stream.send_slice(value).await?;
|
stream.send(value.iter().copied()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use core::str::Utf8Error;
|
use core::str::Utf8Error;
|
||||||
use byteorder::{ByteOrder, NativeEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
|
||||||
|
@ -28,21 +28,21 @@ pub trait ProtoRead {
|
||||||
fn read_u16(&mut self) -> Result<u16, Self::ReadError> {
|
fn read_u16(&mut self) -> Result<u16, Self::ReadError> {
|
||||||
let mut bytes = [0; 2];
|
let mut bytes = [0; 2];
|
||||||
self.read_exact(&mut bytes)?;
|
self.read_exact(&mut bytes)?;
|
||||||
Ok(NativeEndian::read_u16(&bytes))
|
Ok(NetworkEndian::read_u16(&bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_u32(&mut self) -> Result<u32, Self::ReadError> {
|
fn read_u32(&mut self) -> Result<u32, Self::ReadError> {
|
||||||
let mut bytes = [0; 4];
|
let mut bytes = [0; 4];
|
||||||
self.read_exact(&mut bytes)?;
|
self.read_exact(&mut bytes)?;
|
||||||
Ok(NativeEndian::read_u32(&bytes))
|
Ok(NetworkEndian::read_u32(&bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_u64(&mut self) -> Result<u64, Self::ReadError> {
|
fn read_u64(&mut self) -> Result<u64, Self::ReadError> {
|
||||||
let mut bytes = [0; 8];
|
let mut bytes = [0; 8];
|
||||||
self.read_exact(&mut bytes)?;
|
self.read_exact(&mut bytes)?;
|
||||||
Ok(NativeEndian::read_u64(&bytes))
|
Ok(NetworkEndian::read_u64(&bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -85,42 +85,42 @@ pub trait ProtoWrite {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> {
|
fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> {
|
||||||
let mut bytes = [0; 2];
|
let mut bytes = [0; 2];
|
||||||
NativeEndian::write_u16(&mut bytes, value);
|
NetworkEndian::write_u16(&mut bytes, value);
|
||||||
self.write_all(&bytes)
|
self.write_all(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> {
|
fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> {
|
||||||
let mut bytes = [0; 2];
|
let mut bytes = [0; 2];
|
||||||
NativeEndian::write_i16(&mut bytes, value);
|
NetworkEndian::write_i16(&mut bytes, value);
|
||||||
self.write_all(&bytes)
|
self.write_all(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> {
|
fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> {
|
||||||
let mut bytes = [0; 4];
|
let mut bytes = [0; 4];
|
||||||
NativeEndian::write_u32(&mut bytes, value);
|
NetworkEndian::write_u32(&mut bytes, value);
|
||||||
self.write_all(&bytes)
|
self.write_all(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> {
|
fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> {
|
||||||
let mut bytes = [0; 4];
|
let mut bytes = [0; 4];
|
||||||
NativeEndian::write_i32(&mut bytes, value);
|
NetworkEndian::write_i32(&mut bytes, value);
|
||||||
self.write_all(&bytes)
|
self.write_all(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> {
|
fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> {
|
||||||
let mut bytes = [0; 8];
|
let mut bytes = [0; 8];
|
||||||
NativeEndian::write_u64(&mut bytes, value);
|
NetworkEndian::write_u64(&mut bytes, value);
|
||||||
self.write_all(&bytes)
|
self.write_all(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> {
|
fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> {
|
||||||
let mut bytes = [0; 8];
|
let mut bytes = [0; 8];
|
||||||
NativeEndian::write_i64(&mut bytes, value);
|
NetworkEndian::write_i64(&mut bytes, value);
|
||||||
self.write_all(&bytes)
|
self.write_all(&bytes)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use core::str;
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
use cslice::{CSlice, CMutSlice};
|
use cslice::{CSlice, CMutSlice};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use byteorder::{NativeEndian, ByteOrder};
|
|
||||||
|
|
||||||
use core_io::{Write, Error};
|
use core_io::{Write, Error};
|
||||||
use libboard_zynq::smoltcp;
|
use libboard_zynq::smoltcp;
|
||||||
|
@ -10,24 +9,19 @@ 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};
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn alignment_offset(alignment: isize, ptr: isize) -> isize {
|
|
||||||
(alignment - ptr % alignment) % alignment
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
|
unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
|
||||||
let alignment = core::mem::align_of::<T>() as isize;
|
let alignment = core::mem::align_of::<T>() as isize;
|
||||||
let fix = alignment_offset(alignment, ptr as isize);
|
let fix = (alignment - (ptr as isize) % alignment) % alignment;
|
||||||
((ptr as isize) + fix) as *const T
|
((ptr as isize) + fix) as *const T
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
|
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
|
||||||
let alignment = core::mem::align_of::<T>() as isize;
|
let alignment = core::mem::align_of::<T>() as isize;
|
||||||
let fix = alignment_offset(alignment, ptr as isize);
|
let fix = (alignment - (ptr as isize) % alignment) % alignment;
|
||||||
((ptr as isize) + fix) as *mut T
|
((ptr as isize) + fix) as *mut T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +65,6 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Tag::Tuple(it, arity) => {
|
Tag::Tuple(it, arity) => {
|
||||||
*data = (*data).offset(alignment_offset(tag.alignment() as isize, *data as isize));
|
|
||||||
let mut it = it.clone();
|
let mut it = it.clone();
|
||||||
for _ in 0..arity {
|
for _ in 0..arity {
|
||||||
let tag = it.next().expect("truncated tag");
|
let tag = it.next().expect("truncated tag");
|
||||||
|
@ -79,112 +72,23 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Tag::List(it) => {
|
Tag::List(it) | Tag::Array(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;
|
(*ptr).length = proto_async::read_i32(stream).await? 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 +
|
(*ptr).elements = alloc(tag.size() * (*ptr).length as usize).await;
|
||||||
match tag {
|
|
||||||
Tag::Int64 | Tag::Float64 => 4,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
let data = alloc(data_size + 8).await as *mut u8;
|
|
||||||
*ptr = data as *mut List;
|
|
||||||
let ptr = data as *mut List;
|
|
||||||
let data = data.offset(8);
|
|
||||||
|
|
||||||
let alignment = tag.alignment();
|
let mut data = (*ptr).elements;
|
||||||
let mut data = data.offset(alignment_offset(alignment as isize, data as isize)) as *mut ();
|
for _ in 0..(*ptr).length as usize {
|
||||||
(*ptr).length = length as u32;
|
recv_value(stream, tag, &mut data, alloc).await?
|
||||||
(*ptr).elements = data;
|
|
||||||
match tag {
|
|
||||||
Tag::Bool => {
|
|
||||||
let ptr = data as *mut u8;
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
|
||||||
},
|
|
||||||
Tag::Int32 => {
|
|
||||||
let ptr = data as *mut u32;
|
|
||||||
// reading as raw bytes and do endianness conversion later
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
|
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
|
||||||
drop(dest);
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
|
||||||
NativeEndian::from_slice_u32(dest);
|
|
||||||
},
|
|
||||||
Tag::Int64 | Tag::Float64 => {
|
|
||||||
let ptr = data as *mut u64;
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
|
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
|
||||||
drop(dest);
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
|
||||||
NativeEndian::from_slice_u64(dest);
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
for _ in 0..(*ptr).length as usize {
|
|
||||||
recv_value(stream, tag, &mut data, alloc).await?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Tag::Array(it, num_dims) => {
|
|
||||||
consume_value!(*mut (), |buffer| {
|
|
||||||
let mut total_len: u32 = 1;
|
|
||||||
for _ in 0..num_dims {
|
|
||||||
let len = proto_async::read_i32(stream).await? as u32;
|
|
||||||
total_len *= len;
|
|
||||||
consume_value!(u32, |ptr| *ptr = len )
|
|
||||||
}
|
|
||||||
|
|
||||||
let elt_tag = it.clone().next().expect("truncated tag");
|
|
||||||
let data_size = elt_tag.size() * total_len as usize +
|
|
||||||
match elt_tag {
|
|
||||||
Tag::Int64 | Tag::Float64 => 4,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
let mut data = alloc(data_size).await;
|
|
||||||
|
|
||||||
let alignment = tag.alignment();
|
|
||||||
data = data.offset(alignment_offset(alignment as isize, data as isize));
|
|
||||||
*buffer = data;
|
|
||||||
let length = total_len as usize;
|
|
||||||
match elt_tag {
|
|
||||||
Tag::Bool => {
|
|
||||||
let ptr = data as *mut u8;
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
|
||||||
},
|
|
||||||
Tag::Int32 => {
|
|
||||||
let ptr = data as *mut u32;
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
|
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
|
||||||
drop(dest);
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
|
||||||
NativeEndian::from_slice_u32(dest);
|
|
||||||
},
|
|
||||||
Tag::Int64 | Tag::Float64 => {
|
|
||||||
let ptr = data as *mut u64;
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
|
|
||||||
proto_async::read_chunk(stream, dest).await?;
|
|
||||||
drop(dest);
|
|
||||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
|
||||||
NativeEndian::from_slice_u64(dest);
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
for _ in 0..length {
|
|
||||||
recv_value(stream, elt_tag, &mut data, alloc).await?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Tag::Range(it) => {
|
Tag::Range(it) => {
|
||||||
*data = (*data).offset(alignment_offset(tag.alignment() as isize, *data as isize));
|
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
recv_value(stream, tag, data, alloc).await?;
|
recv_value(stream, tag, data, alloc).await?;
|
||||||
recv_value(stream, tag, data, alloc).await?;
|
recv_value(stream, tag, data, alloc).await?;
|
||||||
|
@ -250,80 +154,16 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Tag::List(it) => {
|
Tag::List(it) | Tag::Array(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;
|
|
||||||
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())?;
|
for _ in 0..(*ptr).length as usize {
|
||||||
match tag {
|
send_value(writer, tag, &mut data)?;
|
||||||
Tag::Bool => {
|
|
||||||
// we can pretend this is u8...
|
|
||||||
let ptr1 = align_ptr::<u8>(data);
|
|
||||||
let slice = core::slice::from_raw_parts(ptr1, length as usize);
|
|
||||||
writer.write_all(slice)?;
|
|
||||||
},
|
|
||||||
Tag::Int32 => {
|
|
||||||
let ptr1 = align_ptr::<i32>(data);
|
|
||||||
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 4);
|
|
||||||
writer.write_all(slice)?;
|
|
||||||
},
|
|
||||||
Tag::Int64 | Tag::Float64 => {
|
|
||||||
let ptr1 = align_ptr::<i64>(data);
|
|
||||||
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 8);
|
|
||||||
writer.write_all(slice)?;
|
|
||||||
},
|
|
||||||
// non-primitive types, not sure if this would happen but we can handle it...
|
|
||||||
_ => {
|
|
||||||
for _ in 0..length {
|
|
||||||
send_value(writer, tag, &mut data)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Tag::Array(it, num_dims) => {
|
|
||||||
writer.write_u8(num_dims)?;
|
|
||||||
consume_value!(*const(), |buffer| {
|
|
||||||
let elt_tag = it.clone().next().expect("truncated tag");
|
|
||||||
|
|
||||||
let mut total_len = 1;
|
|
||||||
for _ in 0..num_dims {
|
|
||||||
consume_value!(u32, |len| {
|
|
||||||
writer.write_u32(*len)?;
|
|
||||||
total_len *= *len;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
let mut data = *buffer;
|
|
||||||
let length = total_len as isize;
|
|
||||||
writer.write_u8(elt_tag.as_u8())?;
|
|
||||||
match elt_tag {
|
|
||||||
Tag::Bool => {
|
|
||||||
let ptr1 = align_ptr::<u8>(data);
|
|
||||||
let slice = core::slice::from_raw_parts(ptr1, length as usize);
|
|
||||||
writer.write_all(slice)?;
|
|
||||||
},
|
|
||||||
Tag::Int32 => {
|
|
||||||
let ptr1 = align_ptr::<i32>(data);
|
|
||||||
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 4);
|
|
||||||
writer.write_all(slice)?;
|
|
||||||
},
|
|
||||||
Tag::Int64 | Tag::Float64 => {
|
|
||||||
let ptr1 = align_ptr::<i64>(data);
|
|
||||||
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 8);
|
|
||||||
writer.write_all(slice)?;
|
|
||||||
},
|
|
||||||
// non-primitive types, not sure if this would happen but we can handle it...
|
|
||||||
_ => {
|
|
||||||
for _ in 0..length {
|
|
||||||
send_value(writer, elt_tag, &mut data)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -336,7 +176,7 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||||
}
|
}
|
||||||
Tag::Keyword(it) => {
|
Tag::Keyword(it) => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct Keyword<'a> { name: CSlice<'a, u8> }
|
struct Keyword<'a> { name: CSlice<'a, u8> };
|
||||||
consume_value!(Keyword, |ptr| {
|
consume_value!(Keyword, |ptr| {
|
||||||
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
|
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
|
@ -348,7 +188,7 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||||
}
|
}
|
||||||
Tag::Object => {
|
Tag::Object => {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct Object { id: u32 }
|
struct Object { id: u32 };
|
||||||
consume_value!(*const Object, |ptr|
|
consume_value!(*const Object, |ptr|
|
||||||
writer.write_u32((**ptr).id))
|
writer.write_u32((**ptr).id))
|
||||||
}
|
}
|
||||||
|
@ -405,7 +245,7 @@ mod tag {
|
||||||
ByteArray,
|
ByteArray,
|
||||||
Tuple(TagIterator<'a>, u8),
|
Tuple(TagIterator<'a>, u8),
|
||||||
List(TagIterator<'a>),
|
List(TagIterator<'a>),
|
||||||
Array(TagIterator<'a>, u8),
|
Array(TagIterator<'a>),
|
||||||
Range(TagIterator<'a>),
|
Range(TagIterator<'a>),
|
||||||
Keyword(TagIterator<'a>),
|
Keyword(TagIterator<'a>),
|
||||||
Object
|
Object
|
||||||
|
@ -424,42 +264,14 @@ mod tag {
|
||||||
Tag::ByteArray => b'A',
|
Tag::ByteArray => b'A',
|
||||||
Tag::Tuple(_, _) => b't',
|
Tag::Tuple(_, _) => b't',
|
||||||
Tag::List(_) => b'l',
|
Tag::List(_) => b'l',
|
||||||
Tag::Array(_, _) => b'a',
|
Tag::Array(_) => b'a',
|
||||||
Tag::Range(_) => b'r',
|
Tag::Range(_) => b'r',
|
||||||
Tag::Keyword(_) => b'k',
|
Tag::Keyword(_) => b'k',
|
||||||
Tag::Object => b'O',
|
Tag::Object => b'O',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alignment(self) -> usize {
|
|
||||||
use cslice::CSlice;
|
|
||||||
match self {
|
|
||||||
Tag::None => 1,
|
|
||||||
Tag::Bool => core::mem::align_of::<u8>(),
|
|
||||||
Tag::Int32 => core::mem::align_of::<i32>(),
|
|
||||||
Tag::Int64 => core::mem::align_of::<i64>(),
|
|
||||||
Tag::Float64 => core::mem::align_of::<f64>(),
|
|
||||||
// struct type: align to largest element
|
|
||||||
Tag::Tuple(it, arity) => {
|
|
||||||
let it = it.clone();
|
|
||||||
it.take(arity.into()).map(|t| t.alignment()).max().unwrap()
|
|
||||||
},
|
|
||||||
Tag::Range(it) => {
|
|
||||||
let it = it.clone();
|
|
||||||
it.take(3).map(|t| t.alignment()).max().unwrap()
|
|
||||||
}
|
|
||||||
// CSlice basically
|
|
||||||
Tag::Bytes | Tag::String | Tag::ByteArray =>
|
|
||||||
core::mem::align_of::<CSlice<()>>(),
|
|
||||||
// array buffer is allocated, so no need for alignment first
|
|
||||||
Tag::List(_) | Tag::Array(_, _) => 1,
|
|
||||||
// will not be sent from the host
|
|
||||||
_ => unreachable!("unexpected tag from host")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(self) -> usize {
|
pub fn size(self) -> usize {
|
||||||
use super::alignment_offset;
|
|
||||||
match self {
|
match self {
|
||||||
Tag::None => 0,
|
Tag::None => 0,
|
||||||
Tag::Bool => 1,
|
Tag::Bool => 1,
|
||||||
|
@ -475,13 +287,11 @@ mod tag {
|
||||||
for _ in 0..arity {
|
for _ in 0..arity {
|
||||||
let tag = it.next().expect("truncated tag");
|
let tag = it.next().expect("truncated tag");
|
||||||
size += tag.size();
|
size += tag.size();
|
||||||
// includes padding
|
|
||||||
size += alignment_offset(tag.alignment() as isize, size as isize) as usize;
|
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
Tag::List(_) => 4,
|
Tag::List(_) => 8,
|
||||||
Tag::Array(_, num_dims) => 4 * (1 + num_dims as usize),
|
Tag::Array(_) => 8,
|
||||||
Tag::Range(it) => {
|
Tag::Range(it) => {
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
tag.size() * 3
|
tag.size() * 3
|
||||||
|
@ -499,23 +309,10 @@ mod tag {
|
||||||
|
|
||||||
impl<'a> TagIterator<'a> {
|
impl<'a> TagIterator<'a> {
|
||||||
pub fn new(data: &'a [u8]) -> TagIterator<'a> {
|
pub fn new(data: &'a [u8]) -> TagIterator<'a> {
|
||||||
TagIterator { data }
|
TagIterator { data: data }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> Option<Tag<'a>> {
|
||||||
fn sub(&mut self, count: u8) -> TagIterator<'a> {
|
|
||||||
let data = self.data;
|
|
||||||
for _ in 0..count {
|
|
||||||
self.next().expect("truncated tag");
|
|
||||||
}
|
|
||||||
TagIterator { data: &data[..(data.len() - self.data.len())] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> core::iter::Iterator for TagIterator<'a> {
|
|
||||||
type Item = Tag<'a>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Tag<'a>> {
|
|
||||||
if self.data.len() == 0 {
|
if self.data.len() == 0 {
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
@ -537,17 +334,21 @@ mod tag {
|
||||||
Tag::Tuple(self.sub(count), count)
|
Tag::Tuple(self.sub(count), count)
|
||||||
}
|
}
|
||||||
b'l' => Tag::List(self.sub(1)),
|
b'l' => Tag::List(self.sub(1)),
|
||||||
b'a' => {
|
b'a' => Tag::Array(self.sub(1)),
|
||||||
let count = self.data[0];
|
|
||||||
self.data = &self.data[1..];
|
|
||||||
Tag::Array(self.sub(1), count)
|
|
||||||
}
|
|
||||||
b'r' => Tag::Range(self.sub(1)),
|
b'r' => Tag::Range(self.sub(1)),
|
||||||
b'k' => Tag::Keyword(self.sub(1)),
|
b'k' => Tag::Keyword(self.sub(1)),
|
||||||
b'O' => Tag::Object,
|
b'O' => Tag::Object,
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sub(&mut self, count: u8) -> TagIterator<'a> {
|
||||||
|
let data = self.data;
|
||||||
|
for _ in 0..count {
|
||||||
|
self.next().expect("truncated tag");
|
||||||
|
}
|
||||||
|
TagIterator { data: &data[..(data.len() - self.data.len())] }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Display for TagIterator<'a> {
|
impl<'a> fmt::Display for TagIterator<'a> {
|
||||||
|
@ -588,10 +389,10 @@ mod tag {
|
||||||
it.fmt(f)?;
|
it.fmt(f)?;
|
||||||
write!(f, ")")?;
|
write!(f, ")")?;
|
||||||
}
|
}
|
||||||
Tag::Array(it, num_dims) => {
|
Tag::Array(it) => {
|
||||||
write!(f, "Array(")?;
|
write!(f, "Array(")?;
|
||||||
it.fmt(f)?;
|
it.fmt(f)?;
|
||||||
write!(f, ", {})", num_dims)?;
|
write!(f, ")")?;
|
||||||
}
|
}
|
||||||
Tag::Range(it) => {
|
Tag::Range(it) => {
|
||||||
write!(f, "Range(")?;
|
write!(f, "Range(")?;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use cslice::CSlice;
|
use cslice::CSlice;
|
||||||
use vcell::VolatileCell;
|
use vcell::VolatileCell;
|
||||||
use libcortex_a9::asm;
|
use libcortex_a9::asm;
|
||||||
|
|
||||||
use crate::artiq_raise;
|
use crate::artiq_raise;
|
||||||
use core::sync::atomic::{fence, Ordering};
|
|
||||||
|
|
||||||
use crate::pl::csr;
|
use crate::pl::csr;
|
||||||
|
|
||||||
|
@ -20,33 +20,33 @@ pub struct TimestampedData {
|
||||||
data: i32,
|
data: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C, align(64))]
|
#[repr(C, align(32))]
|
||||||
struct Transaction {
|
struct Transaction {
|
||||||
request_cmd: i8,
|
request_cmd: i8,
|
||||||
data_width: i8,
|
padding0: i8,
|
||||||
padding0: [i8; 2],
|
padding1: i8,
|
||||||
|
padding2: i8,
|
||||||
request_target: i32,
|
request_target: i32,
|
||||||
request_timestamp: i64,
|
request_timestamp: i64,
|
||||||
request_data: [i32; 16],
|
request_data: i64,
|
||||||
padding1: [i64; 2],
|
padding: i64,
|
||||||
reply_status: VolatileCell<i32>,
|
reply_status: VolatileCell<i32>,
|
||||||
reply_data: VolatileCell<i32>,
|
reply_data: VolatileCell<i32>,
|
||||||
reply_timestamp: VolatileCell<i64>,
|
reply_timestamp: VolatileCell<i64>
|
||||||
padding2: [i64; 2],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut TRANSACTION_BUFFER: Transaction = Transaction {
|
static mut TRANSACTION_BUFFER: Transaction = Transaction {
|
||||||
request_cmd: 0,
|
request_cmd: 0,
|
||||||
data_width: 0,
|
padding0: 0,
|
||||||
|
padding1: 0,
|
||||||
|
padding2: 0,
|
||||||
request_target: 0,
|
request_target: 0,
|
||||||
request_timestamp: 0,
|
request_timestamp: 0,
|
||||||
request_data: [0; 16],
|
request_data: 0,
|
||||||
|
padding: 0,
|
||||||
reply_status: VolatileCell::new(0),
|
reply_status: VolatileCell::new(0),
|
||||||
reply_data: VolatileCell::new(0),
|
reply_data: VolatileCell::new(0),
|
||||||
reply_timestamp: VolatileCell::new(0),
|
reply_timestamp: VolatileCell::new(0)
|
||||||
padding0: [0; 2],
|
|
||||||
padding1: [0; 2],
|
|
||||||
padding2: [0; 2]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub extern fn init() {
|
pub extern fn init() {
|
||||||
|
@ -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);
|
||||||
|
@ -103,41 +108,13 @@ pub extern fn output(target: i32, data: i32) {
|
||||||
TRANSACTION_BUFFER.reply_status.set(0);
|
TRANSACTION_BUFFER.reply_status.set(0);
|
||||||
|
|
||||||
TRANSACTION_BUFFER.request_cmd = 0;
|
TRANSACTION_BUFFER.request_cmd = 0;
|
||||||
TRANSACTION_BUFFER.data_width = 1;
|
|
||||||
TRANSACTION_BUFFER.request_target = target;
|
TRANSACTION_BUFFER.request_target = target;
|
||||||
TRANSACTION_BUFFER.request_timestamp = NOW;
|
TRANSACTION_BUFFER.request_timestamp = NOW;
|
||||||
TRANSACTION_BUFFER.request_data[0] = data;
|
TRANSACTION_BUFFER.request_data = data as i64;
|
||||||
|
|
||||||
fence(Ordering::SeqCst);
|
asm::dmb();
|
||||||
asm::sev();
|
asm::sev();
|
||||||
let mut status;
|
|
||||||
loop {
|
|
||||||
status = TRANSACTION_BUFFER.reply_status.get();
|
|
||||||
if status != 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let status = status & !0x10000;
|
|
||||||
if status != 0 {
|
|
||||||
process_exceptional_status(target >> 8, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn output_wide(target: i32, data: CSlice<i32>) {
|
|
||||||
unsafe {
|
|
||||||
// Clear status so we can observe response
|
|
||||||
TRANSACTION_BUFFER.reply_status.set(0);
|
|
||||||
|
|
||||||
TRANSACTION_BUFFER.request_cmd = 0;
|
|
||||||
TRANSACTION_BUFFER.data_width = data.len() as i8;
|
|
||||||
TRANSACTION_BUFFER.request_target = target;
|
|
||||||
TRANSACTION_BUFFER.request_timestamp = NOW;
|
|
||||||
TRANSACTION_BUFFER.request_data[..data.len()].copy_from_slice(data.as_ref());
|
|
||||||
|
|
||||||
fence(Ordering::SeqCst);
|
|
||||||
asm::sev();
|
|
||||||
let mut status;
|
let mut status;
|
||||||
loop {
|
loop {
|
||||||
status = TRANSACTION_BUFFER.reply_status.get();
|
status = TRANSACTION_BUFFER.reply_status.get();
|
||||||
|
@ -153,17 +130,21 @@ pub extern fn output_wide(target: i32, data: CSlice<i32>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub extern fn output_wide(target: i32, data: CSlice<i32>) {
|
||||||
|
// TODO
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
pub extern fn input_timestamp(timeout: i64, channel: i32) -> i64 {
|
pub extern fn input_timestamp(timeout: i64, channel: i32) -> i64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Clear status so we can observe response
|
// Clear status so we can observe response
|
||||||
TRANSACTION_BUFFER.reply_status.set(0);
|
TRANSACTION_BUFFER.reply_status.set(0);
|
||||||
|
|
||||||
TRANSACTION_BUFFER.request_cmd = 1;
|
TRANSACTION_BUFFER.request_cmd = 1;
|
||||||
TRANSACTION_BUFFER.request_timestamp = timeout;
|
TRANSACTION_BUFFER.request_timestamp = NOW;
|
||||||
TRANSACTION_BUFFER.request_target = channel << 8;
|
TRANSACTION_BUFFER.request_target = channel << 8;
|
||||||
TRANSACTION_BUFFER.data_width = 0;
|
|
||||||
|
|
||||||
fence(Ordering::SeqCst);
|
asm::dmb();
|
||||||
asm::sev();
|
asm::sev();
|
||||||
|
|
||||||
let mut status;
|
let mut status;
|
||||||
|
@ -199,9 +180,8 @@ pub extern fn input_data(channel: i32) -> i32 {
|
||||||
TRANSACTION_BUFFER.request_cmd = 1;
|
TRANSACTION_BUFFER.request_cmd = 1;
|
||||||
TRANSACTION_BUFFER.request_timestamp = -1;
|
TRANSACTION_BUFFER.request_timestamp = -1;
|
||||||
TRANSACTION_BUFFER.request_target = channel << 8;
|
TRANSACTION_BUFFER.request_target = channel << 8;
|
||||||
TRANSACTION_BUFFER.data_width = 0;
|
|
||||||
|
|
||||||
fence(Ordering::SeqCst);
|
asm::dmb();
|
||||||
asm::sev();
|
asm::sev();
|
||||||
|
|
||||||
let mut status;
|
let mut status;
|
||||||
|
@ -234,9 +214,8 @@ pub extern fn input_timestamped_data(timeout: i64, channel: i32) -> TimestampedD
|
||||||
TRANSACTION_BUFFER.request_cmd = 1;
|
TRANSACTION_BUFFER.request_cmd = 1;
|
||||||
TRANSACTION_BUFFER.request_timestamp = timeout;
|
TRANSACTION_BUFFER.request_timestamp = timeout;
|
||||||
TRANSACTION_BUFFER.request_target = channel << 8;
|
TRANSACTION_BUFFER.request_target = channel << 8;
|
||||||
TRANSACTION_BUFFER.data_width = 0;
|
|
||||||
|
|
||||||
fence(Ordering::SeqCst);
|
asm::dmb();
|
||||||
asm::sev();
|
asm::sev();
|
||||||
|
|
||||||
let mut status;
|
let mut status;
|
||||||
|
@ -266,17 +245,6 @@ pub extern fn input_timestamped_data(timeout: i64, channel: i32) -> TimestampedD
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_log(data: &[i8]) {
|
pub fn write_log(data: &[i8]) {
|
||||||
let mut word: u32 = 0;
|
// TODO
|
||||||
for i in 0..data.len() {
|
unimplemented!();
|
||||||
word <<= 8;
|
|
||||||
word |= data[i] as u32;
|
|
||||||
if i % 4 == 3 {
|
|
||||||
output((csr::CONFIG_RTIO_LOG_CHANNEL << 8) as i32, word as i32);
|
|
||||||
word = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if word != 0 {
|
|
||||||
output((csr::CONFIG_RTIO_LOG_CHANNEL << 8) as i32, word as i32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,236 +0,0 @@
|
||||||
use log::{info, warn};
|
|
||||||
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 {
|
|
||||||
panic!("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 _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si5324 input to select for locking to an external clock.
|
|
||||||
#[cfg(has_si5324)]
|
|
||||||
const SI5324_EXT_INPUT: si5324::Input = si5324::Input::Ckin1;
|
|
||||||
|
|
||||||
#[cfg(has_si5324)]
|
|
||||||
fn setup_si5324(i2c: &mut I2c, timer: &mut GlobalTimer, clk: RtioClock) {
|
|
||||||
let si5324_settings = 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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => { // 125MHz output like above, default (if chosen option is not supported)
|
|
||||||
warn!("rtio_clock setting '{:?}' is not supported. 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::setup(i2c, &si5324_settings, SI5324_EXT_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() };
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
|
|
@ -1,362 +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 crate::{SEEN_ASYNC_ERRORS, ASYNC_ERROR_BUSY, ASYNC_ERROR_SEQUENCE_ERROR, ASYNC_ERROR_COLLISION};
|
|
||||||
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);
|
|
||||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
|
|
||||||
}
|
|
||||||
Ok(Packet::DestinationCollisionReply { channel }) =>{
|
|
||||||
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}", destination, channel);
|
|
||||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
|
|
||||||
}
|
|
||||||
Ok(Packet::DestinationBusyReply { channel }) =>{
|
|
||||||
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}", destination, channel);
|
|
||||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
|
|
||||||
}
|
|
||||||
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
|
|
||||||
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -0,0 +1,303 @@
|
||||||
|
use core_io::{BufRead, Error, ErrorKind, Read, Result as IoResult, Seek, SeekFrom, Write};
|
||||||
|
use fatfs;
|
||||||
|
use libboard_zynq::sdio::{sd_card::SdCard, CmdTransferError};
|
||||||
|
use log::debug;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
const MBR_SIGNATURE: [u8; 2] = [0x55, 0xAA];
|
||||||
|
const PARTID_FAT12: u8 = 0x01;
|
||||||
|
const PARTID_FAT16_LESS32M: u8 = 0x04;
|
||||||
|
const PARTID_FAT16: u8 = 0x06;
|
||||||
|
const PARTID_FAT32: u8 = 0x0B;
|
||||||
|
const PARTID_FAT32_LBA: u8 = 0x0C;
|
||||||
|
|
||||||
|
fn cmd_error_to_io_error(_: CmdTransferError) -> Error {
|
||||||
|
Error::new(ErrorKind::Other, "Command transfer error")
|
||||||
|
}
|
||||||
|
|
||||||
|
const BLOCK_SIZE: usize = 512;
|
||||||
|
|
||||||
|
/// SdReader struct implementing `Read + BufRead + Write + Seek` traits for `core_io`.
|
||||||
|
/// Used as an adaptor for fatfs crate, but could be used directly for raw data access.
|
||||||
|
///
|
||||||
|
/// Implementation: all read/writes would be split into unaligned and block-aligned parts,
|
||||||
|
/// unaligned read/writes would do a buffered read/write using a block-sized internal buffer,
|
||||||
|
/// while aligned transactions would be sent to the SD card directly for performance reason.
|
||||||
|
pub struct SdReader {
|
||||||
|
/// Internal SdCard handle.
|
||||||
|
sd: SdCard,
|
||||||
|
/// Read buffer with the size of 1 block.
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
/// Address for the next byte.
|
||||||
|
byte_addr: u32,
|
||||||
|
/// Internal index for the next byte.
|
||||||
|
/// Normally in range `[0, BLOCK_SIZE - 1]`.
|
||||||
|
///
|
||||||
|
/// `index = BLOCK_SIZE` means that the `buffer` is invalid for the current `byte_addr`,
|
||||||
|
/// the next `fill_buf` call would fill the buffer.
|
||||||
|
index: usize,
|
||||||
|
/// Dirty flag indicating the content has to be flushed.
|
||||||
|
dirty: bool,
|
||||||
|
/// Base offset for translation from logical address to physical address.
|
||||||
|
offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[allow(unused)]
|
||||||
|
// Partition entry enum, normally we would use entry1.
|
||||||
|
pub enum PartitionEntry {
|
||||||
|
Entry1 = 0x1BE,
|
||||||
|
Entry2 = 0x1CE,
|
||||||
|
Entry3 = 0x1DE,
|
||||||
|
Entry4 = 0x1EE,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdReader {
|
||||||
|
/// Create SdReader from SdCard
|
||||||
|
pub fn new(sd: SdCard) -> SdReader {
|
||||||
|
let mut vec: Vec<u8> = Vec::with_capacity(BLOCK_SIZE);
|
||||||
|
unsafe {
|
||||||
|
vec.set_len(vec.capacity());
|
||||||
|
}
|
||||||
|
SdReader {
|
||||||
|
sd,
|
||||||
|
buffer: vec,
|
||||||
|
byte_addr: 0,
|
||||||
|
index: BLOCK_SIZE,
|
||||||
|
dirty: false,
|
||||||
|
offset: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal read function for unaligned read.
|
||||||
|
/// The read must not cross block boundary.
|
||||||
|
fn read_unaligned(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||||
|
if buf.len() == 0 {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
let filled_buffer = self.fill_buf()?;
|
||||||
|
for (dest, src) in buf.iter_mut().zip(filled_buffer.iter()) {
|
||||||
|
*dest = *src;
|
||||||
|
}
|
||||||
|
self.consume(buf.len());
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal write function for unaligned write.
|
||||||
|
/// The write must not cross block boundary.
|
||||||
|
fn write_unaligned(&mut self, buf: &[u8]) -> IoResult<usize> {
|
||||||
|
if buf.len() == 0 {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
// update buffer if needed, as we will flush the entire block later.
|
||||||
|
self.fill_buf()?;
|
||||||
|
self.dirty = true;
|
||||||
|
let dest_buffer = &mut self.buffer[self.index..];
|
||||||
|
for (src, dest) in buf.iter().zip(dest_buffer.iter_mut()) {
|
||||||
|
*dest = *src;
|
||||||
|
}
|
||||||
|
self.consume(buf.len());
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the slice into three segments, with the middle block-aligned.
|
||||||
|
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
|
||||||
|
fn block_align<'b>(&self, buf: &'b [u8]) -> (&'b [u8], &'b [u8], &'b [u8]) {
|
||||||
|
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE);
|
||||||
|
if head_len > buf.len() {
|
||||||
|
(buf, &[], &[])
|
||||||
|
} else {
|
||||||
|
let remaining_length = buf.len() - head_len;
|
||||||
|
let mid_length = remaining_length - remaining_length % BLOCK_SIZE;
|
||||||
|
let (head, remaining) = buf.split_at(head_len);
|
||||||
|
let (mid, tail) = remaining.split_at(mid_length);
|
||||||
|
(head, mid, tail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the mutable slice into three segments, with the middle block-aligned.
|
||||||
|
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
|
||||||
|
fn block_align_mut<'b>(&self, buf: &'b mut [u8]) -> (&'b mut [u8], &'b mut [u8], &'b mut [u8]) {
|
||||||
|
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE);
|
||||||
|
if head_len > buf.len() {
|
||||||
|
(buf, &mut [], &mut [])
|
||||||
|
} else {
|
||||||
|
let remaining_length = buf.len() - head_len;
|
||||||
|
let mid_length = remaining_length - remaining_length % BLOCK_SIZE;
|
||||||
|
let (head, remaining) = buf.split_at_mut(head_len);
|
||||||
|
let (mid, tail) = remaining.split_at_mut(mid_length);
|
||||||
|
(head, mid, tail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invalidate the buffer, so later unaligned read/write would reload the buffer from SD card.
|
||||||
|
fn invalidate_buffer(&mut self) {
|
||||||
|
self.index = BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the base offset of the SD card, to transform from physical address to logical address.
|
||||||
|
fn set_base_offset(&mut self, offset: u32) -> IoResult<u64> {
|
||||||
|
self.offset = offset;
|
||||||
|
self.seek(SeekFrom::Start(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mount fatfs from partition entry, and return the fatfs object if success.
|
||||||
|
/// This takes the ownership of self, so currently there is no way to recover from an error,
|
||||||
|
/// except creating a new SD card instance.
|
||||||
|
pub fn mount_fatfs(mut self, entry: PartitionEntry) -> IoResult<fatfs::FileSystem<Self>> {
|
||||||
|
let mut buffer: [u8; 4] = [0; 4];
|
||||||
|
self.seek(SeekFrom::Start(0x1FE))?;
|
||||||
|
self.read_exact(&mut buffer[..2])?;
|
||||||
|
// check MBR signature
|
||||||
|
if buffer[..2] != MBR_SIGNATURE {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::InvalidData,
|
||||||
|
"Incorrect signature for MBR sector.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Read partition ID.
|
||||||
|
self.seek(SeekFrom::Start(entry as u64 + 0x4))?;
|
||||||
|
self.read_exact(&mut buffer[..1])?;
|
||||||
|
debug!("Partition ID: {:0X}", buffer[0]);
|
||||||
|
match buffer[0] {
|
||||||
|
PARTID_FAT12 | PARTID_FAT16_LESS32M | PARTID_FAT16 |
|
||||||
|
PARTID_FAT32 | PARTID_FAT32_LBA => {}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::InvalidData,
|
||||||
|
"No FAT partition found for the specified entry.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Read LBA
|
||||||
|
self.seek(SeekFrom::Current(0x3))?;
|
||||||
|
self.read_exact(&mut buffer)?;
|
||||||
|
let mut lba: u32 = 0;
|
||||||
|
// Little endian
|
||||||
|
for i in 0..4 {
|
||||||
|
lba |= (buffer[i] as u32) << (i * 8);
|
||||||
|
}
|
||||||
|
// Set to logical address
|
||||||
|
self.set_base_offset(lba * BLOCK_SIZE as u32)?;
|
||||||
|
// setup fatfs
|
||||||
|
fatfs::FileSystem::new(self, fatfs::FsOptions::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for SdReader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||||
|
let total_length = buf.len();
|
||||||
|
let (a, b, c) = self.block_align_mut(buf);
|
||||||
|
self.read_unaligned(a)?;
|
||||||
|
if b.len() > 0 {
|
||||||
|
// invalidate internal buffer
|
||||||
|
self.invalidate_buffer();
|
||||||
|
if let Err(_) = self.sd.read_block(
|
||||||
|
self.byte_addr / BLOCK_SIZE as u32,
|
||||||
|
(b.len() / BLOCK_SIZE) as u16,
|
||||||
|
b,
|
||||||
|
) {
|
||||||
|
// we have to allow partial read, as per the trait required
|
||||||
|
return Ok(a.len());
|
||||||
|
}
|
||||||
|
self.byte_addr += b.len() as u32;
|
||||||
|
}
|
||||||
|
if let Err(_) = self.read_unaligned(c) {
|
||||||
|
// we have to allow partial read, as per the trait required
|
||||||
|
return Ok(a.len() + b.len());
|
||||||
|
}
|
||||||
|
Ok(total_length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufRead for SdReader {
|
||||||
|
fn fill_buf(&mut self) -> IoResult<&[u8]> {
|
||||||
|
if self.index == BLOCK_SIZE {
|
||||||
|
// flush the buffer if it is dirty before overwriting it with new data
|
||||||
|
if self.dirty {
|
||||||
|
self.flush()?;
|
||||||
|
}
|
||||||
|
// reload buffer
|
||||||
|
self.sd
|
||||||
|
.read_block(self.byte_addr / (BLOCK_SIZE as u32), 1, &mut self.buffer)
|
||||||
|
.map_err(cmd_error_to_io_error)?;
|
||||||
|
self.index = (self.byte_addr as usize) % BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
Ok(&self.buffer[self.index..])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
self.index += amt;
|
||||||
|
self.byte_addr += amt as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for SdReader {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
|
||||||
|
let (a, b, c) = self.block_align(buf);
|
||||||
|
self.write_unaligned(a)?;
|
||||||
|
if b.len() > 0 {
|
||||||
|
self.flush()?;
|
||||||
|
self.invalidate_buffer();
|
||||||
|
if let Err(_) = self.sd.write_block(
|
||||||
|
self.byte_addr / BLOCK_SIZE as u32,
|
||||||
|
(b.len() / BLOCK_SIZE) as u16,
|
||||||
|
b,
|
||||||
|
) {
|
||||||
|
return Ok(a.len());
|
||||||
|
}
|
||||||
|
self.byte_addr += b.len() as u32;
|
||||||
|
}
|
||||||
|
if let Err(_) = self.write_unaligned(c) {
|
||||||
|
return Ok(a.len() + b.len());
|
||||||
|
}
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> IoResult<()> {
|
||||||
|
if self.dirty {
|
||||||
|
let block_addr = (self.byte_addr - self.index as u32) / (BLOCK_SIZE as u32);
|
||||||
|
self.sd
|
||||||
|
.write_block(block_addr, 1, &self.buffer)
|
||||||
|
.map_err(cmd_error_to_io_error)?;
|
||||||
|
self.dirty = false;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Seek for SdReader {
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
|
||||||
|
let raw_target = match pos {
|
||||||
|
SeekFrom::Start(x) => self.offset as i64 + x as i64,
|
||||||
|
SeekFrom::Current(x) => self.byte_addr as i64 + x,
|
||||||
|
SeekFrom::End(_) => panic!("SD card does not support seek from end"),
|
||||||
|
};
|
||||||
|
if raw_target < self.offset as i64 || raw_target > core::u32::MAX as i64 {
|
||||||
|
return Err(Error::new(ErrorKind::InvalidInput, "Invalid address"));
|
||||||
|
}
|
||||||
|
let target_byte_addr = raw_target as u32;
|
||||||
|
let address_same_block =
|
||||||
|
self.byte_addr / (BLOCK_SIZE as u32) == target_byte_addr / (BLOCK_SIZE as u32);
|
||||||
|
// if the buffer was invalidated, we consider seek as different block
|
||||||
|
let same_block = address_same_block && self.index != BLOCK_SIZE;
|
||||||
|
if !same_block {
|
||||||
|
self.flush()?;
|
||||||
|
}
|
||||||
|
self.byte_addr = target_byte_addr;
|
||||||
|
self.index = if same_block {
|
||||||
|
target_byte_addr as usize % BLOCK_SIZE
|
||||||
|
} else {
|
||||||
|
// invalidate the buffer as we moved to a different block
|
||||||
|
BLOCK_SIZE
|
||||||
|
};
|
||||||
|
Ok((self.byte_addr - self.offset) as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SdReader {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// just try to flush it, ignore error if any
|
||||||
|
self.flush().unwrap_or(());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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" }
|
|
|
@ -1,6 +0,0 @@
|
||||||
extern crate build_zynq;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
build_zynq::add_linker_script();
|
|
||||||
build_zynq::cfg();
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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(()) }
|
|
||||||
}
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
[package]
|
||||||
|
name = "szl"
|
||||||
|
description = "Simple Zynq Loader"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706"]
|
||||||
|
default = ["target_zc706"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
cstr_core = { version = "0.2", default-features = false }
|
||||||
|
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
||||||
|
libsupport_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", default-features = false, features = ["dummy_irq_handler"] }
|
||||||
|
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = { version = "1.0.1" }
|
|
@ -0,0 +1,49 @@
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
let out = env::var("OUT_DIR").unwrap();
|
||||||
|
let out_dir = &PathBuf::from(&out);
|
||||||
|
|
||||||
|
compile_unlzma();
|
||||||
|
// Put the linker script somewhere the linker can find it
|
||||||
|
File::create(out_dir.join("link.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("link.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out_dir.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 compile_unlzma() {
|
||||||
|
let cfg = &mut cc::Build::new();
|
||||||
|
cfg.compiler("clang");
|
||||||
|
cfg.no_default_flags(true);
|
||||||
|
cfg.warnings(false);
|
||||||
|
|
||||||
|
cfg.flag("-nostdlib");
|
||||||
|
cfg.flag("-ffreestanding");
|
||||||
|
cfg.flag("-fPIC");
|
||||||
|
cfg.flag("-fno-stack-protector");
|
||||||
|
cfg.flag("--target=armv7-none-eabihf");
|
||||||
|
cfg.flag("-Oz");
|
||||||
|
cfg.flag("-flto=full");
|
||||||
|
|
||||||
|
let sources = vec![
|
||||||
|
"unlzma.c",
|
||||||
|
];
|
||||||
|
|
||||||
|
let root = Path::new("./");
|
||||||
|
for src in sources {
|
||||||
|
println!("cargo:rerun-if-changed={}", src);
|
||||||
|
cfg.file(root.join("src").join(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.compile("unlzma");
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
ENTRY(Reset);
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* 256 kB On-Chip Memory */
|
||||||
|
OCM : ORIGIN = 0, LENGTH = 0x30000
|
||||||
|
OCM3 : ORIGIN = 0xFFFF0000, LENGTH = 0x10000
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
KEEP(*(.text.exceptions));
|
||||||
|
*(.text.boot);
|
||||||
|
*(.text .text.*);
|
||||||
|
} > OCM
|
||||||
|
|
||||||
|
.rodata : ALIGN(4)
|
||||||
|
{
|
||||||
|
*(.rodata .rodata.*);
|
||||||
|
} > OCM
|
||||||
|
|
||||||
|
.data : ALIGN(4)
|
||||||
|
{
|
||||||
|
*(.data .data.*);
|
||||||
|
} > OCM
|
||||||
|
|
||||||
|
.bss (NOLOAD) : ALIGN(4)
|
||||||
|
{
|
||||||
|
__bss_start = .;
|
||||||
|
*(.bss .bss.*);
|
||||||
|
. = ALIGN(4);
|
||||||
|
__bss_end = .;
|
||||||
|
} > OCM3
|
||||||
|
|
||||||
|
.heap (NOLOAD) : ALIGN(8)
|
||||||
|
{
|
||||||
|
__heap0_start = .;
|
||||||
|
__heap0_end = .;
|
||||||
|
} > OCM3
|
||||||
|
|
||||||
|
.stack1 (NOLOAD) : ALIGN(8)
|
||||||
|
{
|
||||||
|
__stack1_end = .;
|
||||||
|
. += 0x4000;
|
||||||
|
__stack1_start = .;
|
||||||
|
} > OCM3
|
||||||
|
|
||||||
|
.stack0 (NOLOAD) : ALIGN(8)
|
||||||
|
{
|
||||||
|
__stack0_end = .;
|
||||||
|
. += 0x4000;
|
||||||
|
__stack0_start = .;
|
||||||
|
} > OCM3
|
||||||
|
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
/* Unused exception related info that only wastes space */
|
||||||
|
*(.ARM.exidx);
|
||||||
|
*(.ARM.exidx.*);
|
||||||
|
*(.ARM.extab.*);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(panic_info_message)]
|
||||||
|
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
use log::{debug, info, error};
|
||||||
|
use cstr_core::CStr;
|
||||||
|
|
||||||
|
use libcortex_a9::{
|
||||||
|
enable_fpu,
|
||||||
|
cache::{dcci_slice, iciallu, bpiall},
|
||||||
|
asm::{dsb, isb},
|
||||||
|
};
|
||||||
|
use libboard_zynq::{
|
||||||
|
self as zynq, println,
|
||||||
|
clocks::Clocks, clocks::source::{ClockSource, ArmPll, IoPll},
|
||||||
|
stdio,
|
||||||
|
logger,
|
||||||
|
timer::GlobalTimer,
|
||||||
|
};
|
||||||
|
use libsupport_zynq as _;
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn unlzma_simple(buf: *const u8, in_len: i32,
|
||||||
|
output: *mut u8,
|
||||||
|
error: extern fn(*const u8)) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn lzma_error(message: *const u8) {
|
||||||
|
let msg = unsafe {CStr::from_ptr(message)}.to_str();
|
||||||
|
if let Ok(msg) = msg {
|
||||||
|
println!("LZMA error: {}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("panicked!");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn main_core0() {
|
||||||
|
GlobalTimer::start();
|
||||||
|
logger::init().unwrap();
|
||||||
|
log::set_max_level(log::LevelFilter::Debug);
|
||||||
|
println!(r#"
|
||||||
|
|
||||||
|
__________ __
|
||||||
|
/ ___/__ / / /
|
||||||
|
\__ \ / / / /
|
||||||
|
___/ / / /__/ /___
|
||||||
|
/____/ /____/_____/
|
||||||
|
|
||||||
|
(C) 2020 M-Labs
|
||||||
|
"#);
|
||||||
|
info!("Simple Zynq Loader starting...");
|
||||||
|
|
||||||
|
enable_fpu();
|
||||||
|
debug!("FPU enabled on Core0");
|
||||||
|
|
||||||
|
const CPU_FREQ: u32 = 800_000_000;
|
||||||
|
|
||||||
|
ArmPll::setup(2 * CPU_FREQ);
|
||||||
|
Clocks::set_cpu_freq(CPU_FREQ);
|
||||||
|
IoPll::setup(1_000_000_000);
|
||||||
|
libboard_zynq::stdio::drop_uart(); // reinitialize UART after clocking change
|
||||||
|
let mut ddr = zynq::ddr::DdrRam::new();
|
||||||
|
|
||||||
|
let payload = include_bytes!("../../../build/szl-payload.bin.lzma");
|
||||||
|
info!("decompressing payload");
|
||||||
|
let result = unsafe {
|
||||||
|
unlzma_simple(payload.as_ptr(), payload.len() as i32, ddr.ptr(), lzma_error)
|
||||||
|
};
|
||||||
|
if result < 0 {
|
||||||
|
error!("decompression failed");
|
||||||
|
} else {
|
||||||
|
// Flush data cache entries for all of DDR, including
|
||||||
|
// Memory/Instruction Synchronization Barriers
|
||||||
|
dcci_slice(unsafe {
|
||||||
|
core::slice::from_raw_parts(ddr.ptr::<u8>(), ddr.size())
|
||||||
|
});
|
||||||
|
dsb();
|
||||||
|
iciallu();
|
||||||
|
bpiall();
|
||||||
|
dsb();
|
||||||
|
isb();
|
||||||
|
|
||||||
|
// Start core0 only, for compatibility with FSBL.
|
||||||
|
info!("executing payload");
|
||||||
|
unsafe {
|
||||||
|
(mem::transmute::<*mut u8, fn()>(ddr.ptr::<u8>()))();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn main_core1() {
|
||||||
|
panic!("core1 started but should not have");
|
||||||
|
}
|
|
@ -0,0 +1,670 @@
|
||||||
|
/*
|
||||||
|
*Taken from: Lzma decompressor for Linux kernel. Shamelessly snarfed
|
||||||
|
*from busybox 1.1.1
|
||||||
|
*
|
||||||
|
*Linux kernel adaptation
|
||||||
|
*Copyright (C) 2006 Alain < alain@knaff.lu >
|
||||||
|
*
|
||||||
|
*Based on small lzma deflate implementation/Small range coder
|
||||||
|
*implementation for lzma.
|
||||||
|
*Copyright (C) 2006 Aurelien Jacobs < aurel@gnuage.org >
|
||||||
|
*
|
||||||
|
*Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/)
|
||||||
|
*Copyright (C) 1999-2005 Igor Pavlov
|
||||||
|
*
|
||||||
|
*Copyrights of the parts, see headers below.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*This program is free software; you can redistribute it and/or
|
||||||
|
*modify it under the terms of the GNU Lesser General Public
|
||||||
|
*License as published by the Free Software Foundation; either
|
||||||
|
*version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
*This program is distributed in the hope that it will be useful,
|
||||||
|
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
*Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
*You should have received a copy of the GNU Lesser General Public
|
||||||
|
*License along with this library; if not, write to the Free Software
|
||||||
|
*Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NULL ((void *)0)
|
||||||
|
#define alloca(size) __builtin_alloca(size)
|
||||||
|
#define malloc alloca
|
||||||
|
static inline void free(void *p) {}
|
||||||
|
|
||||||
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
static long long read_int(unsigned char *ptr, int size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
long long ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
ret = (ret << 8) | ptr[size-i-1];
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ENDIAN_CONVERT(x) \
|
||||||
|
x = (typeof(x))read_int((unsigned char *)&x, sizeof(x))
|
||||||
|
|
||||||
|
|
||||||
|
/* Small range coder implementation for lzma.
|
||||||
|
*Copyright (C) 2006 Aurelien Jacobs < aurel@gnuage.org >
|
||||||
|
*
|
||||||
|
*Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/)
|
||||||
|
*Copyright (c) 1999-2005 Igor Pavlov
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LZMA_IOBUF_SIZE 0x10000
|
||||||
|
|
||||||
|
struct rc {
|
||||||
|
int (*fill)(void*, unsigned int);
|
||||||
|
unsigned char *ptr;
|
||||||
|
unsigned char *buffer;
|
||||||
|
unsigned char *buffer_end;
|
||||||
|
int buffer_size;
|
||||||
|
unsigned int code;
|
||||||
|
unsigned int range;
|
||||||
|
unsigned int bound;
|
||||||
|
void (*error)(char *);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define RC_TOP_BITS 24
|
||||||
|
#define RC_MOVE_BITS 5
|
||||||
|
#define RC_MODEL_TOTAL_BITS 11
|
||||||
|
|
||||||
|
|
||||||
|
static int nofill(void *buffer, unsigned int len)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called twice: once at startup and once in rc_normalize() */
|
||||||
|
static void rc_read(struct rc *rc)
|
||||||
|
{
|
||||||
|
rc->buffer_size = rc->fill((char *)rc->buffer, LZMA_IOBUF_SIZE);
|
||||||
|
if (rc->buffer_size <= 0)
|
||||||
|
rc->error("unexpected EOF");
|
||||||
|
rc->ptr = rc->buffer;
|
||||||
|
rc->buffer_end = rc->buffer + rc->buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called once */
|
||||||
|
static inline void rc_init(struct rc *rc,
|
||||||
|
int (*fill)(void*, unsigned int),
|
||||||
|
unsigned char *buffer, int buffer_size)
|
||||||
|
{
|
||||||
|
if (fill)
|
||||||
|
rc->fill = fill;
|
||||||
|
else
|
||||||
|
rc->fill = nofill;
|
||||||
|
rc->buffer = buffer;
|
||||||
|
rc->buffer_size = buffer_size;
|
||||||
|
rc->buffer_end = rc->buffer + rc->buffer_size;
|
||||||
|
rc->ptr = rc->buffer;
|
||||||
|
|
||||||
|
rc->code = 0;
|
||||||
|
rc->range = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void rc_init_code(struct rc *rc)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
if (rc->ptr >= rc->buffer_end)
|
||||||
|
rc_read(rc);
|
||||||
|
rc->code = (rc->code << 8) | *rc->ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Called twice, but one callsite is in inline'd rc_is_bit_0_helper() */
|
||||||
|
static void rc_do_normalize(struct rc *rc)
|
||||||
|
{
|
||||||
|
if (rc->ptr >= rc->buffer_end)
|
||||||
|
rc_read(rc);
|
||||||
|
rc->range <<= 8;
|
||||||
|
rc->code = (rc->code << 8) | *rc->ptr++;
|
||||||
|
}
|
||||||
|
static inline void rc_normalize(struct rc *rc)
|
||||||
|
{
|
||||||
|
if (rc->range < (1 << RC_TOP_BITS))
|
||||||
|
rc_do_normalize(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called 9 times */
|
||||||
|
/* Why rc_is_bit_0_helper exists?
|
||||||
|
*Because we want to always expose (rc->code < rc->bound) to optimizer
|
||||||
|
*/
|
||||||
|
static inline unsigned int rc_is_bit_0_helper(struct rc *rc, unsigned short int *p)
|
||||||
|
{
|
||||||
|
rc_normalize(rc);
|
||||||
|
rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS);
|
||||||
|
return rc->bound;
|
||||||
|
}
|
||||||
|
static inline int rc_is_bit_0(struct rc *rc, unsigned short int *p)
|
||||||
|
{
|
||||||
|
unsigned int t = rc_is_bit_0_helper(rc, p);
|
||||||
|
return rc->code < t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called ~10 times, but very small, thus inlined */
|
||||||
|
static inline void rc_update_bit_0(struct rc *rc, unsigned short int *p)
|
||||||
|
{
|
||||||
|
rc->range = rc->bound;
|
||||||
|
*p += ((1 << RC_MODEL_TOTAL_BITS) - *p) >> RC_MOVE_BITS;
|
||||||
|
}
|
||||||
|
static inline void rc_update_bit_1(struct rc *rc, unsigned short int *p)
|
||||||
|
{
|
||||||
|
rc->range -= rc->bound;
|
||||||
|
rc->code -= rc->bound;
|
||||||
|
*p -= *p >> RC_MOVE_BITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called 4 times in unlzma loop */
|
||||||
|
static int rc_get_bit(struct rc *rc, unsigned short int *p, int *symbol)
|
||||||
|
{
|
||||||
|
if (rc_is_bit_0(rc, p)) {
|
||||||
|
rc_update_bit_0(rc, p);
|
||||||
|
*symbol *= 2;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
rc_update_bit_1(rc, p);
|
||||||
|
*symbol = *symbol * 2 + 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called once */
|
||||||
|
static inline int rc_direct_bit(struct rc *rc)
|
||||||
|
{
|
||||||
|
rc_normalize(rc);
|
||||||
|
rc->range >>= 1;
|
||||||
|
if (rc->code >= rc->range) {
|
||||||
|
rc->code -= rc->range;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called twice */
|
||||||
|
static inline void
|
||||||
|
rc_bit_tree_decode(struct rc *rc, unsigned short int *p, int num_levels, int *symbol)
|
||||||
|
{
|
||||||
|
int i = num_levels;
|
||||||
|
|
||||||
|
*symbol = 1;
|
||||||
|
while (i--)
|
||||||
|
rc_get_bit(rc, p + *symbol, symbol);
|
||||||
|
*symbol -= 1 << num_levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Small lzma deflate implementation.
|
||||||
|
* Copyright (C) 2006 Aurelien Jacobs < aurel@gnuage.org >
|
||||||
|
*
|
||||||
|
* Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/)
|
||||||
|
* Copyright (C) 1999-2005 Igor Pavlov
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
struct lzma_header {
|
||||||
|
unsigned char pos;
|
||||||
|
unsigned int dict_size;
|
||||||
|
unsigned long long int dst_size;
|
||||||
|
} __attribute__ ((packed)) ;
|
||||||
|
|
||||||
|
|
||||||
|
#define LZMA_BASE_SIZE 1846
|
||||||
|
#define LZMA_LIT_SIZE 768
|
||||||
|
|
||||||
|
#define LZMA_NUM_POS_BITS_MAX 4
|
||||||
|
|
||||||
|
#define LZMA_LEN_NUM_LOW_BITS 3
|
||||||
|
#define LZMA_LEN_NUM_MID_BITS 3
|
||||||
|
#define LZMA_LEN_NUM_HIGH_BITS 8
|
||||||
|
|
||||||
|
#define LZMA_LEN_CHOICE 0
|
||||||
|
#define LZMA_LEN_CHOICE_2 (LZMA_LEN_CHOICE + 1)
|
||||||
|
#define LZMA_LEN_LOW (LZMA_LEN_CHOICE_2 + 1)
|
||||||
|
#define LZMA_LEN_MID (LZMA_LEN_LOW \
|
||||||
|
+ (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_LOW_BITS)))
|
||||||
|
#define LZMA_LEN_HIGH (LZMA_LEN_MID \
|
||||||
|
+(1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_MID_BITS)))
|
||||||
|
#define LZMA_NUM_LEN_PROBS (LZMA_LEN_HIGH + (1 << LZMA_LEN_NUM_HIGH_BITS))
|
||||||
|
|
||||||
|
#define LZMA_NUM_STATES 12
|
||||||
|
#define LZMA_NUM_LIT_STATES 7
|
||||||
|
|
||||||
|
#define LZMA_START_POS_MODEL_INDEX 4
|
||||||
|
#define LZMA_END_POS_MODEL_INDEX 14
|
||||||
|
#define LZMA_NUM_FULL_DISTANCES (1 << (LZMA_END_POS_MODEL_INDEX >> 1))
|
||||||
|
|
||||||
|
#define LZMA_NUM_POS_SLOT_BITS 6
|
||||||
|
#define LZMA_NUM_LEN_TO_POS_STATES 4
|
||||||
|
|
||||||
|
#define LZMA_NUM_ALIGN_BITS 4
|
||||||
|
|
||||||
|
#define LZMA_MATCH_MIN_LEN 2
|
||||||
|
|
||||||
|
#define LZMA_IS_MATCH 0
|
||||||
|
#define LZMA_IS_REP (LZMA_IS_MATCH + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX))
|
||||||
|
#define LZMA_IS_REP_G0 (LZMA_IS_REP + LZMA_NUM_STATES)
|
||||||
|
#define LZMA_IS_REP_G1 (LZMA_IS_REP_G0 + LZMA_NUM_STATES)
|
||||||
|
#define LZMA_IS_REP_G2 (LZMA_IS_REP_G1 + LZMA_NUM_STATES)
|
||||||
|
#define LZMA_IS_REP_0_LONG (LZMA_IS_REP_G2 + LZMA_NUM_STATES)
|
||||||
|
#define LZMA_POS_SLOT (LZMA_IS_REP_0_LONG \
|
||||||
|
+ (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX))
|
||||||
|
#define LZMA_SPEC_POS (LZMA_POS_SLOT \
|
||||||
|
+(LZMA_NUM_LEN_TO_POS_STATES << LZMA_NUM_POS_SLOT_BITS))
|
||||||
|
#define LZMA_ALIGN (LZMA_SPEC_POS \
|
||||||
|
+ LZMA_NUM_FULL_DISTANCES - LZMA_END_POS_MODEL_INDEX)
|
||||||
|
#define LZMA_LEN_CODER (LZMA_ALIGN + (1 << LZMA_NUM_ALIGN_BITS))
|
||||||
|
#define LZMA_REP_LEN_CODER (LZMA_LEN_CODER + LZMA_NUM_LEN_PROBS)
|
||||||
|
#define LZMA_LITERAL (LZMA_REP_LEN_CODER + LZMA_NUM_LEN_PROBS)
|
||||||
|
|
||||||
|
|
||||||
|
struct writer {
|
||||||
|
unsigned char *buffer;
|
||||||
|
unsigned char previous_byte;
|
||||||
|
int buffer_pos;
|
||||||
|
int bufsize;
|
||||||
|
int global_pos;
|
||||||
|
int(*flush)(void*, unsigned int);
|
||||||
|
struct lzma_header *header;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cstate {
|
||||||
|
int state;
|
||||||
|
unsigned int rep0, rep1, rep2, rep3;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int get_pos(struct writer *wr)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
wr->global_pos + wr->buffer_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned char peek_old_byte(struct writer *wr,
|
||||||
|
unsigned int offs)
|
||||||
|
{
|
||||||
|
if (!wr->flush) {
|
||||||
|
int pos;
|
||||||
|
while (offs > wr->header->dict_size)
|
||||||
|
offs -= wr->header->dict_size;
|
||||||
|
pos = wr->buffer_pos - offs;
|
||||||
|
return wr->buffer[pos];
|
||||||
|
} else {
|
||||||
|
unsigned int pos = wr->buffer_pos - offs;
|
||||||
|
while (pos >= wr->header->dict_size)
|
||||||
|
pos += wr->header->dict_size;
|
||||||
|
return wr->buffer[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int write_byte(struct writer *wr, unsigned char byte)
|
||||||
|
{
|
||||||
|
wr->buffer[wr->buffer_pos++] = wr->previous_byte = byte;
|
||||||
|
if (wr->flush && wr->buffer_pos == wr->header->dict_size) {
|
||||||
|
wr->buffer_pos = 0;
|
||||||
|
wr->global_pos += wr->header->dict_size;
|
||||||
|
if (wr->flush((char *)wr->buffer, wr->header->dict_size)
|
||||||
|
!= wr->header->dict_size)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int copy_byte(struct writer *wr, unsigned int offs)
|
||||||
|
{
|
||||||
|
return write_byte(wr, peek_old_byte(wr, offs));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int copy_bytes(struct writer *wr,
|
||||||
|
unsigned int rep0, int len)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
if (copy_byte(wr, rep0))
|
||||||
|
return -1;
|
||||||
|
len--;
|
||||||
|
} while (len != 0 && wr->buffer_pos < wr->header->dst_size);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int process_bit0(struct writer *wr, struct rc *rc,
|
||||||
|
struct cstate *cst, unsigned short int *p,
|
||||||
|
int pos_state, unsigned short int *prob,
|
||||||
|
int lc, unsigned int literal_pos_mask) {
|
||||||
|
int mi = 1;
|
||||||
|
rc_update_bit_0(rc, prob);
|
||||||
|
prob = (p + LZMA_LITERAL +
|
||||||
|
(LZMA_LIT_SIZE
|
||||||
|
* (((get_pos(wr) & literal_pos_mask) << lc)
|
||||||
|
+ (wr->previous_byte >> (8 - lc))))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cst->state >= LZMA_NUM_LIT_STATES) {
|
||||||
|
int match_byte = peek_old_byte(wr, cst->rep0);
|
||||||
|
do {
|
||||||
|
int bit;
|
||||||
|
unsigned short int *prob_lit;
|
||||||
|
|
||||||
|
match_byte <<= 1;
|
||||||
|
bit = match_byte & 0x100;
|
||||||
|
prob_lit = prob + 0x100 + bit + mi;
|
||||||
|
if (rc_get_bit(rc, prob_lit, &mi)) {
|
||||||
|
if (!bit)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (bit)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (mi < 0x100);
|
||||||
|
}
|
||||||
|
while (mi < 0x100) {
|
||||||
|
unsigned short int *prob_lit = prob + mi;
|
||||||
|
rc_get_bit(rc, prob_lit, &mi);
|
||||||
|
}
|
||||||
|
if (cst->state < 4)
|
||||||
|
cst->state = 0;
|
||||||
|
else if (cst->state < 10)
|
||||||
|
cst->state -= 3;
|
||||||
|
else
|
||||||
|
cst->state -= 6;
|
||||||
|
|
||||||
|
return write_byte(wr, mi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int process_bit1(struct writer *wr, struct rc *rc,
|
||||||
|
struct cstate *cst, unsigned short int *p,
|
||||||
|
int pos_state, unsigned short int *prob) {
|
||||||
|
int offset;
|
||||||
|
unsigned short int *prob_len;
|
||||||
|
int num_bits;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
rc_update_bit_1(rc, prob);
|
||||||
|
prob = p + LZMA_IS_REP + cst->state;
|
||||||
|
if (rc_is_bit_0(rc, prob)) {
|
||||||
|
rc_update_bit_0(rc, prob);
|
||||||
|
cst->rep3 = cst->rep2;
|
||||||
|
cst->rep2 = cst->rep1;
|
||||||
|
cst->rep1 = cst->rep0;
|
||||||
|
cst->state = cst->state < LZMA_NUM_LIT_STATES ? 0 : 3;
|
||||||
|
prob = p + LZMA_LEN_CODER;
|
||||||
|
} else {
|
||||||
|
rc_update_bit_1(rc, prob);
|
||||||
|
prob = p + LZMA_IS_REP_G0 + cst->state;
|
||||||
|
if (rc_is_bit_0(rc, prob)) {
|
||||||
|
rc_update_bit_0(rc, prob);
|
||||||
|
prob = (p + LZMA_IS_REP_0_LONG
|
||||||
|
+ (cst->state <<
|
||||||
|
LZMA_NUM_POS_BITS_MAX) +
|
||||||
|
pos_state);
|
||||||
|
if (rc_is_bit_0(rc, prob)) {
|
||||||
|
rc_update_bit_0(rc, prob);
|
||||||
|
|
||||||
|
cst->state = cst->state < LZMA_NUM_LIT_STATES ?
|
||||||
|
9 : 11;
|
||||||
|
return copy_byte(wr, cst->rep0);
|
||||||
|
} else {
|
||||||
|
rc_update_bit_1(rc, prob);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsigned int distance;
|
||||||
|
|
||||||
|
rc_update_bit_1(rc, prob);
|
||||||
|
prob = p + LZMA_IS_REP_G1 + cst->state;
|
||||||
|
if (rc_is_bit_0(rc, prob)) {
|
||||||
|
rc_update_bit_0(rc, prob);
|
||||||
|
distance = cst->rep1;
|
||||||
|
} else {
|
||||||
|
rc_update_bit_1(rc, prob);
|
||||||
|
prob = p + LZMA_IS_REP_G2 + cst->state;
|
||||||
|
if (rc_is_bit_0(rc, prob)) {
|
||||||
|
rc_update_bit_0(rc, prob);
|
||||||
|
distance = cst->rep2;
|
||||||
|
} else {
|
||||||
|
rc_update_bit_1(rc, prob);
|
||||||
|
distance = cst->rep3;
|
||||||
|
cst->rep3 = cst->rep2;
|
||||||
|
}
|
||||||
|
cst->rep2 = cst->rep1;
|
||||||
|
}
|
||||||
|
cst->rep1 = cst->rep0;
|
||||||
|
cst->rep0 = distance;
|
||||||
|
}
|
||||||
|
cst->state = cst->state < LZMA_NUM_LIT_STATES ? 8 : 11;
|
||||||
|
prob = p + LZMA_REP_LEN_CODER;
|
||||||
|
}
|
||||||
|
|
||||||
|
prob_len = prob + LZMA_LEN_CHOICE;
|
||||||
|
if (rc_is_bit_0(rc, prob_len)) {
|
||||||
|
rc_update_bit_0(rc, prob_len);
|
||||||
|
prob_len = (prob + LZMA_LEN_LOW
|
||||||
|
+ (pos_state <<
|
||||||
|
LZMA_LEN_NUM_LOW_BITS));
|
||||||
|
offset = 0;
|
||||||
|
num_bits = LZMA_LEN_NUM_LOW_BITS;
|
||||||
|
} else {
|
||||||
|
rc_update_bit_1(rc, prob_len);
|
||||||
|
prob_len = prob + LZMA_LEN_CHOICE_2;
|
||||||
|
if (rc_is_bit_0(rc, prob_len)) {
|
||||||
|
rc_update_bit_0(rc, prob_len);
|
||||||
|
prob_len = (prob + LZMA_LEN_MID
|
||||||
|
+ (pos_state <<
|
||||||
|
LZMA_LEN_NUM_MID_BITS));
|
||||||
|
offset = 1 << LZMA_LEN_NUM_LOW_BITS;
|
||||||
|
num_bits = LZMA_LEN_NUM_MID_BITS;
|
||||||
|
} else {
|
||||||
|
rc_update_bit_1(rc, prob_len);
|
||||||
|
prob_len = prob + LZMA_LEN_HIGH;
|
||||||
|
offset = ((1 << LZMA_LEN_NUM_LOW_BITS)
|
||||||
|
+ (1 << LZMA_LEN_NUM_MID_BITS));
|
||||||
|
num_bits = LZMA_LEN_NUM_HIGH_BITS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_bit_tree_decode(rc, prob_len, num_bits, &len);
|
||||||
|
len += offset;
|
||||||
|
|
||||||
|
if (cst->state < 4) {
|
||||||
|
int pos_slot;
|
||||||
|
|
||||||
|
cst->state += LZMA_NUM_LIT_STATES;
|
||||||
|
prob =
|
||||||
|
p + LZMA_POS_SLOT +
|
||||||
|
((len <
|
||||||
|
LZMA_NUM_LEN_TO_POS_STATES ? len :
|
||||||
|
LZMA_NUM_LEN_TO_POS_STATES - 1)
|
||||||
|
<< LZMA_NUM_POS_SLOT_BITS);
|
||||||
|
rc_bit_tree_decode(rc, prob,
|
||||||
|
LZMA_NUM_POS_SLOT_BITS,
|
||||||
|
&pos_slot);
|
||||||
|
if (pos_slot >= LZMA_START_POS_MODEL_INDEX) {
|
||||||
|
int i, mi;
|
||||||
|
num_bits = (pos_slot >> 1) - 1;
|
||||||
|
cst->rep0 = 2 | (pos_slot & 1);
|
||||||
|
if (pos_slot < LZMA_END_POS_MODEL_INDEX) {
|
||||||
|
cst->rep0 <<= num_bits;
|
||||||
|
prob = p + LZMA_SPEC_POS +
|
||||||
|
cst->rep0 - pos_slot - 1;
|
||||||
|
} else {
|
||||||
|
num_bits -= LZMA_NUM_ALIGN_BITS;
|
||||||
|
while (num_bits--)
|
||||||
|
cst->rep0 = (cst->rep0 << 1) |
|
||||||
|
rc_direct_bit(rc);
|
||||||
|
prob = p + LZMA_ALIGN;
|
||||||
|
cst->rep0 <<= LZMA_NUM_ALIGN_BITS;
|
||||||
|
num_bits = LZMA_NUM_ALIGN_BITS;
|
||||||
|
}
|
||||||
|
i = 1;
|
||||||
|
mi = 1;
|
||||||
|
while (num_bits--) {
|
||||||
|
if (rc_get_bit(rc, prob + mi, &mi))
|
||||||
|
cst->rep0 |= i;
|
||||||
|
i <<= 1;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
cst->rep0 = pos_slot;
|
||||||
|
if (++(cst->rep0) == 0)
|
||||||
|
return 0;
|
||||||
|
if (cst->rep0 > wr->header->dict_size
|
||||||
|
|| cst->rep0 > get_pos(wr))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += LZMA_MATCH_MIN_LEN;
|
||||||
|
|
||||||
|
return copy_bytes(wr, cst->rep0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int unlzma(unsigned char *buf, int in_len,
|
||||||
|
int(*fill)(void*, unsigned int),
|
||||||
|
int(*flush)(void*, unsigned int),
|
||||||
|
unsigned char *output,
|
||||||
|
int *posp,
|
||||||
|
void(*error)(char *x)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
struct lzma_header header;
|
||||||
|
int lc, pb, lp;
|
||||||
|
unsigned int pos_state_mask;
|
||||||
|
unsigned int literal_pos_mask;
|
||||||
|
unsigned short int *p;
|
||||||
|
int num_probs;
|
||||||
|
struct rc rc;
|
||||||
|
int i, mi;
|
||||||
|
struct writer wr;
|
||||||
|
struct cstate cst;
|
||||||
|
unsigned char *inbuf;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
rc.error = error;
|
||||||
|
|
||||||
|
if (buf)
|
||||||
|
inbuf = buf;
|
||||||
|
else
|
||||||
|
inbuf = malloc(LZMA_IOBUF_SIZE);
|
||||||
|
if (!inbuf) {
|
||||||
|
error("Could not allocate input bufer");
|
||||||
|
goto exit_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cst.state = 0;
|
||||||
|
cst.rep0 = cst.rep1 = cst.rep2 = cst.rep3 = 1;
|
||||||
|
|
||||||
|
wr.header = &header;
|
||||||
|
wr.flush = flush;
|
||||||
|
wr.global_pos = 0;
|
||||||
|
wr.previous_byte = 0;
|
||||||
|
wr.buffer_pos = 0;
|
||||||
|
|
||||||
|
rc_init(&rc, fill, inbuf, in_len);
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(header); i++) {
|
||||||
|
if (rc.ptr >= rc.buffer_end)
|
||||||
|
rc_read(&rc);
|
||||||
|
((unsigned char *)&header)[i] = *rc.ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.pos >= (9 * 5 * 5)) {
|
||||||
|
error("bad header");
|
||||||
|
goto exit_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mi = 0;
|
||||||
|
lc = header.pos;
|
||||||
|
while (lc >= 9) {
|
||||||
|
mi++;
|
||||||
|
lc -= 9;
|
||||||
|
}
|
||||||
|
pb = 0;
|
||||||
|
lp = mi;
|
||||||
|
while (lp >= 5) {
|
||||||
|
pb++;
|
||||||
|
lp -= 5;
|
||||||
|
}
|
||||||
|
pos_state_mask = (1 << pb) - 1;
|
||||||
|
literal_pos_mask = (1 << lp) - 1;
|
||||||
|
|
||||||
|
ENDIAN_CONVERT(header.dict_size);
|
||||||
|
ENDIAN_CONVERT(header.dst_size);
|
||||||
|
|
||||||
|
if (header.dict_size == 0)
|
||||||
|
header.dict_size = 1;
|
||||||
|
|
||||||
|
if (output)
|
||||||
|
wr.buffer = output;
|
||||||
|
else {
|
||||||
|
wr.bufsize = MIN(header.dst_size, header.dict_size);
|
||||||
|
wr.buffer = malloc(wr.bufsize);
|
||||||
|
}
|
||||||
|
if (wr.buffer == NULL)
|
||||||
|
goto exit_1;
|
||||||
|
|
||||||
|
num_probs = LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp));
|
||||||
|
p = (unsigned short int *) malloc(num_probs * sizeof(*p));
|
||||||
|
if (p == 0)
|
||||||
|
goto exit_2;
|
||||||
|
num_probs = LZMA_LITERAL + (LZMA_LIT_SIZE << (lc + lp));
|
||||||
|
for (i = 0; i < num_probs; i++)
|
||||||
|
p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1;
|
||||||
|
|
||||||
|
rc_init_code(&rc);
|
||||||
|
|
||||||
|
while (get_pos(&wr) < header.dst_size) {
|
||||||
|
int pos_state = get_pos(&wr) & pos_state_mask;
|
||||||
|
unsigned short int *prob = p + LZMA_IS_MATCH +
|
||||||
|
(cst.state << LZMA_NUM_POS_BITS_MAX) + pos_state;
|
||||||
|
if (rc_is_bit_0(&rc, prob)) {
|
||||||
|
if (process_bit0(&wr, &rc, &cst, p, pos_state, prob,
|
||||||
|
lc, literal_pos_mask)) {
|
||||||
|
error("LZMA data is corrupt");
|
||||||
|
goto exit_3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (process_bit1(&wr, &rc, &cst, p, pos_state, prob)) {
|
||||||
|
error("LZMA data is corrupt");
|
||||||
|
goto exit_3;
|
||||||
|
}
|
||||||
|
if (cst.rep0 == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (rc.buffer_size <= 0)
|
||||||
|
goto exit_3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (posp)
|
||||||
|
*posp = rc.ptr-rc.buffer;
|
||||||
|
if (!wr.flush || wr.flush(wr.buffer, wr.buffer_pos) == wr.buffer_pos)
|
||||||
|
ret = 0;
|
||||||
|
exit_3:
|
||||||
|
free(p);
|
||||||
|
exit_2:
|
||||||
|
if (!output)
|
||||||
|
free(wr.buffer);
|
||||||
|
exit_1:
|
||||||
|
if (!buf)
|
||||||
|
free(inbuf);
|
||||||
|
exit_0:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unlzma_simple(unsigned char *buf, int in_len,
|
||||||
|
unsigned char *output,
|
||||||
|
void(*error)(char *x))
|
||||||
|
{
|
||||||
|
return unlzma(buf, in_len, NULL, NULL, output, NULL, error);
|
||||||
|
}
|
Loading…
Reference in New Issue